diff options
Diffstat (limited to 'source/blender')
498 files changed, 9474 insertions, 4898 deletions
diff --git a/source/blender/blenfont/intern/blf.c b/source/blender/blenfont/intern/blf.c index a673f4a1bc7..d4f5be617fd 100644 --- a/source/blender/blenfont/intern/blf.c +++ b/source/blender/blenfont/intern/blf.c @@ -162,6 +162,14 @@ int BLF_load_unique(const char *name) } FontBLF *font = blf_font_new(name, filepath); + + /* XXX: Temporarily disable kerning in our main font. Kerning had been accidentally removed from + * our font in 3.1. In 3.4 we disable kerning here in the new version to keep spacing the same + * (T101506). Enable again later with change of font, placement, or rendering - Harley. */ + if (font && BLI_str_endswith(filepath, BLF_DEFAULT_PROPORTIONAL_FONT)) { + font->face_flags &= ~FT_FACE_FLAG_KERNING; + } + MEM_freeN(filepath); if (!font) { diff --git a/source/blender/blenfont/intern/blf_dir.c b/source/blender/blenfont/intern/blf_dir.c index 8534a8c583f..395d981cb0b 100644 --- a/source/blender/blenfont/intern/blf_dir.c +++ b/source/blender/blenfont/intern/blf_dir.c @@ -115,7 +115,7 @@ char *blf_dir_search(const char *file) char *s = NULL; for (dir = global_font_dir.first; dir; dir = dir->next) { - BLI_join_dirfile(full_path, sizeof(full_path), dir->path, file); + BLI_path_join(full_path, sizeof(full_path), dir->path, file); if (BLI_exists(full_path)) { s = BLI_strdup(full_path); break; diff --git a/source/blender/blenfont/intern/blf_font_default.c b/source/blender/blenfont/intern/blf_font_default.c index d35692f6eae..63b1cf34db5 100644 --- a/source/blender/blenfont/intern/blf_font_default.c +++ b/source/blender/blenfont/intern/blf_font_default.c @@ -32,7 +32,7 @@ static int blf_load_font_default(const char *filename, const bool unique) } char filepath[FILE_MAX]; - BLI_join_dirfile(filepath, sizeof(filepath), dir, filename); + BLI_path_join(filepath, sizeof(filepath), dir, filename); return (unique) ? BLF_load_unique(filepath) : BLF_load(filepath); } diff --git a/source/blender/blenkernel/BKE_attribute.hh b/source/blender/blenkernel/BKE_attribute.hh index 7b13b8a2b09..a4f9d73c31e 100644 --- a/source/blender/blenkernel/BKE_attribute.hh +++ b/source/blender/blenkernel/BKE_attribute.hh @@ -793,7 +793,9 @@ class CustomDataAttributes { ~CustomDataAttributes(); CustomDataAttributes(const CustomDataAttributes &other); CustomDataAttributes(CustomDataAttributes &&other); + CustomDataAttributes &operator=(const CustomDataAttributes &other); + CustomDataAttributes &operator=(CustomDataAttributes &&other); void reallocate(int size); diff --git a/source/blender/blenkernel/BKE_blender_version.h b/source/blender/blenkernel/BKE_blender_version.h index 3c8f7d758b6..806fff2099e 100644 --- a/source/blender/blenkernel/BKE_blender_version.h +++ b/source/blender/blenkernel/BKE_blender_version.h @@ -25,7 +25,7 @@ extern "C" { /* Blender file format version. */ #define BLENDER_FILE_VERSION BLENDER_VERSION -#define BLENDER_FILE_SUBVERSION 3 +#define BLENDER_FILE_SUBVERSION 5 /* 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_brush.h b/source/blender/blenkernel/BKE_brush.h index 4d728002c87..a763b3d12c2 100644 --- a/source/blender/blenkernel/BKE_brush.h +++ b/source/blender/blenkernel/BKE_brush.h @@ -18,7 +18,9 @@ extern "C" { struct Brush; struct ImBuf; struct ImagePool; +struct Object; struct Main; +struct MTex; struct Scene; struct ToolSettings; struct UnifiedPaintSettings; @@ -109,6 +111,7 @@ float BKE_brush_curve_strength(const struct Brush *br, float p, float len); */ float BKE_brush_sample_tex_3d(const struct Scene *scene, const struct Brush *br, + const struct MTex *mtex, const float point[3], float rgba[4], int thread, @@ -120,6 +123,24 @@ float BKE_brush_sample_masktex(const struct Scene *scene, struct ImagePool *pool); /** + * Get the mask texture for this given object mode. + * + * This is preferred above using mtex/mask_mtex attributes directly as due to legacy these + * attributes got switched in sculpt mode. + */ +const struct MTex *BKE_brush_mask_texture_get(const struct Brush *brush, + const eObjectMode object_mode); + +/** + * Get the color texture for this given object mode. + * + * This is preferred above using mtex/mask_mtex attributes directly as due to legacy these + * attributes got switched in sculpt mode. + */ +const struct MTex *BKE_brush_color_texture_get(const struct Brush *brush, + const eObjectMode object_mode); + +/** * Radial control. */ struct ImBuf *BKE_brush_gen_radial_control_imbuf(struct Brush *br, diff --git a/source/blender/blenkernel/BKE_bvhutils.h b/source/blender/blenkernel/BKE_bvhutils.h index d22abd235df..a0a6ac58c58 100644 --- a/source/blender/blenkernel/BKE_bvhutils.h +++ b/source/blender/blenkernel/BKE_bvhutils.h @@ -11,6 +11,10 @@ #include "BLI_threads.h" #ifdef __cplusplus +# include <mutex> +#endif + +#ifdef __cplusplus extern "C" { #endif @@ -196,6 +200,8 @@ BVHTree *BKE_bvhtree_from_mesh_get(struct BVHTreeFromMesh *data, BVHCacheType bvh_cache_type, int tree_type); +#ifdef __cplusplus + /** * Builds or queries a BVH-cache for the cache BVH-tree of the request type. */ @@ -204,7 +210,9 @@ BVHTree *BKE_bvhtree_from_editmesh_get(BVHTreeFromEditMesh *data, int tree_type, BVHCacheType bvh_cache_type, struct BVHCache **bvh_cache_p, - ThreadMutex *mesh_eval_mutex); + std::mutex *mesh_eval_mutex); + +#endif /** * Frees data allocated by a call to `bvhtree_from_editmesh_*`. diff --git a/source/blender/blenkernel/BKE_customdata.h b/source/blender/blenkernel/BKE_customdata.h index 61f3a0e1d5e..f761e28cbb4 100644 --- a/source/blender/blenkernel/BKE_customdata.h +++ b/source/blender/blenkernel/BKE_customdata.h @@ -146,15 +146,6 @@ void CustomData_copy(const struct CustomData *source, eCDAllocType alloctype, int totelem); -/** - * Like #CustomData_copy but skips copying layers that are stored as flags on #BMesh. - */ -void CustomData_copy_mesh_to_bmesh(const struct CustomData *source, - struct CustomData *dest, - eCustomDataMask mask, - eCDAllocType alloctype, - int totelem); - /* BMESH_TODO, not really a public function but readfile.c needs it */ void CustomData_update_typemap(struct CustomData *data); @@ -169,15 +160,6 @@ bool CustomData_merge(const struct CustomData *source, int totelem); /** - * Like #CustomData_copy but skips copying layers that are stored as flags on #BMesh. - */ -bool CustomData_merge_mesh_to_bmesh(const struct CustomData *source, - struct CustomData *dest, - eCustomDataMask mask, - eCDAllocType alloctype, - int totelem); - -/** * Reallocate custom data to a new element count. If the new size is larger, the new values use * the #CD_CONSTRUCT behavior, so trivial types must be initialized by the caller. After being * resized, the #CustomData does not contain any referenced layers. @@ -197,6 +179,14 @@ bool CustomData_bmesh_merge(const struct CustomData *source, char htype); /** + * Remove layers that aren't stored in BMesh or are stored as flags on BMesh. + * The `layers` array of the returned #CustomData must be freed, but may be null. + * Used during conversion of #Mesh data to #BMesh storage format. + */ +CustomData CustomData_shallow_copy_remove_non_bmesh_attributes(const CustomData *src, + eCustomDataMask mask); + +/** * NULL's all members and resets the #CustomData.typemap. */ void CustomData_reset(struct CustomData *data); diff --git a/source/blender/blenkernel/BKE_editmesh_cache.h b/source/blender/blenkernel/BKE_editmesh_cache.h index 2640356ccf6..454a07be4e3 100644 --- a/source/blender/blenkernel/BKE_editmesh_cache.h +++ b/source/blender/blenkernel/BKE_editmesh_cache.h @@ -11,15 +11,25 @@ extern "C" { #endif struct BMEditMesh; -struct EditMeshData; -void BKE_editmesh_cache_ensure_poly_normals(struct BMEditMesh *em, struct EditMeshData *emd); -void BKE_editmesh_cache_ensure_vert_normals(struct BMEditMesh *em, struct EditMeshData *emd); +typedef struct EditMeshData { + /** when set, \a vertexNos, polyNos are lazy initialized */ + const float (*vertexCos)[3]; -void BKE_editmesh_cache_ensure_poly_centers(struct BMEditMesh *em, struct EditMeshData *emd); + /** lazy initialize (when \a vertexCos is set) */ + float const (*vertexNos)[3]; + float const (*polyNos)[3]; + /** also lazy init but don't depend on \a vertexCos */ + const float (*polyCos)[3]; +} EditMeshData; + +void BKE_editmesh_cache_ensure_poly_normals(struct BMEditMesh *em, EditMeshData *emd); +void BKE_editmesh_cache_ensure_vert_normals(struct BMEditMesh *em, EditMeshData *emd); + +void BKE_editmesh_cache_ensure_poly_centers(struct BMEditMesh *em, EditMeshData *emd); bool BKE_editmesh_cache_calc_minmax(struct BMEditMesh *em, - struct EditMeshData *emd, + EditMeshData *emd, float min[3], float max[3]); diff --git a/source/blender/blenkernel/BKE_geometry_fields.hh b/source/blender/blenkernel/BKE_geometry_fields.hh index 988e0017f04..2eef67dba98 100644 --- a/source/blender/blenkernel/BKE_geometry_fields.hh +++ b/source/blender/blenkernel/BKE_geometry_fields.hh @@ -75,14 +75,14 @@ class PointCloudFieldContext : public fn::FieldContext { class InstancesFieldContext : public fn::FieldContext { private: - const InstancesComponent &instances_; + const Instances &instances_; public: - InstancesFieldContext(const InstancesComponent &instances) : instances_(instances) + InstancesFieldContext(const Instances &instances) : instances_(instances) { } - const InstancesComponent &instances() const + const Instances &instances() const { return instances_; } @@ -128,13 +128,13 @@ class GeometryFieldContext : public fn::FieldContext { const Mesh *mesh() const; const CurvesGeometry *curves() const; const PointCloud *pointcloud() const; - const InstancesComponent *instances() const; + const Instances *instances() const; private: GeometryFieldContext(const Mesh &mesh, eAttrDomain domain); GeometryFieldContext(const CurvesGeometry &curves, eAttrDomain domain); GeometryFieldContext(const PointCloud &points); - GeometryFieldContext(const InstancesComponent &instances); + GeometryFieldContext(const Instances &instances); }; class GeometryFieldInput : public fn::FieldInput { @@ -187,8 +187,7 @@ class InstancesFieldInput : public fn::FieldInput { GVArray get_varray_for_context(const fn::FieldContext &context, IndexMask mask, ResourceScope &scope) const override; - virtual GVArray get_varray_for_context(const InstancesComponent &instances, - IndexMask mask) const = 0; + virtual GVArray get_varray_for_context(const Instances &instances, IndexMask mask) const = 0; }; class AttributeFieldInput : public GeometryFieldInput { diff --git a/source/blender/blenkernel/BKE_geometry_set.hh b/source/blender/blenkernel/BKE_geometry_set.hh index 2ef9556afc7..b488806c8e7 100644 --- a/source/blender/blenkernel/BKE_geometry_set.hh +++ b/source/blender/blenkernel/BKE_geometry_set.hh @@ -43,6 +43,7 @@ enum class GeometryOwnershipType { namespace blender::bke { class ComponentAttributeProviders; class CurvesEditHints; +class Instances; } // namespace blender::bke class GeometryComponent; @@ -246,6 +247,12 @@ struct GeometrySet { */ static GeometrySet create_with_curves( Curves *curves, GeometryOwnershipType ownership = GeometryOwnershipType::Owned); + /** + * Create a new geometry set that only contains the given instances. + */ + static GeometrySet create_with_instances( + blender::bke::Instances *instances, + GeometryOwnershipType ownership = GeometryOwnershipType::Owned); /* Utility methods for access. */ /** @@ -294,6 +301,10 @@ struct GeometrySet { */ const Curves *get_curves_for_read() const; /** + * Returns read-only instances or null. + */ + const blender::bke::Instances *get_instances_for_read() const; + /** * Returns read-only curve edit hints or null. */ const blender::bke::CurvesEditHints *get_curve_edit_hints_for_read() const; @@ -315,6 +326,10 @@ struct GeometrySet { */ Curves *get_curves_for_write(); /** + * Returns mutable instances or null. No ownership is transferred. + */ + blender::bke::Instances *get_instances_for_write(); + /** * Returns mutable curve edit hints or null. */ blender::bke::CurvesEditHints *get_curve_edit_hints_for_write(); @@ -339,6 +354,11 @@ struct GeometrySet { */ void replace_curves(Curves *curves, GeometryOwnershipType ownership = GeometryOwnershipType::Owned); + /** + * Clear the existing instances and replace them with the given one. + */ + void replace_instances(blender::bke::Instances *instances, + GeometryOwnershipType ownership = GeometryOwnershipType::Owned); private: /** @@ -515,244 +535,35 @@ class CurveComponent : public GeometryComponent { }; /** - * Holds a reference to conceptually unique geometry or a pointer to object/collection data - * that is instanced with a transform in #InstancesComponent. - */ -class InstanceReference { - public: - enum class Type { - /** - * An empty instance. This allows an `InstanceReference` to be default constructed without - * being in an invalid state. There might also be other use cases that we haven't explored much - * yet (such as changing the instance later on, and "disabling" some instances). - */ - None, - Object, - Collection, - 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; - - InstanceReference(Object &object) : type_(Type::Object), data_(&object) - { - } - - InstanceReference(Collection &collection) : type_(Type::Collection), data_(&collection) - { - } - - InstanceReference(GeometrySet geometry_set) - : type_(Type::GeometrySet), - geometry_set_(std::make_unique<GeometrySet>(std::move(geometry_set))) - { - } - - InstanceReference(const InstanceReference &other) : type_(other.type_), data_(other.data_) - { - if (other.geometry_set_) { - geometry_set_ = std::make_unique<GeometrySet>(*other.geometry_set_); - } - } - - InstanceReference(InstanceReference &&other) - : type_(other.type_), data_(other.data_), geometry_set_(std::move(other.geometry_set_)) - { - other.type_ = Type::None; - other.data_ = nullptr; - } - - InstanceReference &operator=(const InstanceReference &other) - { - if (this == &other) { - return *this; - } - this->~InstanceReference(); - new (this) InstanceReference(other); - return *this; - } - - InstanceReference &operator=(InstanceReference &&other) - { - if (this == &other) { - return *this; - } - this->~InstanceReference(); - new (this) InstanceReference(std::move(other)); - return *this; - } - - Type type() const - { - return type_; - } - - Object &object() const - { - BLI_assert(type_ == Type::Object); - return *(Object *)data_; - } - - Collection &collection() const - { - BLI_assert(type_ == Type::Collection); - return *(Collection *)data_; - } - - 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_2(data_, geometry_set_.get()); - } - - friend bool operator==(const InstanceReference &a, const InstanceReference &b) - { - return a.data_ == b.data_ && a.geometry_set_.get() == b.geometry_set_.get(); - } -}; - -/** - * A geometry component that stores instances. The instance data can be any type described by - * #InstanceReference. Geometry instances can even contain instances themselves, for nested - * instancing. Each instance has an index into an array of unique instance data, and a transform. - * The component can also store generic attributes for each instance. - * - * The component works differently from other geometry components in that it stores - * data about instancing directly, rather than owning a pointer to a separate data structure. - * - * This component is not responsible for handling the interface to a render engine, or other - * areas that work with all visible geometry, that is handled by the dependency graph iterator - * (see `DEG_depsgraph_query.h`). + * A geometry component that stores #Instances. */ class InstancesComponent : public GeometryComponent { private: - /** - * Indexed set containing information about the data that is instanced. - * Actual instances store an index ("handle") into this set. - */ - blender::VectorSet<InstanceReference> references_; - - /** Index into `references_`. Determines what data is instanced. */ - blender::Vector<int> instance_reference_handles_; - /** Transformation of the instances. */ - blender::Vector<blender::float4x4> instance_transforms_; - - /* These almost unique ids are generated based on the `id` attribute, which might not contain - * unique ids at all. They are *almost* unique, because under certain very unlikely - * circumstances, they are not unique. Code using these ids should not crash when they are not - * unique but can generally expect them to be unique. */ - mutable std::mutex almost_unique_ids_mutex_; - mutable blender::Array<int> almost_unique_ids_; - - blender::bke::CustomDataAttributes attributes_; + blender::bke::Instances *instances_ = nullptr; + GeometryOwnershipType ownership_ = GeometryOwnershipType::Owned; public: InstancesComponent(); - ~InstancesComponent() = default; + ~InstancesComponent(); GeometryComponent *copy() const override; void clear(); - void reserve(int min_capacity); - /** - * Resize the transform, handles, and attributes to the specified capacity. - * - * \note This function should be used carefully, only when it's guaranteed - * that the data will be filled. - */ - void resize(int capacity); + const blender::bke::Instances *get_for_read() const; + blender::bke::Instances *get_for_write(); - /** - * Returns a handle for the given reference. - * If the reference exists already, the handle of the existing reference is returned. - * Otherwise a new handle is added. - */ - int add_reference(const InstanceReference &reference); - /** - * Add a reference to the instance reference with an index specified by the #instance_handle - * argument. For adding many instances, using #resize and accessing the transform array directly - * is preferred. - */ - void add_instance(int instance_handle, const blender::float4x4 &transform); - - blender::Span<InstanceReference> references() const; - void remove_unused_references(); - - /** - * If references have a collection or object type, convert them into geometry instances - * recursively. After that, the geometry sets can be edited. There may still be instances of - * other types of they can't be converted to geometry sets. - */ - void ensure_geometry_instances(); - /** - * With write access to the instances component, the data in the instanced geometry sets can be - * changed. This is a function on the component rather than each reference to ensure `const` - * correctness for that reason. - */ - GeometrySet &geometry_set_from_reference(int reference_index); - - blender::Span<int> instance_reference_handles() const; - blender::MutableSpan<int> instance_reference_handles(); - blender::MutableSpan<blender::float4x4> instance_transforms(); - blender::Span<blender::float4x4> instance_transforms() const; - - int instances_num() const; - int references_num() const; - - /** - * Remove the indices that are not contained in the mask input, and remove unused instance - * references afterwards. - */ - void remove_instances(const blender::IndexMask mask); - - blender::Span<int> almost_unique_ids() const; - - blender::bke::CustomDataAttributes &instance_attributes(); - const blender::bke::CustomDataAttributes &instance_attributes() const; - - std::optional<blender::bke::AttributeAccessor> attributes() const final; - std::optional<blender::bke::MutableAttributeAccessor> attributes_for_write() final; - - void foreach_referenced_geometry( - blender::FunctionRef<void(const GeometrySet &geometry_set)> callback) const; + void replace(blender::bke::Instances *instances, + GeometryOwnershipType ownership = GeometryOwnershipType::Owned); bool is_empty() const final; bool owns_direct_data() const override; void ensure_owns_direct_data() override; - static constexpr inline GeometryComponentType static_type = GEO_COMPONENT_TYPE_INSTANCES; + std::optional<blender::bke::AttributeAccessor> attributes() const final; + std::optional<blender::bke::MutableAttributeAccessor> attributes_for_write() final; - private: + static constexpr inline GeometryComponentType static_type = GEO_COMPONENT_TYPE_INSTANCES; }; /** diff --git a/source/blender/blenkernel/BKE_instances.hh b/source/blender/blenkernel/BKE_instances.hh new file mode 100644 index 00000000000..f17ebba0dfa --- /dev/null +++ b/source/blender/blenkernel/BKE_instances.hh @@ -0,0 +1,270 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +/** \file + * \ingroup bke + * + * #Instances is a container for geometry instances. It fulfills some key requirements: + * - Support nested instances. + * - Support instance attributes. + * - Support referencing different kinds of instances (objects, collections, geometry sets). + * - Support efficiently iterating over the instanced geometries, i.e. without have to iterate over + * all instances. + * + * #Instances has an ordered set of #InstanceReference. An #InstanceReference contains information + * about a particular instanced geometry. Each #InstanceReference has a handle (integer index) + * which is then stored per instance. Many instances can use the same #InstanceReference. + */ + +#include <mutex> + +#include "BLI_float4x4.hh" +#include "BLI_vector.hh" +#include "BLI_vector_set.hh" + +#include "BKE_attribute.hh" + +struct GeometrySet; +struct Object; +struct Collection; + +namespace blender::bke { + +/** + * Holds a reference to conceptually unique geometry or a pointer to object/collection data + * that is instanced with a transform in #Instances. + */ +class InstanceReference { + public: + enum class Type { + /** + * An empty instance. This allows an `InstanceReference` to be default constructed without + * being in an invalid state. There might also be other use cases that we haven't explored + * much yet (such as changing the instance later on, and "disabling" some instances). + */ + None, + Object, + Collection, + 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; + InstanceReference(Object &object); + InstanceReference(Collection &collection); + InstanceReference(GeometrySet geometry_set); + + InstanceReference(const InstanceReference &other); + InstanceReference(InstanceReference &&other); + + InstanceReference &operator=(const InstanceReference &other); + InstanceReference &operator=(InstanceReference &&other); + + Type type() const; + Object &object() const; + Collection &collection() const; + const GeometrySet &geometry_set() const; + + bool owns_direct_data() const; + void ensure_owns_direct_data(); + + uint64_t hash() const; + friend bool operator==(const InstanceReference &a, const InstanceReference &b); +}; + +class Instances { + private: + /** + * Indexed set containing information about the data that is instanced. + * Actual instances store an index ("handle") into this set. + */ + blender::VectorSet<InstanceReference> references_; + + /** Indices into `references_`. Determines what data is instanced. */ + blender::Vector<int> reference_handles_; + /** Transformation of the instances. */ + blender::Vector<blender::float4x4> transforms_; + + /* These almost unique ids are generated based on the `id` attribute, which might not contain + * unique ids at all. They are *almost* unique, because under certain very unlikely + * circumstances, they are not unique. Code using these ids should not crash when they are not + * unique but can generally expect them to be unique. */ + mutable std::mutex almost_unique_ids_mutex_; + mutable blender::Array<int> almost_unique_ids_; + + CustomDataAttributes attributes_; + + public: + Instances() = default; + Instances(const Instances &other); + + void reserve(int min_capacity); + /** + * Resize the transform, handles, and attributes to the specified capacity. + * + * \note This function should be used carefully, only when it's guaranteed + * that the data will be filled. + */ + void resize(int capacity); + + /** + * Returns a handle for the given reference. + * If the reference exists already, the handle of the existing reference is returned. + * Otherwise a new handle is added. + */ + int add_reference(const InstanceReference &reference); + /** + * Add a reference to the instance reference with an index specified by the #instance_handle + * argument. For adding many instances, using #resize and accessing the transform array + * directly is preferred. + */ + void add_instance(int instance_handle, const blender::float4x4 &transform); + + blender::Span<InstanceReference> references() const; + void remove_unused_references(); + + /** + * If references have a collection or object type, convert them into geometry instances + * recursively. After that, the geometry sets can be edited. There may still be instances of + * other types of they can't be converted to geometry sets. + */ + void ensure_geometry_instances(); + /** + * With write access to the instances component, the data in the instanced geometry sets can be + * changed. This is a function on the component rather than each reference to ensure `const` + * correctness for that reason. + */ + GeometrySet &geometry_set_from_reference(int reference_index); + + blender::Span<int> reference_handles() const; + blender::MutableSpan<int> reference_handles(); + blender::MutableSpan<blender::float4x4> transforms(); + blender::Span<blender::float4x4> transforms() const; + + int instances_num() const; + int references_num() const; + + /** + * Remove the indices that are not contained in the mask input, and remove unused instance + * references afterwards. + */ + void remove(const blender::IndexMask mask); + /** + * Get an id for every instance. These can be used for e.g. motion blur. + */ + blender::Span<int> almost_unique_ids() const; + + blender::bke::AttributeAccessor attributes() const; + blender::bke::MutableAttributeAccessor attributes_for_write(); + + CustomDataAttributes &custom_data_attributes(); + const CustomDataAttributes &custom_data_attributes() const; + + void foreach_referenced_geometry( + blender::FunctionRef<void(const GeometrySet &geometry_set)> callback) const; + + bool owns_direct_data() const; + void ensure_owns_direct_data(); +}; + +/* -------------------------------------------------------------------- */ +/** \name #InstanceReference Inline Methods + * \{ */ + +inline InstanceReference::InstanceReference(Object &object) : type_(Type::Object), data_(&object) +{ +} + +inline InstanceReference::InstanceReference(Collection &collection) + : type_(Type::Collection), data_(&collection) +{ +} + +inline InstanceReference::InstanceReference(const InstanceReference &other) + : type_(other.type_), data_(other.data_) +{ + if (other.geometry_set_) { + geometry_set_ = std::make_unique<GeometrySet>(*other.geometry_set_); + } +} + +inline InstanceReference::InstanceReference(InstanceReference &&other) + : type_(other.type_), data_(other.data_), geometry_set_(std::move(other.geometry_set_)) +{ + other.type_ = Type::None; + other.data_ = nullptr; +} + +inline InstanceReference &InstanceReference::operator=(const InstanceReference &other) +{ + if (this == &other) { + return *this; + } + this->~InstanceReference(); + new (this) InstanceReference(other); + return *this; +} + +inline InstanceReference &InstanceReference::operator=(InstanceReference &&other) +{ + if (this == &other) { + return *this; + } + this->~InstanceReference(); + new (this) InstanceReference(std::move(other)); + return *this; +} + +inline InstanceReference::Type InstanceReference::type() const +{ + return type_; +} + +inline Object &InstanceReference::object() const +{ + BLI_assert(type_ == Type::Object); + return *(Object *)data_; +} + +inline Collection &InstanceReference::collection() const +{ + BLI_assert(type_ == Type::Collection); + return *(Collection *)data_; +} + +inline const GeometrySet &InstanceReference::geometry_set() const +{ + BLI_assert(type_ == Type::GeometrySet); + return *geometry_set_; +} + +inline CustomDataAttributes &Instances::custom_data_attributes() +{ + return attributes_; +} + +inline const CustomDataAttributes &Instances::custom_data_attributes() const +{ + return attributes_; +} + +inline uint64_t InstanceReference::hash() const +{ + return blender::get_default_hash_2(data_, geometry_set_.get()); +} + +inline bool operator==(const InstanceReference &a, const InstanceReference &b) +{ + return a.data_ == b.data_ && a.geometry_set_.get() == b.geometry_set_.get(); +} + +/** \} */ + +} // namespace blender::bke diff --git a/source/blender/blenkernel/BKE_mesh.h b/source/blender/blenkernel/BKE_mesh.h index 0a000f4543a..0ff7f50f71c 100644 --- a/source/blender/blenkernel/BKE_mesh.h +++ b/source/blender/blenkernel/BKE_mesh.h @@ -490,7 +490,7 @@ void BKE_mesh_calc_normals(struct Mesh *me); * Called after calculating all modifiers. */ void BKE_mesh_ensure_normals_for_display(struct Mesh *mesh); -void BKE_mesh_calc_normals_looptri(struct MVert *mverts, +void BKE_mesh_calc_normals_looptri(const struct MVert *mverts, int numVerts, const struct MLoop *mloop, const struct MLoopTri *looptri, diff --git a/source/blender/blenkernel/BKE_mesh_mapping.h b/source/blender/blenkernel/BKE_mesh_mapping.h index da8431d4f27..9d9c2f57f89 100644 --- a/source/blender/blenkernel/BKE_mesh_mapping.h +++ b/source/blender/blenkernel/BKE_mesh_mapping.h @@ -336,7 +336,7 @@ int *BKE_mesh_calc_smoothgroups(const struct MEdge *medge, #endif #ifdef __cplusplus -namespace blender::mesh_topology { +namespace blender::bke::mesh_topology { Array<int> build_loop_to_poly_map(Span<MPoly> polys, int loops_num); @@ -348,5 +348,5 @@ inline int previous_poly_loop(const MPoly &poly, int loop_i) return loop_i - 1 + (loop_i == poly.loopstart) * poly.totloop; } -} // namespace blender::mesh_topology +} // namespace blender::bke::mesh_topology #endif diff --git a/source/blender/blenkernel/BKE_mesh_runtime.h b/source/blender/blenkernel/BKE_mesh_runtime.h index dfefe125a51..27e04c1a4de 100644 --- a/source/blender/blenkernel/BKE_mesh_runtime.h +++ b/source/blender/blenkernel/BKE_mesh_runtime.h @@ -9,6 +9,7 @@ */ //#include "BKE_customdata.h" /* for eCustomDataMask */ +#include "BKE_mesh_types.h" #ifdef __cplusplus extern "C" { @@ -26,21 +27,10 @@ struct Object; struct Scene; /** - * \brief Initialize the runtime of the given mesh. - * - * Function expects that the runtime is already cleared. - */ -void BKE_mesh_runtime_init_data(struct Mesh *mesh); -/** * \brief Free all data (and mutexes) inside the runtime of the given mesh. */ void BKE_mesh_runtime_free_data(struct Mesh *mesh); -/** - * Clear all pointers which we don't want to be shared on copying the datablock. - * However, keep all the flags which defines what the mesh is (for example, that - * it's deformed only, or that its custom data layers are out of date.) - */ -void BKE_mesh_runtime_reset_on_copy(struct Mesh *mesh, int flag); + int BKE_mesh_runtime_looptri_len(const struct Mesh *mesh); void BKE_mesh_runtime_looptri_recalc(struct Mesh *mesh); /** @@ -66,6 +56,11 @@ void BKE_mesh_runtime_verttri_from_looptri(struct MVertTri *r_verttri, const struct MLoopTri *looptri, int looptri_num); +/** \note Only used for access in C. */ +bool BKE_mesh_is_deformed_only(const struct Mesh *mesh); +/** \note Only used for access in C. */ +eMeshWrapperType BKE_mesh_wrapper_type(const struct Mesh *mesh); + /* NOTE: the functions below are defined in DerivedMesh.cc, and are intended to be moved * to a more suitable location when that file is removed. * They should also be renamed to use conventions from BKE, not old DerivedMesh.cc. diff --git a/source/blender/blenkernel/BKE_mesh_types.h b/source/blender/blenkernel/BKE_mesh_types.h index 0b879c1dfe9..80f61086052 100644 --- a/source/blender/blenkernel/BKE_mesh_types.h +++ b/source/blender/blenkernel/BKE_mesh_types.h @@ -1,11 +1,35 @@ /* SPDX-License-Identifier: GPL-2.0-or-later * Copyright 2020 Blender Foundation. All rights reserved. */ + #pragma once /** \file * \ingroup bke */ +#ifdef __cplusplus + +# include <mutex> + +# include "BLI_span.hh" + +# include "DNA_customdata_types.h" + +# include "MEM_guardedalloc.h" + +struct BVHCache; +struct EditMeshData; +struct MLoopTri; +struct ShrinkwrapBoundaryData; +struct SubdivCCG; +struct SubsurfRuntimeData; + +#endif + +#ifdef __cplusplus +extern "C" { +#endif + typedef enum eMeshBatchDirtyMode { BKE_MESH_BATCH_DIRTY_ALL = 0, BKE_MESH_BATCH_DIRTY_SELECT, @@ -14,3 +38,125 @@ typedef enum eMeshBatchDirtyMode { BKE_MESH_BATCH_DIRTY_UVEDIT_ALL, BKE_MESH_BATCH_DIRTY_UVEDIT_SELECT, } eMeshBatchDirtyMode; + +/** #MeshRuntime.wrapper_type */ +typedef enum eMeshWrapperType { + /** Use mesh data (#Mesh.mvert, #Mesh.medge, #Mesh.mloop, #Mesh.mpoly). */ + ME_WRAPPER_TYPE_MDATA = 0, + /** Use edit-mesh data (#Mesh.edit_mesh, #MeshRuntime.edit_data). */ + ME_WRAPPER_TYPE_BMESH = 1, + /** Use subdivision mesh data (#MeshRuntime.mesh_eval). */ + ME_WRAPPER_TYPE_SUBD = 2, +} eMeshWrapperType; + +#ifdef __cplusplus +} +#endif + +#ifdef __cplusplus + +namespace blender::bke { + +/** + * \warning Typical access is done via #Mesh::looptris(). + */ +struct MLoopTri_Store { + /* WARNING! swapping between array (ready-to-be-used data) and array_wip + * (where data is actually computed) + * shall always be protected by same lock as one used for looptris computing. */ + MLoopTri *array = nullptr; + MLoopTri *array_wip = nullptr; + int len = 0; + int len_alloc = 0; +}; + +struct MeshRuntime { + /* Evaluated mesh for objects which do not have effective modifiers. + * This mesh is used as a result of modifier stack evaluation. + * Since modifier stack evaluation is threaded on object level we need some synchronization. */ + Mesh *mesh_eval = nullptr; + std::mutex eval_mutex; + + /* A separate mutex is needed for normal calculation, because sometimes + * the normals are needed while #eval_mutex is already locked. */ + std::mutex normals_mutex; + + /** Needed to ensure some thread-safety during render data pre-processing. */ + std::mutex render_mutex; + + /** Lazily initialized SoA data from the #edit_mesh field in #Mesh. */ + EditMeshData *edit_data = nullptr; + + /** + * Data used to efficiently draw the mesh in the viewport, especially useful when + * the same mesh is used in many objects or instances. See `draw_cache_impl_mesh.cc`. + */ + void *batch_cache = nullptr; + + /** Cache for derived triangulation of the mesh. */ + MLoopTri_Store looptris; + + /** Cache for BVH trees generated for the mesh. Defined in 'BKE_bvhutil.c' */ + BVHCache *bvh_cache = nullptr; + + /** Cache of non-manifold boundary data for Shrink-wrap Target Project. */ + ShrinkwrapBoundaryData *shrinkwrap_data = nullptr; + + /** Needed in case we need to lazily initialize the mesh. */ + CustomData_MeshMasks cd_mask_extra = {}; + + SubdivCCG *subdiv_ccg = nullptr; + int subdiv_ccg_tot_level = 0; + + /** Set by modifier stack if only deformed from original. */ + bool deformed_only = false; + /** + * Copied from edit-mesh (hint, draw with edit-mesh data when true). + * + * Modifiers that edit the mesh data in-place must set this to false + * (most #eModifierTypeType_NonGeometrical modifiers). Otherwise the edit-mesh + * data will be used for drawing, missing changes from modifiers. See T79517. + */ + bool is_original_bmesh = false; + + /** #eMeshWrapperType and others. */ + eMeshWrapperType wrapper_type = ME_WRAPPER_TYPE_MDATA; + /** + * A type mask from wrapper_type, + * in case there are differences in finalizing logic between types. + */ + eMeshWrapperType wrapper_type_finalize = ME_WRAPPER_TYPE_MDATA; + + /** + * Settings for lazily evaluating the subdivision on the CPU if needed. These are + * set in the modifier when GPU subdivision can be performed, and owned by the by + * the modifier in the object. + */ + SubsurfRuntimeData *subsurf_runtime_data = nullptr; + + /** + * Caches for lazily computed vertex and polygon normals. These are stored here rather than in + * #CustomData because they can be calculated on a `const` mesh, and adding custom data layers on + * a `const` mesh is not thread-safe. + */ + bool vert_normals_dirty = false; + bool poly_normals_dirty = false; + float (*vert_normals)[3] = nullptr; + float (*poly_normals)[3] = nullptr; + + /** + * A #BLI_bitmap containing tags for the center vertices of subdivided polygons, set by the + * subdivision surface modifier and used by drawing code instead of polygon center face dots. + */ + uint32_t *subsurf_face_dot_tags = nullptr; + + MeshRuntime() = default; + /** \warning This does not free all data currently. See #BKE_mesh_runtime_free_data. */ + ~MeshRuntime() = default; + + MEM_CXX_CLASS_ALLOC_FUNCS("MeshRuntime") +}; + +} // namespace blender::bke + +#endif diff --git a/source/blender/blenkernel/BKE_nla.h b/source/blender/blenkernel/BKE_nla.h index 9d3000759ab..efadd5c11d6 100644 --- a/source/blender/blenkernel/BKE_nla.h +++ b/source/blender/blenkernel/BKE_nla.h @@ -246,6 +246,24 @@ float BKE_nlastrip_compute_frame_from_previous_strip(struct NlaStrip *strip); */ float BKE_nlastrip_compute_frame_to_next_strip(struct NlaStrip *strip); +/** + * Returns the next strip in this strip's NLA track, or a null pointer. + * + * \param strip The strip to find the next trip from. + * \param check_transitions Whether or not to skip transitions. + * \return The next strip in the track, or NULL if none are present. + */ +struct NlaStrip *BKE_nlastrip_next_in_track(struct NlaStrip *strip, bool skip_transitions); + +/** + * Returns the previous strip in this strip's NLA track, or a null pointer. + * + * \param strip The strip to find the previous trip from. + * \param check_transitions Whether or not to skip transitions. + * \return The previous strip in the track, or NULL if none are present. + */ +struct NlaStrip *BKE_nlastrip_prev_in_track(struct NlaStrip *strip, bool skip_transitions); + /* ............ */ /** diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h index e2ed024c6f6..9ceb5cf1d4c 100644 --- a/source/blender/blenkernel/BKE_node.h +++ b/source/blender/blenkernel/BKE_node.h @@ -792,6 +792,12 @@ void nodeChainIterBackwards(const bNodeTree *ntree, */ void nodeParentsIter(bNode *node, bool (*callback)(bNode *, void *), void *userdata); +/** + * A dangling reroute node is a reroute node that does *not* have a "data source", i.e. no + * non-reroute node is connected to its input. + */ +bool nodeIsDanglingReroute(const struct bNodeTree *ntree, const struct bNode *node); + struct bNodeLink *nodeFindLink(struct bNodeTree *ntree, const struct bNodeSocket *from, const struct bNodeSocket *to); diff --git a/source/blender/blenkernel/BKE_paint.h b/source/blender/blenkernel/BKE_paint.h index 5312292d431..437a22e4782 100644 --- a/source/blender/blenkernel/BKE_paint.h +++ b/source/blender/blenkernel/BKE_paint.h @@ -860,7 +860,19 @@ int *BKE_sculpt_face_sets_ensure(struct Mesh *mesh); * (see #SCULPT_visibility_sync_all_from_faces). */ bool *BKE_sculpt_hide_poly_ensure(struct Mesh *mesh); -int BKE_sculpt_mask_layers_ensure(struct Object *ob, struct MultiresModifierData *mmd); + +/** + * Ensures a mask layer exists. If depsgraph and bmain are non-null, + * a mask doesn't exist and the object has a multi-resolution modifier + * then the scene depsgraph will be evaluated to update the runtime + * subdivision data. + * + * \note always call *before* #BKE_sculpt_update_object_for_edit. + */ +int BKE_sculpt_mask_layers_ensure(struct Depsgraph *depsgraph, + struct Main *bmain, + struct Object *ob, + struct MultiresModifierData *mmd); void BKE_sculpt_toolsettings_data_ensure(struct Scene *scene); struct PBVH *BKE_sculpt_object_pbvh_ensure(struct Depsgraph *depsgraph, struct Object *ob); diff --git a/source/blender/blenkernel/BKE_pbvh.h b/source/blender/blenkernel/BKE_pbvh.h index 467a7c7f306..b375d69b61c 100644 --- a/source/blender/blenkernel/BKE_pbvh.h +++ b/source/blender/blenkernel/BKE_pbvh.h @@ -125,6 +125,7 @@ typedef enum { PBVH_UpdateTopology = 1 << 13, PBVH_UpdateColor = 1 << 14, PBVH_RebuildPixels = 1 << 15, + PBVH_TopologyUpdated = 1 << 16, /* Used internally by pbvh_bmesh.c */ } PBVHNodeFlags; @@ -266,7 +267,8 @@ void BKE_pbvh_build_grids(PBVH *pbvh, void **gridfaces, struct DMFlagMat *flagmats, unsigned int **grid_hidden, - struct Mesh *me); + struct Mesh *me, + struct SubdivCCG *subdiv_ccg); /** * Build a PBVH from a BMesh. */ @@ -485,7 +487,10 @@ struct GSet *BKE_pbvh_bmesh_node_faces(PBVHNode *node); * * Skips triangles that are hidden. */ -void BKE_pbvh_bmesh_node_save_orig(struct BMesh *bm, PBVHNode *node); +void BKE_pbvh_bmesh_node_save_orig(struct BMesh *bm, + struct BMLog *log, + PBVHNode *node, + bool use_original); void BKE_pbvh_bmesh_after_stroke(PBVH *pbvh); /* Update Bounding Box/Redraw and clear flags. */ @@ -500,7 +505,8 @@ void BKE_pbvh_grids_update(PBVH *pbvh, struct CCGElem **grids, void **gridfaces, struct DMFlagMat *flagmats, - unsigned int **grid_hidden); + unsigned int **grid_hidden, + struct CCGKey *key); void BKE_pbvh_subdiv_cgg_set(PBVH *pbvh, struct SubdivCCG *subdiv_ccg); void BKE_pbvh_face_sets_set(PBVH *pbvh, int *face_sets); @@ -664,7 +670,8 @@ void BKE_pbvh_gather_proxies(PBVH *pbvh, PBVHNode ***r_array, int *r_tot); void BKE_pbvh_node_get_bm_orco_data(PBVHNode *node, int (**r_orco_tris)[3], int *r_orco_tris_num, - float (**r_orco_coords)[3]); + float (**r_orco_coords)[3], + struct BMVert ***r_orco_verts); /** * \note doing a full search on all vertices here seems expensive, diff --git a/source/blender/blenkernel/BKE_writeffmpeg.h b/source/blender/blenkernel/BKE_writeffmpeg.h index 736f7548bb4..cfe246eb470 100644 --- a/source/blender/blenkernel/BKE_writeffmpeg.h +++ b/source/blender/blenkernel/BKE_writeffmpeg.h @@ -27,6 +27,7 @@ enum { FFMPEG_OGG = 10, FFMPEG_INVALID = 11, FFMPEG_WEBM = 12, + FFMPEG_AV1 = 13, }; enum { @@ -38,6 +39,7 @@ enum { FFMPEG_PRESET_H264 = 5, FFMPEG_PRESET_THEORA = 6, FFMPEG_PRESET_XVID = 7, + FFMPEG_PRESET_AV1 = 8, }; struct RenderData; diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt index 97bdff217d0..7d43fa7e6af 100644 --- a/source/blender/blenkernel/CMakeLists.txt +++ b/source/blender/blenkernel/CMakeLists.txt @@ -163,6 +163,7 @@ set(SRC intern/image_gpu.cc intern/image_partial_update.cc intern/image_save.cc + intern/instances.cc intern/ipo.c intern/kelvinlet.c intern/key.c @@ -398,6 +399,7 @@ set(SRC BKE_image_partial_update.hh BKE_image_save.h BKE_image_wrappers.hh + BKE_instances.hh BKE_ipo.h BKE_kelvinlet.h BKE_key.h diff --git a/source/blender/blenkernel/intern/DerivedMesh.cc b/source/blender/blenkernel/intern/DerivedMesh.cc index b4cc46619a7..11ef2b08df4 100644 --- a/source/blender/blenkernel/intern/DerivedMesh.cc +++ b/source/blender/blenkernel/intern/DerivedMesh.cc @@ -35,6 +35,7 @@ #include "BKE_colorband.h" #include "BKE_deform.h" #include "BKE_editmesh.h" +#include "BKE_editmesh_cache.h" #include "BKE_geometry_set.hh" #include "BKE_geometry_set_instances.hh" #include "BKE_key.h" @@ -537,7 +538,7 @@ static void mesh_calc_modifier_final_normals(const Mesh *mesh_input, (final_datamask->lmask & CD_MASK_NORMAL) != 0); /* Needed as `final_datamask` is not preserved outside modifier stack evaluation. */ - SubsurfRuntimeData *subsurf_runtime_data = mesh_final->runtime.subsurf_runtime_data; + SubsurfRuntimeData *subsurf_runtime_data = mesh_final->runtime->subsurf_runtime_data; if (subsurf_runtime_data) { subsurf_runtime_data->calc_loop_normals = calc_loop_normals; } @@ -585,11 +586,12 @@ static void mesh_calc_finalize(const Mesh *mesh_input, Mesh *mesh_eval) void BKE_mesh_wrapper_deferred_finalize_mdata(Mesh *me_eval, const CustomData_MeshMasks *cd_mask_finalize) { - if (me_eval->runtime.wrapper_type_finalize & (1 << ME_WRAPPER_TYPE_BMESH)) { + if (me_eval->runtime->wrapper_type_finalize & (1 << ME_WRAPPER_TYPE_BMESH)) { editbmesh_calc_modifier_final_normals(me_eval, cd_mask_finalize); - me_eval->runtime.wrapper_type_finalize &= ~(1 << ME_WRAPPER_TYPE_BMESH); + me_eval->runtime->wrapper_type_finalize = eMeshWrapperType( + me_eval->runtime->wrapper_type_finalize & ~(1 << ME_WRAPPER_TYPE_BMESH)); } - BLI_assert(me_eval->runtime.wrapper_type_finalize == 0); + BLI_assert(me_eval->runtime->wrapper_type_finalize == 0); } /** @@ -1052,7 +1054,7 @@ static void mesh_calc_modifiers(struct Depsgraph *depsgraph, append_mask.lmask |= CD_MASK_PREVIEW_MLOOPCOL; } - mesh_final->runtime.deformed_only = false; + mesh_final->runtime->deformed_only = false; } isPrevDeform = (mti->type == eModifierTypeType_OnlyDeform); @@ -1119,10 +1121,9 @@ static void mesh_calc_modifiers(struct Depsgraph *depsgraph, mesh_calc_finalize(mesh_input, mesh_final); } else { - Mesh_Runtime *runtime = &mesh_input->runtime; + blender::bke::MeshRuntime *runtime = mesh_input->runtime; if (runtime->mesh_eval == nullptr) { - BLI_assert(runtime->eval_mutex != nullptr); - BLI_mutex_lock((ThreadMutex *)runtime->eval_mutex); + std::lock_guard lock{mesh_input->runtime->eval_mutex}; if (runtime->mesh_eval == nullptr) { /* Not yet finalized by any instance, do it now * Isolate since computing normals is multithreaded and we are holding a lock. */ @@ -1138,7 +1139,6 @@ static void mesh_calc_modifiers(struct Depsgraph *depsgraph, /* Already finalized by another instance, reuse. */ mesh_final = runtime->mesh_eval; } - BLI_mutex_unlock((ThreadMutex *)runtime->eval_mutex); } else if (!mesh_has_modifier_final_normals(mesh_input, &final_datamask, runtime->mesh_eval)) { /* Modifier stack was (re-)evaluated with a request for additional normals @@ -1207,7 +1207,7 @@ static void editbmesh_calc_modifier_final_normals(Mesh *mesh_final, const bool calc_loop_normals = ((mesh_final->flag & ME_AUTOSMOOTH) != 0 || (final_datamask->lmask & CD_MASK_NORMAL) != 0); - SubsurfRuntimeData *subsurf_runtime_data = mesh_final->runtime.subsurf_runtime_data; + SubsurfRuntimeData *subsurf_runtime_data = mesh_final->runtime->subsurf_runtime_data; if (subsurf_runtime_data) { subsurf_runtime_data->calc_loop_normals = calc_loop_normals; } @@ -1234,9 +1234,10 @@ static void editbmesh_calc_modifier_final_normals(Mesh *mesh_final, static void editbmesh_calc_modifier_final_normals_or_defer( Mesh *mesh_final, const CustomData_MeshMasks *final_datamask) { - if (mesh_final->runtime.wrapper_type != ME_WRAPPER_TYPE_MDATA) { + if (mesh_final->runtime->wrapper_type != ME_WRAPPER_TYPE_MDATA) { /* Generated at draw time. */ - mesh_final->runtime.wrapper_type_finalize = (1 << mesh_final->runtime.wrapper_type); + mesh_final->runtime->wrapper_type_finalize = eMeshWrapperType( + 1 << mesh_final->runtime->wrapper_type); return; } @@ -1450,7 +1451,7 @@ static void editbmesh_calc_modifiers(struct Depsgraph *depsgraph, deformed_verts = nullptr; } } - mesh_final->runtime.deformed_only = false; + mesh_final->runtime->deformed_only = false; } if (r_cage && i == cageIndex) { @@ -1469,7 +1470,8 @@ static void editbmesh_calc_modifiers(struct Depsgraph *depsgraph, if (!BKE_mesh_runtime_ensure_edit_data(me_orig)) { BKE_mesh_runtime_reset_edit_data(me_orig); } - me_orig->runtime.edit_data->vertexCos = (const float(*)[3])MEM_dupallocN(deformed_verts); + me_orig->runtime->edit_data->vertexCos = (const float(*)[3])MEM_dupallocN( + deformed_verts); } mesh_cage = BKE_mesh_wrapper_from_editmesh_with_coords( em_input, @@ -1583,7 +1585,7 @@ static void mesh_build_data(struct Depsgraph *depsgraph, * object's runtime: this could cause access freed data on depsgraph destruction (mesh who owns * the final result might be freed prior to object). */ Mesh *mesh = (Mesh *)ob->data; - const bool is_mesh_eval_owned = (mesh_eval != mesh->runtime.mesh_eval); + const bool is_mesh_eval_owned = (mesh_eval != mesh->runtime->mesh_eval); BKE_object_eval_assign_data(ob, &mesh_eval->id, is_mesh_eval_owned); /* Add the final mesh as a non-owning component to the geometry set. */ @@ -1643,7 +1645,7 @@ static void editbmesh_build_data(struct Depsgraph *depsgraph, } } - const bool is_mesh_eval_owned = (me_final != mesh->runtime.mesh_eval); + const bool is_mesh_eval_owned = (me_final != mesh->runtime->mesh_eval); BKE_object_eval_assign_data(obedit, &me_final->id, is_mesh_eval_owned); obedit->runtime.editmesh_eval_cage = me_cage; @@ -1899,7 +1901,7 @@ struct MappedUserData { static void make_vertexcos__mapFunc(void *userData, int index, const float co[3], - const float UNUSED(no[3])) + const float /*no*/[3]) { MappedUserData *mappedData = (MappedUserData *)userData; @@ -1914,7 +1916,7 @@ static void make_vertexcos__mapFunc(void *userData, void mesh_get_mapped_verts_coords(Mesh *me_eval, float (*r_cos)[3], const int totcos) { - if (me_eval->runtime.deformed_only == false) { + if (me_eval->runtime->deformed_only == false) { MappedUserData userData; memset(r_cos, 0, sizeof(*r_cos) * totcos); userData.vertexcos = r_cos; diff --git a/source/blender/blenkernel/intern/appdir.c b/source/blender/blenkernel/intern/appdir.c index b2f1e75cd1d..965f48d1e51 100644 --- a/source/blender/blenkernel/intern/appdir.c +++ b/source/blender/blenkernel/intern/appdir.c @@ -191,7 +191,7 @@ bool BKE_appdir_folder_documents(char *dir) char try_documents_path[FILE_MAXDIR]; /* Own attempt at getting a valid Documents path. */ - BLI_path_join(try_documents_path, sizeof(try_documents_path), home_path, N_("Documents"), NULL); + BLI_path_join(try_documents_path, sizeof(try_documents_path), home_path, N_("Documents")); if (!BLI_is_dir(try_documents_path)) { return false; } @@ -214,11 +214,11 @@ bool BKE_appdir_folder_caches(char *r_path, const size_t path_len) #ifdef WIN32 BLI_path_join( - r_path, path_len, caches_root_path, "Blender Foundation", "Blender", "Cache", SEP_STR, NULL); + r_path, path_len, caches_root_path, "Blender Foundation", "Blender", "Cache", SEP_STR); #elif defined(__APPLE__) - BLI_path_join(r_path, path_len, caches_root_path, "Blender", SEP_STR, NULL); + BLI_path_join(r_path, path_len, caches_root_path, "Blender", SEP_STR); #else /* __linux__ */ - BLI_path_join(r_path, path_len, caches_root_path, "blender", SEP_STR, NULL); + BLI_path_join(r_path, path_len, caches_root_path, "blender", SEP_STR); #endif return true; @@ -281,7 +281,9 @@ static bool test_path(char *targetpath, /* Only the last argument should be NULL. */ BLI_assert(!(folder_name == NULL && (subfolder_name != NULL))); - BLI_path_join(targetpath, targetpath_len, path_base, folder_name, subfolder_name, NULL); + const char *path_array[] = {path_base, folder_name, subfolder_name}; + const int path_array_num = (folder_name ? (subfolder_name ? 3 : 2) : 1); + BLI_path_join_array(targetpath, targetpath_len, path_array, path_array_num); if (check_is_dir == false) { CLOG_INFO(&LOG, 3, "using without test: '%s'", targetpath); return true; @@ -365,7 +367,9 @@ static bool get_path_local_ex(char *targetpath, STR_OR_FALLBACK(subfolder_name)); if (folder_name) { /* `subfolder_name` may be NULL. */ - BLI_path_join(relfolder, sizeof(relfolder), folder_name, subfolder_name, NULL); + const char *path_array[] = {folder_name, subfolder_name}; + const int path_array_num = subfolder_name ? 2 : 1; + BLI_path_join_array(relfolder, sizeof(relfolder), path_array, path_array_num); } else { relfolder[0] = '\0'; @@ -379,8 +383,7 @@ static bool get_path_local_ex(char *targetpath, * we must move the blender_version dir with contents to Resources. * Add 4 + 9 for the temporary `/../` path & `Resources`. */ char osx_resourses[FILE_MAX + 4 + 9]; - BLI_path_join( - osx_resourses, sizeof(osx_resourses), g_app.program_dirname, "..", "Resources", NULL); + BLI_path_join(osx_resourses, sizeof(osx_resourses), g_app.program_dirname, "..", "Resources"); /* Remove the '/../' added above. */ BLI_path_normalize(NULL, osx_resourses); path_base = osx_resourses; @@ -525,7 +528,9 @@ static bool get_path_system_ex(char *targetpath, char relfolder[FILE_MAX]; if (folder_name) { /* `subfolder_name` may be NULL. */ - BLI_path_join(relfolder, sizeof(relfolder), folder_name, subfolder_name, NULL); + const char *path_array[] = {folder_name, subfolder_name}; + const int path_array_num = subfolder_name ? 2 : 1; + BLI_path_join_array(relfolder, sizeof(relfolder), path_array, path_array_num); } else { relfolder[0] = '\0'; @@ -949,7 +954,7 @@ bool BKE_appdir_program_python_search(char *fullpath, if (python_bin_dir) { for (int i = 0; i < ARRAY_SIZE(python_names); i++) { - BLI_join_dirfile(fullpath, fullpath_len, python_bin_dir, python_names[i]); + BLI_path_join(fullpath, fullpath_len, python_bin_dir, python_names[i]); if ( #ifdef _WIN32 @@ -1018,7 +1023,7 @@ bool BKE_appdir_app_template_id_search(const char *app_template, char *path, siz { for (int i = 0; i < ARRAY_SIZE(app_template_directory_id); i++) { char subdir[FILE_MAX]; - BLI_join_dirfile(subdir, sizeof(subdir), app_template_directory_search[i], app_template); + BLI_path_join(subdir, sizeof(subdir), app_template_directory_search[i], app_template); if (BKE_appdir_folder_id_ex(app_template_directory_id[i], subdir, path, path_len)) { return true; } @@ -1041,8 +1046,7 @@ bool BKE_appdir_app_template_has_userpref(const char *app_template) } char userpref_path[FILE_MAX]; - BLI_path_join( - userpref_path, sizeof(userpref_path), app_template_path, BLENDER_USERPREF_FILE, NULL); + BLI_path_join(userpref_path, sizeof(userpref_path), app_template_path, BLENDER_USERPREF_FILE); return BLI_exists(userpref_path); } diff --git a/source/blender/blenkernel/intern/asset_catalog.cc b/source/blender/blenkernel/intern/asset_catalog.cc index f7b14cc3479..38c712d7e75 100644 --- a/source/blender/blenkernel/intern/asset_catalog.cc +++ b/source/blender/blenkernel/intern/asset_catalog.cc @@ -284,10 +284,10 @@ AssetCatalog *AssetCatalogService::create_catalog(const AssetCatalogPath &catalo static std::string asset_definition_default_file_path_from_dir(StringRef asset_library_root) { char file_path[PATH_MAX]; - BLI_join_dirfile(file_path, - sizeof(file_path), - asset_library_root.data(), - AssetCatalogService::DEFAULT_CATALOG_FILENAME.data()); + BLI_path_join(file_path, + sizeof(file_path), + asset_library_root.data(), + AssetCatalogService::DEFAULT_CATALOG_FILENAME.data()); return file_path; } @@ -515,8 +515,7 @@ CatalogFilePath AssetCatalogService::find_suitable_cdf_path_for_writing( BLI_path_join(asset_lib_cdf_path, sizeof(asset_lib_cdf_path), suitable_root_path, - DEFAULT_CATALOG_FILENAME.c_str(), - nullptr); + DEFAULT_CATALOG_FILENAME.c_str()); return asset_lib_cdf_path; } diff --git a/source/blender/blenkernel/intern/asset_catalog_test.cc b/source/blender/blenkernel/intern/asset_catalog_test.cc index 81eb1786322..ee2dd652b61 100644 --- a/source/blender/blenkernel/intern/asset_catalog_test.cc +++ b/source/blender/blenkernel/intern/asset_catalog_test.cc @@ -98,7 +98,7 @@ class AssetCatalogTest : public testing::Test { FAIL(); } - asset_library_root_ = test_files_dir + "/" + "asset_library"; + asset_library_root_ = test_files_dir + SEP_STR + "asset_library"; temp_library_path_ = ""; } @@ -116,7 +116,7 @@ class AssetCatalogTest : public testing::Test { { BKE_tempdir_init(""); const CatalogFilePath tempdir = BKE_tempdir_session(); - temp_library_path_ = tempdir + "test-temporary-path/"; + temp_library_path_ = tempdir + "test-temporary-path" + SEP_STR; return temp_library_path_; } @@ -202,9 +202,10 @@ class AssetCatalogTest : public testing::Test { void save_from_memory_into_existing_asset_lib(const bool should_top_level_cdf_exist) { const CatalogFilePath target_dir = create_temp_path(); /* Has trailing slash. */ - const CatalogFilePath original_cdf_file = asset_library_root_ + "/blender_assets.cats.txt"; - const CatalogFilePath registered_asset_lib = target_dir + "my_asset_library/"; - const CatalogFilePath asset_lib_subdir = registered_asset_lib + "subdir/"; + const CatalogFilePath original_cdf_file = asset_library_root_ + SEP_STR + + "blender_assets.cats.txt"; + const CatalogFilePath registered_asset_lib = target_dir + "my_asset_library" + SEP_STR; + const CatalogFilePath asset_lib_subdir = registered_asset_lib + "subdir" + SEP_STR; CatalogFilePath cdf_toplevel = registered_asset_lib + AssetCatalogService::DEFAULT_CATALOG_FILENAME; CatalogFilePath cdf_in_subdir = asset_lib_subdir + @@ -272,7 +273,7 @@ class AssetCatalogTest : public testing::Test { TEST_F(AssetCatalogTest, load_single_file) { AssetCatalogService service(asset_library_root_); - service.load_from_disk(asset_library_root_ + "/" + "blender_assets.cats.txt"); + service.load_from_disk(asset_library_root_ + SEP_STR + "blender_assets.cats.txt"); /* Test getting a non-existent catalog ID. */ EXPECT_EQ(nullptr, service.find_catalog(BLI_uuid_generate_random())); @@ -313,7 +314,7 @@ TEST_F(AssetCatalogTest, load_single_file) TEST_F(AssetCatalogTest, load_catalog_path_backslashes) { AssetCatalogService service(asset_library_root_); - service.load_from_disk(asset_library_root_ + "/" + "blender_assets.cats.txt"); + service.load_from_disk(asset_library_root_ + SEP_STR + "blender_assets.cats.txt"); const AssetCatalog *found_by_id = service.find_catalog(UUID_POSES_ELLIE_BACKSLASHES); ASSERT_NE(nullptr, found_by_id); @@ -332,7 +333,7 @@ TEST_F(AssetCatalogTest, load_catalog_path_backslashes) TEST_F(AssetCatalogTest, is_first_loaded_flag) { AssetCatalogService service(asset_library_root_); - service.load_from_disk(asset_library_root_ + "/" + "blender_assets.cats.txt"); + service.load_from_disk(asset_library_root_ + SEP_STR + "blender_assets.cats.txt"); AssetCatalog *new_cat = service.create_catalog("never/before/seen/path"); EXPECT_FALSE(new_cat->flags.is_first_loaded) @@ -435,7 +436,7 @@ TEST_F(AssetCatalogTest, insert_item_into_tree) TEST_F(AssetCatalogTest, load_single_file_into_tree) { AssetCatalogService service(asset_library_root_); - service.load_from_disk(asset_library_root_ + "/" + "blender_assets.cats.txt"); + service.load_from_disk(asset_library_root_ + SEP_STR + "blender_assets.cats.txt"); /* Contains not only paths from the CDF but also the missing parents (implicitly defined * catalogs). */ @@ -476,7 +477,7 @@ TEST_F(AssetCatalogTest, foreach_in_tree) } AssetCatalogService service(asset_library_root_); - service.load_from_disk(asset_library_root_ + "/" + "blender_assets.cats.txt"); + service.load_from_disk(asset_library_root_ + SEP_STR + "blender_assets.cats.txt"); std::vector<AssetCatalogPath> expected_root_items{{"character", "path"}}; AssetCatalogTree *tree = service.get_catalog_tree(); @@ -499,7 +500,7 @@ TEST_F(AssetCatalogTest, foreach_in_tree) TEST_F(AssetCatalogTest, find_catalog_by_path) { TestableAssetCatalogService service(asset_library_root_); - service.load_from_disk(asset_library_root_ + "/" + + service.load_from_disk(asset_library_root_ + SEP_STR + AssetCatalogService::DEFAULT_CATALOG_FILENAME); AssetCatalog *catalog; @@ -522,7 +523,7 @@ TEST_F(AssetCatalogTest, find_catalog_by_path) TEST_F(AssetCatalogTest, write_single_file) { TestableAssetCatalogService service(asset_library_root_); - service.load_from_disk(asset_library_root_ + "/" + + service.load_from_disk(asset_library_root_ + SEP_STR + AssetCatalogService::DEFAULT_CATALOG_FILENAME); const CatalogFilePath save_to_path = use_temp_path() + @@ -550,7 +551,7 @@ TEST_F(AssetCatalogTest, write_single_file) TEST_F(AssetCatalogTest, read_write_unicode_filepath) { TestableAssetCatalogService service(asset_library_root_); - const CatalogFilePath load_from_path = asset_library_root_ + "/новый/" + + const CatalogFilePath load_from_path = asset_library_root_ + SEP_STR + "новый" + SEP_STR + AssetCatalogService::DEFAULT_CATALOG_FILENAME; service.load_from_disk(load_from_path); @@ -588,8 +589,9 @@ TEST_F(AssetCatalogTest, on_blendfile_save__with_existing_cdf) const CatalogFilePath top_level_dir = create_temp_path(); /* Has trailing slash. */ /* Create a copy of the CDF in SVN, so we can safely write to it. */ - const CatalogFilePath original_cdf_file = asset_library_root_ + "/blender_assets.cats.txt"; - const CatalogFilePath cdf_dirname = top_level_dir + "other_dir/"; + const CatalogFilePath original_cdf_file = asset_library_root_ + SEP_STR + + "blender_assets.cats.txt"; + const CatalogFilePath cdf_dirname = top_level_dir + "other_dir" + SEP_STR; const CatalogFilePath cdf_filename = cdf_dirname + AssetCatalogService::DEFAULT_CATALOG_FILENAME; ASSERT_TRUE(BLI_dir_create_recursive(cdf_dirname.c_str())); ASSERT_EQ(0, BLI_copy(original_cdf_file.c_str(), cdf_filename.c_str())) @@ -600,7 +602,7 @@ TEST_F(AssetCatalogTest, on_blendfile_save__with_existing_cdf) service.load_from_disk(); const AssetCatalog *cat = service.create_catalog("some/catalog/path"); - const CatalogFilePath blendfilename = top_level_dir + "subdir/some_file.blend"; + const CatalogFilePath blendfilename = top_level_dir + "subdir" + SEP_STR + "some_file.blend"; ASSERT_TRUE(service.write_to_disk(blendfilename)); EXPECT_EQ(cdf_filename, service.get_catalog_definition_file()->file_path); @@ -650,7 +652,8 @@ TEST_F(AssetCatalogTest, on_blendfile_save__from_memory_into_empty_directory) TEST_F(AssetCatalogTest, on_blendfile_save__from_memory_into_existing_cdf_and_merge) { const CatalogFilePath target_dir = create_temp_path(); /* Has trailing slash. */ - const CatalogFilePath original_cdf_file = asset_library_root_ + "/blender_assets.cats.txt"; + const CatalogFilePath original_cdf_file = asset_library_root_ + SEP_STR + + "blender_assets.cats.txt"; CatalogFilePath writable_cdf_file = target_dir + AssetCatalogService::DEFAULT_CATALOG_FILENAME; BLI_path_slash_native(writable_cdf_file.data()); ASSERT_EQ(0, BLI_copy(original_cdf_file.c_str(), writable_cdf_file.c_str())); @@ -719,7 +722,7 @@ TEST_F(AssetCatalogTest, create_first_catalog_from_scratch) service.write_to_disk(temp_lib_root + "phony.blend"); EXPECT_TRUE(BLI_is_dir(temp_lib_root.c_str())); - const CatalogFilePath definition_file_path = temp_lib_root + "/" + + const CatalogFilePath definition_file_path = temp_lib_root + SEP_STR + AssetCatalogService::DEFAULT_CATALOG_FILENAME; EXPECT_TRUE(BLI_is_file(definition_file_path.c_str())); @@ -739,7 +742,7 @@ TEST_F(AssetCatalogTest, create_catalog_after_loading_file) /* Copy the asset catalog definition files to a separate location, so that we can test without * overwriting the test file in SVN. */ - const CatalogFilePath default_catalog_path = asset_library_root_ + "/" + + const CatalogFilePath default_catalog_path = asset_library_root_ + SEP_STR + AssetCatalogService::DEFAULT_CATALOG_FILENAME; const CatalogFilePath writable_catalog_path = temp_lib_root + AssetCatalogService::DEFAULT_CATALOG_FILENAME; @@ -801,7 +804,7 @@ TEST_F(AssetCatalogTest, create_catalog_simple_name) TEST_F(AssetCatalogTest, delete_catalog_leaf) { AssetCatalogService service(asset_library_root_); - service.load_from_disk(asset_library_root_ + "/" + "blender_assets.cats.txt"); + service.load_from_disk(asset_library_root_ + SEP_STR + "blender_assets.cats.txt"); /* Delete a leaf catalog, i.e. one that is not a parent of another catalog. * This keeps this particular test easy. */ @@ -833,7 +836,7 @@ TEST_F(AssetCatalogTest, delete_catalog_leaf) TEST_F(AssetCatalogTest, delete_catalog_parent_by_id) { TestableAssetCatalogService service(asset_library_root_); - service.load_from_disk(asset_library_root_ + "/" + "blender_assets.cats.txt"); + service.load_from_disk(asset_library_root_ + SEP_STR + "blender_assets.cats.txt"); /* Delete a parent catalog. */ service.delete_catalog_by_id_soft(UUID_POSES_RUZENA); @@ -847,7 +850,7 @@ TEST_F(AssetCatalogTest, delete_catalog_parent_by_id) TEST_F(AssetCatalogTest, delete_catalog_parent_by_path) { AssetCatalogService service(asset_library_root_); - service.load_from_disk(asset_library_root_ + "/" + "blender_assets.cats.txt"); + service.load_from_disk(asset_library_root_ + SEP_STR + "blender_assets.cats.txt"); /* Create an extra catalog with the to-be-deleted path, and one with a child of that. * This creates some duplicates that are bound to occur in production asset libraries as well. @@ -887,14 +890,14 @@ TEST_F(AssetCatalogTest, delete_catalog_parent_by_path) TEST_F(AssetCatalogTest, delete_catalog_write_to_disk) { TestableAssetCatalogService service(asset_library_root_); - service.load_from_disk(asset_library_root_ + "/" + + service.load_from_disk(asset_library_root_ + SEP_STR + AssetCatalogService::DEFAULT_CATALOG_FILENAME); service.delete_catalog_by_id_soft(UUID_POSES_ELLIE); const CatalogFilePath save_to_path = use_temp_path(); AssetCatalogDefinitionFile *cdf = service.get_catalog_definition_file(); - cdf->write_to_disk(save_to_path + "/" + AssetCatalogService::DEFAULT_CATALOG_FILENAME); + cdf->write_to_disk(save_to_path + SEP_STR + AssetCatalogService::DEFAULT_CATALOG_FILENAME); AssetCatalogService loaded_service(save_to_path); loaded_service.load_from_disk(); @@ -911,7 +914,7 @@ TEST_F(AssetCatalogTest, delete_catalog_write_to_disk) TEST_F(AssetCatalogTest, update_catalog_path) { AssetCatalogService service(asset_library_root_); - service.load_from_disk(asset_library_root_ + "/" + + service.load_from_disk(asset_library_root_ + SEP_STR + AssetCatalogService::DEFAULT_CATALOG_FILENAME); const AssetCatalog *orig_cat = service.find_catalog(UUID_POSES_RUZENA); @@ -940,7 +943,7 @@ TEST_F(AssetCatalogTest, update_catalog_path) TEST_F(AssetCatalogTest, update_catalog_path_simple_name) { AssetCatalogService service(asset_library_root_); - service.load_from_disk(asset_library_root_ + "/" + + service.load_from_disk(asset_library_root_ + SEP_STR + AssetCatalogService::DEFAULT_CATALOG_FILENAME); service.update_catalog_path(UUID_POSES_RUZENA, "charlib/Ružena"); @@ -956,7 +959,7 @@ TEST_F(AssetCatalogTest, update_catalog_path_simple_name) TEST_F(AssetCatalogTest, update_catalog_path_longer_than_simplename) { AssetCatalogService service(asset_library_root_); - service.load_from_disk(asset_library_root_ + "/" + + service.load_from_disk(asset_library_root_ + SEP_STR + AssetCatalogService::DEFAULT_CATALOG_FILENAME); const std::string new_path = "this/is/a/very/long/path/that/exceeds/the/simple-name/length/of/assets"; @@ -978,7 +981,7 @@ TEST_F(AssetCatalogTest, update_catalog_path_longer_than_simplename) TEST_F(AssetCatalogTest, update_catalog_path_add_slashes) { AssetCatalogService service(asset_library_root_); - service.load_from_disk(asset_library_root_ + "/" + + service.load_from_disk(asset_library_root_ + SEP_STR + AssetCatalogService::DEFAULT_CATALOG_FILENAME); const AssetCatalog *orig_cat = service.find_catalog(UUID_POSES_RUZENA); @@ -1019,8 +1022,10 @@ TEST_F(AssetCatalogTest, update_catalog_path_add_slashes) TEST_F(AssetCatalogTest, merge_catalog_files) { const CatalogFilePath cdf_dir = create_temp_path(); - const CatalogFilePath original_cdf_file = asset_library_root_ + "/blender_assets.cats.txt"; - const CatalogFilePath modified_cdf_file = asset_library_root_ + "/modified_assets.cats.txt"; + const CatalogFilePath original_cdf_file = asset_library_root_ + SEP_STR + + "blender_assets.cats.txt"; + const CatalogFilePath modified_cdf_file = asset_library_root_ + SEP_STR + + "modified_assets.cats.txt"; const CatalogFilePath temp_cdf_file = cdf_dir + "blender_assets.cats.txt"; ASSERT_EQ(0, BLI_copy(original_cdf_file.c_str(), temp_cdf_file.c_str())); @@ -1059,8 +1064,10 @@ TEST_F(AssetCatalogTest, merge_catalog_files) TEST_F(AssetCatalogTest, refresh_catalogs_with_modification) { const CatalogFilePath cdf_dir = create_temp_path(); - const CatalogFilePath original_cdf_file = asset_library_root_ + "/blender_assets.cats.txt"; - const CatalogFilePath modified_cdf_file = asset_library_root_ + "/catalog_reload_test.cats.txt"; + const CatalogFilePath original_cdf_file = asset_library_root_ + SEP_STR + + "blender_assets.cats.txt"; + const CatalogFilePath modified_cdf_file = asset_library_root_ + SEP_STR + + "catalog_reload_test.cats.txt"; const CatalogFilePath temp_cdf_file = cdf_dir + "blender_assets.cats.txt"; ASSERT_EQ(0, BLI_copy(original_cdf_file.c_str(), temp_cdf_file.c_str())); @@ -1131,8 +1138,9 @@ TEST_F(AssetCatalogTest, refresh_catalogs_with_modification) TEST_F(AssetCatalogTest, backups) { const CatalogFilePath cdf_dir = create_temp_path(); - const CatalogFilePath original_cdf_file = asset_library_root_ + "/blender_assets.cats.txt"; - const CatalogFilePath writable_cdf_file = cdf_dir + "/blender_assets.cats.txt"; + const CatalogFilePath original_cdf_file = asset_library_root_ + SEP_STR + + "blender_assets.cats.txt"; + const CatalogFilePath writable_cdf_file = cdf_dir + SEP_STR + "blender_assets.cats.txt"; ASSERT_EQ(0, BLI_copy(original_cdf_file.c_str(), writable_cdf_file.c_str())); /* Read a CDF, modify, and write it. */ diff --git a/source/blender/blenkernel/intern/asset_library_service_test.cc b/source/blender/blenkernel/intern/asset_library_service_test.cc index de6180cb684..d105c5644de 100644 --- a/source/blender/blenkernel/intern/asset_library_service_test.cc +++ b/source/blender/blenkernel/intern/asset_library_service_test.cc @@ -39,7 +39,7 @@ class AssetLibraryServiceTest : public testing::Test { if (test_files_dir.empty()) { FAIL(); } - asset_library_root_ = test_files_dir + "/" + "asset_library"; + asset_library_root_ = test_files_dir + SEP_STR + "asset_library"; temp_library_path_ = ""; } @@ -59,7 +59,7 @@ class AssetLibraryServiceTest : public testing::Test { { BKE_tempdir_init(""); const CatalogFilePath tempdir = BKE_tempdir_session(); - temp_library_path_ = tempdir + "test-temporary-path/"; + temp_library_path_ = tempdir + "test-temporary-path" + SEP_STR; return temp_library_path_; } @@ -168,7 +168,8 @@ TEST_F(AssetLibraryServiceTest, has_any_unsaved_catalogs) TEST_F(AssetLibraryServiceTest, has_any_unsaved_catalogs_after_write) { const CatalogFilePath writable_dir = create_temp_path(); /* Has trailing slash. */ - const CatalogFilePath original_cdf_file = asset_library_root_ + "/blender_assets.cats.txt"; + const CatalogFilePath original_cdf_file = asset_library_root_ + SEP_STR + + "blender_assets.cats.txt"; CatalogFilePath writable_cdf_file = writable_dir + AssetCatalogService::DEFAULT_CATALOG_FILENAME; BLI_path_slash_native(writable_cdf_file.data()); ASSERT_EQ(0, BLI_copy(original_cdf_file.c_str(), writable_cdf_file.c_str())); diff --git a/source/blender/blenkernel/intern/attribute_access.cc b/source/blender/blenkernel/intern/attribute_access.cc index b86353bdb74..544427cfdd3 100644 --- a/source/blender/blenkernel/intern/attribute_access.cc +++ b/source/blender/blenkernel/intern/attribute_access.cc @@ -642,15 +642,26 @@ CustomDataAttributes::CustomDataAttributes(CustomDataAttributes &&other) size_ = other.size_; data = other.data; CustomData_reset(&other.data); + other.size_ = 0; } CustomDataAttributes &CustomDataAttributes::operator=(const CustomDataAttributes &other) { - if (this != &other) { - CustomData_copy(&other.data, &data, CD_MASK_ALL, CD_DUPLICATE, other.size_); - size_ = other.size_; + if (this == &other) { + return *this; } + this->~CustomDataAttributes(); + new (this) CustomDataAttributes(other); + return *this; +} +CustomDataAttributes &CustomDataAttributes::operator=(CustomDataAttributes &&other) +{ + if (this == &other) { + return *this; + } + this->~CustomDataAttributes(); + new (this) CustomDataAttributes(std::move(other)); return *this; } diff --git a/source/blender/blenkernel/intern/blender_undo.c b/source/blender/blenkernel/intern/blender_undo.c index a5096b4f9eb..f22dfc6054a 100644 --- a/source/blender/blenkernel/intern/blender_undo.c +++ b/source/blender/blenkernel/intern/blender_undo.c @@ -116,7 +116,7 @@ MemFileUndoData *BKE_memfile_undo_encode(Main *bmain, MemFileUndoData *mfu_prev) counter = counter % U.undosteps; BLI_snprintf(numstr, sizeof(numstr), "%d.blend", counter); - BLI_join_dirfile(filepath, sizeof(filepath), BKE_tempdir_session(), numstr); + BLI_path_join(filepath, sizeof(filepath), BKE_tempdir_session(), numstr); /* success = */ /* UNUSED */ BLO_write_file( bmain, filepath, fileflags, &(const struct BlendFileWriteParams){0}, NULL); diff --git a/source/blender/blenkernel/intern/blendfile.c b/source/blender/blenkernel/intern/blendfile.c index 6546659f6cd..85a43b7c479 100644 --- a/source/blender/blenkernel/intern/blendfile.c +++ b/source/blender/blenkernel/intern/blendfile.c @@ -737,7 +737,7 @@ bool BKE_blendfile_userdef_write_all(ReportList *reports) if ((cfgdir = BKE_appdir_folder_id_create(BLENDER_USER_CONFIG, NULL))) { bool ok_write; - BLI_path_join(filepath, sizeof(filepath), cfgdir, BLENDER_USERPREF_FILE, NULL); + BLI_path_join(filepath, sizeof(filepath), cfgdir, BLENDER_USERPREF_FILE); printf("Writing userprefs: '%s' ", filepath); if (use_template_userpref) { @@ -764,7 +764,7 @@ bool BKE_blendfile_userdef_write_all(ReportList *reports) if (use_template_userpref) { if ((cfgdir = BKE_appdir_folder_id_create(BLENDER_USER_CONFIG, U.app_template))) { /* Also save app-template prefs */ - BLI_path_join(filepath, sizeof(filepath), cfgdir, BLENDER_USERPREF_FILE, NULL); + BLI_path_join(filepath, sizeof(filepath), cfgdir, BLENDER_USERPREF_FILE); printf("Writing userprefs app-template: '%s' ", filepath); if (BKE_blendfile_userdef_write(filepath, reports) != 0) { diff --git a/source/blender/blenkernel/intern/blendfile_link_append.c b/source/blender/blenkernel/intern/blendfile_link_append.c index 394469e19a4..4dd225c09ec 100644 --- a/source/blender/blenkernel/intern/blendfile_link_append.c +++ b/source/blender/blenkernel/intern/blendfile_link_append.c @@ -1494,6 +1494,7 @@ void BKE_blendfile_library_relocate(BlendfileLinkAppendContext *lapp_context, * code is wrong, we need to redo it here after adding them back to main. */ BKE_main_id_refcount_recompute(bmain, false); + BKE_layer_collection_resync_forbid(); /* Note that in reload case, we also want to replace indirect usages. */ const short remap_flags = ID_REMAP_SKIP_NEVER_NULL_USAGE | (do_reload ? 0 : ID_REMAP_SKIP_INDIRECT_USAGE); @@ -1523,6 +1524,8 @@ void BKE_blendfile_library_relocate(BlendfileLinkAppendContext *lapp_context, id_us_plus_no_lib(&old_key->id); } } + BKE_layer_collection_resync_allow(); + BKE_main_collection_sync_remap(bmain); BKE_main_unlock(bmain); @@ -1614,6 +1617,9 @@ void BKE_blendfile_library_relocate(BlendfileLinkAppendContext *lapp_context, (id->tag & LIB_TAG_PRE_EXISTING) == 0) { continue; } + if ((id->override_library->reference->tag & LIB_TAG_MISSING) == 0) { + id->tag &= ~LIB_TAG_MISSING; + } if ((id->override_library->reference->tag & LIB_TAG_PRE_EXISTING) == 0) { BKE_lib_override_library_update(bmain, id); } diff --git a/source/blender/blenkernel/intern/bpath.c b/source/blender/blenkernel/intern/bpath.c index f639d03ae0f..6bf121675bc 100644 --- a/source/blender/blenkernel/intern/bpath.c +++ b/source/blender/blenkernel/intern/bpath.c @@ -155,7 +155,7 @@ bool BKE_bpath_foreach_path_dirfile_fixed_process(BPathForeachPathData *bpath_da char path_src[FILE_MAX]; char path_dst[FILE_MAX]; - BLI_join_dirfile(path_src, sizeof(path_src), path_dir, path_file); + BLI_path_join(path_src, sizeof(path_src), path_dir, path_file); /* So that functions can access the old value. */ BLI_strncpy(path_dst, path_src, FILE_MAX); @@ -279,7 +279,7 @@ static bool missing_files_find__recursive(const char *search_directory, continue; } - BLI_join_dirfile(path, sizeof(path), search_directory, de->d_name); + BLI_path_join(path, sizeof(path), search_directory, de->d_name); if (BLI_stat(path, &status) == -1) { CLOG_WARN(&LOG, "Cannot get file status (`stat()`) of '%s'", path); diff --git a/source/blender/blenkernel/intern/brush.cc b/source/blender/blenkernel/intern/brush.cc index 20d86ea5a2d..6f1435e90b8 100644 --- a/source/blender/blenkernel/intern/brush.cc +++ b/source/blender/blenkernel/intern/brush.cc @@ -1974,19 +1974,37 @@ void BKE_brush_curve_preset(Brush *b, eCurveMappingPreset preset) BKE_curvemapping_changed(cumap, false); } +const struct MTex *BKE_brush_mask_texture_get(const struct Brush *brush, + const eObjectMode object_mode) +{ + if (object_mode == OB_MODE_SCULPT) { + return &brush->mtex; + } + return &brush->mask_mtex; +} + +const struct MTex *BKE_brush_color_texture_get(const struct Brush *brush, + const eObjectMode object_mode) +{ + if (object_mode == OB_MODE_SCULPT) { + return &brush->mask_mtex; + } + return &brush->mtex; +} + float BKE_brush_sample_tex_3d(const Scene *scene, const Brush *br, + const MTex *mtex, const float point[3], float rgba[4], const int thread, struct ImagePool *pool) { UnifiedPaintSettings *ups = &scene->toolsettings->unified_paint_settings; - const MTex *mtex = &br->mtex; float intensity = 1.0; bool hasrgb = false; - if (!mtex->tex) { + if (mtex == nullptr || mtex->tex == nullptr) { intensity = 1; } else if (mtex->brush_map_mode == MTEX_MAP_MODE_3D) { diff --git a/source/blender/blenkernel/intern/bvhutils.cc b/source/blender/blenkernel/intern/bvhutils.cc index 58b377eecdb..afc3e525143 100644 --- a/source/blender/blenkernel/intern/bvhutils.cc +++ b/source/blender/blenkernel/intern/bvhutils.cc @@ -51,13 +51,13 @@ struct BVHCache { * When the `r_locked` is filled and the tree could not be found the caches mutex will be * locked. This mutex can be unlocked by calling `bvhcache_unlock`. * - * When `r_locked` is used the `mesh_eval_mutex` must contain the `Mesh_Runtime.eval_mutex`. + * When `r_locked` is used the `mesh_eval_mutex` must contain the `MeshRuntime.eval_mutex`. */ static bool bvhcache_find(BVHCache **bvh_cache_p, BVHCacheType type, BVHTree **r_tree, bool *r_locked, - ThreadMutex *mesh_eval_mutex) + std::mutex *mesh_eval_mutex) { bool do_lock = r_locked; if (r_locked) { @@ -69,11 +69,10 @@ static bool bvhcache_find(BVHCache **bvh_cache_p, return false; } /* Lazy initialization of the bvh_cache using the `mesh_eval_mutex`. */ - BLI_mutex_lock(mesh_eval_mutex); + std::lock_guard lock{*mesh_eval_mutex}; if (*bvh_cache_p == nullptr) { *bvh_cache_p = bvhcache_init(); } - BLI_mutex_unlock(mesh_eval_mutex); } BVHCache *bvh_cache = *bvh_cache_p; @@ -1222,8 +1221,7 @@ BVHTree *BKE_bvhtree_from_mesh_get(struct BVHTreeFromMesh *data, const BVHCacheType bvh_cache_type, const int tree_type) { - BVHCache **bvh_cache_p = (BVHCache **)&mesh->runtime.bvh_cache; - ThreadMutex *mesh_eval_mutex = (ThreadMutex *)mesh->runtime.eval_mutex; + BVHCache **bvh_cache_p = (BVHCache **)&mesh->runtime->bvh_cache; const MLoopTri *looptri = nullptr; int looptri_len = 0; @@ -1248,7 +1246,7 @@ BVHTree *BKE_bvhtree_from_mesh_get(struct BVHTreeFromMesh *data, bool lock_started = false; data->cached = bvhcache_find( - bvh_cache_p, bvh_cache_type, &data->tree, &lock_started, mesh_eval_mutex); + bvh_cache_p, bvh_cache_type, &data->tree, &lock_started, &mesh->runtime->eval_mutex); if (data->cached) { BLI_assert(lock_started == false); @@ -1352,7 +1350,7 @@ BVHTree *BKE_bvhtree_from_editmesh_get(BVHTreeFromEditMesh *data, const int tree_type, const BVHCacheType bvh_cache_type, BVHCache **bvh_cache_p, - ThreadMutex *mesh_eval_mutex) + std::mutex *mesh_eval_mutex) { bool lock_started = false; diff --git a/source/blender/blenkernel/intern/cloth.c b/source/blender/blenkernel/intern/cloth.c index 56de583e2db..89983eb8f62 100644 --- a/source/blender/blenkernel/intern/cloth.c +++ b/source/blender/blenkernel/intern/cloth.c @@ -826,7 +826,7 @@ static void cloth_from_mesh(ClothModifierData *clmd, const Object *ob, Mesh *mes const MLoop *mloop = BKE_mesh_loops(mesh); const MLoopTri *looptri = BKE_mesh_runtime_looptri_ensure(mesh); const uint mvert_num = mesh->totvert; - const uint looptri_num = mesh->runtime.looptris.len; + const uint looptri_num = BKE_mesh_runtime_looptri_len(mesh); /* Allocate our vertices. */ clmd->clothObject->mvert_num = mvert_num; @@ -1167,7 +1167,7 @@ static Mesh *cloth_make_rest_mesh(ClothModifierData *clmd, Mesh *mesh) MVert *mvert = BKE_mesh_verts_for_write(mesh); /* vertex count is already ensured to match */ - for (unsigned i = 0; i < mesh->totvert; i++, verts++) { + for (uint i = 0; i < mesh->totvert; i++, verts++) { copy_v3_v3(mvert[i].co, verts->xrest); } BKE_mesh_tag_coords_changed(new_mesh); diff --git a/source/blender/blenkernel/intern/curve_to_mesh_convert.cc b/source/blender/blenkernel/intern/curve_to_mesh_convert.cc index b9fea2a27b8..ecf3be9bd81 100644 --- a/source/blender/blenkernel/intern/curve_to_mesh_convert.cc +++ b/source/blender/blenkernel/intern/curve_to_mesh_convert.cc @@ -332,7 +332,8 @@ static eAttrDomain get_attribute_domain_for_mesh(const AttributeAccessor &mesh_a static bool should_add_attribute_to_mesh(const AttributeAccessor &curve_attributes, const AttributeAccessor &mesh_attributes, - const AttributeIDRef &id) + const AttributeIDRef &id, + const AttributeMetaData &meta_data) { /* The position attribute has special non-generic evaluation. */ @@ -346,6 +347,9 @@ static bool should_add_attribute_to_mesh(const AttributeAccessor &curve_attribut if (!id.should_be_kept()) { return false; } + if (meta_data.data_type == CD_PROP_STRING) { + return false; + } return true; } @@ -714,7 +718,7 @@ Mesh *curve_to_mesh_sweep(const CurvesGeometry &main, MutableAttributeAccessor mesh_attributes = mesh->attributes_for_write(); main_attributes.for_all([&](const AttributeIDRef &id, const AttributeMetaData meta_data) { - if (!should_add_attribute_to_mesh(main_attributes, mesh_attributes, id)) { + if (!should_add_attribute_to_mesh(main_attributes, mesh_attributes, id, meta_data)) { return true; } main_attributes_set.add_new(id); @@ -751,7 +755,7 @@ Mesh *curve_to_mesh_sweep(const CurvesGeometry &main, if (main_attributes.contains(id)) { return true; } - if (!should_add_attribute_to_mesh(profile_attributes, mesh_attributes, id)) { + if (!should_add_attribute_to_mesh(profile_attributes, mesh_attributes, id, meta_data)) { return true; } const eAttrDomain src_domain = meta_data.domain; diff --git a/source/blender/blenkernel/intern/curves.cc b/source/blender/blenkernel/intern/curves.cc index 01be22a57b0..72e53023d6d 100644 --- a/source/blender/blenkernel/intern/curves.cc +++ b/source/blender/blenkernel/intern/curves.cc @@ -287,7 +287,10 @@ static void curves_evaluate_modifiers(struct Depsgraph *depsgraph, { /* Modifier evaluation modes. */ const bool use_render = (DEG_get_mode(depsgraph) == DAG_EVAL_RENDER); - const int required_mode = use_render ? eModifierMode_Render : eModifierMode_Realtime; + int required_mode = use_render ? eModifierMode_Render : eModifierMode_Realtime; + if (BKE_object_is_in_editmode(object)) { + required_mode = (ModifierMode)(int(required_mode) | eModifierMode_Editmode); + } ModifierApplyFlag apply_flag = use_render ? MOD_APPLY_RENDER : MOD_APPLY_USECACHE; const ModifierEvalContext mectx = {depsgraph, object, apply_flag}; diff --git a/source/blender/blenkernel/intern/curves_geometry.cc b/source/blender/blenkernel/intern/curves_geometry.cc index f5c845443f1..7c338480c71 100644 --- a/source/blender/blenkernel/intern/curves_geometry.cc +++ b/source/blender/blenkernel/intern/curves_geometry.cc @@ -9,6 +9,7 @@ #include "MEM_guardedalloc.h" +#include "BLI_array_utils.hh" #include "BLI_bounds.hh" #include "BLI_index_mask_ops.hh" #include "BLI_length_parameterize.hh" @@ -1111,21 +1112,11 @@ static void copy_between_buffers(const CPPType &type, src_range.size()); } -template<typename T> -static void copy_with_map(const Span<T> src, const Span<int> map, MutableSpan<T> dst) -{ - threading::parallel_for(map.index_range(), 1024, [&](const IndexRange range) { - for (const int i : range) { - dst[i] = src[map[i]]; - } - }); -} - static void copy_with_map(const GSpan src, const Span<int> map, GMutableSpan dst) { attribute_math::convert_to_static_type(src.type(), [&](auto dummy) { using T = decltype(dummy); - copy_with_map(src.typed<T>(), map, dst.typed<T>()); + array_utils::gather(src.typed<T>(), map, dst.typed<T>()); }); } @@ -1233,6 +1224,10 @@ static CurvesGeometry copy_with_removed_points(const CurvesGeometry &curves, attribute.dst.finish(); } + if (new_curves.curves_num() != curves.curves_num()) { + new_curves.remove_attributes_based_on_types(); + } + return new_curves; } @@ -1338,6 +1333,8 @@ static CurvesGeometry copy_with_removed_curves(const CurvesGeometry &curves, attribute.dst.finish(); } + new_curves.remove_attributes_based_on_types(); + return new_curves; } @@ -1402,6 +1399,9 @@ void CurvesGeometry::reverse_curves(const IndexMask curves_to_reverse) if (meta_data.domain != ATTR_DOMAIN_POINT) { return true; } + if (meta_data.data_type == CD_PROP_STRING) { + return true; + } if (id.is_named() && bezier_handle_names.contains(id.name())) { return true; } diff --git a/source/blender/blenkernel/intern/customdata.cc b/source/blender/blenkernel/intern/customdata.cc index be99a8ee87b..03a0f17a4bb 100644 --- a/source/blender/blenkernel/intern/customdata.cc +++ b/source/blender/blenkernel/intern/customdata.cc @@ -19,6 +19,7 @@ #include "BLI_bitmap.h" #include "BLI_color.hh" +#include "BLI_cpp_type_make.hh" #include "BLI_endian_switch.h" #include "BLI_index_range.hh" #include "BLI_math.h" @@ -2382,16 +2383,21 @@ static bool attribute_stored_in_bmesh_flag(const StringRef name) "material_index"); } -static CustomData shallow_copy_remove_non_bmesh_attributes(const CustomData &src) +CustomData CustomData_shallow_copy_remove_non_bmesh_attributes(const CustomData *src, + const eCustomDataMask mask) { Vector<CustomDataLayer> dst_layers; - for (const CustomDataLayer &layer : Span<CustomDataLayer>{src.layers, src.totlayer}) { - if (!attribute_stored_in_bmesh_flag(layer.name)) { - dst_layers.append(layer); + for (const CustomDataLayer &layer : Span<CustomDataLayer>{src->layers, src->totlayer}) { + if (attribute_stored_in_bmesh_flag(layer.name)) { + continue; + } + if (!(mask & CD_TYPE_AS_MASK(layer.type))) { + continue; } + dst_layers.append(layer); } - CustomData dst = src; + CustomData dst = *src; dst.layers = static_cast<CustomDataLayer *>( MEM_calloc_arrayN(dst_layers.size(), sizeof(CustomDataLayer), __func__)); dst.totlayer = dst_layers.size(); @@ -2402,18 +2408,6 @@ static CustomData shallow_copy_remove_non_bmesh_attributes(const CustomData &src return dst; } -bool CustomData_merge_mesh_to_bmesh(const CustomData *source, - CustomData *dest, - const eCustomDataMask mask, - const eCDAllocType alloctype, - const int totelem) -{ - CustomData source_copy = shallow_copy_remove_non_bmesh_attributes(*source); - const bool result = CustomData_merge(&source_copy, dest, mask, alloctype, totelem); - MEM_SAFE_FREE(source_copy.layers); - return result; -} - void CustomData_realloc(CustomData *data, const int old_size, const int new_size) { BLI_assert(new_size >= 0); @@ -2463,17 +2457,6 @@ void CustomData_copy(const CustomData *source, CustomData_merge(source, dest, mask, alloctype, totelem); } -void CustomData_copy_mesh_to_bmesh(const CustomData *source, - CustomData *dest, - const eCustomDataMask mask, - const eCDAllocType alloctype, - const int totelem) -{ - CustomData source_copy = shallow_copy_remove_non_bmesh_attributes(*source); - CustomData_copy(&source_copy, dest, mask, alloctype, totelem); - MEM_SAFE_FREE(source_copy.layers); -} - static void customData_free_layer__internal(CustomDataLayer *layer, const int totelem) { const LayerTypeInfo *typeInfo; @@ -3697,7 +3680,7 @@ bool CustomData_bmesh_merge(const CustomData *source, destold.layers = static_cast<CustomDataLayer *>(MEM_dupallocN(destold.layers)); } - if (CustomData_merge_mesh_to_bmesh(source, dest, mask, alloctype, 0) == false) { + if (CustomData_merge(source, dest, mask, alloctype, 0) == false) { if (destold.layers) { MEM_freeN(destold.layers); } @@ -5405,6 +5388,8 @@ const blender::CPPType *custom_data_type_to_cpp_type(const eCustomDataType type) return &CPPType::get<int8_t>(); case CD_PROP_BYTE_COLOR: return &CPPType::get<ColorGeometry4b>(); + case CD_PROP_STRING: + return &CPPType::get<MStringProperty>(); default: return nullptr; } @@ -5437,6 +5422,9 @@ eCustomDataType cpp_type_to_custom_data_type(const blender::CPPType &type) if (type.is<ColorGeometry4b>()) { return CD_PROP_BYTE_COLOR; } + if (type.is<MStringProperty>()) { + return CD_PROP_STRING; + } return static_cast<eCustomDataType>(-1); } @@ -5448,3 +5436,5 @@ size_t CustomData_get_elem_size(CustomDataLayer *layer) { return LAYERTYPEINFO[layer->type].size; } + +BLI_CPP_TYPE_MAKE(MStringProperty, MStringProperty, CPPTypeFlags::None); diff --git a/source/blender/blenkernel/intern/editmesh.cc b/source/blender/blenkernel/intern/editmesh.cc index 3a1dcd59f55..b586b4110f2 100644 --- a/source/blender/blenkernel/intern/editmesh.cc +++ b/source/blender/blenkernel/intern/editmesh.cc @@ -189,7 +189,7 @@ struct CageUserData { static void cage_mapped_verts_callback(void *userData, int index, const float co[3], - const float UNUSED(no[3])) + const float /*no*/[3]) { CageUserData *data = static_cast<CageUserData *>(userData); @@ -240,12 +240,12 @@ const float (*BKE_editmesh_vert_coords_when_deformed(Depsgraph *depsgraph, Object *object_eval = DEG_get_evaluated_object(depsgraph, ob); Mesh *editmesh_eval_final = BKE_object_get_editmesh_eval_final(object_eval); - if ((me->runtime.edit_data != nullptr) && (me->runtime.edit_data->vertexCos != nullptr)) { + if ((me->runtime->edit_data != nullptr) && (me->runtime->edit_data->vertexCos != nullptr)) { /* Deformed, and we have deformed coords already. */ - coords = me->runtime.edit_data->vertexCos; + coords = me->runtime->edit_data->vertexCos; } else if ((editmesh_eval_final != nullptr) && - (editmesh_eval_final->runtime.wrapper_type == ME_WRAPPER_TYPE_BMESH)) { + (editmesh_eval_final->runtime->wrapper_type == ME_WRAPPER_TYPE_BMESH)) { /* If this is an edit-mesh type, leave nullptr as we can use the vertex coords. */ } else { diff --git a/source/blender/blenkernel/intern/fluid.c b/source/blender/blenkernel/intern/fluid.c index 0a39207184a..5470231cd07 100644 --- a/source/blender/blenkernel/intern/fluid.c +++ b/source/blender/blenkernel/intern/fluid.c @@ -327,17 +327,17 @@ void BKE_fluid_cache_free(FluidDomainSettings *fds, Object *ob, int cache_map) if (cache_map & FLUID_DOMAIN_OUTDATED_DATA) { flags &= ~(FLUID_DOMAIN_BAKING_DATA | FLUID_DOMAIN_BAKED_DATA | FLUID_DOMAIN_OUTDATED_DATA); - BLI_path_join(temp_dir, sizeof(temp_dir), fds->cache_directory, FLUID_DOMAIN_DIR_CONFIG, NULL); + BLI_path_join(temp_dir, sizeof(temp_dir), fds->cache_directory, FLUID_DOMAIN_DIR_CONFIG); BLI_path_abs(temp_dir, relbase); if (BLI_exists(temp_dir)) { BLI_delete(temp_dir, true, true); } - BLI_path_join(temp_dir, sizeof(temp_dir), fds->cache_directory, FLUID_DOMAIN_DIR_DATA, NULL); + BLI_path_join(temp_dir, sizeof(temp_dir), fds->cache_directory, FLUID_DOMAIN_DIR_DATA); BLI_path_abs(temp_dir, relbase); if (BLI_exists(temp_dir)) { BLI_delete(temp_dir, true, true); } - BLI_path_join(temp_dir, sizeof(temp_dir), fds->cache_directory, FLUID_DOMAIN_DIR_SCRIPT, NULL); + BLI_path_join(temp_dir, sizeof(temp_dir), fds->cache_directory, FLUID_DOMAIN_DIR_SCRIPT); BLI_path_abs(temp_dir, relbase); if (BLI_exists(temp_dir)) { BLI_delete(temp_dir, true, true); @@ -346,7 +346,7 @@ void BKE_fluid_cache_free(FluidDomainSettings *fds, Object *ob, int cache_map) } if (cache_map & FLUID_DOMAIN_OUTDATED_NOISE) { flags &= ~(FLUID_DOMAIN_BAKING_NOISE | FLUID_DOMAIN_BAKED_NOISE | FLUID_DOMAIN_OUTDATED_NOISE); - BLI_path_join(temp_dir, sizeof(temp_dir), fds->cache_directory, FLUID_DOMAIN_DIR_NOISE, NULL); + BLI_path_join(temp_dir, sizeof(temp_dir), fds->cache_directory, FLUID_DOMAIN_DIR_NOISE); BLI_path_abs(temp_dir, relbase); if (BLI_exists(temp_dir)) { BLI_delete(temp_dir, true, true); @@ -355,7 +355,7 @@ void BKE_fluid_cache_free(FluidDomainSettings *fds, Object *ob, int cache_map) } if (cache_map & FLUID_DOMAIN_OUTDATED_MESH) { flags &= ~(FLUID_DOMAIN_BAKING_MESH | FLUID_DOMAIN_BAKED_MESH | FLUID_DOMAIN_OUTDATED_MESH); - BLI_path_join(temp_dir, sizeof(temp_dir), fds->cache_directory, FLUID_DOMAIN_DIR_MESH, NULL); + BLI_path_join(temp_dir, sizeof(temp_dir), fds->cache_directory, FLUID_DOMAIN_DIR_MESH); BLI_path_abs(temp_dir, relbase); if (BLI_exists(temp_dir)) { BLI_delete(temp_dir, true, true); @@ -365,8 +365,7 @@ void BKE_fluid_cache_free(FluidDomainSettings *fds, Object *ob, int cache_map) if (cache_map & FLUID_DOMAIN_OUTDATED_PARTICLES) { flags &= ~(FLUID_DOMAIN_BAKING_PARTICLES | FLUID_DOMAIN_BAKED_PARTICLES | FLUID_DOMAIN_OUTDATED_PARTICLES); - BLI_path_join( - temp_dir, sizeof(temp_dir), fds->cache_directory, FLUID_DOMAIN_DIR_PARTICLES, NULL); + BLI_path_join(temp_dir, sizeof(temp_dir), fds->cache_directory, FLUID_DOMAIN_DIR_PARTICLES); BLI_path_abs(temp_dir, relbase); if (BLI_exists(temp_dir)) { BLI_delete(temp_dir, true, true); @@ -375,7 +374,7 @@ void BKE_fluid_cache_free(FluidDomainSettings *fds, Object *ob, int cache_map) } if (cache_map & FLUID_DOMAIN_OUTDATED_GUIDE) { flags &= ~(FLUID_DOMAIN_BAKING_GUIDE | FLUID_DOMAIN_BAKED_GUIDE | FLUID_DOMAIN_OUTDATED_GUIDE); - BLI_path_join(temp_dir, sizeof(temp_dir), fds->cache_directory, FLUID_DOMAIN_DIR_GUIDE, NULL); + BLI_path_join(temp_dir, sizeof(temp_dir), fds->cache_directory, FLUID_DOMAIN_DIR_GUIDE); BLI_path_abs(temp_dir, relbase); if (BLI_exists(temp_dir)) { BLI_delete(temp_dir, true, true); diff --git a/source/blender/blenkernel/intern/geometry_component_instances.cc b/source/blender/blenkernel/intern/geometry_component_instances.cc index 0b5f7cbf902..be1d9524509 100644 --- a/source/blender/blenkernel/intern/geometry_component_instances.cc +++ b/source/blender/blenkernel/intern/geometry_component_instances.cc @@ -16,6 +16,7 @@ #include "BKE_attribute_math.hh" #include "BKE_geometry_set.hh" #include "BKE_geometry_set_instances.hh" +#include "BKE_instances.hh" #include "attribute_access_intern.hh" @@ -29,8 +30,8 @@ using blender::MutableSpan; using blender::Set; using blender::Span; using blender::VectorSet; - -BLI_CPP_TYPE_MAKE(InstanceReference, InstanceReference, CPPTypeFlags::None) +using blender::bke::InstanceReference; +using blender::bke::Instances; /* -------------------------------------------------------------------- */ /** \name Geometry Component Implementation @@ -40,339 +41,76 @@ InstancesComponent::InstancesComponent() : GeometryComponent(GEO_COMPONENT_TYPE_ { } -GeometryComponent *InstancesComponent::copy() const -{ - InstancesComponent *new_component = new InstancesComponent(); - new_component->instance_reference_handles_ = instance_reference_handles_; - new_component->instance_transforms_ = instance_transforms_; - new_component->references_ = references_; - new_component->attributes_ = attributes_; - return new_component; -} - -void InstancesComponent::reserve(int min_capacity) +InstancesComponent::~InstancesComponent() { - instance_reference_handles_.reserve(min_capacity); - instance_transforms_.reserve(min_capacity); - attributes_.reallocate(min_capacity); + this->clear(); } -void InstancesComponent::resize(int capacity) +GeometryComponent *InstancesComponent::copy() const { - instance_reference_handles_.resize(capacity); - instance_transforms_.resize(capacity); - attributes_.reallocate(capacity); + InstancesComponent *new_component = new InstancesComponent(); + if (instances_ != nullptr) { + new_component->instances_ = new Instances(*instances_); + new_component->ownership_ = GeometryOwnershipType::Owned; + } + return new_component; } void InstancesComponent::clear() { - instance_reference_handles_.clear(); - instance_transforms_.clear(); - attributes_.clear(); - references_.clear(); -} - -void InstancesComponent::add_instance(const int instance_handle, const float4x4 &transform) -{ - BLI_assert(instance_handle >= 0); - BLI_assert(instance_handle < references_.size()); - instance_reference_handles_.append(instance_handle); - instance_transforms_.append(transform); - attributes_.reallocate(this->instances_num()); -} - -blender::Span<int> InstancesComponent::instance_reference_handles() const -{ - return instance_reference_handles_; -} - -blender::MutableSpan<int> InstancesComponent::instance_reference_handles() -{ - return instance_reference_handles_; -} - -blender::MutableSpan<blender::float4x4> InstancesComponent::instance_transforms() -{ - return instance_transforms_; -} -blender::Span<blender::float4x4> InstancesComponent::instance_transforms() const -{ - return instance_transforms_; -} - -GeometrySet &InstancesComponent::geometry_set_from_reference(const int reference_index) -{ - /* If this assert fails, it means #ensure_geometry_instances must be called first or that the - * reference can't be converted to a geometry set. */ - BLI_assert(references_[reference_index].type() == InstanceReference::Type::GeometrySet); - - /* The const cast is okay because the instance's hash in the set - * is not changed by adjusting the data inside the geometry set. */ - return const_cast<GeometrySet &>(references_[reference_index].geometry_set()); -} - -int InstancesComponent::add_reference(const InstanceReference &reference) -{ - return references_.index_of_or_add_as(reference); -} - -blender::Span<InstanceReference> InstancesComponent::references() const -{ - return references_; -} - -template<typename T> -static void copy_data_based_on_mask(Span<T> src, MutableSpan<T> dst, IndexMask mask) -{ - BLI_assert(src.data() != dst.data()); - using namespace blender; - threading::parallel_for(mask.index_range(), 1024, [&](IndexRange range) { - for (const int i : range) { - dst[i] = src[mask[i]]; - } - }); -} - -void InstancesComponent::remove_instances(const IndexMask mask) -{ - using namespace blender; - if (mask.is_range() && mask.as_range().start() == 0) { - /* Deleting from the end of the array can be much faster since no data has to be shifted. */ - this->resize(mask.size()); - this->remove_unused_references(); - return; + BLI_assert(this->is_mutable()); + if (ownership_ == GeometryOwnershipType::Owned) { + delete instances_; } - - Vector<int> new_handles(mask.size()); - copy_data_based_on_mask<int>(this->instance_reference_handles(), new_handles, mask); - instance_reference_handles_ = std::move(new_handles); - Vector<float4x4> new_transforms(mask.size()); - copy_data_based_on_mask<float4x4>(this->instance_transforms(), new_transforms, mask); - instance_transforms_ = std::move(new_transforms); - - const bke::CustomDataAttributes &src_attributes = attributes_; - - bke::CustomDataAttributes dst_attributes; - dst_attributes.reallocate(mask.size()); - - src_attributes.foreach_attribute( - [&](const bke::AttributeIDRef &id, const bke::AttributeMetaData &meta_data) { - if (!id.should_be_kept()) { - return true; - } - - GSpan src = *src_attributes.get_for_read(id); - dst_attributes.create(id, meta_data.data_type); - GMutableSpan dst = *dst_attributes.get_for_write(id); - - attribute_math::convert_to_static_type(src.type(), [&](auto dummy) { - using T = decltype(dummy); - copy_data_based_on_mask<T>(src.typed<T>(), dst.typed<T>(), mask); - }); - return true; - }, - ATTR_DOMAIN_INSTANCE); - - attributes_ = std::move(dst_attributes); - this->remove_unused_references(); + instances_ = nullptr; } -void InstancesComponent::remove_unused_references() +bool InstancesComponent::is_empty() const { - using namespace blender; - using namespace blender::bke; - - const int tot_instances = this->instances_num(); - const int tot_references_before = references_.size(); - - if (tot_instances == 0) { - /* If there are no instances, no reference is needed. */ - references_.clear(); - return; - } - if (tot_references_before == 1) { - /* There is only one reference and at least one instance. So the only existing reference is - * used. Nothing to do here. */ - return; - } - - Array<bool> usage_by_handle(tot_references_before, false); - std::mutex mutex; - - /* Loop over all instances to see which references are used. */ - threading::parallel_for(IndexRange(tot_instances), 1000, [&](IndexRange range) { - /* Use local counter to avoid lock contention. */ - Array<bool> local_usage_by_handle(tot_references_before, false); - - for (const int i : range) { - const int handle = instance_reference_handles_[i]; - BLI_assert(handle >= 0 && handle < tot_references_before); - local_usage_by_handle[handle] = true; - } - - std::lock_guard lock{mutex}; - for (const int i : IndexRange(tot_references_before)) { - usage_by_handle[i] |= local_usage_by_handle[i]; - } - }); - - if (!usage_by_handle.as_span().contains(false)) { - /* All references are used. */ - return; - } - - /* Create new references and a mapping for the handles. */ - Vector<int> handle_mapping; - VectorSet<InstanceReference> new_references; - int next_new_handle = 0; - bool handles_have_to_be_updated = false; - for (const int old_handle : IndexRange(tot_references_before)) { - if (!usage_by_handle[old_handle]) { - /* Add some dummy value. It won't be read again. */ - handle_mapping.append(-1); - } - else { - const InstanceReference &reference = references_[old_handle]; - handle_mapping.append(next_new_handle); - new_references.add_new(reference); - if (old_handle != next_new_handle) { - handles_have_to_be_updated = true; - } - next_new_handle++; + if (instances_ != nullptr) { + if (instances_->instances_num() > 0) { + return false; } } - references_ = new_references; - - if (!handles_have_to_be_updated) { - /* All remaining handles are the same as before, so they don't have to be updated. This happens - * when unused handles are only at the end. */ - return; - } - - /* Update handles of instances. */ - threading::parallel_for(IndexRange(tot_instances), 1000, [&](IndexRange range) { - for (const int i : range) { - instance_reference_handles_[i] = handle_mapping[instance_reference_handles_[i]]; - } - }); -} - -int InstancesComponent::instances_num() const -{ - return instance_transforms_.size(); -} - -int InstancesComponent::references_num() const -{ - return references_.size(); -} - -bool InstancesComponent::is_empty() const -{ - return this->instance_reference_handles_.size() == 0; + return true; } bool InstancesComponent::owns_direct_data() const { - for (const InstanceReference &reference : references_) { - if (!reference.owns_direct_data()) { - return false; - } + if (instances_ != nullptr) { + return instances_->owns_direct_data(); } 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(); + if (instances_ != nullptr) { + instances_->ensure_owns_direct_data(); } } -static blender::Array<int> generate_unique_instance_ids(Span<int> original_ids) +const blender::bke::Instances *InstancesComponent::get_for_read() const { - using namespace blender; - Array<int> unique_ids(original_ids.size()); - - Set<int> used_unique_ids; - used_unique_ids.reserve(original_ids.size()); - Vector<int> instances_with_id_collision; - for (const int instance_index : original_ids.index_range()) { - const int original_id = original_ids[instance_index]; - if (used_unique_ids.add(original_id)) { - /* The original id has not been used by another instance yet. */ - unique_ids[instance_index] = original_id; - } - else { - /* The original id of this instance collided with a previous instance, it needs to be looked - * at again in a second pass. Don't generate a new random id here, because this might collide - * with other existing ids. */ - instances_with_id_collision.append(instance_index); - } - } - - Map<int, RandomNumberGenerator> generator_by_original_id; - for (const int instance_index : instances_with_id_collision) { - const int original_id = original_ids[instance_index]; - RandomNumberGenerator &rng = generator_by_original_id.lookup_or_add_cb(original_id, [&]() { - RandomNumberGenerator rng; - rng.seed_random(original_id); - return rng; - }); - - const int max_iteration = 100; - for (int iteration = 0;; iteration++) { - /* Try generating random numbers until an unused one has been found. */ - const int random_id = rng.get_int32(); - if (used_unique_ids.add(random_id)) { - /* This random id is not used by another instance. */ - unique_ids[instance_index] = random_id; - break; - } - if (iteration == max_iteration) { - /* It seems to be very unlikely that we ever run into this case (assuming there are less - * than 2^30 instances). However, if that happens, it's better to use an id that is not - * unique than to be stuck in an infinite loop. */ - unique_ids[instance_index] = original_id; - break; - } - } - } - - return unique_ids; + return instances_; } -blender::Span<int> InstancesComponent::almost_unique_ids() const +blender::bke::Instances *InstancesComponent::get_for_write() { - std::lock_guard lock(almost_unique_ids_mutex_); - std::optional<GSpan> instance_ids_gspan = attributes_.get_for_read("id"); - if (instance_ids_gspan) { - Span<int> instance_ids = instance_ids_gspan->typed<int>(); - if (almost_unique_ids_.size() != instance_ids.size()) { - almost_unique_ids_ = generate_unique_instance_ids(instance_ids); - } - } - else { - almost_unique_ids_.reinitialize(this->instances_num()); - for (const int i : almost_unique_ids_.index_range()) { - almost_unique_ids_[i] = i; - } + BLI_assert(this->is_mutable()); + if (ownership_ == GeometryOwnershipType::ReadOnly) { + instances_ = new Instances(*instances_); + ownership_ = GeometryOwnershipType::Owned; } - return almost_unique_ids_; + return instances_; } -blender::bke::CustomDataAttributes &InstancesComponent::instance_attributes() +void InstancesComponent::replace(Instances *instances, GeometryOwnershipType ownership) { - return this->attributes_; -} - -const blender::bke::CustomDataAttributes &InstancesComponent::instance_attributes() const -{ - return this->attributes_; + BLI_assert(this->is_mutable()); + this->clear(); + instances_ = instances; + ownership_ = ownership; } namespace blender::bke { @@ -397,16 +135,21 @@ class InstancePositionAttributeProvider final : public BuiltinAttributeProvider GVArray try_get_for_read(const void *owner) const final { - const InstancesComponent &instances_component = *static_cast<const InstancesComponent *>( - owner); - Span<float4x4> transforms = instances_component.instance_transforms(); + const Instances *instances = static_cast<const Instances *>(owner); + if (instances == nullptr) { + return {}; + } + Span<float4x4> transforms = instances->transforms(); return VArray<float3>::ForDerivedSpan<float4x4, get_transform_position>(transforms); } GAttributeWriter try_get_for_write(void *owner) const final { - InstancesComponent &instances_component = *static_cast<InstancesComponent *>(owner); - MutableSpan<float4x4> transforms = instances_component.instance_transforms(); + Instances *instances = static_cast<Instances *>(owner); + if (instances == nullptr) { + return {}; + } + MutableSpan<float4x4> transforms = instances->transforms(); return {VMutableArray<float3>::ForDerivedSpan<float4x4, get_transform_position, set_transform_position>(transforms), @@ -434,16 +177,16 @@ static ComponentAttributeProviders create_attribute_providers_for_instances() static InstancePositionAttributeProvider position; static CustomDataAccessInfo instance_custom_data_access = { [](void *owner) -> CustomData * { - InstancesComponent &inst = *static_cast<InstancesComponent *>(owner); - return &inst.instance_attributes().data; + Instances *instances = static_cast<Instances *>(owner); + return &instances->custom_data_attributes().data; }, [](const void *owner) -> const CustomData * { - const InstancesComponent &inst = *static_cast<const InstancesComponent *>(owner); - return &inst.instance_attributes().data; + const Instances *instances = static_cast<const Instances *>(owner); + return &instances->custom_data_attributes().data; }, [](const void *owner) -> int { - const InstancesComponent &inst = *static_cast<const InstancesComponent *>(owner); - return inst.instances_num(); + const Instances *instances = static_cast<const Instances *>(owner); + return instances->instances_num(); }}; /** @@ -479,10 +222,10 @@ static AttributeAccessorFunctions get_instances_accessor_functions() if (owner == nullptr) { return 0; } - const InstancesComponent &instances = *static_cast<const InstancesComponent *>(owner); + const Instances *instances = static_cast<const Instances *>(owner); switch (domain) { case ATTR_DOMAIN_INSTANCE: - return instances.instances_num(); + return instances->instances_num(); default: return 0; } @@ -508,18 +251,30 @@ static const AttributeAccessorFunctions &get_instances_accessor_functions_ref() return fn; } +blender::bke::AttributeAccessor Instances::attributes() const +{ + return blender::bke::AttributeAccessor(this, + blender::bke::get_instances_accessor_functions_ref()); +} + +blender::bke::MutableAttributeAccessor Instances::attributes_for_write() +{ + return blender::bke::MutableAttributeAccessor( + this, blender::bke::get_instances_accessor_functions_ref()); +} + } // namespace blender::bke std::optional<blender::bke::AttributeAccessor> InstancesComponent::attributes() const { - return blender::bke::AttributeAccessor(this, + return blender::bke::AttributeAccessor(instances_, blender::bke::get_instances_accessor_functions_ref()); } std::optional<blender::bke::MutableAttributeAccessor> InstancesComponent::attributes_for_write() { return blender::bke::MutableAttributeAccessor( - this, blender::bke::get_instances_accessor_functions_ref()); + instances_, blender::bke::get_instances_accessor_functions_ref()); } /** \} */ diff --git a/source/blender/blenkernel/intern/geometry_fields.cc b/source/blender/blenkernel/intern/geometry_fields.cc index b492af4af77..82ffda57398 100644 --- a/source/blender/blenkernel/intern/geometry_fields.cc +++ b/source/blender/blenkernel/intern/geometry_fields.cc @@ -4,6 +4,7 @@ #include "BKE_curves.hh" #include "BKE_geometry_fields.hh" #include "BKE_geometry_set.hh" +#include "BKE_instances.hh" #include "BKE_mesh.h" #include "BKE_pointcloud.h" #include "BKE_type_conversions.hh" @@ -64,7 +65,7 @@ GeometryFieldContext::GeometryFieldContext(const GeometryComponent &component, case GEO_COMPONENT_TYPE_INSTANCES: { const InstancesComponent &instances_component = static_cast<const InstancesComponent &>( component); - geometry_ = &instances_component; + geometry_ = instances_component.get_for_read(); break; } case GEO_COMPONENT_TYPE_VOLUME: @@ -86,7 +87,7 @@ GeometryFieldContext::GeometryFieldContext(const PointCloud &points) : geometry_(&points), type_(GEO_COMPONENT_TYPE_POINT_CLOUD), domain_(ATTR_DOMAIN_POINT) { } -GeometryFieldContext::GeometryFieldContext(const InstancesComponent &instances) +GeometryFieldContext::GeometryFieldContext(const Instances &instances) : geometry_(&instances), type_(GEO_COMPONENT_TYPE_INSTANCES), domain_(ATTR_DOMAIN_INSTANCE) { } @@ -102,7 +103,7 @@ std::optional<AttributeAccessor> GeometryFieldContext::attributes() const if (const PointCloud *pointcloud = this->pointcloud()) { return pointcloud->attributes(); } - if (const InstancesComponent *instances = this->instances()) { + if (const Instances *instances = this->instances()) { return instances->attributes(); } return {}; @@ -124,11 +125,10 @@ const PointCloud *GeometryFieldContext::pointcloud() const static_cast<const PointCloud *>(geometry_) : nullptr; } -const InstancesComponent *GeometryFieldContext::instances() const +const Instances *GeometryFieldContext::instances() const { - return this->type() == GEO_COMPONENT_TYPE_INSTANCES ? - static_cast<const InstancesComponent *>(geometry_) : - nullptr; + return this->type() == GEO_COMPONENT_TYPE_INSTANCES ? static_cast<const Instances *>(geometry_) : + nullptr; } GVArray GeometryFieldInput::get_varray_for_context(const fn::FieldContext &context, @@ -230,7 +230,7 @@ GVArray InstancesFieldInput::get_varray_for_context(const fn::FieldContext &cont { if (const GeometryFieldContext *geometry_context = dynamic_cast<const GeometryFieldContext *>( &context)) { - if (const InstancesComponent *instances = geometry_context->instances()) { + if (const Instances *instances = geometry_context->instances()) { return this->get_varray_for_context(*instances, mask); } } diff --git a/source/blender/blenkernel/intern/geometry_set.cc b/source/blender/blenkernel/intern/geometry_set.cc index 46ff8141504..ee4c398c3d6 100644 --- a/source/blender/blenkernel/intern/geometry_set.cc +++ b/source/blender/blenkernel/intern/geometry_set.cc @@ -9,6 +9,7 @@ #include "BKE_attribute.h" #include "BKE_curves.hh" #include "BKE_geometry_set.hh" +#include "BKE_instances.hh" #include "BKE_lib_id.h" #include "BKE_mesh.h" #include "BKE_mesh_wrapper.h" @@ -31,6 +32,8 @@ using blender::MutableSpan; using blender::Span; using blender::StringRef; using blender::Vector; +using blender::bke::InstanceReference; +using blender::bke::Instances; /* -------------------------------------------------------------------- */ /** \name Geometry Component @@ -256,8 +259,7 @@ std::ostream &operator<<(std::ostream &stream, const GeometrySet &geometry_set) parts.append(std::to_string(BKE_volume_num_grids(volume)) + " volume grids"); } if (geometry_set.has_instances()) { - parts.append(std::to_string( - geometry_set.get_component_for_read<InstancesComponent>()->instances_num()) + + parts.append(std::to_string(geometry_set.get_instances_for_read()->instances_num()) + " instances"); } if (geometry_set.get_curve_edit_hints_for_read()) { @@ -338,6 +340,12 @@ const Curves *GeometrySet::get_curves_for_read() const return (component == nullptr) ? nullptr : component->get_for_read(); } +const Instances *GeometrySet::get_instances_for_read() const +{ + const InstancesComponent *component = this->get_component_for_read<InstancesComponent>(); + return (component == nullptr) ? nullptr : component->get_for_read(); +} + const blender::bke::CurvesEditHints *GeometrySet::get_curve_edit_hints_for_read() const { const GeometryComponentEditData *component = @@ -354,7 +362,8 @@ bool GeometrySet::has_pointcloud() const bool GeometrySet::has_instances() const { const InstancesComponent *component = this->get_component_for_read<InstancesComponent>(); - return component != nullptr && component->instances_num() >= 1; + return component != nullptr && component->get_for_read() != nullptr && + component->get_for_read()->instances_num() >= 1; } bool GeometrySet::has_volume() const @@ -428,6 +437,14 @@ GeometrySet GeometrySet::create_with_curves(Curves *curves, GeometryOwnershipTyp return geometry_set; } +GeometrySet GeometrySet::create_with_instances(Instances *instances, + GeometryOwnershipType ownership) +{ + GeometrySet geometry_set; + geometry_set.replace_instances(instances, ownership); + return geometry_set; +} + void GeometrySet::replace_mesh(Mesh *mesh, GeometryOwnershipType ownership) { if (mesh == nullptr) { @@ -456,6 +473,20 @@ void GeometrySet::replace_curves(Curves *curves, GeometryOwnershipType ownership component.replace(curves, ownership); } +void GeometrySet::replace_instances(Instances *instances, GeometryOwnershipType ownership) +{ + if (instances == nullptr) { + this->remove<InstancesComponent>(); + return; + } + if (instances == this->get_instances_for_read()) { + return; + } + this->remove<InstancesComponent>(); + InstancesComponent &component = this->get_component_for_write<InstancesComponent>(); + component.replace(instances, ownership); +} + void GeometrySet::replace_pointcloud(PointCloud *pointcloud, GeometryOwnershipType ownership) { if (pointcloud == nullptr) { @@ -508,6 +539,12 @@ Curves *GeometrySet::get_curves_for_write() return component == nullptr ? nullptr : component->get_for_write(); } +Instances *GeometrySet::get_instances_for_write() +{ + InstancesComponent *component = this->get_component_ptr<InstancesComponent>(); + return component == nullptr ? nullptr : component->get_for_write(); +} + blender::bke::CurvesEditHints *GeometrySet::get_curve_edit_hints_for_write() { if (!this->has<GeometryComponentEditData>()) { @@ -539,7 +576,7 @@ void GeometrySet::attribute_foreach(const Span<GeometryComponentType> component_ } } if (include_instances && this->has_instances()) { - const InstancesComponent &instances = *this->get_component_for_read<InstancesComponent>(); + const Instances &instances = *this->get_instances_for_read(); instances.foreach_referenced_geometry([&](const GeometrySet &instance_geometry_set) { instance_geometry_set.attribute_foreach(component_types, include_instances, callback); }); @@ -570,7 +607,10 @@ void GeometrySet::gather_attributes_for_propagation( return; } } - + if (meta_data.data_type == CD_PROP_STRING) { + /* Propagating string attributes is not supported yet. */ + return; + } if (!attribute_id.should_be_kept()) { return; } @@ -611,7 +651,7 @@ static void gather_component_types_recursive(const GeometrySet &geometry_set, if (!include_instances) { return; } - const InstancesComponent *instances = geometry_set.get_component_for_read<InstancesComponent>(); + const blender::bke::Instances *instances = geometry_set.get_instances_for_read(); if (instances == nullptr) { return; } @@ -638,12 +678,11 @@ static void gather_mutable_geometry_sets(GeometrySet &geometry_set, } /* In the future this can be improved by deduplicating instance references across different * instances. */ - InstancesComponent &instances_component = - geometry_set.get_component_for_write<InstancesComponent>(); - instances_component.ensure_geometry_instances(); - for (const int handle : instances_component.references().index_range()) { - if (instances_component.references()[handle].type() == InstanceReference::Type::GeometrySet) { - GeometrySet &instance_geometry = instances_component.geometry_set_from_reference(handle); + Instances &instances = *geometry_set.get_instances_for_write(); + instances.ensure_geometry_instances(); + for (const int handle : instances.references().index_range()) { + if (instances.references()[handle].type() == InstanceReference::Type::GeometrySet) { + GeometrySet &instance_geometry = instances.geometry_set_from_reference(handle); gather_mutable_geometry_sets(instance_geometry, r_geometry_sets); } } diff --git a/source/blender/blenkernel/intern/geometry_set_instances.cc b/source/blender/blenkernel/intern/geometry_set_instances.cc index 0ae49a586f1..e078991187d 100644 --- a/source/blender/blenkernel/intern/geometry_set_instances.cc +++ b/source/blender/blenkernel/intern/geometry_set_instances.cc @@ -2,6 +2,7 @@ #include "BKE_collection.h" #include "BKE_geometry_set_instances.hh" +#include "BKE_instances.hh" #include "BKE_material.h" #include "BKE_mesh.h" #include "BKE_mesh_wrapper.h" @@ -62,12 +63,11 @@ GeometrySet object_get_evaluated_geometry_set(const Object &object) return geometry_set; } if (object.type == OB_EMPTY && object.instance_collection != nullptr) { - GeometrySet geometry_set; Collection &collection = *object.instance_collection; - InstancesComponent &instances = geometry_set.get_component_for_write<InstancesComponent>(); - const int handle = instances.add_reference(collection); - instances.add_instance(handle, float4x4::identity()); - return geometry_set; + std::unique_ptr<Instances> instances = std::make_unique<Instances>(); + const int handle = instances->add_reference(collection); + instances->add_instance(handle, float4x4::identity()); + return GeometrySet::create_with_instances(instances.release()); } /* Return by value since there is not always an existing geometry set owned elsewhere to use. */ @@ -115,12 +115,11 @@ static void geometry_set_collect_recursive(const GeometrySet &geometry_set, r_sets.append({geometry_set, {transform}}); if (geometry_set.has_instances()) { - const InstancesComponent &instances_component = - *geometry_set.get_component_for_read<InstancesComponent>(); + const Instances &instances = *geometry_set.get_instances_for_read(); - Span<float4x4> transforms = instances_component.instance_transforms(); - Span<int> handles = instances_component.instance_reference_handles(); - Span<InstanceReference> references = instances_component.references(); + Span<float4x4> transforms = instances.transforms(); + Span<int> handles = instances.reference_handles(); + Span<InstanceReference> references = instances.references(); for (const int i : transforms.index_range()) { const InstanceReference &reference = references[handles[i]]; const float4x4 instance_transform = transform * transforms[i]; @@ -156,9 +155,7 @@ void geometry_set_gather_instances(const GeometrySet &geometry_set, geometry_set_collect_recursive(geometry_set, float4x4::identity(), r_instance_groups); } -} // namespace blender::bke - -void InstancesComponent::foreach_referenced_geometry( +void Instances::foreach_referenced_geometry( blender::FunctionRef<void(const GeometrySet &geometry_set)> callback) const { using namespace blender::bke; @@ -191,7 +188,7 @@ void InstancesComponent::foreach_referenced_geometry( } } -void InstancesComponent::ensure_geometry_instances() +void Instances::ensure_geometry_instances() { using namespace blender; using namespace blender::bke; @@ -211,9 +208,7 @@ void InstancesComponent::ensure_geometry_instances() const Object &object = reference.object(); GeometrySet object_geometry_set = object_get_evaluated_geometry_set(object); if (object_geometry_set.has_instances()) { - InstancesComponent &component = - object_geometry_set.get_component_for_write<InstancesComponent>(); - component.ensure_geometry_instances(); + object_geometry_set.get_instances_for_write()->ensure_geometry_instances(); } new_references.add_new(std::move(object_geometry_set)); break; @@ -221,22 +216,22 @@ void InstancesComponent::ensure_geometry_instances() case InstanceReference::Type::Collection: { /* Create a new reference that contains a geometry set that contains all objects from the * collection as instances. */ - GeometrySet collection_geometry_set; - InstancesComponent &component = - collection_geometry_set.get_component_for_write<InstancesComponent>(); + std::unique_ptr<Instances> instances = std::make_unique<Instances>(); Collection &collection = reference.collection(); FOREACH_COLLECTION_OBJECT_RECURSIVE_BEGIN (&collection, object) { - const int handle = component.add_reference(*object); - component.add_instance(handle, object->obmat); - float4x4 &transform = component.instance_transforms().last(); + const int handle = instances->add_reference(*object); + instances->add_instance(handle, object->obmat); + float4x4 &transform = instances->transforms().last(); sub_v3_v3(transform.values[3], collection.instance_offset); } FOREACH_COLLECTION_OBJECT_RECURSIVE_END; - component.ensure_geometry_instances(); - new_references.add_new(std::move(collection_geometry_set)); + instances->ensure_geometry_instances(); + new_references.add_new(GeometrySet::create_with_instances(instances.release())); break; } } } references_ = std::move(new_references); } + +} // namespace blender::bke diff --git a/source/blender/blenkernel/intern/gpencil_geom.cc b/source/blender/blenkernel/intern/gpencil_geom.cc index 92b11ecaa61..52fcdef8a43 100644 --- a/source/blender/blenkernel/intern/gpencil_geom.cc +++ b/source/blender/blenkernel/intern/gpencil_geom.cc @@ -1856,6 +1856,10 @@ bool BKE_gpencil_stroke_close(bGPDstroke *gps) pt->strength = interpf(pt2->strength, pt1->strength, step); pt->flag = 0; interp_v4_v4v4(pt->vert_color, pt1->vert_color, pt2->vert_color, step); + /* Set point as selected. */ + if (gps->flag & GP_STROKE_SELECT) { + pt->flag |= GP_SPOINT_SELECT; + } /* Set weights. */ if (gps->dvert != nullptr) { diff --git a/source/blender/blenkernel/intern/image.cc b/source/blender/blenkernel/intern/image.cc index 409eb0067b5..eae8b454189 100644 --- a/source/blender/blenkernel/intern/image.cc +++ b/source/blender/blenkernel/intern/image.cc @@ -297,7 +297,7 @@ static void image_foreach_path(ID *id, BPathForeachPathData *bpath_data) /* Put the filepath back together using the new directory and the original file name. */ char new_dir[FILE_MAXDIR]; BLI_split_dir_part(temp_path, new_dir, sizeof(new_dir)); - BLI_join_dirfile(ima->filepath, sizeof(ima->filepath), new_dir, orig_file); + BLI_path_join(ima->filepath, sizeof(ima->filepath), new_dir, orig_file); } } else { @@ -3082,16 +3082,10 @@ void BKE_image_signal(Main *bmain, Image *ima, ImageUser *iuser, int signal) /* Free all but the first tile. */ image_remove_all_tiles(ima); - /* If the remaining tile is generated, we need to again ensure that we - * wouldn't continue to use the old filepath. - * - * Otherwise, if this used to be a UDIM image, get the concrete filepath associated + /* If this used to be a UDIM image, get the concrete filepath associated * with the remaining tile and use that as the new filepath. */ ImageTile *base_tile = BKE_image_get_tile(ima, 0); - if ((base_tile->gen_flag & IMA_GEN_TILE) != 0) { - ima->filepath[0] = '\0'; - } - else if (BKE_image_is_filename_tokenized(ima->filepath)) { + if (BKE_image_is_filename_tokenized(ima->filepath)) { const bool was_relative = BLI_path_is_rel(ima->filepath); eUDIM_TILE_FORMAT tile_format; @@ -3183,10 +3177,14 @@ void BKE_image_signal(Main *bmain, Image *ima, ImageUser *iuser, int signal) * left. */ image_remove_all_tiles(ima); - int remaining_tile_number = ((ImageTile *)ima->tiles.first)->tile_number; + ImageTile *base_tile = BKE_image_get_tile(ima, 0); + int remaining_tile_number = base_tile->tile_number; bool needs_final_cleanup = true; - /* Add in all the new tiles. */ + /* Add in all the new tiles. As the image is proven to be on disk at this point, remove + * the generation flag from the remaining tile in case this was previously a generated + * image. */ + base_tile->gen_flag &= ~IMA_GEN_TILE; LISTBASE_FOREACH (LinkData *, new_tile, &new_tiles) { int new_tile_number = POINTER_AS_INT(new_tile->data); BKE_image_add_tile(ima, new_tile_number, nullptr); @@ -3202,6 +3200,11 @@ void BKE_image_signal(Main *bmain, Image *ima, ImageUser *iuser, int signal) } BLI_freelistN(&new_tiles); } + else if (ima->filepath[0] != '\0') { + /* If the filepath is set at this point remove the generation flag. */ + ImageTile *base_tile = BKE_image_get_tile(ima, 0); + base_tile->gen_flag &= ~IMA_GEN_TILE; + } if (iuser) { image_tag_reload(ima, nullptr, iuser, ima); @@ -3331,7 +3334,7 @@ bool BKE_image_get_tile_info(char *filepath, ListBase *tiles, int *r_tile_start, MEM_SAFE_FREE(udim_pattern); if (all_valid_udim && min_udim <= IMA_UDIM_MAX) { - BLI_join_dirfile(filepath, FILE_MAX, dirname, filename); + BLI_path_join(filepath, FILE_MAX, dirname, filename); *r_tile_start = min_udim; *r_tile_range = max_udim - min_udim + 1; diff --git a/source/blender/blenkernel/intern/image_format.cc b/source/blender/blenkernel/intern/image_format.cc index 8d1aeac76fb..5b861eff166 100644 --- a/source/blender/blenkernel/intern/image_format.cc +++ b/source/blender/blenkernel/intern/image_format.cc @@ -201,6 +201,7 @@ bool BKE_imtype_is_movie(const char imtype) case R_IMF_IMTYPE_H264: case R_IMF_IMTYPE_THEORA: case R_IMF_IMTYPE_XVID: + case R_IMF_IMTYPE_AV1: return true; } return false; @@ -433,7 +434,8 @@ static bool do_add_image_extension(char *string, R_IMF_IMTYPE_FFMPEG, R_IMF_IMTYPE_H264, R_IMF_IMTYPE_THEORA, - R_IMF_IMTYPE_XVID)) { + R_IMF_IMTYPE_XVID, + R_IMF_IMTYPE_AV1)) { if (!BLI_path_extension_check(string, extension_test = ".png")) { extension = extension_test; } @@ -627,7 +629,8 @@ void BKE_image_format_to_imbuf(ImBuf *ibuf, const ImageFormatData *imf) R_IMF_IMTYPE_FFMPEG, R_IMF_IMTYPE_H264, R_IMF_IMTYPE_THEORA, - R_IMF_IMTYPE_XVID)) { + R_IMF_IMTYPE_XVID, + R_IMF_IMTYPE_AV1)) { ibuf->ftype = IMB_FTYPE_PNG; if (imtype == R_IMF_IMTYPE_PNG) { diff --git a/source/blender/blenkernel/intern/image_save.cc b/source/blender/blenkernel/intern/image_save.cc index e227f9cba5e..003211e6288 100644 --- a/source/blender/blenkernel/intern/image_save.cc +++ b/source/blender/blenkernel/intern/image_save.cc @@ -175,12 +175,12 @@ bool BKE_image_save_options_init(ImageSaveOptions *opts, BLI_strncpy(opts->filepath, G.ima, sizeof(opts->filepath)); } else { - BLI_path_join(opts->filepath, sizeof(opts->filepath), "//", DATA_("untitled"), nullptr); + BLI_path_join(opts->filepath, sizeof(opts->filepath), "//", DATA_("untitled")); BLI_path_abs(opts->filepath, BKE_main_blendfile_path(bmain)); } } else { - BLI_path_join(opts->filepath, sizeof(opts->filepath), "//", ima->id.name + 2, nullptr); + BLI_path_join(opts->filepath, sizeof(opts->filepath), "//", ima->id.name + 2); BLI_path_make_safe(opts->filepath); BLI_path_abs(opts->filepath, is_prev_save ? G.ima : BKE_main_blendfile_path(bmain)); } @@ -968,6 +968,7 @@ bool BKE_image_render_write(ReportList *reports, /* optional preview images for exr */ if (ok && (image_format.flag & R_IMF_FLAG_PREVIEW_JPG)) { image_format.imtype = R_IMF_IMTYPE_JPEG90; + image_format.depth = R_IMF_CHAN_DEPTH_8; if (BLI_path_extension_check(filepath, ".exr")) { filepath[strlen(filepath) - 4] = 0; @@ -1025,6 +1026,7 @@ bool BKE_image_render_write(ReportList *reports, /* optional preview images for exr */ if (ok && is_exr_rr && (image_format.flag & R_IMF_FLAG_PREVIEW_JPG)) { image_format.imtype = R_IMF_IMTYPE_JPEG90; + image_format.depth = R_IMF_CHAN_DEPTH_8; if (BLI_path_extension_check(filepath, ".exr")) { filepath[strlen(filepath) - 4] = 0; diff --git a/source/blender/blenkernel/intern/instances.cc b/source/blender/blenkernel/intern/instances.cc new file mode 100644 index 00000000000..4675562e927 --- /dev/null +++ b/source/blender/blenkernel/intern/instances.cc @@ -0,0 +1,337 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "BLI_array_utils.hh" +#include "BLI_cpp_type_make.hh" +#include "BLI_rand.hh" +#include "BLI_task.hh" + +#include "BKE_attribute_math.hh" +#include "BKE_geometry_set.hh" +#include "BKE_instances.hh" + +BLI_CPP_TYPE_MAKE(InstanceReference, blender::bke::InstanceReference, CPPTypeFlags::None) + +namespace blender::bke { + +InstanceReference::InstanceReference(GeometrySet geometry_set) + : type_(Type::GeometrySet), + geometry_set_(std::make_unique<GeometrySet>(std::move(geometry_set))) +{ +} + +void InstanceReference::ensure_owns_direct_data() +{ + if (type_ != Type::GeometrySet) { + return; + } + geometry_set_->ensure_owns_direct_data(); +} + +bool InstanceReference::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(); +} + +Instances::Instances(const Instances &other) + : references_(other.references_), + reference_handles_(other.reference_handles_), + transforms_(other.transforms_), + almost_unique_ids_(other.almost_unique_ids_), + attributes_(other.attributes_) +{ +} + +void Instances::reserve(int min_capacity) +{ + reference_handles_.reserve(min_capacity); + transforms_.reserve(min_capacity); + attributes_.reallocate(min_capacity); +} + +void Instances::resize(int capacity) +{ + reference_handles_.resize(capacity); + transforms_.resize(capacity); + attributes_.reallocate(capacity); +} + +void Instances::add_instance(const int instance_handle, const float4x4 &transform) +{ + BLI_assert(instance_handle >= 0); + BLI_assert(instance_handle < references_.size()); + reference_handles_.append(instance_handle); + transforms_.append(transform); + attributes_.reallocate(this->instances_num()); +} + +blender::Span<int> Instances::reference_handles() const +{ + return reference_handles_; +} + +blender::MutableSpan<int> Instances::reference_handles() +{ + return reference_handles_; +} + +blender::MutableSpan<blender::float4x4> Instances::transforms() +{ + return transforms_; +} +blender::Span<blender::float4x4> Instances::transforms() const +{ + return transforms_; +} + +GeometrySet &Instances::geometry_set_from_reference(const int reference_index) +{ + /* If this assert fails, it means #ensure_geometry_instances must be called first or that the + * reference can't be converted to a geometry set. */ + BLI_assert(references_[reference_index].type() == InstanceReference::Type::GeometrySet); + + /* The const cast is okay because the instance's hash in the set + * is not changed by adjusting the data inside the geometry set. */ + return const_cast<GeometrySet &>(references_[reference_index].geometry_set()); +} + +int Instances::add_reference(const InstanceReference &reference) +{ + return references_.index_of_or_add_as(reference); +} + +blender::Span<InstanceReference> Instances::references() const +{ + return references_; +} + +void Instances::remove(const IndexMask mask) +{ + using namespace blender; + if (mask.is_range() && mask.as_range().start() == 0) { + /* Deleting from the end of the array can be much faster since no data has to be shifted. */ + this->resize(mask.size()); + this->remove_unused_references(); + return; + } + + const Span<int> old_handles = this->reference_handles(); + Vector<int> new_handles(mask.size()); + array_utils::gather(old_handles, mask.indices(), new_handles.as_mutable_span()); + reference_handles_ = std::move(new_handles); + + const Span<float4x4> old_tansforms = this->transforms(); + Vector<float4x4> new_transforms(mask.size()); + array_utils::gather(old_tansforms, mask.indices(), new_transforms.as_mutable_span()); + transforms_ = std::move(new_transforms); + + const bke::CustomDataAttributes &src_attributes = attributes_; + + bke::CustomDataAttributes dst_attributes; + dst_attributes.reallocate(mask.size()); + + src_attributes.foreach_attribute( + [&](const bke::AttributeIDRef &id, const bke::AttributeMetaData &meta_data) { + if (!id.should_be_kept()) { + return true; + } + + GSpan src = *src_attributes.get_for_read(id); + dst_attributes.create(id, meta_data.data_type); + GMutableSpan dst = *dst_attributes.get_for_write(id); + array_utils::gather(src, mask.indices(), dst); + + return true; + }, + ATTR_DOMAIN_INSTANCE); + + attributes_ = std::move(dst_attributes); + this->remove_unused_references(); +} + +void Instances::remove_unused_references() +{ + using namespace blender; + using namespace blender::bke; + + const int tot_instances = this->instances_num(); + const int tot_references_before = references_.size(); + + if (tot_instances == 0) { + /* If there are no instances, no reference is needed. */ + references_.clear(); + return; + } + if (tot_references_before == 1) { + /* There is only one reference and at least one instance. So the only existing reference is + * used. Nothing to do here. */ + return; + } + + Array<bool> usage_by_handle(tot_references_before, false); + std::mutex mutex; + + /* Loop over all instances to see which references are used. */ + threading::parallel_for(IndexRange(tot_instances), 1000, [&](IndexRange range) { + /* Use local counter to avoid lock contention. */ + Array<bool> local_usage_by_handle(tot_references_before, false); + + for (const int i : range) { + const int handle = reference_handles_[i]; + BLI_assert(handle >= 0 && handle < tot_references_before); + local_usage_by_handle[handle] = true; + } + + std::lock_guard lock{mutex}; + for (const int i : IndexRange(tot_references_before)) { + usage_by_handle[i] |= local_usage_by_handle[i]; + } + }); + + if (!usage_by_handle.as_span().contains(false)) { + /* All references are used. */ + return; + } + + /* Create new references and a mapping for the handles. */ + Vector<int> handle_mapping; + VectorSet<InstanceReference> new_references; + int next_new_handle = 0; + bool handles_have_to_be_updated = false; + for (const int old_handle : IndexRange(tot_references_before)) { + if (!usage_by_handle[old_handle]) { + /* Add some dummy value. It won't be read again. */ + handle_mapping.append(-1); + } + else { + const InstanceReference &reference = references_[old_handle]; + handle_mapping.append(next_new_handle); + new_references.add_new(reference); + if (old_handle != next_new_handle) { + handles_have_to_be_updated = true; + } + next_new_handle++; + } + } + references_ = new_references; + + if (!handles_have_to_be_updated) { + /* All remaining handles are the same as before, so they don't have to be updated. This happens + * when unused handles are only at the end. */ + return; + } + + /* Update handles of instances. */ + threading::parallel_for(IndexRange(tot_instances), 1000, [&](IndexRange range) { + for (const int i : range) { + reference_handles_[i] = handle_mapping[reference_handles_[i]]; + } + }); +} + +int Instances::instances_num() const +{ + return transforms_.size(); +} + +int Instances::references_num() const +{ + return references_.size(); +} + +bool Instances::owns_direct_data() const +{ + for (const InstanceReference &reference : references_) { + if (!reference.owns_direct_data()) { + return false; + } + } + return true; +} + +void Instances::ensure_owns_direct_data() +{ + 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) +{ + using namespace blender; + Array<int> unique_ids(original_ids.size()); + + Set<int> used_unique_ids; + used_unique_ids.reserve(original_ids.size()); + Vector<int> instances_with_id_collision; + for (const int instance_index : original_ids.index_range()) { + const int original_id = original_ids[instance_index]; + if (used_unique_ids.add(original_id)) { + /* The original id has not been used by another instance yet. */ + unique_ids[instance_index] = original_id; + } + else { + /* The original id of this instance collided with a previous instance, it needs to be looked + * at again in a second pass. Don't generate a new random id here, because this might collide + * with other existing ids. */ + instances_with_id_collision.append(instance_index); + } + } + + Map<int, RandomNumberGenerator> generator_by_original_id; + for (const int instance_index : instances_with_id_collision) { + const int original_id = original_ids[instance_index]; + RandomNumberGenerator &rng = generator_by_original_id.lookup_or_add_cb(original_id, [&]() { + RandomNumberGenerator rng; + rng.seed_random(original_id); + return rng; + }); + + const int max_iteration = 100; + for (int iteration = 0;; iteration++) { + /* Try generating random numbers until an unused one has been found. */ + const int random_id = rng.get_int32(); + if (used_unique_ids.add(random_id)) { + /* This random id is not used by another instance. */ + unique_ids[instance_index] = random_id; + break; + } + if (iteration == max_iteration) { + /* It seems to be very unlikely that we ever run into this case (assuming there are less + * than 2^30 instances). However, if that happens, it's better to use an id that is not + * unique than to be stuck in an infinite loop. */ + unique_ids[instance_index] = original_id; + break; + } + } + } + + return unique_ids; +} + +blender::Span<int> Instances::almost_unique_ids() const +{ + std::lock_guard lock(almost_unique_ids_mutex_); + std::optional<GSpan> instance_ids_gspan = attributes_.get_for_read("id"); + if (instance_ids_gspan) { + Span<int> instance_ids = instance_ids_gspan->typed<int>(); + if (almost_unique_ids_.size() != instance_ids.size()) { + almost_unique_ids_ = generate_unique_instance_ids(instance_ids); + } + } + else { + almost_unique_ids_.reinitialize(this->instances_num()); + for (const int i : almost_unique_ids_.index_range()) { + almost_unique_ids_[i] = i; + } + } + return almost_unique_ids_; +} + +} // namespace blender::bke diff --git a/source/blender/blenkernel/intern/layer.c b/source/blender/blenkernel/intern/layer.c index fcb0adfde34..06d69b6ff61 100644 --- a/source/blender/blenkernel/intern/layer.c +++ b/source/blender/blenkernel/intern/layer.c @@ -1049,7 +1049,7 @@ static void layer_collection_objects_sync(ViewLayer *view_layer, } /* Holdout and indirect only */ - if ((layer->flag & LAYER_COLLECTION_HOLDOUT) || (base->object->visibility_flag & OB_HOLDOUT)) { + if ((layer->flag & LAYER_COLLECTION_HOLDOUT)) { base->flag_from_collection |= BASE_HOLDOUT; } if (layer->flag & LAYER_COLLECTION_INDIRECT_ONLY) { diff --git a/source/blender/blenkernel/intern/lib_id_delete.c b/source/blender/blenkernel/intern/lib_id_delete.c index f4f5ca7a1d7..1a80376f482 100644 --- a/source/blender/blenkernel/intern/lib_id_delete.c +++ b/source/blender/blenkernel/intern/lib_id_delete.c @@ -16,6 +16,7 @@ #include "BLI_utildefines.h" +#include "BLI_linklist.h" #include "BLI_listbase.h" #include "BKE_anim_data.h" @@ -23,10 +24,10 @@ #include "BKE_idprop.h" #include "BKE_idtype.h" #include "BKE_key.h" +#include "BKE_layer.h" #include "BKE_lib_id.h" #include "BKE_lib_override.h" #include "BKE_lib_remap.h" -#include "BKE_library.h" #include "BKE_main.h" #include "BKE_main_namemap.h" @@ -202,7 +203,6 @@ static size_t id_delete(Main *bmain, const bool do_tagged_deletion) { const int tag = LIB_TAG_DOIT; ListBase *lbarray[INDEX_ID_MAX]; - Link dummy_link = {0}; int base_count, i; /* Used by batch tagged deletion, when we call BKE_id_free then, id is no more in Main database, @@ -217,6 +217,9 @@ static size_t id_delete(Main *bmain, const bool do_tagged_deletion) BKE_main_lock(bmain); if (do_tagged_deletion) { + struct IDRemapper *id_remapper = BKE_id_remapper_create(); + BKE_layer_collection_resync_forbid(); + /* Main idea of batch deletion is to remove all IDs to be deleted from Main database. * This means that we won't have to loop over all deleted IDs to remove usages * of other deleted IDs. @@ -227,7 +230,6 @@ static size_t id_delete(Main *bmain, const bool do_tagged_deletion) bool keep_looping = true; while (keep_looping) { ID *id, *id_next; - ID *last_remapped_id = tagged_deleted_ids.last; keep_looping = false; /* First tag and remove from Main all datablocks directly from target lib. @@ -243,6 +245,7 @@ static size_t id_delete(Main *bmain, const bool do_tagged_deletion) BLI_remlink(lb, id); BKE_main_namemap_remove_name(bmain, id, id->name + 2); BLI_addtail(&tagged_deleted_ids, id); + BKE_id_remapper_add(id_remapper, id, NULL); /* Do not tag as no_main now, we want to unlink it first (lower-level ID management * code has some specific handling of 'no main' IDs that would be a problem in that * case). */ @@ -251,33 +254,38 @@ static size_t id_delete(Main *bmain, const bool do_tagged_deletion) } } } - if (last_remapped_id == NULL) { - dummy_link.next = tagged_deleted_ids.first; - last_remapped_id = (ID *)(&dummy_link); - } - for (id = last_remapped_id->next; id; id = id->next) { - /* Will tag 'never NULL' users of this ID too. - * - * NOTE: #BKE_libblock_unlink() cannot be used here, since it would ignore indirect - * links, this can lead to nasty crashing here in second, actual deleting loop. - * Also, this will also flag users of deleted data that cannot be unlinked - * (object using deleted obdata, etc.), so that they also get deleted. */ - BKE_libblock_remap_locked(bmain, - id, - NULL, - (ID_REMAP_FLAG_NEVER_NULL_USAGE | - ID_REMAP_FORCE_NEVER_NULL_USAGE | - ID_REMAP_FORCE_INTERNAL_RUNTIME_POINTERS)); - /* Since we removed ID from Main, - * we also need to unlink its own other IDs usages ourself. */ - BKE_libblock_relink_ex( - bmain, - id, - NULL, - NULL, - (ID_REMAP_FORCE_INTERNAL_RUNTIME_POINTERS | ID_REMAP_SKIP_USER_CLEAR)); - } + + /* Will tag 'never NULL' users of this ID too. + * + * NOTE: #BKE_libblock_unlink() cannot be used here, since it would ignore indirect + * links, this can lead to nasty crashing here in second, actual deleting loop. + * Also, this will also flag users of deleted data that cannot be unlinked + * (object using deleted obdata, etc.), so that they also get deleted. */ + BKE_libblock_remap_multiple_locked(bmain, + id_remapper, + ID_REMAP_FLAG_NEVER_NULL_USAGE | + ID_REMAP_FORCE_NEVER_NULL_USAGE | + ID_REMAP_FORCE_INTERNAL_RUNTIME_POINTERS); + BKE_id_remapper_clear(id_remapper); + } + + /* Since we removed IDs from Main, their own other IDs usages need to be removed 'manually'. */ + LinkNode *cleanup_ids = NULL; + for (ID *id = tagged_deleted_ids.first; id; id = id->next) { + BLI_linklist_prepend(&cleanup_ids, id); } + BKE_libblock_relink_multiple(bmain, + cleanup_ids, + ID_REMAP_TYPE_CLEANUP, + id_remapper, + ID_REMAP_FORCE_INTERNAL_RUNTIME_POINTERS | + ID_REMAP_SKIP_USER_CLEAR); + + BKE_id_remapper_free(id_remapper); + BLI_linklist_free(cleanup_ids, NULL); + + BKE_layer_collection_resync_allow(); + BKE_main_collection_sync_remap(bmain); /* Now we can safely mark that ID as not being in Main database anymore. */ /* NOTE: This needs to be done in a separate loop than above, otherwise some user-counts of @@ -285,6 +293,10 @@ static size_t id_delete(Main *bmain, const bool do_tagged_deletion) * is never affected). */ for (ID *id = tagged_deleted_ids.first; id; id = id->next) { id->tag |= LIB_TAG_NO_MAIN; + /* Usercount needs to be reset artificially, since some usages may not be cleared in batch + * deletion (typically, if one deleted ID uses another deleted ID, this may not be cleared by + * remapping code, depending on order in which these are handled). */ + id->us = ID_FAKE_USERS(id); } } else { diff --git a/source/blender/blenkernel/intern/mball_tessellate.cc b/source/blender/blenkernel/intern/mball_tessellate.cc index 5e8d2bc6d76..bb3713e770a 100644 --- a/source/blender/blenkernel/intern/mball_tessellate.cc +++ b/source/blender/blenkernel/intern/mball_tessellate.cc @@ -1498,7 +1498,7 @@ Mesh *BKE_mball_polygonize(Depsgraph *depsgraph, Scene *scene, Object *ob) for (int i = 0; i < mesh->totvert; i++) { normalize_v3(process.no[i]); } - mesh->runtime.vert_normals = process.no; + mesh->runtime->vert_normals = process.no; BKE_mesh_vertex_normals_clear_dirty(mesh); mesh->totloop = loop_offset; diff --git a/source/blender/blenkernel/intern/mesh.cc b/source/blender/blenkernel/intern/mesh.cc index 9e7821428d1..0018c217964 100644 --- a/source/blender/blenkernel/intern/mesh.cc +++ b/source/blender/blenkernel/intern/mesh.cc @@ -89,7 +89,7 @@ static void mesh_init_data(ID *id) CustomData_reset(&mesh->pdata); CustomData_reset(&mesh->ldata); - BKE_mesh_runtime_init_data(mesh); + mesh->runtime = new blender::bke::MeshRuntime(); /* A newly created mesh does not have normals, so tag them dirty. This will be cleared * by #BKE_mesh_vertex_normals_clear_dirty or #BKE_mesh_poly_normals_ensure. */ @@ -103,14 +103,19 @@ static void mesh_copy_data(Main *bmain, ID *id_dst, const ID *id_src, const int Mesh *mesh_dst = (Mesh *)id_dst; const Mesh *mesh_src = (const Mesh *)id_src; - BKE_mesh_runtime_reset_on_copy(mesh_dst, flag); + mesh_dst->runtime = new blender::bke::MeshRuntime(); + mesh_dst->runtime->deformed_only = mesh_src->runtime->deformed_only; + mesh_dst->runtime->wrapper_type = mesh_src->runtime->wrapper_type; + mesh_dst->runtime->wrapper_type_finalize = mesh_src->runtime->wrapper_type_finalize; + mesh_dst->runtime->subsurf_runtime_data = mesh_src->runtime->subsurf_runtime_data; + mesh_dst->runtime->cd_mask_extra = mesh_src->runtime->cd_mask_extra; /* Copy face dot tags, since meshes may be duplicated after a subsurf modifier * or node, but we still need to be able to draw face center vertices. */ - mesh_dst->runtime.subsurf_face_dot_tags = static_cast<uint32_t *>( - MEM_dupallocN(mesh_src->runtime.subsurf_face_dot_tags)); + mesh_dst->runtime->subsurf_face_dot_tags = static_cast<uint32_t *>( + MEM_dupallocN(mesh_src->runtime->subsurf_face_dot_tags)); if ((mesh_src->id.tag & LIB_TAG_NO_MAIN) == 0) { /* This is a direct copy of a main mesh, so for now it has the same topology. */ - mesh_dst->runtime.deformed_only = true; + mesh_dst->runtime->deformed_only = true; } /* This option is set for run-time meshes that have been copied from the current objects mode. * Currently this is used for edit-mesh although it could be used for sculpt or other @@ -121,7 +126,7 @@ static void mesh_copy_data(Main *bmain, ID *id_dst, const ID *id_src, const int * * While this could be the callers responsibility, keep here since it's * highly unlikely we want to create a duplicate and not use it for drawing. */ - mesh_dst->runtime.is_original_bmesh = false; + mesh_dst->runtime->is_original_bmesh = false; /* Only do tessface if we have no polys. */ const bool do_tessface = ((mesh_src->totface != 0) && (mesh_src->totpoly == 0)); @@ -194,6 +199,8 @@ static void mesh_free_data(ID *id) BKE_mesh_runtime_free_data(mesh); mesh_clear_geometry(mesh); MEM_SAFE_FREE(mesh->mat); + + delete mesh->runtime; } static void mesh_foreach_id(ID *id, LibraryForeachIDData *data) @@ -229,7 +236,7 @@ static void mesh_blend_write(BlendWriter *writer, ID *id, const void *id_address mesh->mface = nullptr; mesh->totface = 0; memset(&mesh->fdata, 0, sizeof(mesh->fdata)); - mesh->runtime = blender::dna::shallow_zero_initialize(); + mesh->runtime = nullptr; /* Do not store actual geometry data in case this is a library override ID. */ if (ID_IS_OVERRIDE_LIBRARY(mesh) && !is_undo) { @@ -339,8 +346,7 @@ static void mesh_blend_read_data(BlendDataReader *reader, ID *id) mesh->texflag &= ~ME_AUTOSPACE_EVALUATED; mesh->edit_mesh = nullptr; - mesh->runtime = blender::dna::shallow_zero_initialize(); - BKE_mesh_runtime_init_data(mesh); + mesh->runtime = new blender::bke::MeshRuntime(); /* happens with old files */ if (mesh->mselect == nullptr) { @@ -1137,7 +1143,7 @@ static void ensure_orig_index_layer(CustomData &data, const int size) void BKE_mesh_ensure_default_orig_index_customdata(Mesh *mesh) { - BLI_assert(mesh->runtime.wrapper_type == ME_WRAPPER_TYPE_MDATA); + BLI_assert(mesh->runtime->wrapper_type == ME_WRAPPER_TYPE_MDATA); BKE_mesh_ensure_default_orig_index_customdata_no_check(mesh); } @@ -2106,8 +2112,8 @@ void BKE_mesh_split_faces(Mesh *mesh, bool free_loop_normals) } /* Update normals manually to avoid recalculation after this operation. */ - mesh->runtime.vert_normals = (float(*)[3])MEM_reallocN(mesh->runtime.vert_normals, - sizeof(float[3]) * mesh->totvert); + mesh->runtime->vert_normals = (float(*)[3])MEM_reallocN(mesh->runtime->vert_normals, + sizeof(float[3]) * mesh->totvert); /* Perform actual split of vertices and edges. */ split_faces_split_new_verts(mesh, new_verts, num_new_verts); @@ -2141,10 +2147,10 @@ void BKE_mesh_eval_geometry(Depsgraph *depsgraph, Mesh *mesh) /* We are here because something did change in the mesh. This means we can not trust the existing * evaluated mesh, and we don't know what parts of the mesh did change. So we simply delete the * evaluated mesh and let objects to re-create it with updated settings. */ - if (mesh->runtime.mesh_eval != nullptr) { - mesh->runtime.mesh_eval->edit_mesh = nullptr; - BKE_id_free(nullptr, mesh->runtime.mesh_eval); - mesh->runtime.mesh_eval = nullptr; + if (mesh->runtime->mesh_eval != nullptr) { + mesh->runtime->mesh_eval->edit_mesh = nullptr; + BKE_id_free(nullptr, mesh->runtime->mesh_eval); + mesh->runtime->mesh_eval = nullptr; } if (DEG_is_active(depsgraph)) { Mesh *mesh_orig = (Mesh *)DEG_get_original_id(&mesh->id); diff --git a/source/blender/blenkernel/intern/mesh_convert.cc b/source/blender/blenkernel/intern/mesh_convert.cc index b4f729ca507..784d35a8d65 100644 --- a/source/blender/blenkernel/intern/mesh_convert.cc +++ b/source/blender/blenkernel/intern/mesh_convert.cc @@ -903,7 +903,7 @@ static Mesh *mesh_new_from_mesh(Object *object, Mesh *mesh) { /* While we could copy this into the new mesh, * add the data to 'mesh' so future calls to this function don't need to re-convert the data. */ - if (mesh->runtime.wrapper_type == ME_WRAPPER_TYPE_BMESH) { + if (mesh->runtime->wrapper_type == ME_WRAPPER_TYPE_BMESH) { BKE_mesh_wrapper_ensure_mdata(mesh); } else { @@ -1313,6 +1313,7 @@ void BKE_mesh_nomain_to_mesh(Mesh *mesh_src, Mesh *mesh_dst, Object *ob) CustomData_duplicate_referenced_layers(&mesh_src->pdata, mesh_src->totpoly); CustomData_duplicate_referenced_layers(&mesh_src->ldata, mesh_src->totloop); + const bool verts_num_changed = mesh_dst->totvert != mesh_src->totvert; mesh_dst->totvert = mesh_src->totvert; mesh_dst->totedge = mesh_src->totedge; mesh_dst->totpoly = mesh_src->totpoly; @@ -1339,11 +1340,10 @@ void BKE_mesh_nomain_to_mesh(Mesh *mesh_src, Mesh *mesh_dst, Object *ob) const int uid_active = ob ? find_object_active_key_uid(*key_dst, *ob) : -1; move_shapekey_layers_to_keyblocks(*mesh_dst, mesh_src->vdata, *key_dst, uid_active); } - else if (mesh_src->totvert != mesh_dst->totvert) { - CLOG_ERROR(&LOG, "Mesh in Main '%s' lost shape keys", mesh_src->id.name); - if (mesh_src->key) { - id_us_min(&mesh_src->key->id); - } + else if (verts_num_changed) { + CLOG_WARN(&LOG, "Shape key data lost when replacing mesh '%s' in Main", mesh_src->id.name); + id_us_min(&mesh_dst->key->id); + mesh_dst->key = nullptr; } } diff --git a/source/blender/blenkernel/intern/mesh_debug.cc b/source/blender/blenkernel/intern/mesh_debug.cc index ba4f25c74ee..6cf237a7c8c 100644 --- a/source/blender/blenkernel/intern/mesh_debug.cc +++ b/source/blender/blenkernel/intern/mesh_debug.cc @@ -41,9 +41,9 @@ char *BKE_mesh_debug_info(const Mesh *me) BLI_dynstr_appendf(dynstr, " 'totface': %d,\n", me->totface); BLI_dynstr_appendf(dynstr, " 'totpoly': %d,\n", me->totpoly); - BLI_dynstr_appendf(dynstr, " 'runtime.deformed_only': %d,\n", me->runtime.deformed_only); + BLI_dynstr_appendf(dynstr, " 'runtime.deformed_only': %d,\n", me->runtime->deformed_only); BLI_dynstr_appendf( - dynstr, " 'runtime.is_original_bmesh': %d,\n", me->runtime.is_original_bmesh); + dynstr, " 'runtime->is_original_bmesh': %d,\n", me->runtime->is_original_bmesh); BLI_dynstr_append(dynstr, " 'vert_layers': (\n"); CustomData_debug_info_from_layers(&me->vdata, indent8, dynstr); diff --git a/source/blender/blenkernel/intern/mesh_fair.cc b/source/blender/blenkernel/intern/mesh_fair.cc index 5369bc782b6..960e6c43103 100644 --- a/source/blender/blenkernel/intern/mesh_fair.cc +++ b/source/blender/blenkernel/intern/mesh_fair.cc @@ -221,7 +221,7 @@ class MeshFairingContext : public FairingContext { } } - loop_to_poly_map_ = blender::mesh_topology::build_loop_to_poly_map(mpoly_, mloop_.size()); + loop_to_poly_map_ = blender::bke::mesh_topology::build_loop_to_poly_map(mpoly_, mloop_.size()); } ~MeshFairingContext() override diff --git a/source/blender/blenkernel/intern/mesh_iterators.cc b/source/blender/blenkernel/intern/mesh_iterators.cc index 46f9780f891..a99e9b2348d 100644 --- a/source/blender/blenkernel/intern/mesh_iterators.cc +++ b/source/blender/blenkernel/intern/mesh_iterators.cc @@ -36,18 +36,18 @@ void BKE_mesh_foreach_mapped_vert( void *userData, MeshForeachFlag flag) { - if (mesh->edit_mesh != nullptr && mesh->runtime.edit_data != nullptr) { + if (mesh->edit_mesh != nullptr && mesh->runtime->edit_data != nullptr) { BMEditMesh *em = mesh->edit_mesh; BMesh *bm = em->bm; BMIter iter; BMVert *eve; int i; - if (mesh->runtime.edit_data->vertexCos != nullptr) { - const float(*vertexCos)[3] = mesh->runtime.edit_data->vertexCos; + if (mesh->runtime->edit_data->vertexCos != nullptr) { + const float(*vertexCos)[3] = mesh->runtime->edit_data->vertexCos; const float(*vertexNos)[3]; if (flag & MESH_FOREACH_USE_NORMAL) { - BKE_editmesh_cache_ensure_vert_normals(em, mesh->runtime.edit_data); - vertexNos = mesh->runtime.edit_data->vertexNos; + BKE_editmesh_cache_ensure_vert_normals(em, mesh->runtime->edit_data); + vertexNos = mesh->runtime->edit_data->vertexNos; } else { vertexNos = nullptr; @@ -96,14 +96,14 @@ void BKE_mesh_foreach_mapped_edge( void (*func)(void *userData, int index, const float v0co[3], const float v1co[3]), void *userData) { - if (mesh->edit_mesh != nullptr && mesh->runtime.edit_data) { + if (mesh->edit_mesh != nullptr && mesh->runtime->edit_data) { BMEditMesh *em = mesh->edit_mesh; BMesh *bm = em->bm; BMIter iter; BMEdge *eed; int i; - if (mesh->runtime.edit_data->vertexCos != nullptr) { - const float(*vertexCos)[3] = mesh->runtime.edit_data->vertexCos; + if (mesh->runtime->edit_data->vertexCos != nullptr) { + const float(*vertexCos)[3] = mesh->runtime->edit_data->vertexCos; BM_mesh_elem_index_ensure(bm, BM_VERT); BM_ITER_MESH_INDEX (eed, &iter, bm, BM_EDGES_OF_MESH, i) { @@ -154,13 +154,13 @@ void BKE_mesh_foreach_mapped_loop(Mesh *mesh, /* We can't use `dm->getLoopDataLayout(dm)` here, * we want to always access `dm->loopData`, `EditDerivedBMesh` would * return loop data from BMesh itself. */ - if (mesh->edit_mesh != nullptr && mesh->runtime.edit_data) { + if (mesh->edit_mesh != nullptr && mesh->runtime->edit_data) { BMEditMesh *em = mesh->edit_mesh; BMesh *bm = em->bm; BMIter iter; BMFace *efa; - const float(*vertexCos)[3] = mesh->runtime.edit_data->vertexCos; + const float(*vertexCos)[3] = mesh->runtime->edit_data->vertexCos; /* XXX: investigate using EditMesh data. */ const float(*lnors)[3] = (flag & MESH_FOREACH_USE_NORMAL) ? @@ -231,7 +231,7 @@ void BKE_mesh_foreach_mapped_face_center( void *userData, MeshForeachFlag flag) { - if (mesh->edit_mesh != nullptr && mesh->runtime.edit_data != nullptr) { + if (mesh->edit_mesh != nullptr && mesh->runtime->edit_data != nullptr) { BMEditMesh *em = mesh->edit_mesh; BMesh *bm = em->bm; const float(*polyCos)[3]; @@ -240,12 +240,12 @@ void BKE_mesh_foreach_mapped_face_center( BMIter iter; int i; - BKE_editmesh_cache_ensure_poly_centers(em, mesh->runtime.edit_data); - polyCos = mesh->runtime.edit_data->polyCos; /* always set */ + BKE_editmesh_cache_ensure_poly_centers(em, mesh->runtime->edit_data); + polyCos = mesh->runtime->edit_data->polyCos; /* always set */ if (flag & MESH_FOREACH_USE_NORMAL) { - BKE_editmesh_cache_ensure_poly_normals(em, mesh->runtime.edit_data); - polyNos = mesh->runtime.edit_data->polyNos; /* maybe nullptr */ + BKE_editmesh_cache_ensure_poly_normals(em, mesh->runtime->edit_data); + polyNos = mesh->runtime->edit_data->polyNos; /* maybe nullptr */ } else { polyNos = nullptr; @@ -317,7 +317,7 @@ void BKE_mesh_foreach_mapped_subdiv_face_center( BKE_mesh_vertex_normals_ensure(mesh) : nullptr; const int *index = static_cast<const int *>(CustomData_get_layer(&mesh->pdata, CD_ORIGINDEX)); - const BLI_bitmap *facedot_tags = mesh->runtime.subsurf_face_dot_tags; + const BLI_bitmap *facedot_tags = mesh->runtime->subsurf_face_dot_tags; BLI_assert(facedot_tags != nullptr); if (index) { @@ -364,7 +364,7 @@ struct MappedVCosData { static void get_vertexcos__mapFunc(void *user_data, int index, const float co[3], - const float UNUSED(no[3])) + const float /*no*/[3]) { MappedVCosData *mapped_vcos_data = (MappedVCosData *)user_data; diff --git a/source/blender/blenkernel/intern/mesh_mapping.cc b/source/blender/blenkernel/intern/mesh_mapping.cc index 667802d5f48..ed4ae94da7f 100644 --- a/source/blender/blenkernel/intern/mesh_mapping.cc +++ b/source/blender/blenkernel/intern/mesh_mapping.cc @@ -553,7 +553,7 @@ void BKE_mesh_origindex_map_create_looptri(MeshElemMap **r_map, *r_mem = indices; } -namespace blender::mesh_topology { +namespace blender::bke::mesh_topology { Array<int> build_loop_to_poly_map(const Span<MPoly> polys, const int loops_num) { @@ -586,7 +586,7 @@ Array<Vector<int>> build_vert_to_loop_map(const Span<MLoop> loops, const int ver return map; } -} // namespace blender::mesh_topology +} // namespace blender::bke::mesh_topology /** \} */ diff --git a/source/blender/blenkernel/intern/mesh_normals.cc b/source/blender/blenkernel/intern/mesh_normals.cc index e589aff1c73..ebb5a72d137 100644 --- a/source/blender/blenkernel/intern/mesh_normals.cc +++ b/source/blender/blenkernel/intern/mesh_normals.cc @@ -95,72 +95,72 @@ static void add_v3_v3_atomic(float r[3], const float a[3]) void BKE_mesh_normals_tag_dirty(Mesh *mesh) { - mesh->runtime.vert_normals_dirty = true; - mesh->runtime.poly_normals_dirty = true; + mesh->runtime->vert_normals_dirty = true; + mesh->runtime->poly_normals_dirty = true; } float (*BKE_mesh_vertex_normals_for_write(Mesh *mesh))[3] { - if (mesh->runtime.vert_normals == nullptr) { - mesh->runtime.vert_normals = (float(*)[3])MEM_malloc_arrayN( + if (mesh->runtime->vert_normals == nullptr) { + mesh->runtime->vert_normals = (float(*)[3])MEM_malloc_arrayN( mesh->totvert, sizeof(float[3]), __func__); } - BLI_assert(MEM_allocN_len(mesh->runtime.vert_normals) >= sizeof(float[3]) * mesh->totvert); + BLI_assert(MEM_allocN_len(mesh->runtime->vert_normals) >= sizeof(float[3]) * mesh->totvert); - return mesh->runtime.vert_normals; + return mesh->runtime->vert_normals; } float (*BKE_mesh_poly_normals_for_write(Mesh *mesh))[3] { - if (mesh->runtime.poly_normals == nullptr) { - mesh->runtime.poly_normals = (float(*)[3])MEM_malloc_arrayN( + if (mesh->runtime->poly_normals == nullptr) { + mesh->runtime->poly_normals = (float(*)[3])MEM_malloc_arrayN( mesh->totpoly, sizeof(float[3]), __func__); } - BLI_assert(MEM_allocN_len(mesh->runtime.poly_normals) >= sizeof(float[3]) * mesh->totpoly); + BLI_assert(MEM_allocN_len(mesh->runtime->poly_normals) >= sizeof(float[3]) * mesh->totpoly); - return mesh->runtime.poly_normals; + return mesh->runtime->poly_normals; } void BKE_mesh_vertex_normals_clear_dirty(Mesh *mesh) { - mesh->runtime.vert_normals_dirty = false; + mesh->runtime->vert_normals_dirty = false; BKE_mesh_assert_normals_dirty_or_calculated(mesh); } void BKE_mesh_poly_normals_clear_dirty(Mesh *mesh) { - mesh->runtime.poly_normals_dirty = false; + mesh->runtime->poly_normals_dirty = false; BKE_mesh_assert_normals_dirty_or_calculated(mesh); } bool BKE_mesh_vertex_normals_are_dirty(const Mesh *mesh) { - return mesh->runtime.vert_normals_dirty; + return mesh->runtime->vert_normals_dirty; } bool BKE_mesh_poly_normals_are_dirty(const Mesh *mesh) { - return mesh->runtime.poly_normals_dirty; + return mesh->runtime->poly_normals_dirty; } void BKE_mesh_clear_derived_normals(Mesh *mesh) { - MEM_SAFE_FREE(mesh->runtime.vert_normals); - MEM_SAFE_FREE(mesh->runtime.poly_normals); + MEM_SAFE_FREE(mesh->runtime->vert_normals); + MEM_SAFE_FREE(mesh->runtime->poly_normals); - mesh->runtime.vert_normals_dirty = true; - mesh->runtime.poly_normals_dirty = true; + mesh->runtime->vert_normals_dirty = true; + mesh->runtime->poly_normals_dirty = true; } void BKE_mesh_assert_normals_dirty_or_calculated(const Mesh *mesh) { - if (!mesh->runtime.vert_normals_dirty) { - BLI_assert(mesh->runtime.vert_normals || mesh->totvert == 0); + if (!mesh->runtime->vert_normals_dirty) { + BLI_assert(mesh->runtime->vert_normals || mesh->totvert == 0); } - if (!mesh->runtime.poly_normals_dirty) { - BLI_assert(mesh->runtime.poly_normals || mesh->totpoly == 0); + if (!mesh->runtime->poly_normals_dirty) { + BLI_assert(mesh->runtime->poly_normals || mesh->totpoly == 0); } } @@ -348,20 +348,18 @@ void BKE_mesh_calc_normals_poly_and_vertex(const MVert *mvert, const float (*BKE_mesh_vertex_normals_ensure(const Mesh *mesh))[3] { if (!BKE_mesh_vertex_normals_are_dirty(mesh)) { - BLI_assert(mesh->runtime.vert_normals != nullptr || mesh->totvert == 0); - return mesh->runtime.vert_normals; + BLI_assert(mesh->runtime->vert_normals != nullptr || mesh->totvert == 0); + return mesh->runtime->vert_normals; } if (mesh->totvert == 0) { return nullptr; } - ThreadMutex *normals_mutex = (ThreadMutex *)mesh->runtime.normals_mutex; - BLI_mutex_lock(normals_mutex); + std::lock_guard lock{mesh->runtime->normals_mutex}; if (!BKE_mesh_vertex_normals_are_dirty(mesh)) { - BLI_assert(mesh->runtime.vert_normals != nullptr); - BLI_mutex_unlock(normals_mutex); - return mesh->runtime.vert_normals; + BLI_assert(mesh->runtime->vert_normals != nullptr); + return mesh->runtime->vert_normals; } float(*vert_normals)[3]; @@ -390,27 +388,24 @@ const float (*BKE_mesh_vertex_normals_ensure(const Mesh *mesh))[3] BKE_mesh_poly_normals_clear_dirty(&mesh_mutable); }); - BLI_mutex_unlock(normals_mutex); return vert_normals; } const float (*BKE_mesh_poly_normals_ensure(const Mesh *mesh))[3] { if (!BKE_mesh_poly_normals_are_dirty(mesh)) { - BLI_assert(mesh->runtime.poly_normals != nullptr || mesh->totpoly == 0); - return mesh->runtime.poly_normals; + BLI_assert(mesh->runtime->poly_normals != nullptr || mesh->totpoly == 0); + return mesh->runtime->poly_normals; } if (mesh->totpoly == 0) { return nullptr; } - ThreadMutex *normals_mutex = (ThreadMutex *)mesh->runtime.normals_mutex; - BLI_mutex_lock(normals_mutex); + std::lock_guard lock{mesh->runtime->normals_mutex}; if (!BKE_mesh_poly_normals_are_dirty(mesh)) { - BLI_assert(mesh->runtime.poly_normals != nullptr); - BLI_mutex_unlock(normals_mutex); - return mesh->runtime.poly_normals; + BLI_assert(mesh->runtime->poly_normals != nullptr); + return mesh->runtime->poly_normals; } float(*poly_normals)[3]; @@ -435,13 +430,12 @@ const float (*BKE_mesh_poly_normals_ensure(const Mesh *mesh))[3] BKE_mesh_poly_normals_clear_dirty(&mesh_mutable); }); - BLI_mutex_unlock(normals_mutex); return poly_normals; } void BKE_mesh_ensure_normals_for_display(Mesh *mesh) { - switch ((eMeshWrapperType)mesh->runtime.wrapper_type) { + switch (mesh->runtime->wrapper_type) { case ME_WRAPPER_TYPE_SUBD: case ME_WRAPPER_TYPE_MDATA: BKE_mesh_vertex_normals_ensure(mesh); @@ -449,7 +443,7 @@ void BKE_mesh_ensure_normals_for_display(Mesh *mesh) break; case ME_WRAPPER_TYPE_BMESH: { struct BMEditMesh *em = mesh->edit_mesh; - EditMeshData *emd = mesh->runtime.edit_data; + EditMeshData *emd = mesh->runtime->edit_data; if (emd->vertexCos) { BKE_editmesh_cache_ensure_vert_normals(em, emd); BKE_editmesh_cache_ensure_poly_normals(em, emd); @@ -470,7 +464,7 @@ void BKE_mesh_calc_normals(Mesh *mesh) #endif } -void BKE_mesh_calc_normals_looptri(MVert *mverts, +void BKE_mesh_calc_normals_looptri(const MVert *mverts, int numVerts, const MLoop *mloop, const MLoopTri *looptri, @@ -508,7 +502,7 @@ void BKE_mesh_calc_normals_looptri(MVert *mverts, /* Following Mesh convention; we use vertex coordinate itself for normal in this case. */ for (int i = 0; i < numVerts; i++) { - MVert *mv = &mverts[i]; + const MVert *mv = &mverts[i]; float *no = tnorms[i]; if (UNLIKELY(normalize_v3(no) == 0.0f)) { diff --git a/source/blender/blenkernel/intern/mesh_remap.c b/source/blender/blenkernel/intern/mesh_remap.c index cb05d33bc2e..90798ea593d 100644 --- a/source/blender/blenkernel/intern/mesh_remap.c +++ b/source/blender/blenkernel/intern/mesh_remap.c @@ -1529,7 +1529,7 @@ void BKE_mesh_remap_calc_loops_from_mesh(const int mode, BLI_bitmap *looptri_active; looptri_src = BKE_mesh_runtime_looptri_ensure(me_src); - num_looptri_src = me_src->runtime.looptris.len; + num_looptri_src = BKE_mesh_runtime_looptri_len(me_src); looptri_active = BLI_BITMAP_NEW((size_t)num_looptri_src, __func__); for (tindex = 0; tindex < num_trees; tindex++) { diff --git a/source/blender/blenkernel/intern/mesh_runtime.cc b/source/blender/blenkernel/intern/mesh_runtime.cc index bd9f8242274..e90a298ad8d 100644 --- a/source/blender/blenkernel/intern/mesh_runtime.cc +++ b/source/blender/blenkernel/intern/mesh_runtime.cc @@ -17,6 +17,7 @@ #include "BLI_task.hh" #include "BKE_bvhutils.h" +#include "BKE_editmesh_cache.h" #include "BKE_lib_id.h" #include "BKE_mesh.h" #include "BKE_mesh_runtime.h" @@ -30,81 +31,17 @@ using blender::Span; /** \name Mesh Runtime Struct Utils * \{ */ -/** - * \brief Initialize the runtime mutexes of the given mesh. - * - * Any existing mutexes will be overridden. - */ -static void mesh_runtime_init_mutexes(Mesh *mesh) -{ - mesh->runtime.eval_mutex = MEM_new<ThreadMutex>("mesh runtime eval_mutex"); - BLI_mutex_init(static_cast<ThreadMutex *>(mesh->runtime.eval_mutex)); - mesh->runtime.normals_mutex = MEM_new<ThreadMutex>("mesh runtime normals_mutex"); - BLI_mutex_init(static_cast<ThreadMutex *>(mesh->runtime.normals_mutex)); - mesh->runtime.render_mutex = MEM_new<ThreadMutex>("mesh runtime render_mutex"); - BLI_mutex_init(static_cast<ThreadMutex *>(mesh->runtime.render_mutex)); -} - -/** - * \brief free the mutexes of the given mesh runtime. - */ -static void mesh_runtime_free_mutexes(Mesh *mesh) -{ - if (mesh->runtime.eval_mutex != nullptr) { - BLI_mutex_end(static_cast<ThreadMutex *>(mesh->runtime.eval_mutex)); - MEM_freeN(mesh->runtime.eval_mutex); - mesh->runtime.eval_mutex = nullptr; - } - if (mesh->runtime.normals_mutex != nullptr) { - BLI_mutex_end(static_cast<ThreadMutex *>(mesh->runtime.normals_mutex)); - MEM_freeN(mesh->runtime.normals_mutex); - mesh->runtime.normals_mutex = nullptr; - } - if (mesh->runtime.render_mutex != nullptr) { - BLI_mutex_end(static_cast<ThreadMutex *>(mesh->runtime.render_mutex)); - MEM_freeN(mesh->runtime.render_mutex); - mesh->runtime.render_mutex = nullptr; - } -} - -void BKE_mesh_runtime_init_data(Mesh *mesh) -{ - mesh_runtime_init_mutexes(mesh); -} - void BKE_mesh_runtime_free_data(Mesh *mesh) { BKE_mesh_runtime_clear_cache(mesh); - mesh_runtime_free_mutexes(mesh); -} - -void BKE_mesh_runtime_reset_on_copy(Mesh *mesh, const int /*flag*/) -{ - Mesh_Runtime *runtime = &mesh->runtime; - - runtime->mesh_eval = nullptr; - runtime->edit_data = nullptr; - runtime->batch_cache = nullptr; - runtime->subdiv_ccg = nullptr; - runtime->looptris = blender::dna::shallow_zero_initialize(); - runtime->bvh_cache = nullptr; - runtime->shrinkwrap_data = nullptr; - runtime->subsurf_face_dot_tags = nullptr; - - runtime->vert_normals_dirty = true; - runtime->poly_normals_dirty = true; - runtime->vert_normals = nullptr; - runtime->poly_normals = nullptr; - - mesh_runtime_init_mutexes(mesh); } void BKE_mesh_runtime_clear_cache(Mesh *mesh) { - if (mesh->runtime.mesh_eval != nullptr) { - mesh->runtime.mesh_eval->edit_mesh = nullptr; - BKE_id_free(nullptr, mesh->runtime.mesh_eval); - mesh->runtime.mesh_eval = nullptr; + if (mesh->runtime->mesh_eval != nullptr) { + mesh->runtime->mesh_eval->edit_mesh = nullptr; + BKE_id_free(nullptr, mesh->runtime->mesh_eval); + mesh->runtime->mesh_eval = nullptr; } BKE_mesh_runtime_clear_geometry(mesh); BKE_mesh_batch_cache_free(mesh); @@ -131,32 +68,32 @@ static void mesh_ensure_looptri_data(Mesh *mesh) const uint totpoly = mesh->totpoly; const int looptris_len = poly_to_tri_count(totpoly, mesh->totloop); - BLI_assert(mesh->runtime.looptris.array_wip == nullptr); + BLI_assert(mesh->runtime->looptris.array_wip == nullptr); - SWAP(MLoopTri *, mesh->runtime.looptris.array, mesh->runtime.looptris.array_wip); + SWAP(MLoopTri *, mesh->runtime->looptris.array, mesh->runtime->looptris.array_wip); - if ((looptris_len > mesh->runtime.looptris.len_alloc) || - (looptris_len < mesh->runtime.looptris.len_alloc * 2) || (totpoly == 0)) { - MEM_SAFE_FREE(mesh->runtime.looptris.array_wip); - mesh->runtime.looptris.len_alloc = 0; - mesh->runtime.looptris.len = 0; + if ((looptris_len > mesh->runtime->looptris.len_alloc) || + (looptris_len < mesh->runtime->looptris.len_alloc * 2) || (totpoly == 0)) { + MEM_SAFE_FREE(mesh->runtime->looptris.array_wip); + mesh->runtime->looptris.len_alloc = 0; + mesh->runtime->looptris.len = 0; } if (totpoly) { - if (mesh->runtime.looptris.array_wip == nullptr) { - mesh->runtime.looptris.array_wip = static_cast<MLoopTri *>( - MEM_malloc_arrayN(looptris_len, sizeof(*mesh->runtime.looptris.array_wip), __func__)); - mesh->runtime.looptris.len_alloc = looptris_len; + if (mesh->runtime->looptris.array_wip == nullptr) { + mesh->runtime->looptris.array_wip = static_cast<MLoopTri *>( + MEM_malloc_arrayN(looptris_len, sizeof(*mesh->runtime->looptris.array_wip), __func__)); + mesh->runtime->looptris.len_alloc = looptris_len; } - mesh->runtime.looptris.len = looptris_len; + mesh->runtime->looptris.len = looptris_len; } } void BKE_mesh_runtime_looptri_recalc(Mesh *mesh) { mesh_ensure_looptri_data(mesh); - BLI_assert(mesh->totpoly == 0 || mesh->runtime.looptris.array_wip != nullptr); + BLI_assert(mesh->totpoly == 0 || mesh->runtime->looptris.array_wip != nullptr); const Span<MVert> verts = mesh->verts(); const Span<MPoly> polys = mesh->polys(); const Span<MLoop> loops = mesh->loops(); @@ -167,7 +104,7 @@ void BKE_mesh_runtime_looptri_recalc(Mesh *mesh) verts.data(), mesh->totloop, mesh->totpoly, - mesh->runtime.looptris.array_wip, + mesh->runtime->looptris.array_wip, BKE_mesh_poly_normals_ensure(mesh)); } else { @@ -176,43 +113,40 @@ void BKE_mesh_runtime_looptri_recalc(Mesh *mesh) verts.data(), mesh->totloop, mesh->totpoly, - mesh->runtime.looptris.array_wip); + mesh->runtime->looptris.array_wip); } - BLI_assert(mesh->runtime.looptris.array == nullptr); - atomic_cas_ptr((void **)&mesh->runtime.looptris.array, - mesh->runtime.looptris.array, - mesh->runtime.looptris.array_wip); - mesh->runtime.looptris.array_wip = nullptr; + BLI_assert(mesh->runtime->looptris.array == nullptr); + atomic_cas_ptr((void **)&mesh->runtime->looptris.array, + mesh->runtime->looptris.array, + mesh->runtime->looptris.array_wip); + mesh->runtime->looptris.array_wip = nullptr; } int BKE_mesh_runtime_looptri_len(const Mesh *mesh) { /* This is a ported copy of `dm_getNumLoopTri(dm)`. */ const int looptri_len = poly_to_tri_count(mesh->totpoly, mesh->totloop); - BLI_assert(ELEM(mesh->runtime.looptris.len, 0, looptri_len)); + BLI_assert(ELEM(mesh->runtime->looptris.len, 0, looptri_len)); return looptri_len; } const MLoopTri *BKE_mesh_runtime_looptri_ensure(const Mesh *mesh) { - ThreadMutex *mesh_eval_mutex = (ThreadMutex *)mesh->runtime.eval_mutex; - BLI_mutex_lock(mesh_eval_mutex); + std::lock_guard lock{mesh->runtime->eval_mutex}; - MLoopTri *looptri = mesh->runtime.looptris.array; + MLoopTri *looptri = mesh->runtime->looptris.array; if (looptri != nullptr) { - BLI_assert(BKE_mesh_runtime_looptri_len(mesh) == mesh->runtime.looptris.len); + BLI_assert(BKE_mesh_runtime_looptri_len(mesh) == mesh->runtime->looptris.len); } else { /* Must isolate multithreaded tasks while holding a mutex lock. */ blender::threading::isolate_task( [&]() { BKE_mesh_runtime_looptri_recalc(const_cast<Mesh *>(mesh)); }); - looptri = mesh->runtime.looptris.array; + looptri = mesh->runtime->looptris.array; } - BLI_mutex_unlock(mesh_eval_mutex); - return looptri; } @@ -230,17 +164,17 @@ void BKE_mesh_runtime_verttri_from_looptri(MVertTri *r_verttri, bool BKE_mesh_runtime_ensure_edit_data(struct Mesh *mesh) { - if (mesh->runtime.edit_data != nullptr) { + if (mesh->runtime->edit_data != nullptr) { return false; } - mesh->runtime.edit_data = MEM_cnew<EditMeshData>(__func__); + mesh->runtime->edit_data = MEM_cnew<EditMeshData>(__func__); return true; } bool BKE_mesh_runtime_reset_edit_data(Mesh *mesh) { - EditMeshData *edit_data = mesh->runtime.edit_data; + EditMeshData *edit_data = mesh->runtime->edit_data; if (edit_data == nullptr) { return false; } @@ -255,13 +189,13 @@ bool BKE_mesh_runtime_reset_edit_data(Mesh *mesh) bool BKE_mesh_runtime_clear_edit_data(Mesh *mesh) { - if (mesh->runtime.edit_data == nullptr) { + if (mesh->runtime->edit_data == nullptr) { return false; } BKE_mesh_runtime_reset_edit_data(mesh); - MEM_freeN(mesh->runtime.edit_data); - mesh->runtime.edit_data = nullptr; + MEM_freeN(mesh->runtime->edit_data); + mesh->runtime->edit_data = nullptr; return true; } @@ -271,22 +205,22 @@ void BKE_mesh_runtime_clear_geometry(Mesh *mesh) BKE_mesh_tag_coords_changed(mesh); /* TODO(sergey): Does this really belong here? */ - if (mesh->runtime.subdiv_ccg != nullptr) { - BKE_subdiv_ccg_destroy(mesh->runtime.subdiv_ccg); - mesh->runtime.subdiv_ccg = nullptr; + if (mesh->runtime->subdiv_ccg != nullptr) { + BKE_subdiv_ccg_destroy(mesh->runtime->subdiv_ccg); + mesh->runtime->subdiv_ccg = nullptr; } BKE_shrinkwrap_discard_boundary_data(mesh); - MEM_SAFE_FREE(mesh->runtime.subsurf_face_dot_tags); + MEM_SAFE_FREE(mesh->runtime->subsurf_face_dot_tags); } void BKE_mesh_tag_coords_changed(Mesh *mesh) { BKE_mesh_normals_tag_dirty(mesh); - MEM_SAFE_FREE(mesh->runtime.looptris.array); - if (mesh->runtime.bvh_cache) { - bvhcache_free(mesh->runtime.bvh_cache); - mesh->runtime.bvh_cache = nullptr; + MEM_SAFE_FREE(mesh->runtime->looptris.array); + if (mesh->runtime->bvh_cache) { + bvhcache_free(mesh->runtime->bvh_cache); + mesh->runtime->bvh_cache = nullptr; } } @@ -305,6 +239,16 @@ void BKE_mesh_tag_coords_changed_uniformly(Mesh *mesh) } } +bool BKE_mesh_is_deformed_only(const Mesh *mesh) +{ + return mesh->runtime->deformed_only; +} + +eMeshWrapperType BKE_mesh_wrapper_type(const struct Mesh *mesh) +{ + return mesh->runtime->wrapper_type; +} + /** \} */ /* -------------------------------------------------------------------- */ @@ -317,13 +261,13 @@ void (*BKE_mesh_batch_cache_free_cb)(Mesh *me) = nullptr; void BKE_mesh_batch_cache_dirty_tag(Mesh *me, eMeshBatchDirtyMode mode) { - if (me->runtime.batch_cache) { + if (me->runtime->batch_cache) { BKE_mesh_batch_cache_dirty_tag_cb(me, mode); } } void BKE_mesh_batch_cache_free(Mesh *me) { - if (me->runtime.batch_cache) { + if (me->runtime->batch_cache) { BKE_mesh_batch_cache_free_cb(me); } } diff --git a/source/blender/blenkernel/intern/mesh_tangent.cc b/source/blender/blenkernel/intern/mesh_tangent.cc index 1162986aaf5..49ea23a1552 100644 --- a/source/blender/blenkernel/intern/mesh_tangent.cc +++ b/source/blender/blenkernel/intern/mesh_tangent.cc @@ -570,8 +570,6 @@ void BKE_mesh_calc_loop_tangents(Mesh *me_eval, const char (*tangent_names)[MAX_NAME], int tangent_names_len) { - BKE_mesh_runtime_looptri_ensure(me_eval); - /* TODO(@campbellbarton): store in Mesh.runtime to avoid recalculation. */ short tangent_mask = 0; BKE_mesh_calc_loop_tangent_ex( @@ -579,8 +577,8 @@ void BKE_mesh_calc_loop_tangents(Mesh *me_eval, BKE_mesh_polys(me_eval), uint(me_eval->totpoly), BKE_mesh_loops(me_eval), - me_eval->runtime.looptris.array, - uint(me_eval->runtime.looptris.len), + BKE_mesh_runtime_looptri_ensure(me_eval), + uint(BKE_mesh_runtime_looptri_len(me_eval)), &me_eval->ldata, calc_active_tangent, tangent_names, diff --git a/source/blender/blenkernel/intern/mesh_wrapper.cc b/source/blender/blenkernel/intern/mesh_wrapper.cc index 101fad2fce8..61a95fb4d0e 100644 --- a/source/blender/blenkernel/intern/mesh_wrapper.cc +++ b/source/blender/blenkernel/intern/mesh_wrapper.cc @@ -57,13 +57,13 @@ Mesh *BKE_mesh_wrapper_from_editmesh_with_coords(BMEditMesh *em, BKE_mesh_copy_parameters_for_eval(me, me_settings); BKE_mesh_runtime_ensure_edit_data(me); - me->runtime.wrapper_type = ME_WRAPPER_TYPE_BMESH; + me->runtime->wrapper_type = ME_WRAPPER_TYPE_BMESH; if (cd_mask_extra) { - me->runtime.cd_mask_extra = *cd_mask_extra; + me->runtime->cd_mask_extra = *cd_mask_extra; } /* Use edit-mesh directly where possible. */ - me->runtime.is_original_bmesh = true; + me->runtime->is_original_bmesh = true; me->edit_mesh = static_cast<BMEditMesh *>(MEM_dupallocN(em)); me->edit_mesh->is_shallow_copy = true; @@ -81,7 +81,7 @@ Mesh *BKE_mesh_wrapper_from_editmesh_with_coords(BMEditMesh *em, me->totloop = 0; #endif - EditMeshData *edit_data = me->runtime.edit_data; + EditMeshData *edit_data = me->runtime->edit_data; edit_data->vertexCos = vert_coords; return me; } @@ -95,17 +95,14 @@ Mesh *BKE_mesh_wrapper_from_editmesh(BMEditMesh *em, void BKE_mesh_wrapper_ensure_mdata(Mesh *me) { - ThreadMutex *mesh_eval_mutex = (ThreadMutex *)me->runtime.eval_mutex; - BLI_mutex_lock(mesh_eval_mutex); - - if (me->runtime.wrapper_type == ME_WRAPPER_TYPE_MDATA) { - BLI_mutex_unlock(mesh_eval_mutex); + std::lock_guard lock{me->runtime->eval_mutex}; + if (me->runtime->wrapper_type == ME_WRAPPER_TYPE_MDATA) { return; } /* Must isolate multithreaded tasks while holding a mutex lock. */ blender::threading::isolate_task([&]() { - switch (static_cast<eMeshWrapperType>(me->runtime.wrapper_type)) { + switch (static_cast<eMeshWrapperType>(me->runtime->wrapper_type)) { case ME_WRAPPER_TYPE_MDATA: case ME_WRAPPER_TYPE_SUBD: { break; /* Quiet warning. */ @@ -117,10 +114,10 @@ void BKE_mesh_wrapper_ensure_mdata(Mesh *me) me->totloop = 0; BLI_assert(me->edit_mesh != nullptr); - BLI_assert(me->runtime.edit_data != nullptr); + BLI_assert(me->runtime->edit_data != nullptr); BMEditMesh *em = me->edit_mesh; - BM_mesh_bm_to_me_for_eval(em->bm, me, &me->runtime.cd_mask_extra); + BM_mesh_bm_to_me_for_eval(em->bm, me, &me->runtime->cd_mask_extra); /* Adding original index layers assumes that all BMesh mesh wrappers are created from * original edit mode meshes (the only case where adding original indices makes sense). @@ -132,32 +129,30 @@ void BKE_mesh_wrapper_ensure_mdata(Mesh *me) * harmful. */ BKE_mesh_ensure_default_orig_index_customdata_no_check(me); - EditMeshData *edit_data = me->runtime.edit_data; + EditMeshData *edit_data = me->runtime->edit_data; if (edit_data->vertexCos) { BKE_mesh_vert_coords_apply(me, edit_data->vertexCos); - me->runtime.is_original_bmesh = false; + me->runtime->is_original_bmesh = false; } break; } } - if (me->runtime.wrapper_type_finalize) { - BKE_mesh_wrapper_deferred_finalize_mdata(me, &me->runtime.cd_mask_extra); + if (me->runtime->wrapper_type_finalize) { + BKE_mesh_wrapper_deferred_finalize_mdata(me, &me->runtime->cd_mask_extra); } /* Keep type assignment last, so that read-only access only uses the mdata code paths after all * the underlying data has been initialized. */ - me->runtime.wrapper_type = ME_WRAPPER_TYPE_MDATA; + me->runtime->wrapper_type = ME_WRAPPER_TYPE_MDATA; }); - - BLI_mutex_unlock(mesh_eval_mutex); } bool BKE_mesh_wrapper_minmax(const Mesh *me, float min[3], float max[3]) { - switch ((eMeshWrapperType)me->runtime.wrapper_type) { + switch (me->runtime->wrapper_type) { case ME_WRAPPER_TYPE_BMESH: - return BKE_editmesh_cache_calc_minmax(me->edit_mesh, me->runtime.edit_data, min, max); + return BKE_editmesh_cache_calc_minmax(me->edit_mesh, me->runtime->edit_data, min, max); case ME_WRAPPER_TYPE_MDATA: case ME_WRAPPER_TYPE_SUBD: return BKE_mesh_minmax(me, min, max); @@ -174,11 +169,11 @@ void BKE_mesh_wrapper_vert_coords_copy(const Mesh *me, float (*vert_coords)[3], int vert_coords_len) { - switch ((eMeshWrapperType)me->runtime.wrapper_type) { + switch (me->runtime->wrapper_type) { case ME_WRAPPER_TYPE_BMESH: { BMesh *bm = me->edit_mesh->bm; BLI_assert(vert_coords_len <= bm->totvert); - EditMeshData *edit_data = me->runtime.edit_data; + EditMeshData *edit_data = me->runtime->edit_data; if (edit_data->vertexCos != nullptr) { for (int i = 0; i < vert_coords_len; i++) { copy_v3_v3(vert_coords[i], edit_data->vertexCos[i]); @@ -212,11 +207,11 @@ void BKE_mesh_wrapper_vert_coords_copy_with_mat4(const Mesh *me, int vert_coords_len, const float mat[4][4]) { - switch ((eMeshWrapperType)me->runtime.wrapper_type) { + switch (me->runtime->wrapper_type) { case ME_WRAPPER_TYPE_BMESH: { BMesh *bm = me->edit_mesh->bm; BLI_assert(vert_coords_len == bm->totvert); - EditMeshData *edit_data = me->runtime.edit_data; + EditMeshData *edit_data = me->runtime->edit_data; if (edit_data->vertexCos != nullptr) { for (int i = 0; i < vert_coords_len; i++) { mul_v3_m4v3(vert_coords[i], mat, edit_data->vertexCos[i]); @@ -253,7 +248,7 @@ void BKE_mesh_wrapper_vert_coords_copy_with_mat4(const Mesh *me, int BKE_mesh_wrapper_vert_len(const Mesh *me) { - switch ((eMeshWrapperType)me->runtime.wrapper_type) { + switch (me->runtime->wrapper_type) { case ME_WRAPPER_TYPE_BMESH: return me->edit_mesh->bm->totvert; case ME_WRAPPER_TYPE_MDATA: @@ -266,7 +261,7 @@ int BKE_mesh_wrapper_vert_len(const Mesh *me) int BKE_mesh_wrapper_edge_len(const Mesh *me) { - switch ((eMeshWrapperType)me->runtime.wrapper_type) { + switch (me->runtime->wrapper_type) { case ME_WRAPPER_TYPE_BMESH: return me->edit_mesh->bm->totedge; case ME_WRAPPER_TYPE_MDATA: @@ -279,7 +274,7 @@ int BKE_mesh_wrapper_edge_len(const Mesh *me) int BKE_mesh_wrapper_loop_len(const Mesh *me) { - switch ((eMeshWrapperType)me->runtime.wrapper_type) { + switch (me->runtime->wrapper_type) { case ME_WRAPPER_TYPE_BMESH: return me->edit_mesh->bm->totloop; case ME_WRAPPER_TYPE_MDATA: @@ -292,7 +287,7 @@ int BKE_mesh_wrapper_loop_len(const Mesh *me) int BKE_mesh_wrapper_poly_len(const Mesh *me) { - switch ((eMeshWrapperType)me->runtime.wrapper_type) { + switch (me->runtime->wrapper_type) { case ME_WRAPPER_TYPE_BMESH: return me->edit_mesh->bm->totface; case ME_WRAPPER_TYPE_MDATA: @@ -311,7 +306,7 @@ int BKE_mesh_wrapper_poly_len(const Mesh *me) static Mesh *mesh_wrapper_ensure_subdivision(Mesh *me) { - SubsurfRuntimeData *runtime_data = (SubsurfRuntimeData *)me->runtime.subsurf_runtime_data; + SubsurfRuntimeData *runtime_data = (SubsurfRuntimeData *)me->runtime->subsurf_runtime_data; if (runtime_data == nullptr || runtime_data->settings.level == 0) { return me; } @@ -359,24 +354,22 @@ static Mesh *mesh_wrapper_ensure_subdivision(Mesh *me) } if (subdiv_mesh != me) { - if (me->runtime.mesh_eval != nullptr) { - BKE_id_free(nullptr, me->runtime.mesh_eval); + if (me->runtime->mesh_eval != nullptr) { + BKE_id_free(nullptr, me->runtime->mesh_eval); } - me->runtime.mesh_eval = subdiv_mesh; - me->runtime.wrapper_type = ME_WRAPPER_TYPE_SUBD; + me->runtime->mesh_eval = subdiv_mesh; + me->runtime->wrapper_type = ME_WRAPPER_TYPE_SUBD; } - return me->runtime.mesh_eval; + return me->runtime->mesh_eval; } Mesh *BKE_mesh_wrapper_ensure_subdivision(Mesh *me) { - ThreadMutex *mesh_eval_mutex = (ThreadMutex *)me->runtime.eval_mutex; - BLI_mutex_lock(mesh_eval_mutex); + std::lock_guard lock{me->runtime->eval_mutex}; - if (me->runtime.wrapper_type == ME_WRAPPER_TYPE_SUBD) { - BLI_mutex_unlock(mesh_eval_mutex); - return me->runtime.mesh_eval; + if (me->runtime->wrapper_type == ME_WRAPPER_TYPE_SUBD) { + return me->runtime->mesh_eval; } Mesh *result; @@ -384,7 +377,6 @@ Mesh *BKE_mesh_wrapper_ensure_subdivision(Mesh *me) /* Must isolate multithreaded tasks while holding a mutex lock. */ blender::threading::isolate_task([&]() { result = mesh_wrapper_ensure_subdivision(me); }); - BLI_mutex_unlock(mesh_eval_mutex); return result; } diff --git a/source/blender/blenkernel/intern/modifier.cc b/source/blender/blenkernel/intern/modifier.cc index 2eb8ef70a4d..92a7c778b68 100644 --- a/source/blender/blenkernel/intern/modifier.cc +++ b/source/blender/blenkernel/intern/modifier.cc @@ -955,7 +955,7 @@ const char *BKE_modifier_path_relbase_from_global(Object *ob) void BKE_modifier_path_init(char *path, int path_maxlen, const char *name) { const char *blendfile_path = BKE_main_blendfile_path_from_global(); - BLI_join_dirfile(path, path_maxlen, blendfile_path[0] ? "//" : BKE_tempdir_session(), name); + BLI_path_join(path, path_maxlen, blendfile_path[0] ? "//" : BKE_tempdir_session(), name); } /** @@ -963,9 +963,9 @@ void BKE_modifier_path_init(char *path, int path_maxlen, const char *name) */ static void modwrap_dependsOnNormals(Mesh *me) { - switch ((eMeshWrapperType)me->runtime.wrapper_type) { + switch (me->runtime->wrapper_type) { case ME_WRAPPER_TYPE_BMESH: { - EditMeshData *edit_data = me->runtime.edit_data; + EditMeshData *edit_data = me->runtime->edit_data; if (edit_data->vertexCos) { /* Note that 'ensure' is acceptable here since these values aren't modified in-place. * If that changes we'll need to recalculate. */ @@ -993,7 +993,7 @@ struct Mesh *BKE_modifier_modify_mesh(ModifierData *md, { const ModifierTypeInfo *mti = BKE_modifier_get_info(ModifierType(md->type)); - if (me->runtime.wrapper_type == ME_WRAPPER_TYPE_BMESH) { + if (me->runtime->wrapper_type == ME_WRAPPER_TYPE_BMESH) { if ((mti->flags & eModifierTypeFlag_AcceptsBMesh) == 0) { BKE_mesh_wrapper_ensure_mdata(me); } diff --git a/source/blender/blenkernel/intern/multires.cc b/source/blender/blenkernel/intern/multires.cc index 61cfe043927..5ff9602650e 100644 --- a/source/blender/blenkernel/intern/multires.cc +++ b/source/blender/blenkernel/intern/multires.cc @@ -397,7 +397,7 @@ void multires_mark_as_modified(Depsgraph *depsgraph, Object *object, MultiresMod * In a longer term maybe special dependency graph tag can help sanitizing this a bit. */ Object *object_eval = DEG_get_evaluated_object(depsgraph, object); Mesh *mesh = static_cast<Mesh *>(object_eval->data); - SubdivCCG *subdiv_ccg = mesh->runtime.subdiv_ccg; + SubdivCCG *subdiv_ccg = mesh->runtime->subdiv_ccg; if (subdiv_ccg == nullptr) { return; } diff --git a/source/blender/blenkernel/intern/nla.c b/source/blender/blenkernel/intern/nla.c index fd3580a7e88..24663d6db05 100644 --- a/source/blender/blenkernel/intern/nla.c +++ b/source/blender/blenkernel/intern/nla.c @@ -1272,6 +1272,34 @@ float BKE_nlastrip_compute_frame_to_next_strip(NlaStrip *strip) return limit_next; } +NlaStrip *BKE_nlastrip_next_in_track(struct NlaStrip *strip, bool skip_transitions) +{ + NlaStrip *next = strip->next; + while (next != NULL) { + if (skip_transitions && (next->type & NLASTRIP_TYPE_TRANSITION)) { + next = next->next; + } + else { + return next; + } + } + return NULL; +} + +NlaStrip *BKE_nlastrip_prev_in_track(struct NlaStrip *strip, bool skip_transitions) +{ + NlaStrip *prev = strip->prev; + while (prev != NULL) { + if (skip_transitions && (prev->type & NLASTRIP_TYPE_TRANSITION)) { + prev = prev->prev; + } + else { + return prev; + } + } + return NULL; +} + NlaStrip *BKE_nlastrip_find_active(NlaTrack *nlt) { if (nlt == NULL) { @@ -1890,7 +1918,7 @@ bool BKE_nla_action_stash(AnimData *adt, const bool is_liboverride) * NOTE: this must be done *after* adding the strip to the track, or else * the strip locking will prevent the strip from getting added */ - nlt->flag = (NLATRACK_MUTED | NLATRACK_PROTECTED); + nlt->flag |= (NLATRACK_MUTED | NLATRACK_PROTECTED); strip->flag &= ~(NLASTRIP_FLAG_SELECT | NLASTRIP_FLAG_ACTIVE); /* also mark the strip for auto syncing the length, so that the strips accurately diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc index 44daa7d968d..aa25953d85a 100644 --- a/source/blender/blenkernel/intern/node.cc +++ b/source/blender/blenkernel/intern/node.cc @@ -516,10 +516,6 @@ void ntreeBlendWrite(BlendWriter *writer, bNodeTree *ntree) write_node_socket(writer, sock); } - LISTBASE_FOREACH (bNodeLink *, link, &node->internal_links) { - BLO_write_struct(writer, bNodeLink, link); - } - if (node->storage) { if (ELEM(ntree->type, NTREE_SHADER, NTREE_GEOMETRY) && ELEM(node->type, SH_NODE_CURVE_VEC, SH_NODE_CURVE_RGB, SH_NODE_CURVE_FLOAT)) { @@ -703,13 +699,7 @@ void ntreeBlendReadData(BlendDataReader *reader, ID *owner_id, bNodeTree *ntree) BLO_read_data_address(reader, &node->prop); IDP_BlendDataRead(reader, &node->prop); - BLO_read_list(reader, &node->internal_links); - LISTBASE_FOREACH (bNodeLink *, link, &node->internal_links) { - BLO_read_data_address(reader, &link->fromnode); - BLO_read_data_address(reader, &link->fromsock); - BLO_read_data_address(reader, &link->tonode); - BLO_read_data_address(reader, &link->tosock); - } + BLI_listbase_clear(&node->internal_links); if (node->type == CMP_NODE_MOVIEDISTORTION) { /* Do nothing, this is runtime cache and hence handled by generic code using @@ -2157,6 +2147,38 @@ void nodeParentsIter(bNode *node, bool (*callback)(bNode *, void *), void *userd } } +bool nodeIsDanglingReroute(const bNodeTree *ntree, const bNode *node) +{ + ntree->ensure_topology_cache(); + BLI_assert(blender::bke::node_tree_runtime::topology_cache_is_available(*ntree)); + BLI_assert(!ntree->has_available_link_cycle()); + + const bNode *iter_node = node; + if (!iter_node->is_reroute()) { + return false; + } + + while (true) { + const blender::Span<const bNodeLink *> links = + iter_node->input_socket(0).directly_linked_links(); + BLI_assert(links.size() <= 1); + if (links.is_empty()) { + return true; + } + const bNodeLink &link = *links[0]; + if (!link.is_available()) { + return false; + } + if (link.is_muted()) { + return false; + } + iter_node = link.fromnode; + if (!iter_node->is_reroute()) { + return false; + } + } +} + /* ************** Add stuff ********** */ void nodeUniqueName(bNodeTree *ntree, bNode *node) diff --git a/source/blender/blenkernel/intern/object.cc b/source/blender/blenkernel/intern/object.cc index 9417d1afc7e..d98f5e85028 100644 --- a/source/blender/blenkernel/intern/object.cc +++ b/source/blender/blenkernel/intern/object.cc @@ -1691,7 +1691,7 @@ static void object_update_from_subsurf_ccg(Object *object) if (mesh_eval == nullptr) { return; } - SubdivCCG *subdiv_ccg = mesh_eval->runtime.subdiv_ccg; + SubdivCCG *subdiv_ccg = mesh_eval->runtime->subdiv_ccg; if (subdiv_ccg == nullptr) { return; } @@ -1699,7 +1699,7 @@ static void object_update_from_subsurf_ccg(Object *object) if (!subdiv_ccg->dirty.coords && !subdiv_ccg->dirty.hidden) { return; } - const int tot_level = mesh_eval->runtime.subdiv_ccg_tot_level; + const int tot_level = mesh_eval->runtime->subdiv_ccg_tot_level; Object *object_orig = DEG_get_original_object(object); Mesh *mesh_orig = (Mesh *)object_orig->data; multiresModifier_reshapeFromCCG(tot_level, mesh_orig, subdiv_ccg); @@ -1902,6 +1902,7 @@ bool BKE_object_is_in_editmode(const Object *ob) /* Grease Pencil object has no edit mode data. */ return GPENCIL_EDIT_MODE((bGPdata *)ob->data); case OB_CURVES: + /* Curves object has no edit mode data. */ return ob->mode == OB_MODE_EDIT; default: return false; @@ -3218,7 +3219,7 @@ static void give_parvert(Object *par, int nr, float vec[3]) int count = 0; int numVerts = me_eval->totvert; - if (em && me_eval->runtime.wrapper_type == ME_WRAPPER_TYPE_BMESH) { + if (em && me_eval->runtime->wrapper_type == ME_WRAPPER_TYPE_BMESH) { numVerts = em->bm->totvert; if (em->bm->elem_table_dirty & BM_VERT) { #ifdef VPARENT_THREADING_HACK @@ -3233,8 +3234,8 @@ static void give_parvert(Object *par, int nr, float vec[3]) #endif } if (nr < numVerts) { - if (me_eval && me_eval->runtime.edit_data && me_eval->runtime.edit_data->vertexCos) { - add_v3_v3(vec, me_eval->runtime.edit_data->vertexCos[nr]); + if (me_eval && me_eval->runtime->edit_data && me_eval->runtime->edit_data->vertexCos) { + add_v3_v3(vec, me_eval->runtime->edit_data->vertexCos[nr]); } else { const BMVert *v = BM_vert_at_index(em->bm, nr); diff --git a/source/blender/blenkernel/intern/object_dupli.cc b/source/blender/blenkernel/intern/object_dupli.cc index 306e508dc83..d43eff6f9b4 100644 --- a/source/blender/blenkernel/intern/object_dupli.cc +++ b/source/blender/blenkernel/intern/object_dupli.cc @@ -41,6 +41,7 @@ #include "BKE_geometry_set.hh" #include "BKE_global.h" #include "BKE_idprop.h" +#include "BKE_instances.hh" #include "BKE_lattice.h" #include "BKE_main.h" #include "BKE_mesh.h" @@ -70,6 +71,8 @@ using blender::float3; using blender::float4x4; using blender::Span; using blender::Vector; +using blender::bke::InstanceReference; +using blender::bke::Instances; namespace geo_log = blender::nodes::geo_eval_log; /* -------------------------------------------------------------------- */ @@ -423,8 +426,8 @@ static const Mesh *mesh_data_from_duplicator_object(Object *ob, /* Note that this will only show deformation if #eModifierMode_OnCage is enabled. * We could change this but it matches 2.7x behavior. */ me_eval = BKE_object_get_editmesh_eval_cage(ob); - if ((me_eval == nullptr) || (me_eval->runtime.wrapper_type == ME_WRAPPER_TYPE_BMESH)) { - EditMeshData *emd = me_eval ? me_eval->runtime.edit_data : nullptr; + if ((me_eval == nullptr) || (me_eval->runtime->wrapper_type == ME_WRAPPER_TYPE_BMESH)) { + EditMeshData *emd = me_eval ? me_eval->runtime->edit_data : nullptr; /* Only assign edit-mesh in the case we can't use `me_eval`. */ *r_em = em; @@ -874,8 +877,8 @@ static void make_duplis_geometry_set_impl(const DupliContext *ctx, } const bool creates_duplis_for_components = component_index >= 1; - const InstancesComponent *component = geometry_set.get_component_for_read<InstancesComponent>(); - if (component == nullptr) { + const Instances *instances = geometry_set.get_instances_for_read(); + if (instances == nullptr) { return; } @@ -890,13 +893,13 @@ static void make_duplis_geometry_set_impl(const DupliContext *ctx, 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(); - Span<InstanceReference> references = component->references(); + Span<float4x4> instance_offset_matrices = instances->transforms(); + Span<int> reference_handles = instances->reference_handles(); + Span<int> almost_unique_ids = instances->almost_unique_ids(); + Span<InstanceReference> references = instances->references(); for (int64_t i : instance_offset_matrices.index_range()) { - const InstanceReference &reference = references[instance_reference_handles[i]]; + const InstanceReference &reference = references[reference_handles[i]]; const int id = almost_unique_ids[i]; const DupliContext *ctx_for_instance = instances_ctx; diff --git a/source/blender/blenkernel/intern/object_update.cc b/source/blender/blenkernel/intern/object_update.cc index 5328d956cee..e53090bcffe 100644 --- a/source/blender/blenkernel/intern/object_update.cc +++ b/source/blender/blenkernel/intern/object_update.cc @@ -292,6 +292,8 @@ void BKE_object_batch_cache_dirty_tag(Object *ob) BKE_lattice_batch_cache_dirty_tag((struct Lattice *)ob->data, BKE_LATTICE_BATCH_DIRTY_ALL); break; case OB_CURVES_LEGACY: + case OB_SURF: + case OB_FONT: BKE_curve_batch_cache_dirty_tag((struct Curve *)ob->data, BKE_CURVE_BATCH_DIRTY_ALL); break; case OB_MBALL: { @@ -373,10 +375,8 @@ void BKE_object_select_update(Depsgraph *depsgraph, Object *object) DEG_debug_print_eval(depsgraph, __func__, object->id.name, object); if (object->type == OB_MESH && !object->runtime.is_data_eval_owned) { Mesh *mesh_input = (Mesh *)object->runtime.data_orig; - Mesh_Runtime *mesh_runtime = &mesh_input->runtime; - BLI_mutex_lock(static_cast<ThreadMutex *>(mesh_runtime->eval_mutex)); + std::lock_guard lock{mesh_input->runtime->eval_mutex}; BKE_object_data_select_update(depsgraph, static_cast<ID *>(object->data)); - BLI_mutex_unlock(static_cast<ThreadMutex *>(mesh_runtime->eval_mutex)); } else { BKE_object_data_select_update(depsgraph, static_cast<ID *>(object->data)); diff --git a/source/blender/blenkernel/intern/ocean.c b/source/blender/blenkernel/intern/ocean.c index 396c3443a73..bcf382b6022 100644 --- a/source/blender/blenkernel/intern/ocean.c +++ b/source/blender/blenkernel/intern/ocean.c @@ -1144,7 +1144,7 @@ static void cache_filename( break; } - BLI_join_dirfile(cachepath, sizeof(cachepath), path, fname); + BLI_path_join(cachepath, sizeof(cachepath), path, fname); BKE_image_path_from_imtype( string, cachepath, relbase, frame, R_IMF_IMTYPE_OPENEXR, true, true, ""); diff --git a/source/blender/blenkernel/intern/packedFile.c b/source/blender/blenkernel/intern/packedFile.c index 901b42ac0b2..0c9d9f5b048 100644 --- a/source/blender/blenkernel/intern/packedFile.c +++ b/source/blender/blenkernel/intern/packedFile.c @@ -545,7 +545,7 @@ static void unpack_generate_paths(const char *name, break; } if (dir_name) { - BLI_path_join(r_relpath, relpathlen, "//", dir_name, tempname, NULL); + BLI_path_join(r_relpath, relpathlen, "//", dir_name, tempname); } } diff --git a/source/blender/blenkernel/intern/paint.cc b/source/blender/blenkernel/intern/paint.cc index 00535ea5528..408cd117e32 100644 --- a/source/blender/blenkernel/intern/paint.cc +++ b/source/blender/blenkernel/intern/paint.cc @@ -54,6 +54,7 @@ #include "BKE_object.h" #include "BKE_paint.h" #include "BKE_pbvh.h" +#include "BKE_scene.h" #include "BKE_subdiv_ccg.h" #include "BKE_subsurf.h" @@ -1042,6 +1043,8 @@ eObjectMode BKE_paint_object_mode_from_paintmode(ePaintMode mode) return OB_MODE_TEXTURE_PAINT; case PAINT_MODE_SCULPT_UV: return OB_MODE_EDIT; + case PAINT_MODE_SCULPT_CURVES: + return OB_MODE_SCULPT_CURVES; case PAINT_MODE_INVALID: default: return OB_MODE_OBJECT; @@ -1743,7 +1746,7 @@ static void sculpt_update_object( ss->hide_poly = (bool *)CustomData_get_layer_named(&me->pdata, CD_PROP_BOOL, ".hide_poly"); - ss->subdiv_ccg = me_eval->runtime.subdiv_ccg; + ss->subdiv_ccg = me_eval->runtime->subdiv_ccg; PBVH *pbvh = BKE_sculpt_object_pbvh_ensure(depsgraph, ob); BLI_assert(pbvh == ss->pbvh); @@ -1785,8 +1788,8 @@ static void sculpt_update_object( /* If the fully evaluated mesh has the same topology as the deform-only version, use it. * This matters because crazyspace evaluation is very restrictive and excludes even modifiers * that simply recompute vertex weights (which can even include Geometry Nodes). */ - if (me_eval_deform->polys().data() == me_eval->polys().data() && - me_eval_deform->loops().data() == me_eval->loops().data() && + if (me_eval_deform->totpoly == me_eval->totpoly && + me_eval_deform->totloop == me_eval->totloop && me_eval_deform->totvert == me_eval->totvert) { BKE_sculptsession_free_deformMats(ss); @@ -1986,7 +1989,10 @@ bool *BKE_sculpt_hide_poly_ensure(Mesh *mesh) &mesh->pdata, CD_PROP_BOOL, CD_SET_DEFAULT, nullptr, mesh->totpoly, ".hide_poly")); } -int BKE_sculpt_mask_layers_ensure(Object *ob, MultiresModifierData *mmd) +int BKE_sculpt_mask_layers_ensure(Depsgraph *depsgraph, + Main *bmain, + Object *ob, + MultiresModifierData *mmd) { Mesh *me = static_cast<Mesh *>(ob->data); const Span<MPoly> polys = me->polys(); @@ -2045,6 +2051,9 @@ int BKE_sculpt_mask_layers_ensure(Object *ob, MultiresModifierData *mmd) } /* The evaluated multires CCG must be updated to contain the new data. */ DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); + if (depsgraph) { + BKE_scene_graph_evaluated_ensure(depsgraph, bmain); + } ret |= SCULPT_MASK_LAYER_CALC_LOOP; } @@ -2237,7 +2246,8 @@ static PBVH *build_pbvh_from_ccg(Object *ob, SubdivCCG *subdiv_ccg, bool respect (void **)subdiv_ccg->grid_faces, subdiv_ccg->grid_flag_mats, subdiv_ccg->grid_hidden, - base_mesh); + base_mesh, + subdiv_ccg); pbvh_show_mask_set(pbvh, ob->sculpt->show_mask); pbvh_show_face_sets_set(pbvh, ob->sculpt->show_face_sets); return pbvh; @@ -2258,7 +2268,7 @@ PBVH *BKE_sculpt_object_pbvh_ensure(Depsgraph *depsgraph, Object *ob) if (BKE_pbvh_type(pbvh) == PBVH_GRIDS) { Object *object_eval = DEG_get_evaluated_object(depsgraph, ob); Mesh *mesh_eval = static_cast<Mesh *>(object_eval->data); - SubdivCCG *subdiv_ccg = mesh_eval->runtime.subdiv_ccg; + SubdivCCG *subdiv_ccg = mesh_eval->runtime->subdiv_ccg; if (subdiv_ccg != nullptr) { BKE_sculpt_bvh_update_from_ccg(pbvh, subdiv_ccg); } @@ -2277,8 +2287,8 @@ PBVH *BKE_sculpt_object_pbvh_ensure(Depsgraph *depsgraph, Object *ob) else { Object *object_eval = DEG_get_evaluated_object(depsgraph, ob); Mesh *mesh_eval = static_cast<Mesh *>(object_eval->data); - if (mesh_eval->runtime.subdiv_ccg != nullptr) { - pbvh = build_pbvh_from_ccg(ob, mesh_eval->runtime.subdiv_ccg, respect_hide); + if (mesh_eval->runtime->subdiv_ccg != nullptr) { + pbvh = build_pbvh_from_ccg(ob, mesh_eval->runtime->subdiv_ccg, respect_hide); } else if (ob->type == OB_MESH) { Mesh *me_eval_deform = object_eval->runtime.mesh_deform_eval; @@ -2287,19 +2297,23 @@ PBVH *BKE_sculpt_object_pbvh_ensure(Depsgraph *depsgraph, Object *ob) } BKE_pbvh_pmap_set(pbvh, ob->sculpt->pmap); - sculpt_attribute_update_refs(ob); - ob->sculpt->pbvh = pbvh; + + sculpt_attribute_update_refs(ob); return pbvh; } void BKE_sculpt_bvh_update_from_ccg(PBVH *pbvh, SubdivCCG *subdiv_ccg) { + CCGKey key; + BKE_subdiv_ccg_key_top_level(&key, subdiv_ccg); + BKE_pbvh_grids_update(pbvh, subdiv_ccg->grids, (void **)subdiv_ccg->grid_faces, subdiv_ccg->grid_flag_mats, - subdiv_ccg->grid_hidden); + subdiv_ccg->grid_hidden, + &key); } bool BKE_sculptsession_use_pbvh_draw(const Object *ob, const View3D * /*v3d*/) diff --git a/source/blender/blenkernel/intern/particle.c b/source/blender/blenkernel/intern/particle.c index 6d42d344b86..d6dd3cf0dbb 100644 --- a/source/blender/blenkernel/intern/particle.c +++ b/source/blender/blenkernel/intern/particle.c @@ -60,6 +60,7 @@ #include "BKE_material.h" #include "BKE_mesh.h" #include "BKE_mesh_legacy_convert.h" +#include "BKE_mesh_runtime.h" #include "BKE_modifier.h" #include "BKE_object.h" #include "BKE_particle.h" @@ -1933,7 +1934,7 @@ int psys_particle_dm_face_lookup(Mesh *mesh_final, index_mf_to_mpoly_deformed = CustomData_get_layer(&mesh_original->fdata, CD_ORIGINDEX); } else { - BLI_assert(mesh_final->runtime.deformed_only); + BLI_assert(BKE_mesh_is_deformed_only(mesh_final)); index_mf_to_mpoly_deformed = index_mf_to_mpoly; } BLI_assert(index_mf_to_mpoly_deformed); @@ -2023,7 +2024,7 @@ static int psys_map_index_on_dm(Mesh *mesh, return 0; } - if (mesh->runtime.deformed_only || index_dmcache == DMCACHE_ISCHILD) { + if (BKE_mesh_is_deformed_only(mesh) || index_dmcache == DMCACHE_ISCHILD) { /* for meshes that are either only deformed or for child particles, the * index and fw do not require any mapping, so we can directly use it */ if (from == PART_FROM_VERT) { diff --git a/source/blender/blenkernel/intern/particle_distribute.c b/source/blender/blenkernel/intern/particle_distribute.c index 4c56a8a9275..0301b83a043 100644 --- a/source/blender/blenkernel/intern/particle_distribute.c +++ b/source/blender/blenkernel/intern/particle_distribute.c @@ -29,6 +29,7 @@ #include "BKE_lib_id.h" #include "BKE_mesh.h" #include "BKE_mesh_legacy_convert.h" +#include "BKE_mesh_runtime.h" #include "BKE_object.h" #include "BKE_particle.h" @@ -899,7 +900,7 @@ static int psys_thread_context_init_distribute(ParticleThreadContext *ctx, return 0; } - if (!final_mesh->runtime.deformed_only && + if (!BKE_mesh_is_deformed_only(final_mesh) && !CustomData_get_layer(&final_mesh->fdata, CD_ORIGINDEX)) { printf( "Can't create particles with the current modifier stack, disable destructive modifiers\n"); diff --git a/source/blender/blenkernel/intern/particle_system.c b/source/blender/blenkernel/intern/particle_system.c index dec874caff4..c72bbe2fd08 100644 --- a/source/blender/blenkernel/intern/particle_system.c +++ b/source/blender/blenkernel/intern/particle_system.c @@ -48,6 +48,7 @@ #include "BKE_lib_id.h" #include "BKE_lib_query.h" #include "BKE_mesh_legacy_convert.h" +#include "BKE_mesh_runtime.h" #include "BKE_particle.h" #include "BKE_bvhutils.h" @@ -319,7 +320,7 @@ void psys_calc_dmcache(Object *ob, Mesh *mesh_final, Mesh *mesh_original, Partic PARTICLE_P; /* CACHE LOCATIONS */ - if (!mesh_final->runtime.deformed_only) { + if (!BKE_mesh_is_deformed_only(mesh_final)) { /* Will use later to speed up subsurf/evaluated mesh. */ LinkNode *node, *nodedmelem, **nodearray; int totdmelem, totelem, i; diff --git a/source/blender/blenkernel/intern/pbvh.c b/source/blender/blenkernel/intern/pbvh.c index a7595952cac..98e89b09060 100644 --- a/source/blender/blenkernel/intern/pbvh.c +++ b/source/blender/blenkernel/intern/pbvh.c @@ -39,8 +39,10 @@ #define LEAF_LIMIT 10000 -//#define PERFCNTRS +/* Uncomment to test that faces are only assigned to one PBVHNode */ +//#define VALIDATE_UNIQUE_NODE_FACES +//#define PERFCNTRS #define STACK_FIXED_DEPTH 100 typedef struct PBVHStack { @@ -422,6 +424,56 @@ static bool leaf_needs_material_split(PBVH *pbvh, int offset, int count) return false; } +static int adjust_partition_faces(PBVH *pbvh, int offset, int mid, int count) +{ + int poly = pbvh->looptri[pbvh->prim_indices[mid]].poly; + + /* Scan backwards. */ + while (mid > offset + 2) { /* First node should have at least 1 primitive */ + if (pbvh->looptri[pbvh->prim_indices[mid - 1]].poly != poly) { + return mid; + } + + mid--; + } + + /* If that didn't work try scanning forward. */ + while (mid < pbvh->totprim + count) { + if (pbvh->looptri[pbvh->prim_indices[mid]].poly != poly) { + break; + } + + mid++; + } + + return mid; +} + +static int adjust_partition_grids(PBVH *pbvh, int offset, int mid, int count) +{ + int poly = BKE_subdiv_ccg_grid_to_face_index(pbvh->subdiv_ccg, pbvh->prim_indices[mid]); + + /* Scan backwards. */ + while (mid > offset + 2) { /* First node should have at least 1 primitive */ + if (BKE_subdiv_ccg_grid_to_face_index(pbvh->subdiv_ccg, pbvh->prim_indices[mid - 1]) != poly) { + return mid; + } + + mid--; + } + + /* If that didn't work try scanning forward. */ + while (mid < pbvh->totprim + count) { + if (BKE_subdiv_ccg_grid_to_face_index(pbvh->subdiv_ccg, pbvh->prim_indices[mid]) != poly) { + break; + } + + mid++; + } + + return mid; +} + /* Recursively build a node in the tree * * vb is the voxel box around all of the primitives contained in @@ -478,6 +530,13 @@ static void build_sub(PBVH *pbvh, int node_index, BB *cb, BBC *prim_bbc, int off end = partition_indices_material(pbvh, offset, offset + count - 1); } + if (pbvh->header.type == PBVH_FACES) { + end = adjust_partition_faces(pbvh, offset, end, count); + } + else { + end = adjust_partition_grids(pbvh, offset, end, count); + } + /* Build children */ build_sub(pbvh, pbvh->nodes[node_index].children_offset, NULL, prim_bbc, offset, end - offset); build_sub(pbvh, @@ -587,6 +646,73 @@ static void pbvh_draw_args_init(PBVH *pbvh, PBVH_GPU_Args *args, PBVHNode *node) } } +#ifdef VALIDATE_UNIQUE_NODE_FACES +static void pbvh_validate_node_prims(PBVH *pbvh) +{ + int totface = 0; + + if (pbvh->header.type == PBVH_BMESH) { + return; + } + + for (int i = 0; i < pbvh->totnode; i++) { + PBVHNode *node = pbvh->nodes + i; + + if (!(node->flag & PBVH_Leaf)) { + continue; + } + + for (int j = 0; j < node->totprim; j++) { + int poly; + + if (pbvh->header.type == PBVH_FACES) { + poly = pbvh->looptri[node->prim_indices[j]].poly; + } + else { + poly = BKE_subdiv_ccg_grid_to_face_index(pbvh->subdiv_ccg, node->prim_indices[j]); + } + + totface = max_ii(totface, poly + 1); + } + } + + int *facemap = (int *)MEM_malloc_arrayN(totface, sizeof(*facemap), __func__); + + for (int i = 0; i < totface; i++) { + facemap[i] = -1; + } + + for (int i = 0; i < pbvh->totnode; i++) { + PBVHNode *node = pbvh->nodes + i; + + if (!(node->flag & PBVH_Leaf)) { + continue; + } + + for (int j = 0; j < node->totprim; j++) { + int poly; + + if (pbvh->header.type == PBVH_FACES) { + poly = pbvh->looptri[node->prim_indices[j]].poly; + } + else { + poly = BKE_subdiv_ccg_grid_to_face_index(pbvh->subdiv_ccg, node->prim_indices[j]); + } + + if (facemap[poly] != -1 && facemap[poly] != i) { + printf("%s: error: face spanned multiple nodes (old: %d new: %d)\n", + __func__, + facemap[poly], + i); + } + + facemap[poly] = i; + } + } + MEM_SAFE_FREE(facemap); +} +#endif + void BKE_pbvh_build_mesh(PBVH *pbvh, Mesh *mesh, const MPoly *mpoly, @@ -645,6 +771,32 @@ void BKE_pbvh_build_mesh(PBVH *pbvh, BB_expand(&cb, bbc->bcentroid); } + /* Ensure all primitives belonging to the same base face + * have the same bounds. This is needed to prevent them + * from being swapped away from each other inside the partition + * array. + */ + for (int i = 0; i < looptri_num; i++) { + const MLoopTri *lt = &looptri[i]; + int poly = lt->poly; + BBC *bbc = prim_bbc + i; + int j = i + 1; + + while (j < looptri_num && looptri[j].poly == poly) { + BBC *bbc2 = prim_bbc + j; + + BB_expand((BB *)bbc, bbc2->bmin); + BB_expand((BB *)bbc, bbc2->bmax); + j++; + } + + j = i + 1; + while (j < looptri_num && looptri[j].poly == poly) { + prim_bbc[j] = prim_bbc[i]; + j++; + } + } + if (looptri_num) { pbvh_build(pbvh, &cb, prim_bbc, looptri_num); } @@ -655,6 +807,10 @@ void BKE_pbvh_build_mesh(PBVH *pbvh, memset(pbvh->vert_bitmap, 0, sizeof(bool) * totvert); BKE_pbvh_update_active_vcol(pbvh, mesh); + +#ifdef VALIDATE_UNIQUE_NODE_FACES + pbvh_validate_node_prims(pbvh); +#endif } void BKE_pbvh_build_grids(PBVH *pbvh, @@ -664,7 +820,8 @@ void BKE_pbvh_build_grids(PBVH *pbvh, void **gridfaces, DMFlagMat *flagmats, BLI_bitmap **grid_hidden, - Mesh *me) + Mesh *me, + SubdivCCG *subdiv_ccg) { const int gridsize = key->grid_size; @@ -675,6 +832,7 @@ void BKE_pbvh_build_grids(PBVH *pbvh, pbvh->totgrid = totgrid; pbvh->gridkey = *key; pbvh->grid_hidden = grid_hidden; + pbvh->subdiv_ccg = subdiv_ccg; pbvh->leaf_limit = max_ii(LEAF_LIMIT / (gridsize * gridsize), 1); /* We need the base mesh attribute layout for PBVH draw. */ @@ -706,11 +864,40 @@ void BKE_pbvh_build_grids(PBVH *pbvh, BB_expand(&cb, bbc->bcentroid); } + /* Ensure all primitives belonging to the same base face + * have the same bounds. This is needed to prevent them + * from being swapped away from each other inside the partition + * array. + */ + for (int i = 0; i < totgrid; i++) { + int poly = BKE_subdiv_ccg_grid_to_face_index(pbvh->subdiv_ccg, i); + + BBC *bbc = prim_bbc + i; + int j = i + 1; + + while (j < totgrid && BKE_subdiv_ccg_grid_to_face_index(pbvh->subdiv_ccg, j) == poly) { + BBC *bbc2 = prim_bbc + j; + + BB_expand((BB *)bbc, bbc2->bmin); + BB_expand((BB *)bbc, bbc2->bmax); + j++; + } + + j = i + 1; + while (j < totgrid && BKE_subdiv_ccg_grid_to_face_index(pbvh->subdiv_ccg, j) == poly) { + prim_bbc[j] = prim_bbc[i]; + j++; + } + } + if (totgrid) { pbvh_build(pbvh, &cb, prim_bbc, totgrid); } MEM_freeN(prim_bbc); +#ifdef VALIDATE_UNIQUE_NODE_FACES + pbvh_validate_node_prims(pbvh); +#endif } PBVH *BKE_pbvh_new(PBVHType type) @@ -719,6 +906,11 @@ PBVH *BKE_pbvh_new(PBVHType type) pbvh->respect_hide = true; pbvh->draw_cache_invalid = true; pbvh->header.type = type; + + /* Initialize this to true, instead of waiting for a draw engine + * to set it. Prevents a crash in draw manager instancing code. + */ + pbvh->is_drawing = true; return pbvh; } @@ -2044,11 +2236,16 @@ void BKE_pbvh_node_get_proxies(PBVHNode *node, PBVHProxyNode **proxies, int *pro void BKE_pbvh_node_get_bm_orco_data(PBVHNode *node, int (**r_orco_tris)[3], int *r_orco_tris_num, - float (**r_orco_coords)[3]) + float (**r_orco_coords)[3], + BMVert ***r_orco_verts) { *r_orco_tris = node->bm_ortri; *r_orco_tris_num = node->bm_tot_ortri; *r_orco_coords = node->bm_orco; + + if (r_orco_verts) { + *r_orco_verts = node->bm_orvert; + } } bool BKE_pbvh_node_has_vert_with_normal_update_tag(PBVH *pbvh, PBVHNode *node) @@ -2867,9 +3064,14 @@ void BKE_pbvh_draw_debug_cb(PBVH *pbvh, } } -void BKE_pbvh_grids_update( - PBVH *pbvh, CCGElem **grids, void **gridfaces, DMFlagMat *flagmats, BLI_bitmap **grid_hidden) +void BKE_pbvh_grids_update(PBVH *pbvh, + CCGElem **grids, + void **gridfaces, + DMFlagMat *flagmats, + BLI_bitmap **grid_hidden, + CCGKey *key) { + pbvh->gridkey = *key; pbvh->grids = grids; pbvh->gridfaces = gridfaces; diff --git a/source/blender/blenkernel/intern/pbvh_bmesh.c b/source/blender/blenkernel/intern/pbvh_bmesh.c index 516e1fb4639..3b0f35263d3 100644 --- a/source/blender/blenkernel/intern/pbvh_bmesh.c +++ b/source/blender/blenkernel/intern/pbvh_bmesh.c @@ -493,7 +493,7 @@ static BMVert *pbvh_bmesh_vert_create(PBVH *pbvh, BLI_gset_insert(node->bm_unique_verts, v); BM_ELEM_CD_SET_INT(v, pbvh->cd_vert_node_offset, node_index); - node->flag |= PBVH_UpdateDrawBuffers | PBVH_UpdateBB; + node->flag |= PBVH_UpdateDrawBuffers | PBVH_UpdateBB | PBVH_TopologyUpdated; /* Log the new vertex */ BM_log_vert_added(pbvh->bm_log, v, cd_vert_mask_offset); @@ -519,7 +519,7 @@ static BMFace *pbvh_bmesh_face_create( BM_ELEM_CD_SET_INT(f, pbvh->cd_face_node_offset, node_index); /* mark node for update */ - node->flag |= PBVH_UpdateDrawBuffers | PBVH_UpdateNormals; + node->flag |= PBVH_UpdateDrawBuffers | PBVH_UpdateNormals | PBVH_TopologyUpdated; node->flag &= ~PBVH_FullyHidden; /* Log the new face */ @@ -594,7 +594,7 @@ static void pbvh_bmesh_vert_ownership_transfer(PBVH *pbvh, PBVHNode *new_owner, { PBVHNode *current_owner = pbvh_bmesh_node_from_vert(pbvh, v); /* mark node for update */ - current_owner->flag |= PBVH_UpdateDrawBuffers | PBVH_UpdateBB; + current_owner->flag |= PBVH_UpdateDrawBuffers | PBVH_UpdateBB | PBVH_TopologyUpdated; BLI_assert(current_owner != new_owner); @@ -608,7 +608,7 @@ static void pbvh_bmesh_vert_ownership_transfer(PBVH *pbvh, PBVHNode *new_owner, BLI_assert(!BLI_gset_haskey(new_owner->bm_other_verts, v)); /* mark node for update */ - new_owner->flag |= PBVH_UpdateDrawBuffers | PBVH_UpdateBB; + new_owner->flag |= PBVH_UpdateDrawBuffers | PBVH_UpdateBB | PBVH_TopologyUpdated; } static void pbvh_bmesh_vert_remove(PBVH *pbvh, BMVert *v) @@ -631,7 +631,7 @@ static void pbvh_bmesh_vert_remove(PBVH *pbvh, BMVert *v) f_node_index_prev = f_node_index; PBVHNode *f_node = &pbvh->nodes[f_node_index]; - f_node->flag |= PBVH_UpdateDrawBuffers | PBVH_UpdateBB; + f_node->flag |= PBVH_UpdateDrawBuffers | PBVH_UpdateBB | PBVH_TopologyUpdated; /* Remove current ownership */ BLI_gset_remove(f_node->bm_other_verts, v, NULL); @@ -680,7 +680,7 @@ static void pbvh_bmesh_face_remove(PBVH *pbvh, BMFace *f) BM_log_face_removed(pbvh->bm_log, f); /* mark node for update */ - f_node->flag |= PBVH_UpdateDrawBuffers | PBVH_UpdateNormals; + f_node->flag |= PBVH_UpdateDrawBuffers | PBVH_UpdateNormals | PBVH_TopologyUpdated; } static void pbvh_bmesh_edge_loops(BLI_Buffer *buf, BMEdge *e) @@ -701,14 +701,9 @@ static void pbvh_bmesh_edge_loops(BLI_Buffer *buf, BMEdge *e) static void pbvh_bmesh_node_drop_orig(PBVHNode *node) { - if (node->bm_orco) { - MEM_freeN(node->bm_orco); - } - if (node->bm_ortri) { - MEM_freeN(node->bm_ortri); - } - node->bm_orco = NULL; - node->bm_ortri = NULL; + MEM_SAFE_FREE(node->bm_orco); + MEM_SAFE_FREE(node->bm_ortri); + MEM_SAFE_FREE(node->bm_orvert); node->bm_tot_ortri = 0; } @@ -1507,29 +1502,51 @@ bool pbvh_bmesh_node_raycast(PBVHNode *node, bool hit = false; float nearest_vertex_co[3] = {0.0f}; + BLI_assert(!use_original || (BLI_gset_len(node->bm_faces) > 0 && node->bm_tot_ortri)); + + use_original = use_original && node->bm_tot_ortri; + + GSetIterator gs_iter; + if (use_original && node->bm_tot_ortri) { for (int i = 0; i < node->bm_tot_ortri; i++) { - const int *t = node->bm_ortri[i]; - hit |= ray_face_intersection_tri(ray_start, - isect_precalc, - node->bm_orco[t[0]], - node->bm_orco[t[1]], - node->bm_orco[t[2]], - depth); + float *cos[3]; + + cos[0] = node->bm_orco[node->bm_ortri[i][0]]; + cos[1] = node->bm_orco[node->bm_ortri[i][1]]; + cos[2] = node->bm_orco[node->bm_ortri[i][2]]; + + if (ray_face_intersection_tri(ray_start, isect_precalc, cos[0], cos[1], cos[2], depth)) { + hit = true; + + if (r_face_normal) { + normal_tri_v3(r_face_normal, cos[0], cos[1], cos[2]); + } + + if (r_active_vertex) { + float location[3] = {0.0f}; + madd_v3_v3v3fl(location, ray_start, ray_normal, *depth); + for (int j = 0; j < 3; j++) { + if (len_squared_v3v3(location, cos[j]) < + len_squared_v3v3(location, nearest_vertex_co)) { + copy_v3_v3(nearest_vertex_co, cos[j]); + r_active_vertex->i = (intptr_t)node->bm_orvert[node->bm_ortri[i][j]]; + } + } + } + } } } else { - GSetIterator gs_iter; - GSET_ITER (gs_iter, node->bm_faces) { BMFace *f = BLI_gsetIterator_getKey(&gs_iter); BLI_assert(f->len == 3); + if (!BM_elem_flag_test(f, BM_ELEM_HIDDEN)) { BMVert *v_tri[3]; BM_face_as_array_vert_tri(f, v_tri); - if (ray_face_intersection_tri( ray_start, isect_precalc, v_tri[0]->co, v_tri[1]->co, v_tri[2]->co, depth)) { hit = true; @@ -2016,6 +2033,21 @@ bool BKE_pbvh_bmesh_update_topology(PBVH *pbvh, BLI_buffer_free(&edge_loops); BLI_buffer_free(&deleted_faces); + /* Go over all changed nodes and check if anything needs to be updated. */ + for (int n = 0; n < pbvh->totnode; n++) { + PBVHNode *node = &pbvh->nodes[n]; + + if (node->flag & PBVH_Leaf && node->flag & PBVH_TopologyUpdated) { + node->flag &= ~PBVH_TopologyUpdated; + + if (node->bm_ortri) { + /* Reallocate original triangle data. */ + pbvh_bmesh_node_drop_orig(node); + BKE_pbvh_bmesh_node_save_orig(pbvh->header.bm, pbvh->bm_log, node, true); + } + } + } + #ifdef USE_VERIFY pbvh_bmesh_verify(pbvh); #endif @@ -2023,7 +2055,7 @@ bool BKE_pbvh_bmesh_update_topology(PBVH *pbvh, return modified; } -void BKE_pbvh_bmesh_node_save_orig(BMesh *bm, PBVHNode *node) +void BKE_pbvh_bmesh_node_save_orig(BMesh *bm, BMLog *log, PBVHNode *node, bool use_original) { /* Skip if original coords/triangles are already saved */ if (node->bm_orco) { @@ -2036,19 +2068,38 @@ void BKE_pbvh_bmesh_node_save_orig(BMesh *bm, PBVHNode *node) node->bm_orco = MEM_mallocN(sizeof(*node->bm_orco) * totvert, __func__); node->bm_ortri = MEM_mallocN(sizeof(*node->bm_ortri) * tottri, __func__); + node->bm_orvert = MEM_mallocN(sizeof(*node->bm_orvert) * totvert, __func__); /* Copy out the vertices and assign a temporary index */ int i = 0; GSetIterator gs_iter; GSET_ITER (gs_iter, node->bm_unique_verts) { BMVert *v = BLI_gsetIterator_getKey(&gs_iter); - copy_v3_v3(node->bm_orco[i], v->co); + const float *origco = BM_log_original_vert_co(log, v); + + if (use_original && origco) { + copy_v3_v3(node->bm_orco[i], origco); + } + else { + copy_v3_v3(node->bm_orco[i], v->co); + } + + node->bm_orvert[i] = v; BM_elem_index_set(v, i); /* set_dirty! */ i++; } GSET_ITER (gs_iter, node->bm_other_verts) { BMVert *v = BLI_gsetIterator_getKey(&gs_iter); - copy_v3_v3(node->bm_orco[i], v->co); + const float *origco = BM_log_original_vert_co(log, v); + + if (use_original && origco) { + copy_v3_v3(node->bm_orco[i], BM_log_original_vert_co(log, v)); + } + else { + copy_v3_v3(node->bm_orco[i], v->co); + } + + node->bm_orvert[i] = v; BM_elem_index_set(v, i); /* set_dirty! */ i++; } diff --git a/source/blender/blenkernel/intern/pbvh_intern.h b/source/blender/blenkernel/intern/pbvh_intern.h index bdfd3ad3d09..368a9ffa1ea 100644 --- a/source/blender/blenkernel/intern/pbvh_intern.h +++ b/source/blender/blenkernel/intern/pbvh_intern.h @@ -96,7 +96,7 @@ struct PBVHNode { /* Indicates whether this node is a leaf or not; also used for * marking various updates that need to be applied. */ - PBVHNodeFlags flag : 16; + PBVHNodeFlags flag : 32; /* Used for raycasting: how close bb is to the ray point. */ float tmin; @@ -116,8 +116,11 @@ struct PBVHNode { GSet *bm_faces; GSet *bm_unique_verts; GSet *bm_other_verts; + + /* Deprecated. Stores original coordinates of triangles. */ float (*bm_orco)[3]; int (*bm_ortri)[3]; + BMVert **bm_orvert; int bm_tot_ortri; /* Used to store the brush color during a stroke and composite it over the original color */ diff --git a/source/blender/blenkernel/intern/pointcache.c b/source/blender/blenkernel/intern/pointcache.c index 5ec69f9bc45..ac98bed2cf0 100644 --- a/source/blender/blenkernel/intern/pointcache.c +++ b/source/blender/blenkernel/intern/pointcache.c @@ -2640,7 +2640,7 @@ void BKE_ptcache_id_clear(PTCacheID *pid, int mode, uint cfra) if (STREQLEN(filepath, de->d_name, len)) { /* Do we have the right prefix. */ if (mode == PTCACHE_CLEAR_ALL) { pid->cache->last_exact = MIN2(pid->cache->startframe, 0); - BLI_join_dirfile(path_full, sizeof(path_full), path, de->d_name); + BLI_path_join(path_full, sizeof(path_full), path, de->d_name); BLI_delete(path_full, false, false); } else { @@ -2650,7 +2650,7 @@ void BKE_ptcache_id_clear(PTCacheID *pid, int mode, uint cfra) if (frame != -1) { if ((mode == PTCACHE_CLEAR_BEFORE && frame < cfra) || (mode == PTCACHE_CLEAR_AFTER && frame > cfra)) { - BLI_join_dirfile(path_full, sizeof(path_full), path, de->d_name); + BLI_path_join(path_full, sizeof(path_full), path, de->d_name); BLI_delete(path_full, false, false); if (pid->cache->cached_frames && frame >= sta && frame <= end) { pid->cache->cached_frames[frame - sta] = 0; @@ -3524,7 +3524,7 @@ void BKE_ptcache_disk_cache_rename(PTCacheID *pid, const char *name_src, const c const int frame = ptcache_frame_from_filename(de->d_name, ext); if (frame != -1) { - BLI_join_dirfile(old_path_full, sizeof(old_path_full), path, de->d_name); + BLI_path_join(old_path_full, sizeof(old_path_full), path, de->d_name); ptcache_filepath(pid, new_path_full, frame, true, true); BLI_rename(old_path_full, new_path_full); } diff --git a/source/blender/blenkernel/intern/pointcloud.cc b/source/blender/blenkernel/intern/pointcloud.cc index c73bbb91965..119daccce20 100644 --- a/source/blender/blenkernel/intern/pointcloud.cc +++ b/source/blender/blenkernel/intern/pointcloud.cc @@ -239,12 +239,6 @@ PointCloud *BKE_pointcloud_new_nomain(const int totpoint) nullptr, ID_PT, BKE_idtype_idcode_to_name(ID_PT), LIB_ID_CREATE_LOCALIZE)); pointcloud_init_data(&pointcloud->id); - CustomData_add_layer_named(&pointcloud->pdata, - CD_PROP_FLOAT, - CD_SET_DEFAULT, - nullptr, - pointcloud->totpoint, - POINTCLOUD_ATTR_RADIUS); CustomData_realloc(&pointcloud->pdata, 0, totpoint); pointcloud->totpoint = totpoint; diff --git a/source/blender/blenkernel/intern/preferences.c b/source/blender/blenkernel/intern/preferences.c index b2e795901fb..dd76f9eddc1 100644 --- a/source/blender/blenkernel/intern/preferences.c +++ b/source/blender/blenkernel/intern/preferences.c @@ -115,8 +115,7 @@ void BKE_preferences_asset_library_default_add(UserDef *userdef) userdef, DATA_(BKE_PREFS_ASSET_LIBRARY_DEFAULT_NAME), NULL); /* Add new "Default" library under '[doc_path]/Blender/Assets'. */ - BLI_path_join( - library->path, sizeof(library->path), documents_path, N_("Blender"), N_("Assets"), NULL); + BLI_path_join(library->path, sizeof(library->path), documents_path, N_("Blender"), N_("Assets")); } /** \} */ diff --git a/source/blender/blenkernel/intern/rigidbody.c b/source/blender/blenkernel/intern/rigidbody.c index 6b4cddb05f2..ffc6bc8d7a3 100644 --- a/source/blender/blenkernel/intern/rigidbody.c +++ b/source/blender/blenkernel/intern/rigidbody.c @@ -404,7 +404,7 @@ static rbCollisionShape *rigidbody_get_shape_trimesh_from_mesh(Object *ob) const MVert *mvert = BKE_mesh_verts(mesh); totvert = mesh->totvert; looptri = BKE_mesh_runtime_looptri_ensure(mesh); - tottri = mesh->runtime.looptris.len; + tottri = BKE_mesh_runtime_looptri_len(mesh); const MLoop *mloop = BKE_mesh_loops(mesh); /* sanity checking - potential case when no data will be present */ @@ -679,7 +679,7 @@ void BKE_rigidbody_calc_volume(Object *ob, float *r_vol) const MVert *mvert = BKE_mesh_verts(mesh); totvert = mesh->totvert; lt = BKE_mesh_runtime_looptri_ensure(mesh); - tottri = mesh->runtime.looptris.len; + tottri = BKE_mesh_runtime_looptri_len(mesh); const MLoop *mloop = BKE_mesh_loops(mesh); if (totvert > 0 && tottri > 0) { @@ -753,7 +753,7 @@ void BKE_rigidbody_calc_center_of_mass(Object *ob, float r_center[3]) const MVert *mvert = BKE_mesh_verts(mesh); totvert = mesh->totvert; looptri = BKE_mesh_runtime_looptri_ensure(mesh); - tottri = mesh->runtime.looptris.len; + tottri = BKE_mesh_runtime_looptri_len(mesh); const MLoop *mloop = BKE_mesh_loops(mesh); if (totvert > 0 && tottri > 0) { diff --git a/source/blender/blenkernel/intern/scene.cc b/source/blender/blenkernel/intern/scene.cc index d6183210186..bd26075f81f 100644 --- a/source/blender/blenkernel/intern/scene.cc +++ b/source/blender/blenkernel/intern/scene.cc @@ -244,7 +244,7 @@ static void scene_init_data(ID *id) /* Master Collection */ scene->master_collection = BKE_collection_master_add(scene); - BKE_view_layer_add(scene, "ViewLayer", nullptr, VIEWLAYER_ADD_NEW); + BKE_view_layer_add(scene, DATA_("ViewLayer"), nullptr, VIEWLAYER_ADD_NEW); } static void scene_copy_markers(Scene *scene_dst, const Scene *scene_src, const int flag) @@ -280,6 +280,9 @@ static void scene_copy_data(Main *bmain, ID *id_dst, const ID *id_src, const int } /* View Layers */ + LISTBASE_FOREACH (ViewLayer *, view_layer, &scene_src->view_layers) { + BKE_view_layer_synced_ensure(scene_src, view_layer); + } BLI_duplicatelist(&scene_dst->view_layers, &scene_src->view_layers); for (ViewLayer *view_layer_src = static_cast<ViewLayer *>(scene_src->view_layers.first), *view_layer_dst = static_cast<ViewLayer *>(scene_dst->view_layers.first); diff --git a/source/blender/blenkernel/intern/shrinkwrap.cc b/source/blender/blenkernel/intern/shrinkwrap.cc index 703b012d170..65226a5db9d 100644 --- a/source/blender/blenkernel/intern/shrinkwrap.cc +++ b/source/blender/blenkernel/intern/shrinkwrap.cc @@ -140,7 +140,7 @@ bool BKE_shrinkwrap_init_tree( } if (shrinkType == MOD_SHRINKWRAP_TARGET_PROJECT) { - data->boundary = mesh->runtime.shrinkwrap_data; + data->boundary = mesh->runtime->shrinkwrap_data; } return true; @@ -153,7 +153,7 @@ void BKE_shrinkwrap_free_tree(ShrinkwrapTreeData *data) void BKE_shrinkwrap_discard_boundary_data(Mesh *mesh) { - ShrinkwrapBoundaryData *data = mesh->runtime.shrinkwrap_data; + ShrinkwrapBoundaryData *data = mesh->runtime->shrinkwrap_data; if (data != nullptr) { MEM_freeN((void *)data->edge_is_boundary); @@ -164,7 +164,7 @@ void BKE_shrinkwrap_discard_boundary_data(Mesh *mesh) MEM_freeN(data); } - mesh->runtime.shrinkwrap_data = nullptr; + mesh->runtime->shrinkwrap_data = nullptr; } /* Accumulate edge for average boundary edge direction. */ @@ -327,7 +327,7 @@ void BKE_shrinkwrap_compute_boundary_data(Mesh *mesh) { BKE_shrinkwrap_discard_boundary_data(mesh); - mesh->runtime.shrinkwrap_data = shrinkwrap_build_boundary_data(mesh); + mesh->runtime->shrinkwrap_data = shrinkwrap_build_boundary_data(mesh); } /** diff --git a/source/blender/blenkernel/intern/subdiv_ccg.cc b/source/blender/blenkernel/intern/subdiv_ccg.cc index 6f583f760ef..bf09be444b1 100644 --- a/source/blender/blenkernel/intern/subdiv_ccg.cc +++ b/source/blender/blenkernel/intern/subdiv_ccg.cc @@ -128,7 +128,7 @@ static void subdiv_ccg_alloc_elements(SubdivCCG *subdiv_ccg, Subdiv *subdiv) subdiv_ccg->num_grids = num_grids; subdiv_ccg->grids = static_cast<CCGElem **>( MEM_calloc_arrayN(num_grids, sizeof(CCGElem *), "subdiv ccg grids")); - subdiv_ccg->grids_storage = static_cast<unsigned char *>( + subdiv_ccg->grids_storage = static_cast<uchar *>( MEM_calloc_arrayN(num_grids, size_t(grid_area) * element_size, "subdiv ccg grids storage")); const size_t grid_size_in_bytes = size_t(grid_area) * element_size; for (int grid_index = 0; grid_index < num_grids; grid_index++) { @@ -286,7 +286,7 @@ static void subdiv_ccg_eval_special_grid(CCGEvalGridsData *data, const int face_ static void subdiv_ccg_eval_grids_task(void *__restrict userdata_v, const int face_index, - const TaskParallelTLS *__restrict UNUSED(tls)) + const TaskParallelTLS *__restrict /*tls*/) { CCGEvalGridsData *data = static_cast<CCGEvalGridsData *>(userdata_v); SubdivCCG *subdiv_ccg = data->subdiv_ccg; @@ -615,7 +615,7 @@ Mesh *BKE_subdiv_to_ccg_mesh(Subdiv *subdiv, return nullptr; } Mesh *result = BKE_mesh_new_nomain_from_template(coarse_mesh, 0, 0, 0, 0, 0); - result->runtime.subdiv_ccg = subdiv_ccg; + result->runtime->subdiv_ccg = subdiv_ccg; return result; } @@ -779,7 +779,7 @@ static void subdiv_ccg_recalc_inner_normal_task(void *__restrict userdata_v, subdiv_ccg_average_inner_face_normals(data->subdiv_ccg, data->key, tls, grid_index); } -static void subdiv_ccg_recalc_inner_normal_free(const void *__restrict UNUSED(userdata), +static void subdiv_ccg_recalc_inner_normal_free(const void *__restrict /*userdata*/, void *__restrict tls_v) { RecalcInnerNormalsTLSData *tls = static_cast<RecalcInnerNormalsTLSData *>(tls_v); @@ -842,7 +842,7 @@ static void subdiv_ccg_recalc_modified_inner_normal_task(void *__restrict userda subdiv_ccg_average_inner_face_grids(subdiv_ccg, key, face); } -static void subdiv_ccg_recalc_modified_inner_normal_free(const void *__restrict UNUSED(userdata), +static void subdiv_ccg_recalc_modified_inner_normal_free(const void *__restrict /*userdata*/, void *__restrict tls_v) { RecalcInnerNormalsTLSData *tls = static_cast<RecalcInnerNormalsTLSData *>(tls_v); @@ -1016,7 +1016,7 @@ static void subdiv_ccg_average_inner_face_grids(SubdivCCG *subdiv_ccg, static void subdiv_ccg_average_inner_grids_task(void *__restrict userdata_v, const int face_index, - const TaskParallelTLS *__restrict UNUSED(tls_v)) + const TaskParallelTLS *__restrict /*tls_v*/) { AverageInnerGridsData *data = static_cast<AverageInnerGridsData *>(userdata_v); SubdivCCG *subdiv_ccg = data->subdiv_ccg; @@ -1095,7 +1095,7 @@ static void subdiv_ccg_average_grids_boundaries_task(void *__restrict userdata_v subdiv_ccg_average_grids_boundary(subdiv_ccg, key, adjacent_edge, tls); } -static void subdiv_ccg_average_grids_boundaries_free(const void *__restrict UNUSED(userdata), +static void subdiv_ccg_average_grids_boundaries_free(const void *__restrict /*userdata*/, void *__restrict tls_v) { AverageGridsBoundariesTLSData *tls = static_cast<AverageGridsBoundariesTLSData *>(tls_v); @@ -1137,7 +1137,7 @@ static void subdiv_ccg_average_grids_corners(SubdivCCG *subdiv_ccg, static void subdiv_ccg_average_grids_corners_task(void *__restrict userdata_v, const int n, - const TaskParallelTLS *__restrict UNUSED(tls_v)) + const TaskParallelTLS *__restrict /*tls_v*/) { AverageGridsCornerData *data = static_cast<AverageGridsCornerData *>(userdata_v); const int adjacent_vertex_index = data->adjacent_vert_index_map ? @@ -1323,10 +1323,9 @@ struct StitchFacesInnerGridsData { CCGFace **effected_ccg_faces; }; -static void subdiv_ccg_stitch_face_inner_grids_task( - void *__restrict userdata_v, - const int face_index, - const TaskParallelTLS *__restrict UNUSED(tls_v)) +static void subdiv_ccg_stitch_face_inner_grids_task(void *__restrict userdata_v, + const int face_index, + const TaskParallelTLS *__restrict /*tls_v*/) { StitchFacesInnerGridsData *data = static_cast<StitchFacesInnerGridsData *>(userdata_v); SubdivCCG *subdiv_ccg = data->subdiv_ccg; @@ -1447,7 +1446,7 @@ BLI_INLINE bool is_inner_edge_grid_coordinate(const SubdivCCG *subdiv_ccg, return false; } -BLI_INLINE SubdivCCGCoord coord_at_prev_row(const SubdivCCG *UNUSED(subdiv_ccg), +BLI_INLINE SubdivCCGCoord coord_at_prev_row(const SubdivCCG * /*subdiv_ccg*/, const SubdivCCGCoord *coord) { BLI_assert(coord->y > 0); @@ -1465,7 +1464,7 @@ BLI_INLINE SubdivCCGCoord coord_at_next_row(const SubdivCCG *subdiv_ccg, return result; } -BLI_INLINE SubdivCCGCoord coord_at_prev_col(const SubdivCCG *UNUSED(subdiv_ccg), +BLI_INLINE SubdivCCGCoord coord_at_prev_col(const SubdivCCG * /*subdiv_ccg*/, const SubdivCCGCoord *coord) { BLI_assert(coord->x > 0); diff --git a/source/blender/blenkernel/intern/subdiv_mesh.cc b/source/blender/blenkernel/intern/subdiv_mesh.cc index 00183ea90c2..3b97c1f5e68 100644 --- a/source/blender/blenkernel/intern/subdiv_mesh.cc +++ b/source/blender/blenkernel/intern/subdiv_mesh.cc @@ -528,9 +528,9 @@ static bool subdiv_mesh_topology_info(const SubdivForeachContext *foreach_contex subdiv_context->coarse_mesh, num_vertices, num_edges, 0, num_loops, num_polygons, mask); subdiv_mesh_ctx_cache_custom_data_layers(subdiv_context); subdiv_mesh_prepare_accumulator(subdiv_context, num_vertices); - MEM_SAFE_FREE(subdiv_context->subdiv_mesh->runtime.subsurf_face_dot_tags); - subdiv_context->subdiv_mesh->runtime.subsurf_face_dot_tags = BLI_BITMAP_NEW(num_vertices, - __func__); + MEM_SAFE_FREE(subdiv_context->subdiv_mesh->runtime->subsurf_face_dot_tags); + subdiv_context->subdiv_mesh->runtime->subsurf_face_dot_tags = BLI_BITMAP_NEW(num_vertices, + __func__); return true; } @@ -595,7 +595,7 @@ static void evaluate_vertex_and_apply_displacement_copy(const SubdivMeshContext /* Evaluate undeformed texture coordinate. */ subdiv_vertex_orco_evaluate(ctx, ptex_face_index, u, v, subdiv_vertex_index); /* Remove face-dot flag. This can happen if there is more than one subsurf modifier. */ - BLI_BITMAP_DISABLE(ctx->subdiv_mesh->runtime.subsurf_face_dot_tags, subdiv_vertex_index); + BLI_BITMAP_DISABLE(ctx->subdiv_mesh->runtime->subsurf_face_dot_tags, subdiv_vertex_index); } static void evaluate_vertex_and_apply_displacement_interpolate( @@ -753,7 +753,7 @@ static void subdiv_mesh_tag_center_vertex(const MPoly *coarse_poly, Mesh *subdiv_mesh) { if (subdiv_mesh_is_center_vertex(coarse_poly, u, v)) { - BLI_BITMAP_ENABLE(subdiv_mesh->runtime.subsurf_face_dot_tags, subdiv_vertex_index); + BLI_BITMAP_ENABLE(subdiv_mesh->runtime->subsurf_face_dot_tags, subdiv_vertex_index); } } diff --git a/source/blender/blenkernel/intern/subdiv_modifier.cc b/source/blender/blenkernel/intern/subdiv_modifier.cc index a5529e9b8fa..84b772db045 100644 --- a/source/blender/blenkernel/intern/subdiv_modifier.cc +++ b/source/blender/blenkernel/intern/subdiv_modifier.cc @@ -11,6 +11,7 @@ #include "DNA_scene_types.h" #include "DNA_userdef_types.h" +#include "BKE_mesh.h" #include "BKE_modifier.h" #include "BKE_subdiv.h" @@ -143,7 +144,7 @@ bool BKE_subsurf_modifier_can_do_gpu_subdiv(const Scene *scene, bool BKE_subsurf_modifier_has_gpu_subdiv(const Mesh *mesh) { - SubsurfRuntimeData *runtime_data = mesh->runtime.subsurf_runtime_data; + SubsurfRuntimeData *runtime_data = mesh->runtime->subsurf_runtime_data; return runtime_data && runtime_data->has_gpu_subdiv; } diff --git a/source/blender/blenkernel/intern/volume.cc b/source/blender/blenkernel/intern/volume.cc index 7c54b4d3f2f..e81657f9ef0 100644 --- a/source/blender/blenkernel/intern/volume.cc +++ b/source/blender/blenkernel/intern/volume.cc @@ -445,7 +445,7 @@ struct VolumeGrid { * may actually be loaded by another user while this is false. But only after * calling load() and is_loaded changes to true is it safe to access. * - * Const write access to this must be protected by `entry->mutex`. + * `const` write access to this must be protected by `entry->mutex`. */ mutable bool is_loaded; }; @@ -480,7 +480,7 @@ struct VolumeGridVector : public std::list<VolumeGrid> { metadata.reset(); } - /* Mutex for file loading of grids list. Const write access to the fields after this must be + /* Mutex for file loading of grids list. `const` write access to the fields after this must be * protected by locking with this mutex. */ mutable std::mutex mutex; /* Absolute file path that grids have been loaded from. */ diff --git a/source/blender/blenkernel/intern/writeavi.c b/source/blender/blenkernel/intern/writeavi.c index dbdf8cc395d..de2e196c163 100644 --- a/source/blender/blenkernel/intern/writeavi.c +++ b/source/blender/blenkernel/intern/writeavi.c @@ -122,7 +122,8 @@ bMovieHandle *BKE_movie_handle_get(const char imtype) R_IMF_IMTYPE_FFMPEG, R_IMF_IMTYPE_H264, R_IMF_IMTYPE_XVID, - R_IMF_IMTYPE_THEORA)) { + R_IMF_IMTYPE_THEORA, + R_IMF_IMTYPE_AV1)) { mh.start_movie = BKE_ffmpeg_start; mh.append_movie = BKE_ffmpeg_append; mh.end_movie = BKE_ffmpeg_end; diff --git a/source/blender/blenkernel/intern/writeffmpeg.c b/source/blender/blenkernel/intern/writeffmpeg.c index 99df07b6105..0d3a790ba00 100644 --- a/source/blender/blenkernel/intern/writeffmpeg.c +++ b/source/blender/blenkernel/intern/writeffmpeg.c @@ -299,6 +299,10 @@ static const char **get_file_extensions(int format) static const char *rv[] = {".webm", NULL}; return rv; } + case FFMPEG_AV1: { + static const char *rv[] = {".mp4", ".mkv", NULL}; + return rv; + } default: return NULL; } @@ -455,6 +459,204 @@ static AVRational calc_time_base(uint den, double num, int codec_id) return time_base; } +static const AVCodec *get_av1_encoder( + FFMpegContext *context, RenderData *rd, AVDictionary **opts, int rectx, int recty) +{ + /* There are three possible encoders for AV1: libaom-av1, librav1e, and libsvtav1. librav1e tends + * to give the best compression quality while libsvtav1 tends to be the fastest encoder. One of + * each will be picked based on the preset setting, and if a particular encoder is not available, + * then use the default returned by FFMpeg. */ + const AVCodec *codec = NULL; + switch (context->ffmpeg_preset) { + case FFM_PRESET_BEST: + /* Default to libaom-av1 for BEST preset due to it performing better than rav1e in terms of + * video quality (VMAF scores). Fallback to rav1e if libaom-av1 isn't available. */ + codec = avcodec_find_encoder_by_name("libaom-av1"); + if (!codec) { + codec = avcodec_find_encoder_by_name("librav1e"); + } + break; + case FFM_PRESET_REALTIME: + codec = avcodec_find_encoder_by_name("libsvtav1"); + break; + case FFM_PRESET_GOOD: + default: + codec = avcodec_find_encoder_by_name("libaom-av1"); + break; + } + + /* Use the default AV1 encoder if the specified encoder wasn't found. */ + if (!codec) { + codec = avcodec_find_encoder(AV_CODEC_ID_AV1); + } + + /* Apply AV1 encoder specific settings. */ + if (codec) { + if (strcmp(codec->name, "librav1e") == 0) { + /* Set "tiles" to 8 to enable multi-threaded encoding. */ + if (rd->threads > 8) { + ffmpeg_dict_set_int(opts, "tiles", rd->threads); + } + else { + ffmpeg_dict_set_int(opts, "tiles", 8); + } + + /* Use a reasonable speed setting based on preset. Speed ranges from 0-10. + * Must check context->ffmpeg_preset again in case this encoder was selected due to the + * absence of another. */ + switch (context->ffmpeg_preset) { + case FFM_PRESET_BEST: + ffmpeg_dict_set_int(opts, "speed", 4); + break; + case FFM_PRESET_REALTIME: + ffmpeg_dict_set_int(opts, "speed", 10); + break; + case FFM_PRESET_GOOD: + default: + ffmpeg_dict_set_int(opts, "speed", 6); + break; + } + if (context->ffmpeg_crf >= 0) { + /* librav1e does not use -crf, but uses -qp in the range of 0-255. Calculates the roughly + * equivalent float, and truncates it to an integer. */ + unsigned int qp_value = ((float)context->ffmpeg_crf) * 255.0F / 51.0F; + if (qp_value > 255) { + qp_value = 255; + } + ffmpeg_dict_set_int(opts, "qp", qp_value); + } + /* Set gop_size as rav1e's "--keyint". */ + char buffer[64]; + BLI_snprintf(buffer, sizeof(buffer), "keyint=%d", context->ffmpeg_gop_size); + av_dict_set(opts, "rav1e-params", buffer, 0); + } + else if (strcmp(codec->name, "libsvtav1") == 0) { + /* Set preset value based on ffmpeg_preset. + * Must check context->ffmpeg_preset again in case this encoder was selected due to the + * absence of another. */ + switch (context->ffmpeg_preset) { + case FFM_PRESET_REALTIME: + ffmpeg_dict_set_int(opts, "preset", 8); + break; + case FFM_PRESET_BEST: + ffmpeg_dict_set_int(opts, "preset", 3); + break; + case FFM_PRESET_GOOD: + default: + ffmpeg_dict_set_int(opts, "preset", 5); + break; + } + if (context->ffmpeg_crf >= 0) { + /* libsvtav1 does not support crf until FFmpeg builds since 2022-02-24, use qp as fallback. + */ + ffmpeg_dict_set_int(opts, "qp", context->ffmpeg_crf); + } + } + else if (strcmp(codec->name, "libaom-av1") == 0) { + /* Speed up libaom-av1 encoding by enabling multithreading and setting tiles. */ + ffmpeg_dict_set_int(opts, "row-mt", 1); + const char *tiles_string = NULL; + bool tiles_string_is_dynamic = false; + if (rd->threads > 0) { + /* See if threads is a square. */ + int threads_sqrt = sqrtf(rd->threads); + if (threads_sqrt < 4) { + /* Ensure a default minimum. */ + threads_sqrt = 4; + } + if (is_power_of_2_i(threads_sqrt) && threads_sqrt * threads_sqrt == rd->threads) { + /* Is a square num, therefore just do "sqrt x sqrt" for tiles parameter. */ + int digits = 0; + for (int t_sqrt_copy = threads_sqrt; t_sqrt_copy > 0; t_sqrt_copy /= 10) { + ++digits; + } + /* A char array need only an alignment of 1. */ + char *tiles_string_mut = (char *)calloc(digits * 2 + 2, 1); + BLI_snprintf(tiles_string_mut, digits * 2 + 2, "%dx%d", threads_sqrt, threads_sqrt); + tiles_string_is_dynamic = true; + tiles_string = tiles_string_mut; + } + else { + /* Is not a square num, set greater side based on longer side, or use a square if both + sides are equal. */ + int sqrt_p2 = power_of_2_min_i(threads_sqrt); + if (sqrt_p2 < 2) { + /* Ensure a default minimum. */ + sqrt_p2 = 2; + } + int sqrt_p2_next = power_of_2_min_i((int)rd->threads / sqrt_p2); + if (sqrt_p2_next < 1) { + sqrt_p2_next = 1; + } + if (sqrt_p2 > sqrt_p2_next) { + /* Ensure sqrt_p2_next is greater or equal to sqrt_p2. */ + int temp = sqrt_p2; + sqrt_p2 = sqrt_p2_next; + sqrt_p2_next = temp; + } + int combined_digits = 0; + for (int sqrt_p2_copy = sqrt_p2; sqrt_p2_copy > 0; sqrt_p2_copy /= 10) { + ++combined_digits; + } + for (int sqrt_p2_copy = sqrt_p2_next; sqrt_p2_copy > 0; sqrt_p2_copy /= 10) { + ++combined_digits; + } + /* A char array need only an alignment of 1. */ + char *tiles_string_mut = (char *)calloc(combined_digits + 2, 1); + if (rectx > recty) { + BLI_snprintf(tiles_string_mut, combined_digits + 2, "%dx%d", sqrt_p2_next, sqrt_p2); + } + else if (rectx < recty) { + BLI_snprintf(tiles_string_mut, combined_digits + 2, "%dx%d", sqrt_p2, sqrt_p2_next); + } + else { + BLI_snprintf(tiles_string_mut, combined_digits + 2, "%dx%d", sqrt_p2, sqrt_p2); + } + tiles_string_is_dynamic = true; + tiles_string = tiles_string_mut; + } + } + else { + /* Thread count unknown, default to 8. */ + if (rectx > recty) { + tiles_string = "4x2"; + } + else if (rectx < recty) { + tiles_string = "2x4"; + } + else { + tiles_string = "2x2"; + } + } + av_dict_set(opts, "tiles", tiles_string, 0); + if (tiles_string_is_dynamic) { + free((void *)tiles_string); + } + /* libaom-av1 uses "cpu-used" instead of "preset" for defining compression quality. + * This value is in a range from 0-8. 0 and 8 are extremes, but we will allow 8. + * Must check context->ffmpeg_preset again in case this encoder was selected due to the + * absence of another. */ + switch (context->ffmpeg_preset) { + case FFM_PRESET_REALTIME: + ffmpeg_dict_set_int(opts, "cpu-used", 8); + break; + case FFM_PRESET_BEST: + ffmpeg_dict_set_int(opts, "cpu-used", 4); + break; + case FFM_PRESET_GOOD: + default: + ffmpeg_dict_set_int(opts, "cpu-used", 6); + break; + } + + /* CRF related settings is similar to H264 for libaom-av1, so we will rely on those settings + * applied later. */ + } + } + + return codec; +} + /* prepare a video stream for the output file */ static AVStream *alloc_video_stream(FFMpegContext *context, @@ -480,7 +682,14 @@ static AVStream *alloc_video_stream(FFMpegContext *context, /* Set up the codec context */ - codec = avcodec_find_encoder(codec_id); + if (codec_id == AV_CODEC_ID_AV1) { + /* Use get_av1_encoder() to get the ideal (hopefully) encoder for AV1 based + * on given parameters, and also set up opts. */ + codec = get_av1_encoder(context, rd, &opts, rectx, recty); + } + else { + codec = avcodec_find_encoder(codec_id); + } if (!codec) { fprintf(stderr, "Couldn't find valid video codec\n"); context->video_codec = NULL; @@ -568,7 +777,9 @@ static AVStream *alloc_video_stream(FFMpegContext *context, default: printf("Unknown preset number %i, ignoring.\n", context->ffmpeg_preset); } - if (preset_name != NULL) { + /* "codec_id != AV_CODEC_ID_AV1" is required due to "preset" already being set by an AV1 codec. + */ + if (preset_name != NULL && codec_id != AV_CODEC_ID_AV1) { av_dict_set(&opts, "preset", preset_name, 0); } if (deadline_name != NULL) { @@ -951,6 +1162,9 @@ static int start_ffmpeg_impl(FFMpegContext *context, case FFMPEG_FLV: video_codec = AV_CODEC_ID_FLV1; break; + case FFMPEG_AV1: + video_codec = AV_CODEC_ID_AV1; + break; default: /* These containers are not restricted to any specific codec types. * Currently we expect these to be .avi, .mov, .mkv, and .mp4. @@ -1482,6 +1696,18 @@ void BKE_ffmpeg_preset_set(RenderData *rd, int preset) rd->ffcodecdata.mux_packet_size = 2048; rd->ffcodecdata.mux_rate = 10080000; break; + case FFMPEG_PRESET_AV1: + rd->ffcodecdata.type = FFMPEG_AV1; + rd->ffcodecdata.codec = AV_CODEC_ID_AV1; + rd->ffcodecdata.video_bitrate = 6000; + rd->ffcodecdata.gop_size = is_ntsc ? 18 : 15; + rd->ffcodecdata.rc_max_rate = 9000; + rd->ffcodecdata.rc_min_rate = 0; + rd->ffcodecdata.rc_buffer_size = 224 * 8; + rd->ffcodecdata.mux_packet_size = 2048; + rd->ffcodecdata.mux_rate = 10080000; + + break; } } @@ -1521,6 +1747,12 @@ void BKE_ffmpeg_image_type_verify(RenderData *rd, const ImageFormatData *imf) audio = 1; } } + else if (imf->imtype == R_IMF_IMTYPE_AV1) { + if (rd->ffcodecdata.codec != AV_CODEC_ID_AV1) { + BKE_ffmpeg_preset_set(rd, FFMPEG_PRESET_AV1); + audio = 1; + } + } if (audio && rd->ffcodecdata.audio_codec < 0) { rd->ffcodecdata.audio_codec = AV_CODEC_ID_NONE; diff --git a/source/blender/blenlib/BLI_array_utils.hh b/source/blender/blenlib/BLI_array_utils.hh index 95b3bde10f4..264ac00e034 100644 --- a/source/blender/blenlib/BLI_array_utils.hh +++ b/source/blender/blenlib/BLI_array_utils.hh @@ -42,6 +42,11 @@ void gather(const GVArray &src, IndexMask indices, GMutableSpan dst, int64_t gra /** * Fill the destination span by gathering indexed values from the `src` array. */ +void gather(GSpan src, IndexMask indices, GMutableSpan dst, int64_t grain_size = 4096); + +/** + * Fill the destination span by gathering indexed values from the `src` array. + */ template<typename T> inline void gather(const VArray<T> &src, const IndexMask indices, diff --git a/source/blender/blenlib/BLI_path_util.h b/source/blender/blenlib/BLI_path_util.h index 1e45e76afe1..d4d2ddead71 100644 --- a/source/blender/blenlib/BLI_path_util.h +++ b/source/blender/blenlib/BLI_path_util.h @@ -7,7 +7,9 @@ */ #include "BLI_compiler_attrs.h" +#include "BLI_compiler_compat.h" #include "BLI_utildefines.h" +#include "BLI_utildefines_variadic.h" #ifdef __cplusplus extern "C" { @@ -69,17 +71,15 @@ const char *BLI_path_extension(const char *filepath) ATTR_NONNULL(); */ void BLI_path_append(char *__restrict dst, size_t maxlen, const char *__restrict file) ATTR_NONNULL(); + /** - * Simple appending of filename to dir, does not check for valid path! - * Puts result into `dst`, which may be same area as `dir`. - * - * \note Consider using #BLI_path_join for more general path joining - * that de-duplicates separators and can handle an arbitrary number of paths. + * See #BLI_path_join doc-string. */ -void BLI_join_dirfile(char *__restrict dst, - size_t maxlen, - const char *__restrict dir, - const char *__restrict file) ATTR_NONNULL(); +size_t BLI_path_join_array(char *__restrict dst, + const size_t dst_len, + const char *path_array[], + const int path_array_num); + /** * Join multiple strings into a path, ensuring only a single path separator between each, * and trailing slash is kept. @@ -95,8 +95,92 @@ void BLI_join_dirfile(char *__restrict dst, * \note If you want a trailing slash, add `SEP_STR` as the last path argument, * duplicate slashes will be cleaned up. */ -size_t BLI_path_join(char *__restrict dst, size_t dst_len, const char *path, ...) - ATTR_NONNULL(1, 3) ATTR_SENTINEL(0); +#define BLI_path_join(...) VA_NARGS_CALL_OVERLOAD(_BLI_path_join_, __VA_ARGS__) + +#define _BLI_PATH_JOIN_ARGS_1 char *__restrict dst, size_t dst_len, const char *a +#define _BLI_PATH_JOIN_ARGS_2 _BLI_PATH_JOIN_ARGS_1, const char *b +#define _BLI_PATH_JOIN_ARGS_3 _BLI_PATH_JOIN_ARGS_2, const char *c +#define _BLI_PATH_JOIN_ARGS_4 _BLI_PATH_JOIN_ARGS_3, const char *d +#define _BLI_PATH_JOIN_ARGS_5 _BLI_PATH_JOIN_ARGS_4, const char *e +#define _BLI_PATH_JOIN_ARGS_6 _BLI_PATH_JOIN_ARGS_5, const char *f +#define _BLI_PATH_JOIN_ARGS_7 _BLI_PATH_JOIN_ARGS_6, const char *g +#define _BLI_PATH_JOIN_ARGS_8 _BLI_PATH_JOIN_ARGS_7, const char *h +#define _BLI_PATH_JOIN_ARGS_9 _BLI_PATH_JOIN_ARGS_8, const char *i +#define _BLI_PATH_JOIN_ARGS_10 _BLI_PATH_JOIN_ARGS_9, const char *j + +BLI_INLINE size_t _BLI_path_join_3(_BLI_PATH_JOIN_ARGS_1) ATTR_NONNULL(); +BLI_INLINE size_t _BLI_path_join_4(_BLI_PATH_JOIN_ARGS_2) ATTR_NONNULL(); +BLI_INLINE size_t _BLI_path_join_5(_BLI_PATH_JOIN_ARGS_3) ATTR_NONNULL(); +BLI_INLINE size_t _BLI_path_join_6(_BLI_PATH_JOIN_ARGS_4) ATTR_NONNULL(); +BLI_INLINE size_t _BLI_path_join_7(_BLI_PATH_JOIN_ARGS_5) ATTR_NONNULL(); +BLI_INLINE size_t _BLI_path_join_8(_BLI_PATH_JOIN_ARGS_6) ATTR_NONNULL(); +BLI_INLINE size_t _BLI_path_join_9(_BLI_PATH_JOIN_ARGS_7) ATTR_NONNULL(); +BLI_INLINE size_t _BLI_path_join_10(_BLI_PATH_JOIN_ARGS_8) ATTR_NONNULL(); +BLI_INLINE size_t _BLI_path_join_11(_BLI_PATH_JOIN_ARGS_9) ATTR_NONNULL(); +BLI_INLINE size_t _BLI_path_join_12(_BLI_PATH_JOIN_ARGS_10) ATTR_NONNULL(); + +BLI_INLINE size_t _BLI_path_join_3(_BLI_PATH_JOIN_ARGS_1) +{ + const char *path_array[] = {a}; + return BLI_path_join_array(dst, dst_len, path_array, ARRAY_SIZE(path_array)); +} +BLI_INLINE size_t _BLI_path_join_4(_BLI_PATH_JOIN_ARGS_2) +{ + const char *path_array[] = {a, b}; + return BLI_path_join_array(dst, dst_len, path_array, ARRAY_SIZE(path_array)); +} +BLI_INLINE size_t _BLI_path_join_5(_BLI_PATH_JOIN_ARGS_3) +{ + const char *path_array[] = {a, b, c}; + return BLI_path_join_array(dst, dst_len, path_array, ARRAY_SIZE(path_array)); +} +BLI_INLINE size_t _BLI_path_join_6(_BLI_PATH_JOIN_ARGS_4) +{ + const char *path_array[] = {a, b, c, d}; + return BLI_path_join_array(dst, dst_len, path_array, ARRAY_SIZE(path_array)); +} +BLI_INLINE size_t _BLI_path_join_7(_BLI_PATH_JOIN_ARGS_5) +{ + const char *path_array[] = {a, b, c, d, e}; + return BLI_path_join_array(dst, dst_len, path_array, ARRAY_SIZE(path_array)); +} +BLI_INLINE size_t _BLI_path_join_8(_BLI_PATH_JOIN_ARGS_6) +{ + const char *path_array[] = {a, b, c, d, e, f}; + return BLI_path_join_array(dst, dst_len, path_array, ARRAY_SIZE(path_array)); +} +BLI_INLINE size_t _BLI_path_join_9(_BLI_PATH_JOIN_ARGS_7) +{ + const char *path_array[] = {a, b, c, d, e, f, g}; + return BLI_path_join_array(dst, dst_len, path_array, ARRAY_SIZE(path_array)); +} +BLI_INLINE size_t _BLI_path_join_10(_BLI_PATH_JOIN_ARGS_8) +{ + const char *path_array[] = {a, b, c, d, e, f, g, h}; + return BLI_path_join_array(dst, dst_len, path_array, ARRAY_SIZE(path_array)); +} +BLI_INLINE size_t _BLI_path_join_11(_BLI_PATH_JOIN_ARGS_9) +{ + const char *path_array[] = {a, b, c, d, e, f, g, h, i}; + return BLI_path_join_array(dst, dst_len, path_array, ARRAY_SIZE(path_array)); +} +BLI_INLINE size_t _BLI_path_join_12(_BLI_PATH_JOIN_ARGS_10) +{ + const char *path_array[] = {a, b, c, d, e, f, g, h, i, j}; + return BLI_path_join_array(dst, dst_len, path_array, ARRAY_SIZE(path_array)); +} + +#undef _BLI_PATH_JOIN_ARGS_1 +#undef _BLI_PATH_JOIN_ARGS_2 +#undef _BLI_PATH_JOIN_ARGS_3 +#undef _BLI_PATH_JOIN_ARGS_4 +#undef _BLI_PATH_JOIN_ARGS_5 +#undef _BLI_PATH_JOIN_ARGS_6 +#undef _BLI_PATH_JOIN_ARGS_7 +#undef _BLI_PATH_JOIN_ARGS_8 +#undef _BLI_PATH_JOIN_ARGS_9 +#undef _BLI_PATH_JOIN_ARGS_10 + /** * Like Python's `os.path.basename()` * diff --git a/source/blender/blenlib/BLI_string.h b/source/blender/blenlib/BLI_string.h index 15926e8f2d2..17abcf52ecc 100644 --- a/source/blender/blenlib/BLI_string.h +++ b/source/blender/blenlib/BLI_string.h @@ -309,6 +309,28 @@ void BLI_str_format_byte_unit(char dst[15], long long int bytes, bool base_10) A */ void BLI_str_format_decimal_unit(char dst[7], int number_to_format) ATTR_NONNULL(); /** + * Format a count to up to 3 places (plus minus sign, plus '\0' terminator) string using long + * number names abbreviations. Used to produce a compact representation of large numbers as + * integers. + * + * It shows a lower bound instead of rounding the number. + * + * 1 -> 1 + * 15 -> 15 + * 155 -> 155 + * 1555 -> 1K + * 15555 -> 15K + * 155555 -> .1M + * 1555555 -> 1M + * 15555555 -> 15M + * 155555555 -> .1B + * 1000000000 -> 1B + * ... + * + * Length of 5 is the maximum of the resulting string, for example, `-15K\0`. + */ +void BLI_str_format_integer_unit(char dst[5], int number_to_format) ATTR_NONNULL(); +/** * Compare two strings without regard to case. * * \retval True if the strings are equal, false otherwise. diff --git a/source/blender/blenlib/intern/BLI_filelist.c b/source/blender/blenlib/intern/BLI_filelist.c index 1ce6beab933..4bcb023691a 100644 --- a/source/blender/blenlib/intern/BLI_filelist.c +++ b/source/blender/blenlib/intern/BLI_filelist.c @@ -174,7 +174,7 @@ static void bli_builddir(struct BuildDirCtx *dir_ctx, const char *dirname) struct direntry *file = &dir_ctx->files[dir_ctx->files_num]; while (dlink) { char fullname[PATH_MAX]; - BLI_join_dirfile(fullname, sizeof(fullname), dirname, dlink->name); + BLI_path_join(fullname, sizeof(fullname), dirname, dlink->name); memset(file, 0, sizeof(struct direntry)); file->relname = dlink->name; file->path = BLI_strdup(fullname); diff --git a/source/blender/blenlib/intern/array_utils.cc b/source/blender/blenlib/intern/array_utils.cc index a837d6aceec..2a231228dcb 100644 --- a/source/blender/blenlib/intern/array_utils.cc +++ b/source/blender/blenlib/intern/array_utils.cc @@ -28,4 +28,9 @@ void gather(const GVArray &src, }); } +void gather(const GSpan src, const IndexMask indices, GMutableSpan dst, const int64_t grain_size) +{ + gather(GVArray::ForSpan(src), indices, dst, grain_size); +} + } // namespace blender::array_utils diff --git a/source/blender/blenlib/intern/fileops.c b/source/blender/blenlib/intern/fileops.c index 3abd482d6b3..a157302e51e 100644 --- a/source/blender/blenlib/intern/fileops.c +++ b/source/blender/blenlib/intern/fileops.c @@ -627,7 +627,7 @@ static void join_dirfile_alloc(char **dst, size_t *alloc_len, const char *dir, c *alloc_len = len; - BLI_join_dirfile(*dst, len + 1, dir, file); + BLI_path_join(*dst, len + 1, dir, file); } static char *strip_last_slash(const char *dir) @@ -1184,7 +1184,7 @@ static const char *check_destination(const char *file, const char *to) len = strlen(to) + strlen(filename) + 1; path = MEM_callocN(len + 1, "check_destination path"); - BLI_join_dirfile(path, len + 1, to, filename); + BLI_path_join(path, len + 1, to, filename); MEM_freeN(str); diff --git a/source/blender/blenlib/intern/path_util.c b/source/blender/blenlib/intern/path_util.c index 6ff4d57aecb..afe8c3cc033 100644 --- a/source/blender/blenlib/intern/path_util.c +++ b/source/blender/blenlib/intern/path_util.c @@ -625,7 +625,7 @@ bool BLI_path_parent_dir(char *path) const char parent_dir[] = {'.', '.', SEP, '\0'}; /* "../" or "..\\" */ char tmp[FILE_MAX + 4]; - BLI_join_dirfile(tmp, sizeof(tmp), path, parent_dir); + BLI_path_join(tmp, sizeof(tmp), path, parent_dir); BLI_path_normalize(NULL, tmp); /* does all the work of normalizing the path for us */ if (!BLI_path_extension_check(tmp, parent_dir)) { @@ -1025,7 +1025,7 @@ bool BLI_path_abs_from_cwd(char *path, const size_t maxlen) if (BLI_current_working_dir(cwd, sizeof(cwd))) { char origpath[FILE_MAX]; BLI_strncpy(origpath, path, FILE_MAX); - BLI_join_dirfile(path, maxlen, cwd, origpath); + BLI_path_join(path, maxlen, cwd, origpath); } else { printf("Could not get the current working directory - $PWD for an unknown reason.\n"); @@ -1448,56 +1448,20 @@ void BLI_path_append(char *__restrict dst, const size_t maxlen, const char *__re BLI_strncpy(dst + dirlen, file, maxlen - dirlen); } -void BLI_join_dirfile(char *__restrict dst, - const size_t maxlen, - const char *__restrict dir, - const char *__restrict file) -{ -#ifdef DEBUG_STRSIZE - memset(dst, 0xff, sizeof(*dst) * maxlen); -#endif - size_t dirlen = BLI_strnlen(dir, maxlen); - - /* Arguments can't match. */ - BLI_assert(!ELEM(dst, dir, file)); - - /* Files starting with a separator cause a double-slash which could later be interpreted - * as a relative path where: `dir == "/"` and `file == "/file"` would result in "//file". */ - BLI_assert(file[0] != SEP); - - if (dirlen == maxlen) { - memcpy(dst, dir, dirlen); - dst[dirlen - 1] = '\0'; - return; /* dir fills the path */ - } - - memcpy(dst, dir, dirlen + 1); - - if (dirlen + 1 >= maxlen) { - return; /* fills the path */ - } - - /* inline BLI_path_slash_ensure */ - if ((dirlen > 0) && !ELEM(dst[dirlen - 1], SEP, ALTSEP)) { - dst[dirlen++] = SEP; - dst[dirlen] = '\0'; - } - - if (dirlen >= maxlen) { - return; /* fills the path */ - } - - BLI_strncpy(dst + dirlen, file, maxlen - dirlen); -} - -size_t BLI_path_join(char *__restrict dst, const size_t dst_len, const char *path, ...) +size_t BLI_path_join_array(char *__restrict dst, + const size_t dst_len, + const char *path_array[], + const int path_array_num) { + BLI_assert(path_array_num > 0); #ifdef DEBUG_STRSIZE memset(dst, 0xff, sizeof(*dst) * dst_len); #endif if (UNLIKELY(dst_len == 0)) { return 0; } + const char *path = path_array[0]; + const size_t dst_last = dst_len - 1; size_t ofs = BLI_strncpy_rlen(dst, path, dst_len); @@ -1519,9 +1483,8 @@ size_t BLI_path_join(char *__restrict dst, const size_t dst_len, const char *pat has_trailing_slash = (path[len] != '\0'); } - va_list args; - va_start(args, path); - while ((path = (const char *)va_arg(args, const char *))) { + for (int path_index = 1; path_index < path_array_num; path_index++) { + path = path_array[path_index]; has_trailing_slash = false; const char *path_init = path; while (ELEM(path[0], SEP, ALTSEP)) { @@ -1556,7 +1519,6 @@ size_t BLI_path_join(char *__restrict dst, const size_t dst_len, const char *pat has_trailing_slash = (path_init != path); } } - va_end(args); if (has_trailing_slash) { if ((ofs != dst_last) && (ofs != 0) && (ELEM(dst[ofs - 1], SEP, ALTSEP) == 0)) { diff --git a/source/blender/blenlib/intern/string.c b/source/blender/blenlib/intern/string.c index 89d31c5e93f..755d2dbd55d 100644 --- a/source/blender/blenlib/intern/string.c +++ b/source/blender/blenlib/intern/string.c @@ -1176,4 +1176,34 @@ void BLI_str_format_decimal_unit(char dst[7], int number_to_format) BLI_snprintf(dst, dst_len, "%.*f%s", decimals, number_to_format_converted, units[order]); } +void BLI_str_format_integer_unit(char dst[5], const int number_to_format) +{ + float number_to_format_converted = number_to_format; + int order = 0; + const float base = 1000; + const char *units[] = {"", "K", "M", "B"}; + const int units_num = ARRAY_SIZE(units); + + while ((fabsf(number_to_format_converted) >= base) && ((order + 1) < units_num)) { + number_to_format_converted /= base; + order++; + } + + const bool add_dot = (abs(number_to_format) > 99999) && fabsf(number_to_format_converted) > 99; + + if (add_dot) { + number_to_format_converted /= 100; + order++; + } + + const size_t dst_len = 5; + BLI_snprintf(dst, + dst_len, + "%s%s%d%s", + number_to_format < 0 ? "-" : "", + add_dot ? "." : "", + (int)floorf(fabsf(number_to_format_converted)), + units[order]); +} + /** \} */ diff --git a/source/blender/blenlib/tests/BLI_path_util_test.cc b/source/blender/blenlib/tests/BLI_path_util_test.cc index 2d415534693..89e537235db 100644 --- a/source/blender/blenlib/tests/BLI_path_util_test.cc +++ b/source/blender/blenlib/tests/BLI_path_util_test.cc @@ -207,7 +207,7 @@ TEST(path_util, NameAtIndex_NoneComplexNeg) char result[(out_size) + 1024]; \ /* check we don't write past the last byte */ \ result[out_size] = '\0'; \ - BLI_path_join(result, out_size, __VA_ARGS__, NULL); \ + BLI_path_join(result, out_size, __VA_ARGS__); \ /* simplify expected string */ \ BLI_str_replace_char(result, '\\', '/'); \ EXPECT_STREQ(result, expect); \ diff --git a/source/blender/blenlib/tests/BLI_string_test.cc b/source/blender/blenlib/tests/BLI_string_test.cc index 9bdf6075c70..d726fbccf20 100644 --- a/source/blender/blenlib/tests/BLI_string_test.cc +++ b/source/blender/blenlib/tests/BLI_string_test.cc @@ -515,6 +515,103 @@ TEST(string, StrFormatDecimalUnits) EXPECT_STREQ("-2.1B", size_str); } +/* BLI_str_format_integer_unit */ +TEST(string, StrFormatIntegerUnits) +{ + char size_str[7]; + int size; + + BLI_str_format_integer_unit(size_str, size = 0); + EXPECT_STREQ("0", size_str); + BLI_str_format_integer_unit(size_str, size = 1); + EXPECT_STREQ("1", size_str); + BLI_str_format_integer_unit(size_str, size = 10); + EXPECT_STREQ("10", size_str); + BLI_str_format_integer_unit(size_str, size = 15); + EXPECT_STREQ("15", size_str); + BLI_str_format_integer_unit(size_str, size = 100); + EXPECT_STREQ("100", size_str); + BLI_str_format_integer_unit(size_str, size = 155); + EXPECT_STREQ("155", size_str); + BLI_str_format_integer_unit(size_str, size = 1000); + EXPECT_STREQ("1K", size_str); + BLI_str_format_integer_unit(size_str, size = 1555); + EXPECT_STREQ("1K", size_str); + BLI_str_format_integer_unit(size_str, size = 10000); + EXPECT_STREQ("10K", size_str); + BLI_str_format_integer_unit(size_str, size = 15555); + EXPECT_STREQ("15K", size_str); + BLI_str_format_integer_unit(size_str, size = 100000); + EXPECT_STREQ(".1M", size_str); + BLI_str_format_integer_unit(size_str, size = 155555); + EXPECT_STREQ(".1M", size_str); + BLI_str_format_integer_unit(size_str, size = 1000000); + EXPECT_STREQ("1M", size_str); + BLI_str_format_integer_unit(size_str, size = 1555555); + EXPECT_STREQ("1M", size_str); + BLI_str_format_integer_unit(size_str, size = 2555555); + EXPECT_STREQ("2M", size_str); + BLI_str_format_integer_unit(size_str, size = 10000000); + EXPECT_STREQ("10M", size_str); + BLI_str_format_integer_unit(size_str, size = 15555555); + EXPECT_STREQ("15M", size_str); + BLI_str_format_integer_unit(size_str, size = 100000000); + EXPECT_STREQ(".1B", size_str); + BLI_str_format_integer_unit(size_str, size = 155555555); + EXPECT_STREQ(".1B", size_str); + BLI_str_format_integer_unit(size_str, size = 255555555); + EXPECT_STREQ(".2B", size_str); + BLI_str_format_integer_unit(size_str, size = 1000000000); + EXPECT_STREQ("1B", size_str); + + /* Largest possible value. */ + BLI_str_format_integer_unit(size_str, size = INT32_MAX); + EXPECT_STREQ("2B", size_str); + + BLI_str_format_integer_unit(size_str, size = -0); + EXPECT_STREQ("0", size_str); + BLI_str_format_integer_unit(size_str, size = -1); + EXPECT_STREQ("-1", size_str); + BLI_str_format_integer_unit(size_str, size = -10); + EXPECT_STREQ("-10", size_str); + BLI_str_format_integer_unit(size_str, size = -15); + EXPECT_STREQ("-15", size_str); + BLI_str_format_integer_unit(size_str, size = -100); + EXPECT_STREQ("-100", size_str); + BLI_str_format_integer_unit(size_str, size = -155); + EXPECT_STREQ("-155", size_str); + BLI_str_format_integer_unit(size_str, size = -1000); + EXPECT_STREQ("-1K", size_str); + BLI_str_format_integer_unit(size_str, size = -1555); + EXPECT_STREQ("-1K", size_str); + BLI_str_format_integer_unit(size_str, size = -10000); + EXPECT_STREQ("-10K", size_str); + BLI_str_format_integer_unit(size_str, size = -15555); + EXPECT_STREQ("-15K", size_str); + BLI_str_format_integer_unit(size_str, size = -100000); + EXPECT_STREQ("-.1M", size_str); + BLI_str_format_integer_unit(size_str, size = -155555); + EXPECT_STREQ("-.1M", size_str); + BLI_str_format_integer_unit(size_str, size = -1000000); + EXPECT_STREQ("-1M", size_str); + BLI_str_format_integer_unit(size_str, size = -1555555); + EXPECT_STREQ("-1M", size_str); + BLI_str_format_integer_unit(size_str, size = -10000000); + EXPECT_STREQ("-10M", size_str); + BLI_str_format_integer_unit(size_str, size = -15555555); + EXPECT_STREQ("-15M", size_str); + BLI_str_format_integer_unit(size_str, size = -100000000); + EXPECT_STREQ("-.1B", size_str); + BLI_str_format_integer_unit(size_str, size = -155555555); + EXPECT_STREQ("-.1B", size_str); + BLI_str_format_integer_unit(size_str, size = -1000000000); + EXPECT_STREQ("-1B", size_str); + + /* Smallest possible value. */ + BLI_str_format_integer_unit(size_str, size = -INT32_MAX); + EXPECT_STREQ("-2B", size_str); +} + struct WordInfo { WordInfo() = default; WordInfo(int start, int end) : start(start), end(end) diff --git a/source/blender/blenloader/BLO_readfile.h b/source/blender/blenloader/BLO_readfile.h index 93040fa01ee..75a1956ce12 100644 --- a/source/blender/blenloader/BLO_readfile.h +++ b/source/blender/blenloader/BLO_readfile.h @@ -182,6 +182,11 @@ void BLO_blendfiledata_free(BlendFileData *bfd); typedef struct BLODataBlockInfo { char name[64]; /* MAX_NAME */ struct AssetMetaData *asset_data; + /* Optimization: Tag data-blocks for which we know there is no preview. + * Knowing this can be used to skip the (potentially expensive) preview loading process. If this + * is set to true it means we looked for a preview and couldn't find one. False may mean that + * either no preview was found, or that it wasn't looked for in the first place. */ + bool no_preview_found; } BLODataBlockInfo; /** diff --git a/source/blender/blenloader/CMakeLists.txt b/source/blender/blenloader/CMakeLists.txt index f0209d1337c..86793d38b0b 100644 --- a/source/blender/blenloader/CMakeLists.txt +++ b/source/blender/blenloader/CMakeLists.txt @@ -47,7 +47,7 @@ set(SRC intern/versioning_400.cc intern/versioning_common.cc intern/versioning_cycles.c - intern/versioning_defaults.c + intern/versioning_defaults.cc intern/versioning_dna.c intern/versioning_legacy.c intern/versioning_userdef.c diff --git a/source/blender/blenloader/intern/blend_validate.cc b/source/blender/blenloader/intern/blend_validate.cc index 0f43b20c391..7ac0a4fe1af 100644 --- a/source/blender/blenloader/intern/blend_validate.cc +++ b/source/blender/blenloader/intern/blend_validate.cc @@ -61,7 +61,7 @@ bool BLO_main_validate_libraries(Main *bmain, ReportList *reports) for (Main *curmain = bmain->next; curmain != nullptr; curmain = curmain->next) { Library *curlib = curmain->curlib; if (curlib == nullptr) { - BKE_report(reports, RPT_ERROR, "Library database with nullptr library data-block!"); + BKE_report(reports, RPT_ERROR, "Library database with null library data-block pointer!"); continue; } @@ -103,7 +103,7 @@ bool BLO_main_validate_libraries(Main *bmain, ReportList *reports) is_valid = false; BKE_reportf(reports, RPT_ERROR, - "ID %s has nullptr lib pointer while being in library %s!", + "ID %s has null lib pointer while being in library %s!", id->name, curlib->filepath); continue; diff --git a/source/blender/blenloader/intern/readblenentry.cc b/source/blender/blenloader/intern/readblenentry.cc index 55ac2d31277..ead796c0e28 100644 --- a/source/blender/blenloader/intern/readblenentry.cc +++ b/source/blender/blenloader/intern/readblenentry.cc @@ -138,11 +138,15 @@ LinkNode *BLO_blendhandle_get_datablock_info(BlendHandle *bh, BHead *bhead; int tot = 0; + const int sdna_nr_preview_image = DNA_struct_find_nr(fd->filesdna, "PreviewImage"); + for (bhead = blo_bhead_first(fd); bhead; bhead = blo_bhead_next(fd, bhead)) { if (bhead->code == ENDB) { break; } if (bhead->code == ofblocktype) { + BHead *id_bhead = bhead; + const char *name = blo_bhead_id_name(fd, bhead) + 2; AssetMetaData *asset_meta_data = blo_bhead_id_asset_data_address(fd, bhead); @@ -165,6 +169,17 @@ LinkNode *BLO_blendhandle_get_datablock_info(BlendHandle *bh, STRNCPY(info->name, name); info->asset_data = asset_meta_data; + bool has_preview = false; + /* See if we can find a preview in the data of this ID. */ + for (BHead *data_bhead = blo_bhead_next(fd, id_bhead); data_bhead->code == DATA; + data_bhead = blo_bhead_next(fd, data_bhead)) { + if (data_bhead->SDNAnr == sdna_nr_preview_image) { + has_preview = true; + break; + } + } + info->no_preview_found = !has_preview; + BLI_linklist_prepend(&infos, info); tot++; } diff --git a/source/blender/blenloader/intern/versioning_250.c b/source/blender/blenloader/intern/versioning_250.c index 9e5ef41892a..0b543ad735b 100644 --- a/source/blender/blenloader/intern/versioning_250.c +++ b/source/blender/blenloader/intern/versioning_250.c @@ -630,7 +630,7 @@ static bool seq_sound_proxy_update_cb(Sequence *seq, void *user_data) Main *bmain = (Main *)user_data; if (seq->type == SEQ_TYPE_SOUND_HD) { char str[FILE_MAX]; - BLI_join_dirfile(str, sizeof(str), seq->strip->dir, seq->strip->stripdata->name); + BLI_path_join(str, sizeof(str), seq->strip->dir, seq->strip->stripdata->name); BLI_path_abs(str, BKE_main_blendfile_path(bmain)); seq->sound = BKE_sound_new_file(bmain, str); } @@ -2316,7 +2316,6 @@ static void lib_node_do_versions_group_indices(bNode *gnode) /* deprecated */ sock->own_index = link->fromsock->own_index; sock->to_index = 0; - sock->groupsock = NULL; } } } @@ -2329,7 +2328,6 @@ static void lib_node_do_versions_group_indices(bNode *gnode) /* deprecated */ sock->own_index = link->tosock->own_index; sock->to_index = 0; - sock->groupsock = NULL; } } } diff --git a/source/blender/blenloader/intern/versioning_280.c b/source/blender/blenloader/intern/versioning_280.c index 061840aee7a..1a8fec49516 100644 --- a/source/blender/blenloader/intern/versioning_280.c +++ b/source/blender/blenloader/intern/versioning_280.c @@ -3379,7 +3379,7 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain) SpaceImage *sima = (SpaceImage *)sl; sima->flag &= ~(SI_FLAG_UNUSED_0 | SI_FLAG_UNUSED_1 | SI_FLAG_UNUSED_3 | SI_FLAG_UNUSED_6 | SI_FLAG_UNUSED_7 | SI_FLAG_UNUSED_8 | - SI_FLAG_UNUSED_17 | SI_CUSTOM_GRID | SI_FLAG_UNUSED_23 | + SI_FLAG_UNUSED_17 | SI_FLAG_UNUSED_18 | SI_FLAG_UNUSED_23 | SI_FLAG_UNUSED_24); break; } diff --git a/source/blender/blenloader/intern/versioning_300.cc b/source/blender/blenloader/intern/versioning_300.cc index 0584dd6b059..a2bd7fd2fd1 100644 --- a/source/blender/blenloader/intern/versioning_300.cc +++ b/source/blender/blenloader/intern/versioning_300.cc @@ -3604,6 +3604,13 @@ void blo_do_versions_300(FileData *fd, Library * /*lib*/, Main *bmain) v3d->overlay.flag |= V3D_OVERLAY_VIEWER_ATTRIBUTE; v3d->overlay.viewer_attribute_opacity = 1.0f; } + if (sl->spacetype == SPACE_IMAGE) { + SpaceImage *sima = (SpaceImage *)sl; + if (sima->flag & SI_FLAG_UNUSED_18) { /* Was #SI_CUSTOM_GRID. */ + sima->grid_shape_source = SI_GRID_SHAPE_FIXED; + sima->flag &= ~SI_FLAG_UNUSED_18; + } + } } } } @@ -3616,6 +3623,36 @@ void blo_do_versions_300(FileData *fd, Library * /*lib*/, Main *bmain) } } + if (!MAIN_VERSION_ATLEAST(bmain, 304, 4)) { + /* Update brush sculpt settings. */ + LISTBASE_FOREACH (Brush *, brush, &bmain->brushes) { + brush->automasking_cavity_factor = 1.0f; + } + } + + if (!MAIN_VERSION_ATLEAST(bmain, 304, 5)) { + /* Fix for T101622 - update flags of sequence editor regions that were not initialized + * properly. */ + LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) { + LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { + LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) { + ListBase *regionbase = (sl == area->spacedata.first) ? &area->regionbase : + &sl->regionbase; + if (sl->spacetype == SPACE_SEQ) { + LISTBASE_FOREACH (ARegion *, region, regionbase) { + if (region->regiontype == RGN_TYPE_TOOLS) { + region->v2d.flag &= ~V2D_VIEWSYNC_AREA_VERTICAL; + } + if (region->regiontype == RGN_TYPE_CHANNELS) { + region->v2d.flag |= V2D_VIEWSYNC_AREA_VERTICAL; + } + } + } + } + } + } + } + /** * Versioning code until next subversion bump goes here. * diff --git a/source/blender/blenloader/intern/versioning_defaults.c b/source/blender/blenloader/intern/versioning_defaults.cc index 06903865381..da23e9cb49f 100644 --- a/source/blender/blenloader/intern/versioning_defaults.c +++ b/source/blender/blenloader/intern/versioning_defaults.cc @@ -15,6 +15,7 @@ #include "BLI_listbase.h" #include "BLI_math.h" +#include "BLI_math_vec_types.hh" #include "BLI_string.h" #include "BLI_system.h" #include "BLI_utildefines.h" @@ -36,6 +37,7 @@ #include "DNA_workspace_types.h" #include "BKE_appdir.h" +#include "BKE_attribute.hh" #include "BKE_brush.h" #include "BKE_colortools.h" #include "BKE_curveprofile.h" @@ -119,7 +121,7 @@ static void blo_update_defaults_screen(bScreen *screen, if (area->spacetype == SPACE_IMAGE) { if (STREQ(workspace_name, "UV Editing")) { - SpaceImage *sima = area->spacedata.first; + SpaceImage *sima = static_cast<SpaceImage *>(area->spacedata.first); if (sima->mode == SI_MODE_VIEW) { sima->mode = SI_MODE_UV; } @@ -127,7 +129,7 @@ static void blo_update_defaults_screen(bScreen *screen, } else if (area->spacetype == SPACE_ACTION) { /* Show markers region, hide channels and collapse summary in timelines. */ - SpaceAction *saction = area->spacedata.first; + SpaceAction *saction = static_cast<SpaceAction *>(area->spacedata.first); saction->flag |= SACTION_SHOW_MARKERS; if (saction->mode == SACTCONT_TIMELINE) { saction->ads.flag |= ADS_FLAG_SUMMARY_COLLAPSED; @@ -148,15 +150,15 @@ static void blo_update_defaults_screen(bScreen *screen, } } else if (area->spacetype == SPACE_GRAPH) { - SpaceGraph *sipo = area->spacedata.first; + SpaceGraph *sipo = static_cast<SpaceGraph *>(area->spacedata.first); sipo->flag |= SIPO_SHOW_MARKERS; } else if (area->spacetype == SPACE_NLA) { - SpaceNla *snla = area->spacedata.first; + SpaceNla *snla = static_cast<SpaceNla *>(area->spacedata.first); snla->flag |= SNLA_SHOW_MARKERS; } else if (area->spacetype == SPACE_SEQ) { - SpaceSeq *seq = area->spacedata.first; + SpaceSeq *seq = static_cast<SpaceSeq *>(area->spacedata.first); seq->flag |= SEQ_SHOW_MARKERS | SEQ_ZOOM_TO_FIT | SEQ_USE_PROXIES | SEQ_SHOW_OVERLAY; seq->render_size = SEQ_RENDER_SIZE_PROXY_100; seq->timeline_overlay.flag |= SEQ_TIMELINE_SHOW_STRIP_SOURCE | SEQ_TIMELINE_SHOW_STRIP_NAME | @@ -166,12 +168,12 @@ static void blo_update_defaults_screen(bScreen *screen, } else if (area->spacetype == SPACE_TEXT) { /* Show syntax and line numbers in Script workspace text editor. */ - SpaceText *stext = area->spacedata.first; + SpaceText *stext = static_cast<SpaceText *>(area->spacedata.first); stext->showsyntax = true; stext->showlinenrs = true; } else if (area->spacetype == SPACE_VIEW3D) { - View3D *v3d = area->spacedata.first; + View3D *v3d = static_cast<View3D *>(area->spacedata.first); /* Screen space cavity by default for faster performance. */ v3d->shading.cavity_type = V3D_SHADING_CAVITY_CURVATURE; v3d->shading.flag |= V3D_SHADING_SPECULAR_HIGHLIGHT; @@ -195,7 +197,7 @@ static void blo_update_defaults_screen(bScreen *screen, v3d->overlay.normals_constant_screen_size = 7.0f; } else if (area->spacetype == SPACE_CLIP) { - SpaceClip *sclip = area->spacedata.first; + SpaceClip *sclip = static_cast<SpaceClip *>(area->spacedata.first); sclip->around = V3D_AROUND_CENTER_MEDIAN; sclip->mask_info.blend_factor = 0.7f; sclip->mask_info.draw_flag = MASK_DRAWFLAG_SPLINE; @@ -206,7 +208,9 @@ static void blo_update_defaults_screen(bScreen *screen, const bool hide_image_tool_header = STREQ(workspace_name, "Rendering"); LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) { - ListBase *regionbase = (sl == area->spacedata.first) ? &area->regionbase : &sl->regionbase; + ListBase *regionbase = (sl == static_cast<SpaceLink *>(area->spacedata.first)) ? + &area->regionbase : + &sl->regionbase; LISTBASE_FOREACH (ARegion *, region, regionbase) { if (region->regiontype == RGN_TYPE_TOOL_HEADER) { @@ -226,12 +230,12 @@ static void blo_update_defaults_screen(bScreen *screen, if (app_template && STREQ(app_template, "2D_Animation")) { LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { if (area->spacetype == SPACE_ACTION) { - SpaceAction *saction = area->spacedata.first; + SpaceAction *saction = static_cast<SpaceAction *>(area->spacedata.first); /* Enable Sliders. */ saction->flag |= SACTION_SLIDERS; } else if (area->spacetype == SPACE_VIEW3D) { - View3D *v3d = area->spacedata.first; + View3D *v3d = static_cast<View3D *>(area->spacedata.first); /* Set Material Color by default. */ v3d->shading.color_type = V3D_SHADING_MATERIAL_COLOR; /* Enable Annotations. */ @@ -252,7 +256,7 @@ void BLO_update_defaults_workspace(WorkSpace *workspace, const char *app_templat if (blo_is_builtin_template(app_template)) { /* Clear all tools to use default options instead, ignore the tool saved in the file. */ while (!BLI_listbase_is_empty(&workspace->tools)) { - BKE_workspace_tool_remove(workspace, workspace->tools.first); + BKE_workspace_tool_remove(workspace, static_cast<bToolRef *>(workspace->tools.first)); } /* For 2D animation template. */ @@ -268,7 +272,7 @@ void BLO_update_defaults_workspace(WorkSpace *workspace, const char *app_templat LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { LISTBASE_FOREACH (ARegion *, region, &area->regionbase) { if (area->spacetype == SPACE_VIEW3D) { - View3D *v3d = area->spacedata.first; + View3D *v3d = static_cast<View3D *>(area->spacedata.first); v3d->shading.flag &= ~V3D_SHADING_CAVITY; copy_v3_fl(v3d->shading.single_color, 1.0f); STRNCPY(v3d->shading.matcap, "basic_1"); @@ -296,7 +300,8 @@ static void blo_update_defaults_scene(Main *bmain, Scene *scene) } /* Rename render layers. */ - BKE_view_layer_rename(bmain, scene, scene->view_layers.first, "ViewLayer"); + BKE_view_layer_rename( + bmain, scene, static_cast<ViewLayer *>(scene->view_layers.first), "ViewLayer"); /* Disable Z pass by default. */ LISTBASE_FOREACH (ViewLayer *, view_layer, &scene->view_layers) { @@ -308,7 +313,7 @@ static void blo_update_defaults_scene(Main *bmain, Scene *scene) scene->eevee.bloom_clamp = 0.0f; scene->eevee.motion_blur_shutter = 0.5f; - copy_v3_v3(scene->display.light_direction, (float[3]){M_SQRT1_3, M_SQRT1_3, M_SQRT1_3}); + copy_v3_v3(scene->display.light_direction, blender::float3(M_SQRT1_3)); copy_v2_fl2(scene->safe_areas.title, 0.1f, 0.05f); copy_v2_fl2(scene->safe_areas.action, 0.035f, 0.035f); @@ -344,9 +349,9 @@ static void blo_update_defaults_scene(Main *bmain, Scene *scene) } /* Correct default startup UV's. */ - Mesh *me = BLI_findstring(&bmain->meshes, "Cube", offsetof(ID, name) + 2); + Mesh *me = static_cast<Mesh *>(BLI_findstring(&bmain->meshes, "Cube", offsetof(ID, name) + 2)); if (me && (me->totloop == 24) && CustomData_has_layer(&me->ldata, CD_MLOOPUV)) { - MLoopUV *mloopuv = CustomData_get_layer(&me->ldata, CD_MLOOPUV); + MLoopUV *mloopuv = static_cast<MLoopUV *>(CustomData_get_layer(&me->ldata, CD_MLOOPUV)); const float uv_values[24][2] = { {0.625, 0.50}, {0.875, 0.50}, {0.875, 0.75}, {0.625, 0.75}, {0.375, 0.75}, {0.625, 0.75}, {0.625, 1.00}, {0.375, 1.00}, {0.375, 0.00}, {0.625, 0.00}, {0.625, 0.25}, {0.375, 0.25}, @@ -373,7 +378,7 @@ static void blo_update_defaults_scene(Main *bmain, Scene *scene) void BLO_update_defaults_startup_blend(Main *bmain, const char *app_template) { /* For all app templates. */ - for (WorkSpace *workspace = bmain->workspaces.first; workspace; workspace = workspace->id.next) { + LISTBASE_FOREACH (WorkSpace *, workspace, &bmain->workspaces) { BLO_update_defaults_workspace(workspace, app_template); } @@ -389,7 +394,8 @@ void BLO_update_defaults_startup_blend(Main *bmain, const char *app_template) do_versions_rename_id(bmain, ID_BR, "Draw Pen", "Pen"); /* Pen Soft brush. */ - brush = (Brush *)do_versions_rename_id(bmain, ID_BR, "Draw Soft", "Pencil Soft"); + brush = reinterpret_cast<Brush *>( + do_versions_rename_id(bmain, ID_BR, "Draw Soft", "Pencil Soft")); if (brush) { brush->gpencil_settings->icon_id = GP_BRUSH_ICON_PEN; } @@ -407,7 +413,8 @@ void BLO_update_defaults_startup_blend(Main *bmain, const char *app_template) do_versions_rename_id(bmain, ID_BR, "Draw Block", "Marker Chisel"); /* Remove useless Fill Area.001 brush. */ - brush = BLI_findstring(&bmain->brushes, "Fill Area.001", offsetof(ID, name) + 2); + brush = static_cast<Brush *>( + BLI_findstring(&bmain->brushes, "Fill Area.001", offsetof(ID, name) + 2)); if (brush) { BKE_id_delete(bmain, brush); } @@ -421,21 +428,24 @@ void BLO_update_defaults_startup_blend(Main *bmain, const char *app_template) do_versions_rename_id(bmain, ID_MA, "Black Dots", "Dots Stroke"); /* Dots Stroke. */ - ma = BLI_findstring(&bmain->materials, "Dots Stroke", offsetof(ID, name) + 2); + ma = static_cast<Material *>( + BLI_findstring(&bmain->materials, "Dots Stroke", offsetof(ID, name) + 2)); if (ma == NULL) { ma = BKE_gpencil_material_add(bmain, "Dots Stroke"); } ma->gp_style->mode = GP_MATERIAL_MODE_DOT; /* Squares Stroke. */ - ma = BLI_findstring(&bmain->materials, "Squares Stroke", offsetof(ID, name) + 2); + ma = static_cast<Material *>( + BLI_findstring(&bmain->materials, "Squares Stroke", offsetof(ID, name) + 2)); if (ma == NULL) { ma = BKE_gpencil_material_add(bmain, "Squares Stroke"); } ma->gp_style->mode = GP_MATERIAL_MODE_SQUARE; /* Change Solid Stroke settings. */ - ma = BLI_findstring(&bmain->materials, "Solid Stroke", offsetof(ID, name) + 2); + ma = static_cast<Material *>( + BLI_findstring(&bmain->materials, "Solid Stroke", offsetof(ID, name) + 2)); if (ma != NULL) { ma->gp_style->mix_rgba[3] = 1.0f; ma->gp_style->texture_offset[0] = -0.5f; @@ -443,7 +453,8 @@ void BLO_update_defaults_startup_blend(Main *bmain, const char *app_template) } /* Change Solid Fill settings. */ - ma = BLI_findstring(&bmain->materials, "Solid Fill", offsetof(ID, name) + 2); + ma = static_cast<Material *>( + BLI_findstring(&bmain->materials, "Solid Fill", offsetof(ID, name) + 2)); if (ma != NULL) { ma->gp_style->flag &= ~GP_MATERIAL_STROKE_SHOW; ma->gp_style->mix_rgba[3] = 1.0f; @@ -451,14 +462,15 @@ void BLO_update_defaults_startup_blend(Main *bmain, const char *app_template) ma->gp_style->mix_factor = 0.5f; } - Object *ob = BLI_findstring(&bmain->objects, "Stroke", offsetof(ID, name) + 2); + Object *ob = static_cast<Object *>( + BLI_findstring(&bmain->objects, "Stroke", offsetof(ID, name) + 2)); if (ob && ob->type == OB_GPENCIL) { ob->dtx |= OB_USE_GPENCIL_LIGHTS; } } /* Reset all grease pencil brushes. */ - Scene *scene = bmain->scenes.first; + Scene *scene = static_cast<Scene *>(bmain->scenes.first); BKE_brush_gpencil_paint_presets(bmain, scene->toolsettings, true); BKE_brush_gpencil_sculpt_presets(bmain, scene->toolsettings, true); BKE_brush_gpencil_vertex_presets(bmain, scene->toolsettings, true); @@ -511,7 +523,7 @@ void BLO_update_defaults_startup_blend(Main *bmain, const char *app_template) } /* Scenes */ - for (Scene *scene = bmain->scenes.first; scene; scene = scene->id.next) { + LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) { blo_update_defaults_scene(bmain, scene); if (app_template && STREQ(app_template, "Video_Editing")) { @@ -537,7 +549,7 @@ void BLO_update_defaults_startup_blend(Main *bmain, const char *app_template) do_versions_rename_id(bmain, ID_LA, "Lamp", "Light"); if (app_template && STREQ(app_template, "2D_Animation")) { - for (Object *object = bmain->objects.first; object; object = object->id.next) { + LISTBASE_FOREACH (Object *, object, &bmain->objects) { if (object->type == OB_GPENCIL) { /* Set grease pencil object in drawing mode */ bGPdata *gpd = (bGPdata *)object->data; @@ -548,7 +560,7 @@ void BLO_update_defaults_startup_blend(Main *bmain, const char *app_template) } } - for (Mesh *mesh = bmain->meshes.first; mesh; mesh = mesh->id.next) { + LISTBASE_FOREACH (Mesh *, mesh, &bmain->meshes) { /* Match default for new meshes. */ mesh->smoothresh = DEG2RADF(30); /* Match voxel remesher options for all existing meshes in templates. */ @@ -565,22 +577,23 @@ void BLO_update_defaults_startup_blend(Main *bmain, const char *app_template) CustomData_free_layers(&mesh->vdata, CD_PAINT_MASK, mesh->totvert); CustomData_free_layers(&mesh->ldata, CD_GRID_PAINT_MASK, mesh->totloop); } + mesh->attributes_for_write().remove(".sculpt_face_set"); } - for (Camera *camera = bmain->cameras.first; camera; camera = camera->id.next) { + LISTBASE_FOREACH (Camera *, camera, &bmain->cameras) { /* Initialize to a useful value. */ camera->dof.focus_distance = 10.0f; camera->dof.aperture_fstop = 2.8f; } - for (Light *light = bmain->lights.first; light; light = light->id.next) { + LISTBASE_FOREACH (Light *, light, &bmain->lights) { /* Fix lights defaults. */ light->clipsta = 0.05f; light->att_dist = 40.0f; } /* Materials */ - for (Material *ma = bmain->materials.first; ma; ma = ma->id.next) { + LISTBASE_FOREACH (Material *, ma, &bmain->materials) { /* Update default material to be a bit more rough. */ ma->roughness = 0.5f; @@ -588,7 +601,8 @@ void BLO_update_defaults_startup_blend(Main *bmain, const char *app_template) LISTBASE_FOREACH (bNode *, node, &ma->nodetree->nodes) { if (node->type == SH_NODE_BSDF_PRINCIPLED) { bNodeSocket *roughness_socket = nodeFindSocket(node, SOCK_IN, "Roughness"); - bNodeSocketValueFloat *roughness_data = roughness_socket->default_value; + bNodeSocketValueFloat *roughness_data = static_cast<bNodeSocketValueFloat *>( + roughness_socket->default_value); roughness_data->value = 0.5f; node->custom2 = SHD_SUBSURFACE_RANDOM_WALK; BKE_ntree_update_tag_node_property(ma->nodetree, node); @@ -606,13 +620,14 @@ void BLO_update_defaults_startup_blend(Main *bmain, const char *app_template) /* Enable for UV sculpt (other brush types will be created as needed), * without this the grab brush will be active but not selectable from the list. */ const char *brush_name = "Grab"; - Brush *brush = BLI_findstring(&bmain->brushes, brush_name, offsetof(ID, name) + 2); + Brush *brush = static_cast<Brush *>( + BLI_findstring(&bmain->brushes, brush_name, offsetof(ID, name) + 2)); if (brush) { brush->ob_mode |= OB_MODE_EDIT; } } - for (Brush *brush = bmain->brushes.first; brush; brush = brush->id.next) { + LISTBASE_FOREACH (Brush *, brush, &bmain->brushes) { brush->blur_kernel_radius = 2; /* Use full strength for all non-sculpt brushes, @@ -632,13 +647,15 @@ void BLO_update_defaults_startup_blend(Main *bmain, const char *app_template) Brush *brush; brush_name = "Smear"; - brush = BLI_findstring(&bmain->brushes, brush_name, offsetof(ID, name) + 2); + brush = static_cast<Brush *>( + BLI_findstring(&bmain->brushes, brush_name, offsetof(ID, name) + 2)); if (brush) { brush->spacing = 3.0; } brush_name = "Draw Sharp"; - brush = BLI_findstring(&bmain->brushes, brush_name, offsetof(ID, name) + 2); + brush = static_cast<Brush *>( + BLI_findstring(&bmain->brushes, brush_name, offsetof(ID, name) + 2)); if (!brush) { brush = BKE_brush_add(bmain, brush_name, OB_MODE_SCULPT); id_us_min(&brush->id); @@ -646,7 +663,8 @@ void BLO_update_defaults_startup_blend(Main *bmain, const char *app_template) } brush_name = "Elastic Deform"; - brush = BLI_findstring(&bmain->brushes, brush_name, offsetof(ID, name) + 2); + brush = static_cast<Brush *>( + BLI_findstring(&bmain->brushes, brush_name, offsetof(ID, name) + 2)); if (!brush) { brush = BKE_brush_add(bmain, brush_name, OB_MODE_SCULPT); id_us_min(&brush->id); @@ -654,7 +672,8 @@ void BLO_update_defaults_startup_blend(Main *bmain, const char *app_template) } brush_name = "Pose"; - brush = BLI_findstring(&bmain->brushes, brush_name, offsetof(ID, name) + 2); + brush = static_cast<Brush *>( + BLI_findstring(&bmain->brushes, brush_name, offsetof(ID, name) + 2)); if (!brush) { brush = BKE_brush_add(bmain, brush_name, OB_MODE_SCULPT); id_us_min(&brush->id); @@ -662,7 +681,8 @@ void BLO_update_defaults_startup_blend(Main *bmain, const char *app_template) } brush_name = "Multi-plane Scrape"; - brush = BLI_findstring(&bmain->brushes, brush_name, offsetof(ID, name) + 2); + brush = static_cast<Brush *>( + BLI_findstring(&bmain->brushes, brush_name, offsetof(ID, name) + 2)); if (!brush) { brush = BKE_brush_add(bmain, brush_name, OB_MODE_SCULPT); id_us_min(&brush->id); @@ -670,7 +690,8 @@ void BLO_update_defaults_startup_blend(Main *bmain, const char *app_template) } brush_name = "Clay Thumb"; - brush = BLI_findstring(&bmain->brushes, brush_name, offsetof(ID, name) + 2); + brush = static_cast<Brush *>( + BLI_findstring(&bmain->brushes, brush_name, offsetof(ID, name) + 2)); if (!brush) { brush = BKE_brush_add(bmain, brush_name, OB_MODE_SCULPT); id_us_min(&brush->id); @@ -678,7 +699,8 @@ void BLO_update_defaults_startup_blend(Main *bmain, const char *app_template) } brush_name = "Cloth"; - brush = BLI_findstring(&bmain->brushes, brush_name, offsetof(ID, name) + 2); + brush = static_cast<Brush *>( + BLI_findstring(&bmain->brushes, brush_name, offsetof(ID, name) + 2)); if (!brush) { brush = BKE_brush_add(bmain, brush_name, OB_MODE_SCULPT); id_us_min(&brush->id); @@ -686,7 +708,8 @@ void BLO_update_defaults_startup_blend(Main *bmain, const char *app_template) } brush_name = "Slide Relax"; - brush = BLI_findstring(&bmain->brushes, brush_name, offsetof(ID, name) + 2); + brush = static_cast<Brush *>( + BLI_findstring(&bmain->brushes, brush_name, offsetof(ID, name) + 2)); if (!brush) { brush = BKE_brush_add(bmain, brush_name, OB_MODE_SCULPT); id_us_min(&brush->id); @@ -694,7 +717,8 @@ void BLO_update_defaults_startup_blend(Main *bmain, const char *app_template) } brush_name = "Paint"; - brush = BLI_findstring(&bmain->brushes, brush_name, offsetof(ID, name) + 2); + brush = static_cast<Brush *>( + BLI_findstring(&bmain->brushes, brush_name, offsetof(ID, name) + 2)); if (!brush) { brush = BKE_brush_add(bmain, brush_name, OB_MODE_SCULPT); id_us_min(&brush->id); @@ -702,7 +726,8 @@ void BLO_update_defaults_startup_blend(Main *bmain, const char *app_template) } brush_name = "Smear"; - brush = BLI_findstring(&bmain->brushes, brush_name, offsetof(ID, name) + 2); + brush = static_cast<Brush *>( + BLI_findstring(&bmain->brushes, brush_name, offsetof(ID, name) + 2)); if (!brush) { brush = BKE_brush_add(bmain, brush_name, OB_MODE_SCULPT); id_us_min(&brush->id); @@ -710,7 +735,8 @@ void BLO_update_defaults_startup_blend(Main *bmain, const char *app_template) } brush_name = "Boundary"; - brush = BLI_findstring(&bmain->brushes, brush_name, offsetof(ID, name) + 2); + brush = static_cast<Brush *>( + BLI_findstring(&bmain->brushes, brush_name, offsetof(ID, name) + 2)); if (!brush) { brush = BKE_brush_add(bmain, brush_name, OB_MODE_SCULPT); id_us_min(&brush->id); @@ -718,7 +744,8 @@ void BLO_update_defaults_startup_blend(Main *bmain, const char *app_template) } brush_name = "Simplify"; - brush = BLI_findstring(&bmain->brushes, brush_name, offsetof(ID, name) + 2); + brush = static_cast<Brush *>( + BLI_findstring(&bmain->brushes, brush_name, offsetof(ID, name) + 2)); if (!brush) { brush = BKE_brush_add(bmain, brush_name, OB_MODE_SCULPT); id_us_min(&brush->id); @@ -726,7 +753,8 @@ void BLO_update_defaults_startup_blend(Main *bmain, const char *app_template) } brush_name = "Draw Face Sets"; - brush = BLI_findstring(&bmain->brushes, brush_name, offsetof(ID, name) + 2); + brush = static_cast<Brush *>( + BLI_findstring(&bmain->brushes, brush_name, offsetof(ID, name) + 2)); if (!brush) { brush = BKE_brush_add(bmain, brush_name, OB_MODE_SCULPT); id_us_min(&brush->id); @@ -734,7 +762,8 @@ void BLO_update_defaults_startup_blend(Main *bmain, const char *app_template) } brush_name = "Multires Displacement Eraser"; - brush = BLI_findstring(&bmain->brushes, brush_name, offsetof(ID, name) + 2); + brush = static_cast<Brush *>( + BLI_findstring(&bmain->brushes, brush_name, offsetof(ID, name) + 2)); if (!brush) { brush = BKE_brush_add(bmain, brush_name, OB_MODE_SCULPT); id_us_min(&brush->id); @@ -742,7 +771,8 @@ void BLO_update_defaults_startup_blend(Main *bmain, const char *app_template) } brush_name = "Multires Displacement Smear"; - brush = BLI_findstring(&bmain->brushes, brush_name, offsetof(ID, name) + 2); + brush = static_cast<Brush *>( + BLI_findstring(&bmain->brushes, brush_name, offsetof(ID, name) + 2)); if (!brush) { brush = BKE_brush_add(bmain, brush_name, OB_MODE_SCULPT); id_us_min(&brush->id); @@ -750,7 +780,7 @@ void BLO_update_defaults_startup_blend(Main *bmain, const char *app_template) } /* Use the same tool icon color in the brush cursor */ - for (brush = bmain->brushes.first; brush; brush = brush->id.next) { + LISTBASE_FOREACH (Brush *, brush, &bmain->brushes) { if (brush->ob_mode & OB_MODE_SCULPT) { BLI_assert(brush->sculpt_tool != 0); BKE_brush_sculpt_reset(brush); diff --git a/source/blender/blenloader/tests/blendfile_loading_base_test.cc b/source/blender/blenloader/tests/blendfile_loading_base_test.cc index 615e30a728e..b657970d45f 100644 --- a/source/blender/blenloader/tests/blendfile_loading_base_test.cc +++ b/source/blender/blenloader/tests/blendfile_loading_base_test.cc @@ -117,7 +117,7 @@ bool BlendfileLoadingBaseTest::blendfile_load(const char *filepath) } char abspath[FILENAME_MAX]; - BLI_path_join(abspath, sizeof(abspath), test_assets_dir.c_str(), filepath, nullptr); + BLI_path_join(abspath, sizeof(abspath), test_assets_dir.c_str(), filepath); BlendFileReadReport bf_reports = {nullptr}; bfile = BLO_read_from_file(abspath, BLO_READ_SKIP_NONE, &bf_reports); diff --git a/source/blender/blentranslation/intern/blt_lang.c b/source/blender/blentranslation/intern/blt_lang.c index 0662adfb9ed..b8523f289c7 100644 --- a/source/blender/blentranslation/intern/blt_lang.c +++ b/source/blender/blentranslation/intern/blt_lang.c @@ -72,7 +72,7 @@ static void fill_locales(void) free_locales(); - BLI_join_dirfile(languages, FILE_MAX, languages_path, "languages"); + BLI_path_join(languages, FILE_MAX, languages_path, "languages"); line = lines = BLI_file_read_as_lines(languages); /* This whole "parsing" code is a bit weak, in that it expects strictly formatted input file... diff --git a/source/blender/bmesh/CMakeLists.txt b/source/blender/bmesh/CMakeLists.txt index 0efa5f73ae4..77223ecd1c7 100644 --- a/source/blender/bmesh/CMakeLists.txt +++ b/source/blender/bmesh/CMakeLists.txt @@ -207,6 +207,22 @@ if(WITH_GMP) ) endif() +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_bmesh "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") if(MSVC AND NOT MSVC_CLANG) diff --git a/source/blender/bmesh/intern/bmesh_construct.c b/source/blender/bmesh/intern/bmesh_construct.c index 3ee9fa7aee4..8125130490a 100644 --- a/source/blender/bmesh/intern/bmesh_construct.c +++ b/source/blender/bmesh/intern/bmesh_construct.c @@ -507,26 +507,32 @@ void BM_mesh_copy_init_customdata_from_mesh_array(BMesh *bm_dst, for (int i = 0; i < me_src_array_len; i++) { const Mesh *me_src = me_src_array[i]; + CustomData mesh_vdata = CustomData_shallow_copy_remove_non_bmesh_attributes( + &me_src->vdata, CD_MASK_BMESH.vmask); + CustomData mesh_edata = CustomData_shallow_copy_remove_non_bmesh_attributes( + &me_src->edata, CD_MASK_BMESH.emask); + CustomData mesh_pdata = CustomData_shallow_copy_remove_non_bmesh_attributes( + &me_src->pdata, CD_MASK_BMESH.lmask); + CustomData mesh_ldata = CustomData_shallow_copy_remove_non_bmesh_attributes( + &me_src->ldata, CD_MASK_BMESH.pmask); + if (i == 0) { - CustomData_copy_mesh_to_bmesh( - &me_src->vdata, &bm_dst->vdata, CD_MASK_BMESH.vmask, CD_SET_DEFAULT, 0); - CustomData_copy_mesh_to_bmesh( - &me_src->edata, &bm_dst->edata, CD_MASK_BMESH.emask, CD_SET_DEFAULT, 0); - CustomData_copy_mesh_to_bmesh( - &me_src->ldata, &bm_dst->ldata, CD_MASK_BMESH.lmask, CD_SET_DEFAULT, 0); - CustomData_copy_mesh_to_bmesh( - &me_src->pdata, &bm_dst->pdata, CD_MASK_BMESH.pmask, CD_SET_DEFAULT, 0); + CustomData_copy(&mesh_vdata, &bm_dst->vdata, CD_MASK_BMESH.vmask, CD_SET_DEFAULT, 0); + CustomData_copy(&mesh_edata, &bm_dst->edata, CD_MASK_BMESH.emask, CD_SET_DEFAULT, 0); + CustomData_copy(&mesh_pdata, &bm_dst->pdata, CD_MASK_BMESH.pmask, CD_SET_DEFAULT, 0); + CustomData_copy(&mesh_ldata, &bm_dst->ldata, CD_MASK_BMESH.lmask, CD_SET_DEFAULT, 0); } else { - CustomData_merge_mesh_to_bmesh( - &me_src->vdata, &bm_dst->vdata, CD_MASK_BMESH.vmask, CD_SET_DEFAULT, 0); - CustomData_merge_mesh_to_bmesh( - &me_src->edata, &bm_dst->edata, CD_MASK_BMESH.emask, CD_SET_DEFAULT, 0); - CustomData_merge_mesh_to_bmesh( - &me_src->ldata, &bm_dst->ldata, CD_MASK_BMESH.lmask, CD_SET_DEFAULT, 0); - CustomData_merge_mesh_to_bmesh( - &me_src->pdata, &bm_dst->pdata, CD_MASK_BMESH.pmask, CD_SET_DEFAULT, 0); + CustomData_merge(&mesh_vdata, &bm_dst->vdata, CD_MASK_BMESH.vmask, CD_SET_DEFAULT, 0); + CustomData_merge(&mesh_edata, &bm_dst->edata, CD_MASK_BMESH.emask, CD_SET_DEFAULT, 0); + CustomData_merge(&mesh_pdata, &bm_dst->pdata, CD_MASK_BMESH.pmask, CD_SET_DEFAULT, 0); + CustomData_merge(&mesh_ldata, &bm_dst->ldata, CD_MASK_BMESH.lmask, CD_SET_DEFAULT, 0); } + + MEM_SAFE_FREE(mesh_vdata.layers); + MEM_SAFE_FREE(mesh_edata.layers); + MEM_SAFE_FREE(mesh_pdata.layers); + MEM_SAFE_FREE(mesh_ldata.layers); } CustomData_bmesh_init_pool(&bm_dst->vdata, allocsize->totvert, BM_VERT); diff --git a/source/blender/bmesh/intern/bmesh_mesh_convert.cc b/source/blender/bmesh/intern/bmesh_mesh_convert.cc index dfbdd64ee7c..d65cac08db8 100644 --- a/source/blender/bmesh/intern/bmesh_mesh_convert.cc +++ b/source/blender/bmesh/intern/bmesh_mesh_convert.cc @@ -129,6 +129,10 @@ static BMFace *bm_face_create_from_mpoly(BMesh &bm, void BM_mesh_bm_from_me(BMesh *bm, const Mesh *me, const struct BMeshFromMeshParams *params) { + if (!me) { + /* Sanity check. */ + return; + } const bool is_new = !(bm->totvert || (bm->vdata.totlayer || bm->edata.totlayer || bm->pdata.totlayer || bm->ldata.totlayer)); KeyBlock *actkey; @@ -136,19 +140,35 @@ void BM_mesh_bm_from_me(BMesh *bm, const Mesh *me, const struct BMeshFromMeshPar CustomData_MeshMasks mask = CD_MASK_BMESH; CustomData_MeshMasks_update(&mask, ¶ms->cd_mask_extra); - if (!me || !me->totvert) { - if (me && is_new) { /* No verts? still copy custom-data layout. */ - CustomData_copy_mesh_to_bmesh(&me->vdata, &bm->vdata, mask.vmask, CD_CONSTRUCT, 0); - CustomData_copy_mesh_to_bmesh(&me->edata, &bm->edata, mask.emask, CD_CONSTRUCT, 0); - CustomData_copy_mesh_to_bmesh(&me->ldata, &bm->ldata, mask.lmask, CD_CONSTRUCT, 0); - CustomData_copy_mesh_to_bmesh(&me->pdata, &bm->pdata, mask.pmask, CD_CONSTRUCT, 0); + CustomData mesh_vdata = CustomData_shallow_copy_remove_non_bmesh_attributes(&me->vdata, + mask.vmask); + CustomData mesh_edata = CustomData_shallow_copy_remove_non_bmesh_attributes(&me->edata, + mask.emask); + CustomData mesh_pdata = CustomData_shallow_copy_remove_non_bmesh_attributes(&me->pdata, + mask.pmask); + CustomData mesh_ldata = CustomData_shallow_copy_remove_non_bmesh_attributes(&me->ldata, + mask.lmask); + BLI_SCOPED_DEFER([&]() { + MEM_SAFE_FREE(mesh_vdata.layers); + MEM_SAFE_FREE(mesh_edata.layers); + MEM_SAFE_FREE(mesh_pdata.layers); + MEM_SAFE_FREE(mesh_ldata.layers); + }); + + if (me->totvert == 0) { + if (is_new) { + /* No verts? still copy custom-data layout. */ + CustomData_copy(&mesh_vdata, &bm->vdata, mask.vmask, CD_CONSTRUCT, 0); + CustomData_copy(&mesh_edata, &bm->edata, mask.emask, CD_CONSTRUCT, 0); + CustomData_copy(&mesh_pdata, &bm->pdata, mask.pmask, CD_CONSTRUCT, 0); + CustomData_copy(&mesh_ldata, &bm->ldata, mask.lmask, CD_CONSTRUCT, 0); CustomData_bmesh_init_pool(&bm->vdata, me->totvert, BM_VERT); CustomData_bmesh_init_pool(&bm->edata, me->totedge, BM_EDGE); CustomData_bmesh_init_pool(&bm->ldata, me->totloop, BM_LOOP); CustomData_bmesh_init_pool(&bm->pdata, me->totpoly, BM_FACE); } - return; /* Sanity check. */ + return; } const float(*vert_normals)[3] = nullptr; @@ -157,16 +177,16 @@ void BM_mesh_bm_from_me(BMesh *bm, const Mesh *me, const struct BMeshFromMeshPar } if (is_new) { - CustomData_copy_mesh_to_bmesh(&me->vdata, &bm->vdata, mask.vmask, CD_SET_DEFAULT, 0); - CustomData_copy_mesh_to_bmesh(&me->edata, &bm->edata, mask.emask, CD_SET_DEFAULT, 0); - CustomData_copy_mesh_to_bmesh(&me->ldata, &bm->ldata, mask.lmask, CD_SET_DEFAULT, 0); - CustomData_copy_mesh_to_bmesh(&me->pdata, &bm->pdata, mask.pmask, CD_SET_DEFAULT, 0); + CustomData_copy(&mesh_vdata, &bm->vdata, mask.vmask, CD_SET_DEFAULT, 0); + CustomData_copy(&mesh_edata, &bm->edata, mask.emask, CD_SET_DEFAULT, 0); + CustomData_copy(&mesh_pdata, &bm->pdata, mask.pmask, CD_SET_DEFAULT, 0); + CustomData_copy(&mesh_ldata, &bm->ldata, mask.lmask, CD_SET_DEFAULT, 0); } else { - CustomData_bmesh_merge(&me->vdata, &bm->vdata, mask.vmask, CD_SET_DEFAULT, bm, BM_VERT); - CustomData_bmesh_merge(&me->edata, &bm->edata, mask.emask, CD_SET_DEFAULT, bm, BM_EDGE); - CustomData_bmesh_merge(&me->ldata, &bm->ldata, mask.lmask, CD_SET_DEFAULT, bm, BM_LOOP); - CustomData_bmesh_merge(&me->pdata, &bm->pdata, mask.pmask, CD_SET_DEFAULT, bm, BM_FACE); + CustomData_bmesh_merge(&mesh_vdata, &bm->vdata, mask.vmask, CD_SET_DEFAULT, bm, BM_VERT); + CustomData_bmesh_merge(&mesh_edata, &bm->edata, mask.emask, CD_SET_DEFAULT, bm, BM_EDGE); + CustomData_bmesh_merge(&mesh_pdata, &bm->pdata, mask.pmask, CD_SET_DEFAULT, bm, BM_FACE); + CustomData_bmesh_merge(&mesh_ldata, &bm->ldata, mask.lmask, CD_SET_DEFAULT, bm, BM_LOOP); } /* -------------------------------------------------------------------- */ @@ -302,7 +322,7 @@ void BM_mesh_bm_from_me(BMesh *bm, const Mesh *me, const struct BMeshFromMeshPar } /* Copy Custom Data */ - CustomData_to_bmesh_block(&me->vdata, &bm->vdata, i, &v->head.data, true); + CustomData_to_bmesh_block(&mesh_vdata, &bm->vdata, i, &v->head.data, true); /* Set shape key original index. */ if (cd_shape_keyindex_offset != -1) { @@ -338,7 +358,7 @@ void BM_mesh_bm_from_me(BMesh *bm, const Mesh *me, const struct BMeshFromMeshPar } /* Copy Custom Data */ - CustomData_to_bmesh_block(&me->edata, &bm->edata, i, &e->head.data, true); + CustomData_to_bmesh_block(&mesh_edata, &bm->edata, i, &e->head.data, true); } if (is_new) { bm->elem_index_dirty &= ~BM_EDGE; /* Added in order, clear dirty flag. */ @@ -397,11 +417,11 @@ void BM_mesh_bm_from_me(BMesh *bm, const Mesh *me, const struct BMeshFromMeshPar BM_elem_index_set(l_iter, totloops++); /* set_ok */ /* Save index of corresponding #MLoop. */ - CustomData_to_bmesh_block(&me->ldata, &bm->ldata, j++, &l_iter->head.data, true); + CustomData_to_bmesh_block(&mesh_ldata, &bm->ldata, j++, &l_iter->head.data, true); } while ((l_iter = l_iter->next) != l_first); /* Copy Custom Data */ - CustomData_to_bmesh_block(&me->pdata, &bm->pdata, i, &f->head.data, true); + CustomData_to_bmesh_block(&mesh_pdata, &bm->pdata, i, &f->head.data, true); if (params->calc_face_normal) { BM_face_normal_update(f); @@ -951,10 +971,10 @@ void BM_mesh_bm_to_me(Main *bmain, BMesh *bm, Mesh *me, const struct BMeshToMesh { CustomData_MeshMasks mask = CD_MASK_MESH; CustomData_MeshMasks_update(&mask, ¶ms->cd_mask_extra); - CustomData_copy_mesh_to_bmesh(&bm->vdata, &me->vdata, mask.vmask, CD_SET_DEFAULT, me->totvert); - CustomData_copy_mesh_to_bmesh(&bm->edata, &me->edata, mask.emask, CD_SET_DEFAULT, me->totedge); - CustomData_copy_mesh_to_bmesh(&bm->ldata, &me->ldata, mask.lmask, CD_SET_DEFAULT, me->totloop); - CustomData_copy_mesh_to_bmesh(&bm->pdata, &me->pdata, mask.pmask, CD_SET_DEFAULT, me->totpoly); + CustomData_copy(&bm->vdata, &me->vdata, mask.vmask, CD_SET_DEFAULT, me->totvert); + CustomData_copy(&bm->edata, &me->edata, mask.emask, CD_SET_DEFAULT, me->totedge); + CustomData_copy(&bm->ldata, &me->ldata, mask.lmask, CD_SET_DEFAULT, me->totloop); + CustomData_copy(&bm->pdata, &me->pdata, mask.pmask, CD_SET_DEFAULT, me->totpoly); } CustomData_add_layer(&me->vdata, CD_MVERT, CD_SET_DEFAULT, nullptr, me->totvert); @@ -1238,7 +1258,7 @@ void BM_mesh_bm_to_me_for_eval(BMesh *bm, Mesh *me, const CustomData_MeshMasks * * different than the BMesh's. */ BKE_mesh_clear_derived_normals(me); - me->runtime.deformed_only = true; + me->runtime->deformed_only = true; bke::MutableAttributeAccessor mesh_attributes = me->attributes_for_write(); diff --git a/source/blender/bmesh/operators/bmo_normals.c b/source/blender/bmesh/operators/bmo_normals.c index 04eaa43c899..0d321e1de8d 100644 --- a/source/blender/bmesh/operators/bmo_normals.c +++ b/source/blender/bmesh/operators/bmo_normals.c @@ -3,7 +3,7 @@ /** \file * \ingroup bmesh * - * normal recalculation. + * Functionality for flipping faces to make normals consistent. */ #include "MEM_guardedalloc.h" @@ -47,7 +47,7 @@ static bool bmo_recalc_normal_loop_filter_cb(const BMLoop *l, void *UNUSED(user_ * +------------+ * </pre> * - * In the example above, the a\ face can point towards the \a center + * In the example above, the \a face can point towards the \a center * which would end up flipping the normals inwards. * * To take these spikes into account, find the furthest face-loop-vertex. diff --git a/source/blender/compositor/intern/COM_Debug.cc b/source/blender/compositor/intern/COM_Debug.cc index d0f0be590f6..d184e5540ea 100644 --- a/source/blender/compositor/intern/COM_Debug.cc +++ b/source/blender/compositor/intern/COM_Debug.cc @@ -428,7 +428,7 @@ void DebugInfo::graphviz(const ExecutionSystem *system, StringRefNull name) else { BLI_strncpy(basename, (name + ".dot").c_str(), sizeof(basename)); } - BLI_join_dirfile(filepath, sizeof(filepath), BKE_tempdir_session(), basename); + BLI_path_join(filepath, sizeof(filepath), BKE_tempdir_session(), basename); file_index_++; std::cout << "Writing compositor debug to: " << filepath << "\n"; diff --git a/source/blender/compositor/nodes/COM_CryptomatteNode.cc b/source/blender/compositor/nodes/COM_CryptomatteNode.cc index 751ac0003bf..ee31fce0ea8 100644 --- a/source/blender/compositor/nodes/COM_CryptomatteNode.cc +++ b/source/blender/compositor/nodes/COM_CryptomatteNode.cc @@ -10,6 +10,7 @@ #include "COM_MultilayerImageOperation.h" #include "COM_RenderLayersProg.h" #include "COM_SetAlphaMultiplyOperation.h" +#include "COM_SetAlphaReplaceOperation.h" #include "COM_SetColorOperation.h" namespace blender::compositor { @@ -48,7 +49,7 @@ void CryptomatteBaseNode::convert_to_operations(NodeConverter &converter, converter.map_output_socket(output_image_socket, apply_mask_operation->get_output_socket(0)); NodeOutput *output_pick_socket = this->get_output_socket(2); - SetAlphaMultiplyOperation *extract_pick_operation = new SetAlphaMultiplyOperation(); + SetAlphaReplaceOperation *extract_pick_operation = new SetAlphaReplaceOperation(); converter.add_operation(extract_pick_operation); converter.add_input_value(extract_pick_operation->get_input_socket(1), 1.0f); converter.add_link(cryptomatte_operation->get_output_socket(0), diff --git a/source/blender/compositor/nodes/COM_OutputFileNode.cc b/source/blender/compositor/nodes/COM_OutputFileNode.cc index c83bcf42efd..fc4270cc222 100644 --- a/source/blender/compositor/nodes/COM_OutputFileNode.cc +++ b/source/blender/compositor/nodes/COM_OutputFileNode.cc @@ -104,7 +104,7 @@ void OutputFileNode::convert_to_operations(NodeConverter &converter, char path[FILE_MAX]; /* combine file path for the input */ - BLI_join_dirfile(path, FILE_MAX, storage->base_path, sockdata->path); + BLI_path_join(path, FILE_MAX, storage->base_path, sockdata->path); NodeOperation *output_operation = nullptr; diff --git a/source/blender/compositor/realtime_compositor/CMakeLists.txt b/source/blender/compositor/realtime_compositor/CMakeLists.txt index 1f1333332f5..bab0b5385ec 100644 --- a/source/blender/compositor/realtime_compositor/CMakeLists.txt +++ b/source/blender/compositor/realtime_compositor/CMakeLists.txt @@ -2,6 +2,7 @@ set(INC . + algorithms ../../blenkernel ../../blenlib ../../gpu @@ -53,6 +54,10 @@ set(SRC COM_static_shader_manager.hh COM_texture_pool.hh COM_utilities.hh + + algorithms/intern/algorithm_parallel_reduction.cc + + algorithms/COM_algorithm_parallel_reduction.hh ) set(LIB diff --git a/source/blender/compositor/realtime_compositor/COM_domain.hh b/source/blender/compositor/realtime_compositor/COM_domain.hh index 54d712f7578..99b40ae61cf 100644 --- a/source/blender/compositor/realtime_compositor/COM_domain.hh +++ b/source/blender/compositor/realtime_compositor/COM_domain.hh @@ -28,7 +28,7 @@ struct RealizationOptions { * result involves projecting it on a different domain, which in turn, involves sampling the * result at arbitrary locations, the interpolation identifies the method used for computing the * value at those arbitrary locations. */ - Interpolation interpolation = Interpolation::Nearest; + Interpolation interpolation = Interpolation::Bilinear; /* If true, the result will be repeated infinitely along the horizontal axis when realizing the * result. If false, regions outside of bounds of the result along the horizontal axis will be * filled with zeros. */ diff --git a/source/blender/compositor/realtime_compositor/algorithms/COM_algorithm_parallel_reduction.hh b/source/blender/compositor/realtime_compositor/algorithms/COM_algorithm_parallel_reduction.hh new file mode 100644 index 00000000000..f6d479f9bbe --- /dev/null +++ b/source/blender/compositor/realtime_compositor/algorithms/COM_algorithm_parallel_reduction.hh @@ -0,0 +1,103 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +#include "BLI_math_vec_types.hh" + +#include "GPU_texture.h" + +#include "COM_context.hh" + +namespace blender::realtime_compositor { + +/* -------------------------------------------------------------------- + * Sum Reductions. + */ + +/* Computes the sum of the red channel of all pixels in the given texture. */ +float sum_red(Context &context, GPUTexture *texture); + +/* Computes the sum of the green channel of all pixels in the given texture. */ +float sum_green(Context &context, GPUTexture *texture); + +/* Computes the sum of the blue channel of all pixels in the given texture. */ +float sum_blue(Context &context, GPUTexture *texture); + +/* Computes the sum of the luminance of all pixels in the given texture, using the given luminance + * coefficients to compute the luminance. */ +float sum_luminance(Context &context, GPUTexture *texture, float3 luminance_coefficients); + +/* Computes the sum of the logarithm of the luminance of all pixels in the given texture, using the + * given luminance coefficients to compute the luminance. */ +float sum_log_luminance(Context &context, GPUTexture *texture, float3 luminance_coefficients); + +/* Computes the sum of the colors of all pixels in the given texture. */ +float4 sum_color(Context &context, GPUTexture *texture); + +/* -------------------------------------------------------------------- + * Sum Of Squared Difference Reductions. + */ + +/* Computes the sum of the squared difference between the red channel of all pixels in the given + * texture and the given subtrahend. This can be used to compute the standard deviation if the + * given subtrahend is the mean. */ +float sum_red_squared_difference(Context &context, GPUTexture *texture, float subtrahend); + +/* Computes the sum of the squared difference between the green channel of all pixels in the given + * texture and the given subtrahend. This can be used to compute the standard deviation if the + * given subtrahend is the mean. */ +float sum_green_squared_difference(Context &context, GPUTexture *texture, float subtrahend); + +/* Computes the sum of the squared difference between the blue channel of all pixels in the given + * texture and the given subtrahend. This can be used to compute the standard deviation if the + * given subtrahend is the mean. */ +float sum_blue_squared_difference(Context &context, GPUTexture *texture, float subtrahend); + +/* Computes the sum of the squared difference between the luminance of all pixels in the given + * texture and the given subtrahend, using the given luminance coefficients to compute the + * luminance. This can be used to compute the standard deviation if the given subtrahend is the + * mean. */ +float sum_luminance_squared_difference(Context &context, + GPUTexture *texture, + float3 luminance_coefficients, + float subtrahend); + +/* -------------------------------------------------------------------- + * Maximum Reductions. + */ + +/* Computes the maximum luminance of all pixels in the given texture, using the given luminance + * coefficients to compute the luminance. */ +float maximum_luminance(Context &context, GPUTexture *texture, float3 luminance_coefficients); + +/* Computes the maximum float of all pixels in the given float texture, limited to the given range. + * Values outside of the given range are ignored. If non of the pixel values are in the range, the + * lower bound of the range is returned. For instance, if the given range is [-10, 10] and the + * image contains the values {2, 5, 11}, the maximum will be 5, since 11 is outside of the range. + * This is particularly useful for Z Depth normalization, since Z Depth can contain near infinite + * values, so enforcing an upper bound is beneficial. */ +float maximum_float_in_range(Context &context, + GPUTexture *texture, + float lower_bound, + float upper_bound); + +/* -------------------------------------------------------------------- + * Minimum Reductions. + */ + +/* Computes the minimum luminance of all pixels in the given texture, using the given luminance + * coefficients to compute the luminance. */ +float minimum_luminance(Context &context, GPUTexture *texture, float3 luminance_coefficients); + +/* Computes the minimum float of all pixels in the given float texture, limited to the given range. + * Values outside of the given range are ignored. If non of the pixel values are in the range, the + * upper bound of the range is returned. For instance, if the given range is [-10, 10] and the + * image contains the values {-11, 2, 5}, the minimum will be 2, since -11 is outside of the range. + * This is particularly useful for Z Depth normalization, since Z Depth can contain near infinite + * values, so enforcing a lower bound is beneficial. */ +float minimum_float_in_range(Context &context, + GPUTexture *texture, + float lower_bound, + float upper_bound); + +} // namespace blender::realtime_compositor diff --git a/source/blender/compositor/realtime_compositor/algorithms/intern/algorithm_parallel_reduction.cc b/source/blender/compositor/realtime_compositor/algorithms/intern/algorithm_parallel_reduction.cc new file mode 100644 index 00000000000..9672431992d --- /dev/null +++ b/source/blender/compositor/realtime_compositor/algorithms/intern/algorithm_parallel_reduction.cc @@ -0,0 +1,309 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "BLI_math_vec_types.hh" +#include "BLI_math_vector.hh" + +#include "MEM_guardedalloc.h" + +#include "GPU_compute.h" +#include "GPU_shader.h" +#include "GPU_texture.h" + +#include "COM_context.hh" +#include "COM_utilities.hh" + +#include "COM_algorithm_parallel_reduction.hh" + +namespace blender::realtime_compositor { + +/* Reduces the given texture into a single value and returns it. The return value should be freed + * by a call to MEM_freeN. The return value is either a pointer to a float, or a pointer to an + * array of floats that represents a vector. This depends on the given format, which should be + * compatible with the reduction shader. + * + * The given reduction shader should be bound when calling the function and the shader is expected + * to be derived from the compositor_parallel_reduction.glsl shader, see that file for more + * information. Also see the compositor_parallel_reduction_info.hh file for example shader + * definitions. */ +static float *parallel_reduction_dispatch(Context &context, + GPUTexture *texture, + GPUShader *shader, + eGPUTextureFormat format) +{ + GPU_shader_uniform_1b(shader, "is_initial_reduction", true); + + GPUTexture *texture_to_reduce = texture; + int2 size_to_reduce = int2(GPU_texture_width(texture), GPU_texture_height(texture)); + + /* Dispatch the reduction shader until the texture reduces to a single pixel. */ + while (size_to_reduce != int2(1)) { + const int2 reduced_size = math::divide_ceil(size_to_reduce, int2(16)); + GPUTexture *reduced_texture = context.texture_pool().acquire(reduced_size, format); + + GPU_memory_barrier(GPU_BARRIER_TEXTURE_FETCH); + const int texture_image_unit = GPU_shader_get_texture_binding(shader, "input_tx"); + GPU_texture_bind(texture_to_reduce, texture_image_unit); + + const int image_unit = GPU_shader_get_texture_binding(shader, "output_img"); + GPU_texture_image_bind(reduced_texture, image_unit); + + GPU_compute_dispatch(shader, reduced_size.x, reduced_size.y, 1); + + GPU_texture_image_unbind(reduced_texture); + GPU_texture_unbind(texture_to_reduce); + + /* Release the input texture only if it is not the source texture, since the source texture is + * not acquired or owned by the function. */ + if (texture_to_reduce != texture) { + context.texture_pool().release(texture_to_reduce); + } + + texture_to_reduce = reduced_texture; + size_to_reduce = reduced_size; + + GPU_shader_uniform_1b(shader, "is_initial_reduction", false); + } + + GPU_memory_barrier(GPU_BARRIER_TEXTURE_UPDATE); + float *pixel = static_cast<float *>(GPU_texture_read(texture_to_reduce, GPU_DATA_FLOAT, 0)); + + /* Release the final texture only if it is not the source texture, since the source texture is + * not acquired or owned by the function. */ + if (texture_to_reduce != texture) { + context.texture_pool().release(texture_to_reduce); + } + + return pixel; +} + +/* -------------------------------------------------------------------- + * Sum Reductions. + */ + +float sum_red(Context &context, GPUTexture *texture) +{ + GPUShader *shader = context.shader_manager().get("compositor_sum_red"); + GPU_shader_bind(shader); + + float *reduced_value = parallel_reduction_dispatch(context, texture, shader, GPU_R32F); + const float sum = *reduced_value; + MEM_freeN(reduced_value); + GPU_shader_unbind(); + + return sum; +} + +float sum_green(Context &context, GPUTexture *texture) +{ + GPUShader *shader = context.shader_manager().get("compositor_sum_green"); + GPU_shader_bind(shader); + + float *reduced_value = parallel_reduction_dispatch(context, texture, shader, GPU_R32F); + const float sum = *reduced_value; + MEM_freeN(reduced_value); + GPU_shader_unbind(); + + return sum; +} + +float sum_blue(Context &context, GPUTexture *texture) +{ + GPUShader *shader = context.shader_manager().get("compositor_sum_blue"); + GPU_shader_bind(shader); + + float *reduced_value = parallel_reduction_dispatch(context, texture, shader, GPU_R32F); + const float sum = *reduced_value; + MEM_freeN(reduced_value); + GPU_shader_unbind(); + + return sum; +} + +float sum_luminance(Context &context, GPUTexture *texture, float3 luminance_coefficients) +{ + GPUShader *shader = context.shader_manager().get("compositor_sum_luminance"); + GPU_shader_bind(shader); + + GPU_shader_uniform_3fv(shader, "luminance_coefficients", luminance_coefficients); + + float *reduced_value = parallel_reduction_dispatch(context, texture, shader, GPU_R32F); + const float sum = *reduced_value; + MEM_freeN(reduced_value); + GPU_shader_unbind(); + + return sum; +} + +float sum_log_luminance(Context &context, GPUTexture *texture, float3 luminance_coefficients) +{ + GPUShader *shader = context.shader_manager().get("compositor_sum_log_luminance"); + GPU_shader_bind(shader); + + GPU_shader_uniform_3fv(shader, "luminance_coefficients", luminance_coefficients); + + float *reduced_value = parallel_reduction_dispatch(context, texture, shader, GPU_R32F); + const float sum = *reduced_value; + MEM_freeN(reduced_value); + GPU_shader_unbind(); + + return sum; +} + +float4 sum_color(Context &context, GPUTexture *texture) +{ + GPUShader *shader = context.shader_manager().get("compositor_sum_color"); + GPU_shader_bind(shader); + + float *reduced_value = parallel_reduction_dispatch(context, texture, shader, GPU_RGBA32F); + const float4 sum = float4(reduced_value); + MEM_freeN(reduced_value); + GPU_shader_unbind(); + + return sum; +} + +/* -------------------------------------------------------------------- + * Sum Of Squared Difference Reductions. + */ + +float sum_red_squared_difference(Context &context, GPUTexture *texture, float subtrahend) +{ + GPUShader *shader = context.shader_manager().get("compositor_sum_red_squared_difference"); + GPU_shader_bind(shader); + + GPU_shader_uniform_1f(shader, "subtrahend", subtrahend); + + float *reduced_value = parallel_reduction_dispatch(context, texture, shader, GPU_R32F); + const float sum = *reduced_value; + MEM_freeN(reduced_value); + GPU_shader_unbind(); + + return sum; +} + +float sum_green_squared_difference(Context &context, GPUTexture *texture, float subtrahend) +{ + GPUShader *shader = context.shader_manager().get("compositor_sum_green_squared_difference"); + GPU_shader_bind(shader); + + GPU_shader_uniform_1f(shader, "subtrahend", subtrahend); + + float *reduced_value = parallel_reduction_dispatch(context, texture, shader, GPU_R32F); + const float sum = *reduced_value; + MEM_freeN(reduced_value); + GPU_shader_unbind(); + + return sum; +} + +float sum_blue_squared_difference(Context &context, GPUTexture *texture, float subtrahend) +{ + GPUShader *shader = context.shader_manager().get("compositor_sum_blue_squared_difference"); + GPU_shader_bind(shader); + + GPU_shader_uniform_1f(shader, "subtrahend", subtrahend); + + float *reduced_value = parallel_reduction_dispatch(context, texture, shader, GPU_R32F); + const float sum = *reduced_value; + MEM_freeN(reduced_value); + GPU_shader_unbind(); + + return sum; +} + +float sum_luminance_squared_difference(Context &context, + GPUTexture *texture, + float3 luminance_coefficients, + float subtrahend) +{ + GPUShader *shader = context.shader_manager().get("compositor_sum_luminance_squared_difference"); + GPU_shader_bind(shader); + + GPU_shader_uniform_3fv(shader, "luminance_coefficients", luminance_coefficients); + GPU_shader_uniform_1f(shader, "subtrahend", subtrahend); + + float *reduced_value = parallel_reduction_dispatch(context, texture, shader, GPU_R32F); + const float sum = *reduced_value; + MEM_freeN(reduced_value); + GPU_shader_unbind(); + + return sum; +} + +/* -------------------------------------------------------------------- + * Maximum Reductions. + */ + +float maximum_luminance(Context &context, GPUTexture *texture, float3 luminance_coefficients) +{ + GPUShader *shader = context.shader_manager().get("compositor_maximum_luminance"); + GPU_shader_bind(shader); + + GPU_shader_uniform_3fv(shader, "luminance_coefficients", luminance_coefficients); + + float *reduced_value = parallel_reduction_dispatch(context, texture, shader, GPU_R32F); + const float maximum = *reduced_value; + MEM_freeN(reduced_value); + GPU_shader_unbind(); + + return maximum; +} + +float maximum_float_in_range(Context &context, + GPUTexture *texture, + float lower_bound, + float upper_bound) +{ + GPUShader *shader = context.shader_manager().get("compositor_maximum_float_in_range"); + GPU_shader_bind(shader); + + GPU_shader_uniform_1f(shader, "lower_bound", lower_bound); + GPU_shader_uniform_1f(shader, "upper_bound", upper_bound); + + float *reduced_value = parallel_reduction_dispatch(context, texture, shader, GPU_R32F); + const float maximum = *reduced_value; + MEM_freeN(reduced_value); + GPU_shader_unbind(); + + return maximum; +} + +/* -------------------------------------------------------------------- + * Minimum Reductions. + */ + +float minimum_luminance(Context &context, GPUTexture *texture, float3 luminance_coefficients) +{ + GPUShader *shader = context.shader_manager().get("compositor_minimum_luminance"); + GPU_shader_bind(shader); + + GPU_shader_uniform_3fv(shader, "luminance_coefficients", luminance_coefficients); + + float *reduced_value = parallel_reduction_dispatch(context, texture, shader, GPU_R32F); + const float minimum = *reduced_value; + MEM_freeN(reduced_value); + GPU_shader_unbind(); + + return minimum; +} + +float minimum_float_in_range(Context &context, + GPUTexture *texture, + float lower_bound, + float upper_bound) +{ + GPUShader *shader = context.shader_manager().get("compositor_minimum_float_in_range"); + GPU_shader_bind(shader); + + GPU_shader_uniform_1f(shader, "lower_bound", lower_bound); + GPU_shader_uniform_1f(shader, "upper_bound", upper_bound); + + float *reduced_value = parallel_reduction_dispatch(context, texture, shader, GPU_R32F); + const float minimum = *reduced_value; + MEM_freeN(reduced_value); + GPU_shader_unbind(); + + return minimum; +} + +} // namespace blender::realtime_compositor diff --git a/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc b/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc index f95c0700a47..c84852788fd 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc @@ -453,6 +453,11 @@ static int foreach_id_cow_detect_need_for_update_callback(LibraryIDLinkCallbackD if (id == nullptr) { return IDWALK_RET_NOP; } + if (!ID_TYPE_IS_COW(GS(id->name))) { + /* No need to go further if the id never had a cow copy in the depsgraph. This function is + * only concerned with keeping the mapping between original and COW ids intact. */ + return IDWALK_RET_NOP; + } DepsgraphNodeBuilder *builder = static_cast<DepsgraphNodeBuilder *>(cb_data->user_data); ID *id_cow_self = cb_data->id_self; diff --git a/source/blender/draw/CMakeLists.txt b/source/blender/draw/CMakeLists.txt index d82ae4cd32c..1c17f92f073 100644 --- a/source/blender/draw/CMakeLists.txt +++ b/source/blender/draw/CMakeLists.txt @@ -71,13 +71,13 @@ set(SRC intern/draw_attributes.cc intern/draw_cache_impl_curve.cc intern/draw_cache_impl_curves.cc - intern/draw_cache_impl_gpencil.c + intern/draw_cache_impl_gpencil.cc intern/draw_cache_impl_lattice.c intern/draw_cache_impl_mesh.cc intern/draw_cache_impl_particles.c intern/draw_cache_impl_pointcloud.cc intern/draw_cache_impl_subdivision.cc - intern/draw_cache_impl_volume.c + intern/draw_cache_impl_volume.cc intern/draw_color_management.cc intern/draw_command.cc intern/draw_common.c @@ -731,6 +731,21 @@ if(WITH_GTESTS) endif() endif() +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_draw "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") @@ -756,3 +771,4 @@ if(WITH_GTESTS) blender_add_test_lib(bf_draw_tests "${TEST_SRC}" "${INC};${TEST_INC}" "${INC_SYS}" "${LIB};${TEST_LIB}") endif() endif() + diff --git a/source/blender/draw/engines/eevee/eevee_materials.c b/source/blender/draw/engines/eevee/eevee_materials.c index ff7dda1152c..068b18f1117 100644 --- a/source/blender/draw/engines/eevee/eevee_materials.c +++ b/source/blender/draw/engines/eevee/eevee_materials.c @@ -759,7 +759,8 @@ BLI_INLINE Material *eevee_object_material_get(Object *ob, int slot, bool holdou BLI_INLINE EeveeMaterialCache eevee_material_cache_get( EEVEE_Data *vedata, EEVEE_ViewLayerData *sldata, Object *ob, int slot, bool is_hair) { - const bool holdout = (ob->base_flag & BASE_HOLDOUT) != 0; + const bool holdout = ((ob->base_flag & BASE_HOLDOUT) != 0) || + ((ob->visibility_flag & OB_HOLDOUT) != 0); EeveeMaterialCache matcache; Material *ma = eevee_object_material_get(ob, slot, holdout); switch (ma->blend_method) { @@ -812,6 +813,10 @@ void EEVEE_materials_cache_populate(EEVEE_Data *vedata, bool use_sculpt_pbvh = BKE_sculptsession_use_pbvh_draw(ob, draw_ctx->v3d) && !DRW_state_is_image_render(); + if (ob->sculpt && ob->sculpt->pbvh) { + BKE_pbvh_is_drawing_set(ob->sculpt->pbvh, use_sculpt_pbvh); + } + /* First get materials for this mesh. */ if (ELEM(ob->type, OB_MESH, OB_SURF)) { const int materials_len = DRW_cache_object_material_count_get(ob); @@ -919,17 +924,14 @@ void EEVEE_particle_hair_cache_populate(EEVEE_Data *vedata, if (matcache.depth_grp) { *matcache.depth_grp_p = DRW_shgroup_hair_create_sub( ob, psys, md, matcache.depth_grp, NULL); - DRW_shgroup_add_material_resources(*matcache.depth_grp_p, matcache.shading_gpumat); } if (matcache.shading_grp) { *matcache.shading_grp_p = DRW_shgroup_hair_create_sub( ob, psys, md, matcache.shading_grp, matcache.shading_gpumat); - DRW_shgroup_add_material_resources(*matcache.shading_grp_p, matcache.shading_gpumat); } if (matcache.shadow_grp) { *matcache.shadow_grp_p = DRW_shgroup_hair_create_sub( ob, psys, md, matcache.shadow_grp, NULL); - DRW_shgroup_add_material_resources(*matcache.shadow_grp_p, matcache.shading_gpumat); *cast_shadow = true; } @@ -949,16 +951,13 @@ void EEVEE_object_curves_cache_populate(EEVEE_Data *vedata, if (matcache.depth_grp) { *matcache.depth_grp_p = DRW_shgroup_curves_create_sub(ob, matcache.depth_grp, NULL); - DRW_shgroup_add_material_resources(*matcache.depth_grp_p, matcache.shading_gpumat); } if (matcache.shading_grp) { *matcache.shading_grp_p = DRW_shgroup_curves_create_sub( ob, matcache.shading_grp, matcache.shading_gpumat); - DRW_shgroup_add_material_resources(*matcache.shading_grp_p, matcache.shading_gpumat); } if (matcache.shadow_grp) { *matcache.shadow_grp_p = DRW_shgroup_curves_create_sub(ob, matcache.shadow_grp, NULL); - DRW_shgroup_add_material_resources(*matcache.shadow_grp_p, matcache.shading_gpumat); *cast_shadow = true; } diff --git a/source/blender/draw/engines/eevee/shaders/cubemap_lib.glsl b/source/blender/draw/engines/eevee/shaders/cubemap_lib.glsl index 90272400915..5af317b7398 100644 --- a/source/blender/draw/engines/eevee/shaders/cubemap_lib.glsl +++ b/source/blender/draw/engines/eevee/shaders/cubemap_lib.glsl @@ -96,7 +96,7 @@ vec4 cubemap_seamless(sampler2DArray tex, vec4 cubevec, float lod) /* Mix all colors to get the corner color. */ vec4 col3 = (col + col1 + col2) / 3.0; - vec2 mix_fac = uv_border * 0.5; + vec2 mix_fac = saturate(uv_border * 0.5); return mix(mix(col, col2, mix_fac.x), mix(col1, col3, mix_fac.x), mix_fac.y); } else if (any(border)) { @@ -108,7 +108,7 @@ vec4 cubemap_seamless(sampler2DArray tex, vec4 cubevec, float lod) uv = cubemap_face_coord(cubevec.xyz, face); coord = vec3(uv, cubevec.w * 6.0 + face); - float mix_fac = max(uv_border.x, uv_border.y) * 0.5; + float mix_fac = saturate(max(uv_border.x, uv_border.y) * 0.5); return mix(col, textureLod(tex, coord, lod), mix_fac); } else { diff --git a/source/blender/draw/engines/eevee/shaders/effect_dof_scatter_vert.glsl b/source/blender/draw/engines/eevee/shaders/effect_dof_scatter_vert.glsl index 51a351babd3..5f04cdcebfa 100644 --- a/source/blender/draw/engines/eevee/shaders/effect_dof_scatter_vert.glsl +++ b/source/blender/draw/engines/eevee/shaders/effect_dof_scatter_vert.glsl @@ -95,7 +95,11 @@ void main() weights = dof_layer_weight(cocs) * dof_sample_weight(cocs); /* Filter NaNs. */ - weights = select(weights, vec4(0.0), equal(cocs, vec4(0.0))); + for (int i = 0; i < 4; i++) { + if (isnan(weights[i]) || isinf(weights[i])) { + weights[i] = 0.0; + } + } color1 = colors[0] * weights[0]; color2 = colors[1] * weights[1]; diff --git a/source/blender/draw/engines/eevee_next/eevee_shader_shared.hh b/source/blender/draw/engines/eevee_next/eevee_shader_shared.hh index fd06cdc7f23..f6a96aaaff2 100644 --- a/source/blender/draw/engines/eevee_next/eevee_shader_shared.hh +++ b/source/blender/draw/engines/eevee_next/eevee_shader_shared.hh @@ -560,7 +560,7 @@ struct LightCullingData { uint local_lights_len; /** Items that are **NOT** processed by the 2.5D culling (i.e: Sun Lights). */ uint sun_lights_len; - /** Number of items that passes the first culling test. */ + /** Number of items that passes the first culling test. (local lights only) */ uint visible_count; /** Extent of one square tile in pixels. */ float tile_size; diff --git a/source/blender/draw/engines/eevee_next/eevee_sync.cc b/source/blender/draw/engines/eevee_next/eevee_sync.cc index 08cda6f47cf..cbd735ec29c 100644 --- a/source/blender/draw/engines/eevee_next/eevee_sync.cc +++ b/source/blender/draw/engines/eevee_next/eevee_sync.cc @@ -248,17 +248,17 @@ static void gpencil_stroke_sync(bGPDlayer * /*gpl*/, return; } + GPUBatch *geom = DRW_cache_gpencil_get(iter.ob, iter.cfra); + if (show_fill) { - GPUBatch *geom = DRW_cache_gpencil_fills_get(iter.ob, iter.cfra); int vfirst = gps->runtime.fill_start * 3; int vcount = gps->tot_triangles * 3; gpencil_drawcall_add(iter, geom, material, vfirst, vcount, false); } if (show_stroke) { - GPUBatch *geom = DRW_cache_gpencil_strokes_get(iter.ob, iter.cfra); /* Start one vert before to have gl_InstanceID > 0 (see shader). */ - int vfirst = gps->runtime.stroke_start - 1; + int vfirst = gps->runtime.stroke_start * 3; /* Include "potential" cyclic vertex and start adj vertex (see shader). */ int vcount = gps->totpoints + 1 + 1; gpencil_drawcall_add(iter, geom, material, vfirst, vcount, true); diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_geom_gpencil_vert.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_geom_gpencil_vert.glsl index 38debf14eda..87a5bf71c45 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_geom_gpencil_vert.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_geom_gpencil_vert.glsl @@ -16,30 +16,18 @@ void main() float hardness; vec2 thickness; - gl_Position = gpencil_vertex(ma, - ma1, - ma2, - ma3, - pos, - pos1, - pos2, - pos3, - uv1, - uv2, - col1, - col2, - fcol1, - /* TODO */ - vec4(1024.0, 1024.0, 1.0 / 1024.0, 1.0 / 1024.0), - interp.P, - interp.N, - g_color, - strength, - g_uvs, - sspos, - aspect, - thickness, - hardness); + gl_Position = gpencil_vertex( + /* TODO */ + vec4(1024.0, 1024.0, 1.0 / 1024.0, 1.0 / 1024.0), + interp.P, + interp.N, + g_color, + strength, + g_uvs, + sspos, + aspect, + thickness, + hardness); #ifdef MAT_VELOCITY /* GPencil do not support deformation motion blur. */ vec3 lP_curr = transform_point(ModelMatrixInverse, interp.P); diff --git a/source/blender/draw/engines/eevee_next/shaders/infos/eevee_material_info.hh b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_material_info.hh index b632564a9ca..7883bf01aeb 100644 --- a/source/blender/draw/engines/eevee_next/shaders/infos/eevee_material_info.hh +++ b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_material_info.hh @@ -114,11 +114,11 @@ GPU_SHADER_CREATE_INFO(eevee_surf_deferred) // .image_out(5, Qualifier::WRITE, GPU_R11F_G11F_B10F, "gbuff_emission") /* Render-passes. */ // .image_out(6, Qualifier::READ_WRITE, GPU_RGBA16F, "rpass_volume_light") - /* TODO: AOVs maybe? */ .fragment_source("eevee_surf_deferred_frag.glsl") - // .additional_info("eevee_aov_out", "eevee_sampling_data", "eevee_camera", - // "eevee_utility_texture") - ; + .additional_info("eevee_camera", + "eevee_utility_texture", + "eevee_sampling_data", + "eevee_aov_out"); GPU_SHADER_CREATE_INFO(eevee_surf_forward) .vertex_out(eevee_surf_iface) diff --git a/source/blender/draw/engines/gpencil/gpencil_engine.c b/source/blender/draw/engines/gpencil/gpencil_engine.c index b24e4c605e4..21e536ffbd4 100644 --- a/source/blender/draw/engines/gpencil/gpencil_engine.c +++ b/source/blender/draw/engines/gpencil/gpencil_engine.c @@ -342,7 +342,6 @@ typedef struct gpIterPopulateData { int stroke_index_offset; /* Infos for call batching. */ struct GPUBatch *geom; - bool instancing; int vfirst, vcount; } gpIterPopulateData; @@ -352,12 +351,7 @@ static void gpencil_drawcall_flush(gpIterPopulateData *iter) { #if !DISABLE_BATCHING if (iter->geom != NULL) { - if (iter->instancing) { - DRW_shgroup_call_instance_range(iter->grp, iter->ob, iter->geom, iter->vfirst, iter->vcount); - } - else { - DRW_shgroup_call_range(iter->grp, iter->ob, iter->geom, iter->vfirst, iter->vcount); - } + DRW_shgroup_call_range(iter->grp, iter->ob, iter->geom, iter->vfirst, iter->vcount); } #endif @@ -367,25 +361,22 @@ static void gpencil_drawcall_flush(gpIterPopulateData *iter) } /* Group draw-calls that are consecutive and with the same type. Reduces GPU driver overhead. */ -static void gpencil_drawcall_add( - gpIterPopulateData *iter, struct GPUBatch *geom, bool instancing, int v_first, int v_count) +static void gpencil_drawcall_add(gpIterPopulateData *iter, + struct GPUBatch *geom, + int v_first, + int v_count) { #if DISABLE_BATCHING - if (instancing) { - DRW_shgroup_call_instance_range(iter->grp, iter->ob, geom, v_first, v_count); - } - else { - DRW_shgroup_call_range(iter->grp, iter->ob, geom, v_first, v_count); - } + DRW_shgroup_call_range(iter->grp, iter->ob, geom, v_first, v_count); + return; #endif int last = iter->vfirst + iter->vcount; /* Interrupt draw-call grouping if the sequence is not consecutive. */ - if ((geom != iter->geom) || (v_first - last > 3)) { + if ((geom != iter->geom) || (v_first - last > 0)) { gpencil_drawcall_flush(iter); } iter->geom = geom; - iter->instancing = instancing; if (iter->vfirst == -1) { iter->vfirst = v_first; } @@ -516,25 +507,36 @@ static void gpencil_stroke_cache_populate(bGPDlayer *gpl, bool do_sbuffer = (iter->do_sbuffer_call == DRAW_NOW); + GPUBatch *geom = do_sbuffer ? DRW_cache_gpencil_sbuffer_get(iter->ob, show_fill) : + DRW_cache_gpencil_get(iter->ob, iter->pd->cfra); + if (geom != iter->geom) { + gpencil_drawcall_flush(iter); + + GPUVertBuf *position_tx = do_sbuffer ? + DRW_cache_gpencil_sbuffer_position_buffer_get(iter->ob, + show_fill) : + DRW_cache_gpencil_position_buffer_get(iter->ob, iter->pd->cfra); + GPUVertBuf *color_tx = do_sbuffer ? + DRW_cache_gpencil_sbuffer_color_buffer_get(iter->ob, show_fill) : + DRW_cache_gpencil_color_buffer_get(iter->ob, iter->pd->cfra); + DRW_shgroup_buffer_texture(iter->grp, "gp_pos_tx", position_tx); + DRW_shgroup_buffer_texture(iter->grp, "gp_col_tx", color_tx); + } + if (show_fill) { - GPUBatch *geom = do_sbuffer ? DRW_cache_gpencil_sbuffer_fill_get(iter->ob) : - DRW_cache_gpencil_fills_get(iter->ob, iter->pd->cfra); int vfirst = gps->runtime.fill_start * 3; int vcount = gps->tot_triangles * 3; - gpencil_drawcall_add(iter, geom, false, vfirst, vcount); + gpencil_drawcall_add(iter, geom, vfirst, vcount); } if (show_stroke) { - GPUBatch *geom = do_sbuffer ? DRW_cache_gpencil_sbuffer_stroke_get(iter->ob) : - DRW_cache_gpencil_strokes_get(iter->ob, iter->pd->cfra); - /* Start one vert before to have gl_InstanceID > 0 (see shader). */ - int vfirst = gps->runtime.stroke_start - 1; - /* Include "potential" cyclic vertex and start adj vertex (see shader). */ - int vcount = gps->totpoints + 1 + 1; - gpencil_drawcall_add(iter, geom, true, vfirst, vcount); + int vfirst = gps->runtime.stroke_start * 3; + bool is_cyclic = ((gps->flag & GP_STROKE_CYCLIC) != 0) && (gps->totpoints > 2); + int vcount = (gps->totpoints + (int)is_cyclic) * 2 * 3; + gpencil_drawcall_add(iter, geom, vfirst, vcount); } - iter->stroke_index_last = gps->runtime.stroke_start + gps->totpoints + 1; + iter->stroke_index_last = gps->runtime.vertex_start + gps->totpoints + 1; } static void gpencil_sbuffer_cache_populate_fast(GPENCIL_Data *vedata, gpIterPopulateData *iter) diff --git a/source/blender/draw/engines/gpencil/gpencil_shader_shared.h b/source/blender/draw/engines/gpencil/gpencil_shader_shared.h index 4c621e955b9..3f0f73e7c13 100644 --- a/source/blender/draw/engines/gpencil/gpencil_shader_shared.h +++ b/source/blender/draw/engines/gpencil/gpencil_shader_shared.h @@ -41,6 +41,9 @@ enum gpLightType { GP_LIGHT_TYPE_AMBIENT = 3u, }; +#define GP_IS_STROKE_VERTEX_BIT (1 << 30) +#define GP_VERTEX_ID_SHIFT 2 + /* Avoid compiler funkiness with enum types not being strongly typed in C. */ #ifndef GPU_SHADER # define gpMaterialFlag uint diff --git a/source/blender/draw/engines/gpencil/shaders/gpencil_common_lib.glsl b/source/blender/draw/engines/gpencil/shaders/gpencil_common_lib.glsl index 7ddfdc5f65c..642939136c8 100644 --- a/source/blender/draw/engines/gpencil/shaders/gpencil_common_lib.glsl +++ b/source/blender/draw/engines/gpencil/shaders/gpencil_common_lib.glsl @@ -1,92 +1,4 @@ -/* Must match C declaration. */ -struct gpMaterial { - vec4 stroke_color; - vec4 fill_color; - vec4 fill_mix_color; - vec4 fill_uv_rot_scale; - vec4 fill_uv_offset; - /* Put float/int at the end to avoid padding error */ - /* Some drivers are completely messing the alignment or the fetches here. - * We are forced to pack these into vec4 otherwise we only get 0.0 as value. */ - vec4 gp_mat_packed_1; - // float stroke_texture_mix; - // float stroke_u_scale; - // float fill_texture_mix; - // int gp_flag; - /* Please ensure 16 byte alignment (multiple of vec4). */ -}; - -#define MATERIAL(m) materials[m + gpMaterialOffset] - -#define stroke_texture_mix gp_mat_packed_1.x -#define stroke_u_scale gp_mat_packed_1.y -#define fill_texture_mix gp_mat_packed_1.z -#define GP_FLAG(m) floatBitsToInt(MATERIAL(m).gp_mat_packed_1.w) - -/* flag */ -#define GP_STROKE_ALIGNMENT_STROKE 1 -#define GP_STROKE_ALIGNMENT_OBJECT 2 -#define GP_STROKE_ALIGNMENT_FIXED 3 -#define GP_STROKE_ALIGNMENT 0x3 -#define GP_STROKE_OVERLAP (1 << 2) -#define GP_STROKE_TEXTURE_USE (1 << 3) -#define GP_STROKE_TEXTURE_STENCIL (1 << 4) -#define GP_STROKE_TEXTURE_PREMUL (1 << 5) -#define GP_STROKE_DOTS (1 << 6) -#define GP_STROKE_HOLDOUT (1 << 7) -#define GP_FILL_HOLDOUT (1 << 8) -#define GP_FILL_TEXTURE_USE (1 << 10) -#define GP_FILL_TEXTURE_PREMUL (1 << 11) -#define GP_FILL_TEXTURE_CLIP (1 << 12) -#define GP_FILL_GRADIENT_USE (1 << 13) -#define GP_FILL_GRADIENT_RADIAL (1 << 14) -/* High bits are used to pass material ID to fragment shader. */ -#define GP_MATID_SHIFT 16 - -/* Multiline defines can crash blender with certain GPU drivers. */ -/* clang-format off */ -#define GP_FILL_FLAGS (GP_FILL_TEXTURE_USE | GP_FILL_TEXTURE_PREMUL | GP_FILL_TEXTURE_CLIP | GP_FILL_GRADIENT_USE | GP_FILL_GRADIENT_RADIAL | GP_FILL_HOLDOUT) -/* clang-format on */ - -#define GP_FLAG_TEST(flag, val) (((flag) & (val)) != 0) - -/* Must match C declaration. */ -struct gpLight { - vec4 color_type; - vec4 right; - vec4 up; - vec4 forward; - vec4 position; - /* Please ensure 16 byte alignment (multiple of vec4). */ -}; - -#define spot_size right.w -#define spot_blend up.w - -#define GP_LIGHT_TYPE_POINT 0.0 -#define GP_LIGHT_TYPE_SPOT 1.0 -#define GP_LIGHT_TYPE_SUN 2.0 -#define GP_LIGHT_TYPE_AMBIENT 3.0 - -#ifdef GP_MATERIAL_BUFFER_LEN - -layout(std140) uniform gpMaterialBlock -{ - gpMaterial materials[GP_MATERIAL_BUFFER_LEN]; -}; - -#endif - -#ifdef GPENCIL_LIGHT_BUFFER_LEN - -layout(std140) uniform gpLightBlock -{ - gpLight lights[GPENCIL_LIGHT_BUFFER_LEN]; -}; - -#endif - /* Must match eGPLayerBlendModes */ #define MODE_REGULAR 0 #define MODE_HARDLIGHT 1 @@ -149,510 +61,3 @@ void blend_mode_output( break; } } - -#ifndef USE_GPU_SHADER_CREATE_INFO - -IN_OUT ShaderStageInterface -{ - vec4 finalColorMul; - vec4 finalColorAdd; - vec3 finalPos; - vec2 finalUvs; - noperspective float strokeThickness; - noperspective float unclampedThickness; - noperspective float strokeHardeness; - flat vec2 strokeAspect; - flat vec2 strokePt1; - flat vec2 strokePt2; - flat int matFlag; - flat float depth; -}; - -#endif - -#ifdef GPU_FRAGMENT_SHADER - -# define linearstep(p0, p1, v) (clamp(((v) - (p0)) / abs((p1) - (p0)), 0.0, 1.0)) - -float stroke_round_cap_mask(vec2 p1, vec2 p2, vec2 aspect, float thickness, float hardfac) -{ - /* We create our own uv space to avoid issues with triangulation and linear - * interpolation artifacts. */ - vec2 line = p2.xy - p1.xy; - vec2 pos = gl_FragCoord.xy - p1.xy; - float line_len = length(line); - float half_line_len = line_len * 0.5; - /* Normalize */ - line = (line_len > 0.0) ? (line / line_len) : vec2(1.0, 0.0); - /* Create a uv space that englobe the whole segment into a capsule. */ - vec2 uv_end; - uv_end.x = max(abs(dot(line, pos) - half_line_len) - half_line_len, 0.0); - uv_end.y = dot(vec2(-line.y, line.x), pos); - /* Divide by stroke radius. */ - uv_end /= thickness; - uv_end *= aspect; - - float dist = clamp(1.0 - length(uv_end) * 2.0, 0.0, 1.0); - if (hardfac > 0.999) { - return step(1e-8, dist); - } - else { - /* Modulate the falloff profile */ - float hardness = 1.0 - hardfac; - dist = pow(dist, mix(0.01, 10.0, hardness)); - return smoothstep(0.0, 1.0, dist); - } -} - -#endif - -uniform vec2 sizeViewport; -uniform vec2 sizeViewportInv; - -/* Per Object */ -uniform bool strokeOrder3d; -uniform int gpMaterialOffset; -uniform float thicknessScale; -uniform float thicknessWorldScale; -#define thicknessIsScreenSpace (thicknessWorldScale < 0.0) - -#ifdef GPU_VERTEX_SHADER - -/* Per Layer */ -uniform float thicknessOffset; -uniform float vertexColorOpacity; -uniform vec4 layerTint; -uniform float layerOpacity; /* Used for onion skin. */ -uniform float strokeIndexOffset = 0.0; - -/* All of these attributes are quad loaded the same way - * as GL_LINES_ADJACENCY would feed a geometry shader: - * - ma reference the previous adjacency point. - * - ma1 reference the current line first point. - * - ma2 reference the current line second point. - * - ma3 reference the next adjacency point. - * Note that we are rendering quad instances and not using any index buffer (except for fills). - */ -/* x is material index, y is stroke_id, z is point_id, w is aspect & rotation & hardness packed. */ -in ivec4 ma; -in ivec4 ma1; -in ivec4 ma2; -in ivec4 ma3; -/* Position contains thickness in 4th component. */ -in vec4 pos; /* Prev adj vert */ -in vec4 pos1; /* Current edge */ -in vec4 pos2; /* Current edge */ -in vec4 pos3; /* Next adj vert */ -/* xy is UV for fills, z is U of stroke, w is strength. */ -in vec4 uv1; -in vec4 uv2; -in vec4 col1; -in vec4 col2; -in vec4 fcol1; -/* WARNING: Max attribute count is actually 14 because OSX OpenGL implementation - * considers gl_VertexID and gl_InstanceID as vertex attribute. (see T74536) */ -# define stroke_id1 ma1.y -# define point_id1 ma1.z -# define thickness1 pos1.w -# define thickness2 pos2.w -# define strength1 uv1.w -# define strength2 uv2.w -/* Packed! need to be decoded. */ -# define hardness1 ma1.w -# define hardness2 ma2.w -# define uvrot1 ma1.w -# define aspect1 ma1.w - -vec2 decode_aspect(int packed_data) -{ - float asp = float(uint(packed_data) & 0x1FFu) * (1.0 / 255.0); - return (asp > 1.0) ? vec2(1.0, (asp - 1.0)) : vec2(asp, 1.0); -} - -float decode_uvrot(int packed_data) -{ - uint udata = uint(packed_data); - float uvrot = 1e-8 + float((udata & 0x1FE00u) >> 9u) * (1.0 / 255.0); - return ((udata & 0x20000u) != 0u) ? -uvrot : uvrot; -} - -float decode_hardness(int packed_data) -{ - return float((uint(packed_data) & 0x3FC0000u) >> 18u) * (1.0 / 255.0); -} - -void discard_vert() -{ - /* We set the vertex at the camera origin to generate 0 fragments. */ - gl_Position = vec4(0.0, 0.0, -3e36, 0.0); -} - -vec2 project_to_screenspace(vec4 v) -{ - return ((v.xy / v.w) * 0.5 + 0.5) * sizeViewport; -} - -vec2 rotate_90deg(vec2 v) -{ - /* Counter Clock-Wise. */ - return vec2(-v.y, v.x); -} - -mat4 model_matrix_get() -{ - return ModelMatrix; -} - -vec3 transform_point(mat4 m, vec3 v) -{ - return (m * vec4(v, 1.0)).xyz; -} - -vec2 safe_normalize(vec2 v) -{ - float len_sqr = dot(v, v); - if (len_sqr > 0.0) { - return v / sqrt(len_sqr); - } - else { - return vec2(1.0, 0.0); - } -} - -vec2 safe_normalize_len(vec2 v, out float len) -{ - len = sqrt(dot(v, v)); - if (len > 0.0) { - return v / len; - } - else { - return vec2(1.0, 0.0); - } -} - -float stroke_thickness_modulate(float thickness) -{ - /* Modify stroke thickness by object and layer factors. */ - thickness *= thicknessScale; - thickness += thicknessOffset; - thickness = max(1.0, thickness); - - if (thicknessIsScreenSpace) { - /* Multiply offset by view Z so that offset is constant in screenspace. - * (e.i: does not change with the distance to camera) */ - thickness *= gl_Position.w; - } - else { - /* World space point size. */ - thickness *= thicknessWorldScale * drw_view.winmat[1][1] * sizeViewport.y; - } - return thickness; -} - -float clamp_small_stroke_thickness(float thickness) -{ - /* To avoid aliasing artifacts, we clamp the line thickness and - * reduce its opacity in the fragment shader. */ - float min_thickness = gl_Position.w * 1.3; - thickness = max(min_thickness, thickness); - - return thickness; -} - -# ifdef GP_MATERIAL_BUFFER_LEN -void color_output(vec4 stroke_col, vec4 vert_col, float vert_strength, float mix_tex) -{ - /* Mix stroke with other colors. */ - vec4 mixed_col = stroke_col; - mixed_col.rgb = mix(mixed_col.rgb, vert_col.rgb, vert_col.a * vertexColorOpacity); - mixed_col.rgb = mix(mixed_col.rgb, layerTint.rgb, layerTint.a); - mixed_col.a *= vert_strength * layerOpacity; - /** - * This is what the fragment shader looks like. - * out = col * finalColorMul + col.a * finalColorAdd. - * finalColorMul is how much of the texture color to keep. - * finalColorAdd is how much of the mixed color to add. - * Note that we never add alpha. This is to keep the texture act as a stencil. - * We do however, modulate the alpha (reduce it). - */ - /* We add the mixed color. This is 100% mix (no texture visible). */ - finalColorMul = vec4(mixed_col.aaa, mixed_col.a); - finalColorAdd = vec4(mixed_col.rgb * mixed_col.a, 0.0); - /* Then we blend according to the texture mix factor. - * Note that we keep the alpha modulation. */ - finalColorMul.rgb *= mix_tex; - finalColorAdd.rgb *= 1.0 - mix_tex; -} -# endif - -void stroke_vertex() -{ - int m = ma1.x; - bool is_dot = false; - bool is_squares = false; - -# ifdef GP_MATERIAL_BUFFER_LEN - if (m != -1) { - is_dot = GP_FLAG_TEST(GP_FLAG(m), GP_STROKE_ALIGNMENT); - is_squares = !GP_FLAG_TEST(GP_FLAG(m), GP_STROKE_DOTS); - } -# endif - - /* Special Case. Stroke with single vert are rendered as dots. Do not discard them. */ - if (!is_dot && ma.x == -1 && ma2.x == -1) { - is_dot = true; - is_squares = false; - } - - /* Endpoints, we discard the vertices. */ - if (ma1.x == -1 || (!is_dot && ma2.x == -1)) { - discard_vert(); - return; - } - - mat4 model_mat = model_matrix_get(); - - /* Avoid using a vertex attribute for quad positioning. */ - float x = float(gl_VertexID & 1) * 2.0 - 1.0; /* [-1..1] */ - float y = float(gl_VertexID & 2) - 1.0; /* [-1..1] */ - - bool use_curr = is_dot || (x == -1.0); - - vec3 wpos_adj = transform_point(model_mat, (use_curr) ? pos.xyz : pos3.xyz); - vec3 wpos1 = transform_point(model_mat, pos1.xyz); - vec3 wpos2 = transform_point(model_mat, pos2.xyz); - - vec4 ndc_adj = point_world_to_ndc(wpos_adj); - vec4 ndc1 = point_world_to_ndc(wpos1); - vec4 ndc2 = point_world_to_ndc(wpos2); - - gl_Position = (use_curr) ? ndc1 : ndc2; - finalPos = (use_curr) ? wpos1 : wpos2; - - vec2 ss_adj = project_to_screenspace(ndc_adj); - vec2 ss1 = project_to_screenspace(ndc1); - vec2 ss2 = project_to_screenspace(ndc2); - /* Screenspace Lines tangents. */ - float line_len; - vec2 line = safe_normalize_len(ss2 - ss1, line_len); - vec2 line_adj = safe_normalize((use_curr) ? (ss1 - ss_adj) : (ss_adj - ss2)); - - float thickness = abs((use_curr) ? thickness1 : thickness2); - thickness = stroke_thickness_modulate(thickness); - float clampedThickness = clamp_small_stroke_thickness(thickness); - - finalUvs = vec2(x, y) * 0.5 + 0.5; - strokeHardeness = decode_hardness(use_curr ? hardness1 : hardness2); - - if (is_dot) { -# ifdef GP_MATERIAL_BUFFER_LEN - int alignement = GP_FLAG(m) & GP_STROKE_ALIGNMENT; - /* For one point strokes use object alignment. */ - if (ma.x == -1 && ma2.x == -1 && alignement == GP_STROKE_ALIGNMENT_STROKE) { - alignement = GP_STROKE_ALIGNMENT_OBJECT; - } -# endif - - vec2 x_axis; -# ifdef GP_MATERIAL_BUFFER_LEN - if (alignement == GP_STROKE_ALIGNMENT_STROKE) { - x_axis = (ma2.x == -1) ? line_adj : line; - } - else if (alignement == GP_STROKE_ALIGNMENT_FIXED) { - /* Default for no-material drawing. */ - x_axis = vec2(1.0, 0.0); - } - else -# endif - { /* GP_STROKE_ALIGNMENT_OBJECT */ - vec4 ndc_x = point_world_to_ndc(wpos1 + model_mat[0].xyz); - vec2 ss_x = project_to_screenspace(ndc_x); - x_axis = safe_normalize(ss_x - ss1); - } - - /* Rotation: Encoded as Cos + Sin sign. */ - float uv_rot = decode_uvrot(uvrot1); - float rot_sin = sqrt(max(0.0, 1.0 - uv_rot * uv_rot)) * sign(uv_rot); - float rot_cos = abs(uv_rot); - x_axis = mat2(rot_cos, -rot_sin, rot_sin, rot_cos) * x_axis; - -# ifdef GP_MATERIAL_BUFFER_LEN - if (is_dot) { - float alignment_cos = MATERIAL(m).fill_uv_offset.z; - float alignment_sin = MATERIAL(m).fill_uv_offset.w; - x_axis = mat2(alignment_cos, -alignment_sin, alignment_sin, alignment_cos) * x_axis; - } -# endif - - vec2 y_axis = rotate_90deg(x_axis); - - strokeAspect = decode_aspect(aspect1); - - x *= strokeAspect.x; - y *= strokeAspect.y; - - /* Invert for vertex shader. */ - strokeAspect = 1.0 / strokeAspect; - - gl_Position.xy += (x * x_axis + y * y_axis) * sizeViewportInv.xy * clampedThickness; - - strokePt1 = ss1; - strokePt2 = ss1 + x_axis * 0.5; - strokeThickness = (is_squares) ? 1e18 : (clampedThickness / gl_Position.w); - unclampedThickness = (is_squares) ? 1e18 : (thickness / gl_Position.w); - } - else { - bool is_stroke_start = (ma.x == -1 && x == -1); - bool is_stroke_end = (ma3.x == -1 && x == 1); - - /* Mitter tangent vector. */ - vec2 miter_tan = safe_normalize(line_adj + line); - float miter_dot = dot(miter_tan, line_adj); - /* Break corners after a certain angle to avoid really thick corners. */ - const float miter_limit = 0.5; /* cos(60°) */ - bool miter_break = (miter_dot < miter_limit); - miter_tan = (miter_break || is_stroke_start || is_stroke_end) ? line : (miter_tan / miter_dot); - - vec2 miter = rotate_90deg(miter_tan); - - strokePt1.xy = ss1; - strokePt2.xy = ss2; - strokeThickness = clampedThickness / gl_Position.w; - unclampedThickness = thickness / gl_Position.w; - strokeAspect = vec2(1.0); - - vec2 screen_ofs = miter * y; - - /* Reminder: we packed the cap flag into the sign of strength and thickness sign. */ - if ((is_stroke_start && strength1 > 0.0) || (is_stroke_end && thickness1 > 0.0) || - (miter_break && !is_stroke_start && !is_stroke_end)) { - screen_ofs += line * x; - } - - gl_Position.xy += screen_ofs * sizeViewportInv.xy * clampedThickness; - - finalUvs.x = (use_curr) ? uv1.z : uv2.z; -# ifdef GP_MATERIAL_BUFFER_LEN - finalUvs.x *= MATERIAL(m).stroke_u_scale; -# endif - } - -# ifdef GP_MATERIAL_BUFFER_LEN - vec4 vert_col = (use_curr) ? col1 : col2; - float vert_strength = abs((use_curr) ? strength1 : strength2); - vec4 stroke_col = MATERIAL(m).stroke_color; - float mix_tex = MATERIAL(m).stroke_texture_mix; - - /* Special case: We don't use vertex color if material Holdout. */ - if (GP_FLAG_TEST(GP_FLAG(m), GP_STROKE_HOLDOUT)) { - vert_col = vec4(0.0); - } - - color_output(stroke_col, vert_col, vert_strength, mix_tex); - - matFlag = GP_FLAG(m) & ~GP_FILL_FLAGS; -# endif - - if (strokeOrder3d) { - /* Use the fragment depth (see fragment shader). */ - depth = -1.0; - } -# ifdef GP_MATERIAL_BUFFER_LEN - else if (GP_FLAG_TEST(GP_FLAG(m), GP_STROKE_OVERLAP)) { - /* Use the index of the point as depth. - * This means the stroke can overlap itself. */ - depth = (point_id1 + strokeIndexOffset + 1.0) * 0.0000002; - } -# endif - else { - /* Use the index of first point of the stroke as depth. - * We render using a greater depth test this means the stroke - * cannot overlap itself. - * We offset by one so that the fill can be overlapped by its stroke. - * The offset is ok since we pad the strokes data because of adjacency infos. */ - depth = (stroke_id1 + strokeIndexOffset + 1.0) * 0.0000002; - } -} - -void fill_vertex() -{ - mat4 model_mat = model_matrix_get(); - - vec3 wpos = transform_point(model_mat, pos1.xyz); - gl_Position = point_world_to_ndc(wpos); - finalPos = wpos; - -# ifdef GP_MATERIAL_BUFFER_LEN - int m = ma1.x; - - vec4 fill_col = MATERIAL(m).fill_color; - float mix_tex = MATERIAL(m).fill_texture_mix; - - /* Special case: We don't modulate alpha in gradient mode. */ - if (GP_FLAG_TEST(GP_FLAG(m), GP_FILL_GRADIENT_USE)) { - fill_col.a = 1.0; - } - - /* Decode fill opacity. */ - vec4 fcol_decode = vec4(fcol1.rgb, floor(fcol1.a / 10.0)); - float fill_opacity = fcol1.a - (fcol_decode.a * 10); - fcol_decode.a /= 10000.0; - - /* Special case: We don't use vertex color if material Holdout. */ - if (GP_FLAG_TEST(GP_FLAG(m), GP_FILL_HOLDOUT)) { - fcol_decode = vec4(0.0); - } - - /* Apply opacity. */ - fill_col.a *= fill_opacity; - /* If factor is > 1 force opacity. */ - if (fill_opacity > 1.0) { - fill_col.a += fill_opacity - 1.0; - } - - fill_col.a = clamp(fill_col.a, 0.0, 1.0); - - color_output(fill_col, fcol_decode, 1.0, mix_tex); - - matFlag = GP_FLAG(m) & GP_FILL_FLAGS; - matFlag |= m << GP_MATID_SHIFT; - - vec2 loc = MATERIAL(m).fill_uv_offset.xy; - mat2x2 rot_scale = mat2x2(MATERIAL(m).fill_uv_rot_scale.xy, MATERIAL(m).fill_uv_rot_scale.zw); - finalUvs = rot_scale * uv1.xy + loc; -# endif - - strokeHardeness = 1.0; - strokeThickness = 1e18; - unclampedThickness = 1e20; - strokeAspect = vec2(1.0); - strokePt1 = strokePt2 = vec2(0.0); - - if (strokeOrder3d) { - /* Use the fragment depth (see fragment shader). */ - depth = -1.0; - /* We still offset the fills a little to avoid overlaps */ - gl_Position.z += 0.000002; - } - else { - /* Use the index of first point of the stroke as depth. */ - depth = (stroke_id1 + strokeIndexOffset) * 0.0000002; - } -} - -void gpencil_vertex() -{ - /* Trick to detect if a drawcall is stroke or fill. - * This does mean that we need to draw an empty stroke segment before starting - * to draw the real stroke segments. */ - bool is_fill = (gl_InstanceID == 0); - - if (!is_fill) { - stroke_vertex(); - } - else { - fill_vertex(); - } -} - -#endif diff --git a/source/blender/draw/engines/gpencil/shaders/gpencil_vert.glsl b/source/blender/draw/engines/gpencil/shaders/gpencil_vert.glsl index 8ed03b23809..9b1db09ab3c 100644 --- a/source/blender/draw/engines/gpencil/shaders/gpencil_vert.glsl +++ b/source/blender/draw/engines/gpencil/shaders/gpencil_vert.glsl @@ -31,23 +31,11 @@ void main() vec4 vert_color; vec3 vert_N; + ivec4 ma1 = floatBitsToInt(texelFetch(gp_pos_tx, gpencil_stroke_point_id() * 3 + 1)); gpMaterial gp_mat = materials[ma1.x + gpMaterialOffset]; gpMaterialFlag gp_flag = floatBitsToUint(gp_mat._flag); - gl_Position = gpencil_vertex(ma, - ma1, - ma2, - ma3, - pos, - pos1, - pos2, - pos3, - uv1, - uv2, - col1, - col2, - fcol1, - vec4(viewportSize, 1.0 / viewportSize), + gl_Position = gpencil_vertex(vec4(viewportSize, 1.0 / viewportSize), gp_flag, gp_mat._alignment_rot, gp_interp.pos, @@ -60,7 +48,7 @@ void main() gp_interp.thickness, gp_interp.hardness); - if (GPENCIL_IS_STROKE_VERTEX) { + if (gpencil_is_stroke_vertex()) { if (!flag_test(gp_flag, GP_STROKE_ALIGNMENT)) { gp_interp.uv.x *= gp_mat._stroke_u_scale; } @@ -96,6 +84,9 @@ void main() } } else { + int stroke_point_id = gpencil_stroke_point_id(); + vec4 uv1 = texelFetch(gp_col_tx, stroke_point_id * 2 + 2); + vec4 fcol1 = texelFetch(gp_col_tx, stroke_point_id * 2 + 1); vec4 fill_col = gp_mat.fill_color; /* Special case: We don't modulate alpha in gradient mode. */ diff --git a/source/blender/draw/engines/gpencil/shaders/infos/gpencil_info.hh b/source/blender/draw/engines/gpencil/shaders/infos/gpencil_info.hh index edd51e71242..da2776254e6 100644 --- a/source/blender/draw/engines/gpencil/shaders/infos/gpencil_info.hh +++ b/source/blender/draw/engines/gpencil/shaders/infos/gpencil_info.hh @@ -22,10 +22,10 @@ GPU_SHADER_CREATE_INFO(gpencil_geometry) .do_static_compilation(true) .define("GP_LIGHT") .typedef_source("gpencil_defines.h") - .sampler(0, ImageType::FLOAT_2D, "gpFillTexture") - .sampler(1, ImageType::FLOAT_2D, "gpStrokeTexture") - .sampler(2, ImageType::DEPTH_2D, "gpSceneDepthTexture") - .sampler(3, ImageType::FLOAT_2D, "gpMaskTexture") + .sampler(2, ImageType::FLOAT_2D, "gpFillTexture") + .sampler(3, ImageType::FLOAT_2D, "gpStrokeTexture") + .sampler(4, ImageType::DEPTH_2D, "gpSceneDepthTexture") + .sampler(5, ImageType::FLOAT_2D, "gpMaskTexture") .uniform_buf(4, "gpMaterial", "materials[GPENCIL_MATERIAL_BUFFER_LEN]", Frequency::BATCH) .uniform_buf(3, "gpLight", "lights[GPENCIL_LIGHT_BUFFER_LEN]", Frequency::BATCH) .push_constant(Type::VEC2, "viewportSize") diff --git a/source/blender/draw/engines/image/image_drawing_mode.hh b/source/blender/draw/engines/image/image_drawing_mode.hh index 21dac8009f6..8913b7469ed 100644 --- a/source/blender/draw/engines/image/image_drawing_mode.hh +++ b/source/blender/draw/engines/image/image_drawing_mode.hh @@ -332,6 +332,7 @@ template<typename TextureMethod> class ScreenSpaceDrawingMode : public AbstractD offset++; } } + IMB_gpu_clamp_half_float(&extracted_buffer); GPU_texture_update_sub(texture, GPU_DATA_FLOAT, @@ -388,6 +389,7 @@ template<typename TextureMethod> class ScreenSpaceDrawingMode : public AbstractD } BKE_image_release_ibuf(image, tile_buffer, lock); } + IMB_gpu_clamp_half_float(&texture_buffer); GPU_texture_update(info.texture, GPU_DATA_FLOAT, texture_buffer.rect_float); imb_freerectImbuf_all(&texture_buffer); } diff --git a/source/blender/draw/engines/overlay/overlay_grid.cc b/source/blender/draw/engines/overlay/overlay_grid.cc index e31c40fff41..7c221e67691 100644 --- a/source/blender/draw/engines/overlay/overlay_grid.cc +++ b/source/blender/draw/engines/overlay/overlay_grid.cc @@ -59,8 +59,10 @@ void OVERLAY_grid_init(OVERLAY_Data *vedata) const bool draw_grid = is_uv_edit || !ED_space_image_has_buffer(sima); if (background_enabled && draw_grid) { grid_flag |= SHOW_GRID; - if (is_uv_edit && (sima->flag & SI_CUSTOM_GRID) != 0) { - grid_flag |= CUSTOM_GRID; + if (is_uv_edit) { + if (sima->grid_shape_source != SI_GRID_SHAPE_DYNAMIC) { + grid_flag |= CUSTOM_GRID; + } } } diff --git a/source/blender/draw/engines/overlay/overlay_outline.cc b/source/blender/draw/engines/overlay/overlay_outline.cc index 5ea02376b67..50d42effe00 100644 --- a/source/blender/draw/engines/overlay/overlay_outline.cc +++ b/source/blender/draw/engines/overlay/overlay_outline.cc @@ -172,7 +172,6 @@ void OVERLAY_outline_cache_init(OVERLAY_Data *vedata) typedef struct iterData { Object *ob; DRWShadingGroup *stroke_grp; - DRWShadingGroup *fill_grp; int cfra; float plane[4]; } iterData; @@ -193,12 +192,17 @@ static void gpencil_layer_cache_populate(bGPDlayer *gpl, * Convert to world units (by default, 1 meter = 2000 pixels). */ float thickness_scale = (is_screenspace) ? -1.0f : (gpd->pixfactor / 2000.0f); + GPUVertBuf *position_tx = DRW_cache_gpencil_position_buffer_get(iter->ob, iter->cfra); + GPUVertBuf *color_tx = DRW_cache_gpencil_color_buffer_get(iter->ob, iter->cfra); + DRWShadingGroup *grp = iter->stroke_grp = DRW_shgroup_create_sub(iter->stroke_grp); DRW_shgroup_uniform_bool_copy(grp, "gpStrokeOrder3d", is_stroke_order_3d); DRW_shgroup_uniform_float_copy(grp, "gpThicknessScale", object_scale); DRW_shgroup_uniform_float_copy(grp, "gpThicknessOffset", float(gpl->line_change)); DRW_shgroup_uniform_float_copy(grp, "gpThicknessWorldScale", thickness_scale); DRW_shgroup_uniform_vec4_copy(grp, "gpDepthPlane", iter->plane); + DRW_shgroup_buffer_texture(grp, "gp_pos_tx", position_tx); + DRW_shgroup_buffer_texture(grp, "gp_col_tx", color_tx); } static void gpencil_stroke_cache_populate(bGPDlayer * /*gpl*/, @@ -219,20 +223,19 @@ static void gpencil_stroke_cache_populate(bGPDlayer * /*gpl*/, return; } + struct GPUBatch *geom = DRW_cache_gpencil_get(iter->ob, iter->cfra); + if (show_fill) { - struct GPUBatch *geom = DRW_cache_gpencil_fills_get(iter->ob, iter->cfra); int vfirst = gps->runtime.fill_start * 3; int vcount = gps->tot_triangles * 3; - DRW_shgroup_call_range(iter->fill_grp, iter->ob, geom, vfirst, vcount); + DRW_shgroup_call_range(iter->stroke_grp, iter->ob, geom, vfirst, vcount); } if (show_stroke) { - struct GPUBatch *geom = DRW_cache_gpencil_strokes_get(iter->ob, iter->cfra); - /* Start one vert before to have gl_InstanceID > 0 (see shader). */ - int vfirst = gps->runtime.stroke_start - 1; - /* Include "potential" cyclic vertex and start adj vertex (see shader). */ - int vcount = gps->totpoints + 1 + 1; - DRW_shgroup_call_instance_range(iter->stroke_grp, iter->ob, geom, vfirst, vcount); + int vfirst = gps->runtime.stroke_start * 3; + /* Include "potential" cyclic vertex (see shader). */ + int vcount = (gps->totpoints + 1) * 2 * 3; + DRW_shgroup_call_range(iter->stroke_grp, iter->ob, geom, vfirst, vcount); } } @@ -247,7 +250,6 @@ static void OVERLAY_outline_gpencil(OVERLAY_PrivateData *pd, Object *ob) iterData iter{}; iter.ob = ob; iter.stroke_grp = pd->outlines_gpencil_grp; - iter.fill_grp = DRW_shgroup_create_sub(pd->outlines_gpencil_grp); iter.cfra = pd->cfra; if (gpd->draw_mode == GP_DRAWMODE_2D) { diff --git a/source/blender/draw/engines/overlay/overlay_particle.cc b/source/blender/draw/engines/overlay/overlay_particle.cc index 6f77a777ba0..c9e3ccba008 100644 --- a/source/blender/draw/engines/overlay/overlay_particle.cc +++ b/source/blender/draw/engines/overlay/overlay_particle.cc @@ -181,14 +181,14 @@ void OVERLAY_particle_cache_populate(OVERLAY_Data *vedata, Object *ob) default: case PART_DRAW_DOT: grp = DRW_shgroup_create_sub(pd->particle_dots_grp); - DRW_shgroup_uniform_vec4_copy(grp, "color", color); + DRW_shgroup_uniform_vec4_copy(grp, "ucolor", color); DRW_shgroup_call(grp, geom, nullptr); break; case PART_DRAW_AXIS: case PART_DRAW_CIRC: case PART_DRAW_CROSS: grp = DRW_shgroup_create_sub(pd->particle_shapes_grp); - DRW_shgroup_uniform_vec4_copy(grp, "color", color); + DRW_shgroup_uniform_vec4_copy(grp, "ucolor", color); shape = DRW_cache_particles_get_prim(draw_as); DRW_shgroup_call_instances_with_attrs(grp, nullptr, shape, geom); break; diff --git a/source/blender/draw/engines/overlay/shaders/overlay_motion_path_line_vert_no_geom.glsl b/source/blender/draw/engines/overlay/shaders/overlay_motion_path_line_vert_no_geom.glsl index e3ddeb5c6a4..abaa814a4dc 100644 --- a/source/blender/draw/engines/overlay/shaders/overlay_motion_path_line_vert_no_geom.glsl +++ b/source/blender/draw/engines/overlay/shaders/overlay_motion_path_line_vert_no_geom.glsl @@ -108,8 +108,8 @@ void main() vec3 in_pos0 = vertex_fetch_attribute(base_vertex_id, pos, vec3); vec3 in_pos1 = vertex_fetch_attribute(base_vertex_id + 1, pos, vec3); - vec4 out_pos0 = ProjectionMatrix * (ViewMatrix * vec4(in_pos0, 1.0)); - vec4 out_pos1 = ProjectionMatrix * (ViewMatrix * vec4(in_pos1, 1.0)); + vec4 out_pos0 = drw_view.winmat * (drw_view.viewmat * vec4(in_pos0, 1.0)); + vec4 out_pos1 = drw_view.winmat * (drw_view.viewmat * vec4(in_pos1, 1.0)); /* Final calculations required for Geometry Shader alternative. * We need to calculate values for each vertex position to correctly determine the final output @@ -130,28 +130,28 @@ void main() float line_size = float(lineThickness) * sizePixel; if (quad_vertex_id == 0) { - view_clipping_distances(out_pos0); + view_clipping_distances(out_pos0.xyz); interp.color = finalColor_geom[0]; t = edge_dir * (line_size * (is_persp ? out_pos0.w : 1.0)); gl_Position = out_pos0 + vec4(t, 0.0, 0.0); } else if (quad_vertex_id == 1 || quad_vertex_id == 3) { - view_clipping_distances(out_pos0); + view_clipping_distances(out_pos0.xyz); interp.color = finalColor_geom[0]; t = edge_dir * (line_size * (is_persp ? out_pos0.w : 1.0)); gl_Position = out_pos0 - vec4(t, 0.0, 0.0); } else if (quad_vertex_id == 2 || quad_vertex_id == 5) { - view_clipping_distances(out_pos1); + view_clipping_distances(out_pos1.xyz); interp.color = finalColor_geom[1]; t = edge_dir * (line_size * (is_persp ? out_pos1.w : 1.0)); gl_Position = out_pos1 + vec4(t, 0.0, 0.0); } else if (quad_vertex_id == 4) { - view_clipping_distances(out_pos1); + view_clipping_distances(out_pos1.xyz); interp.color = finalColor_geom[1]; t = edge_dir * (line_size * (is_persp ? out_pos1.w : 1.0)); diff --git a/source/blender/draw/engines/overlay/shaders/overlay_outline_prepass_gpencil_vert.glsl b/source/blender/draw/engines/overlay/shaders/overlay_outline_prepass_gpencil_vert.glsl index 7d8fb0c2cb8..851e0884354 100644 --- a/source/blender/draw/engines/overlay/shaders/overlay_outline_prepass_gpencil_vert.glsl +++ b/source/blender/draw/engines/overlay/shaders/overlay_outline_prepass_gpencil_vert.glsl @@ -34,20 +34,7 @@ void main() float unused_strength; vec2 unused_uv; - gl_Position = gpencil_vertex(ma, - ma1, - ma2, - ma3, - pos, - pos1, - pos2, - pos3, - uv1, - uv2, - col1, - col2, - fcol1, - vec4(sizeViewport, sizeViewportInv), + gl_Position = gpencil_vertex(vec4(sizeViewport, sizeViewportInv), world_pos, unused_N, unused_color, diff --git a/source/blender/draw/engines/workbench/workbench_engine.c b/source/blender/draw/engines/workbench/workbench_engine.c index 36a980bd506..fecdad5802c 100644 --- a/source/blender/draw/engines/workbench/workbench_engine.c +++ b/source/blender/draw/engines/workbench/workbench_engine.c @@ -14,6 +14,7 @@ #include "BLI_alloca.h" #include "BKE_editmesh.h" +#include "BKE_mesh_runtime.h" #include "BKE_modifier.h" #include "BKE_object.h" #include "BKE_paint.h" @@ -231,7 +232,7 @@ static void workbench_cache_hair_populate(WORKBENCH_PrivateData *wpd, static const CustomData *workbench_mesh_get_loop_custom_data(const Mesh *mesh) { - if (mesh->runtime.wrapper_type == ME_WRAPPER_TYPE_BMESH) { + if (BKE_mesh_wrapper_type(mesh) == ME_WRAPPER_TYPE_BMESH) { BLI_assert(mesh->edit_mesh != NULL); BLI_assert(mesh->edit_mesh->bm != NULL); return &mesh->edit_mesh->bm->ldata; @@ -241,7 +242,7 @@ static const CustomData *workbench_mesh_get_loop_custom_data(const Mesh *mesh) static const CustomData *workbench_mesh_get_vert_custom_data(const Mesh *mesh) { - if (mesh->runtime.wrapper_type == ME_WRAPPER_TYPE_BMESH) { + if (BKE_mesh_wrapper_type(mesh) == ME_WRAPPER_TYPE_BMESH) { BLI_assert(mesh->edit_mesh != NULL); BLI_assert(mesh->edit_mesh->bm != NULL); return &mesh->edit_mesh->bm->vdata; diff --git a/source/blender/draw/intern/DRW_gpu_wrapper.hh b/source/blender/draw/intern/DRW_gpu_wrapper.hh index d53c5fbeaa5..3be50d471e2 100644 --- a/source/blender/draw/intern/DRW_gpu_wrapper.hh +++ b/source/blender/draw/intern/DRW_gpu_wrapper.hh @@ -924,6 +924,35 @@ class TextureFromPool : public Texture, NonMovable { GPUTexture *stencil_view() = delete; }; +class TextureRef : public Texture { + public: + TextureRef() = default; + + ~TextureRef() + { + this->tx_ = nullptr; + } + + void wrap(GPUTexture *tex) + { + this->tx_ = tex; + } + + /** Remove methods that are forbidden with this type of textures. */ + bool ensure_1d(int, int, eGPUTextureFormat, float *) = delete; + bool ensure_1d_array(int, int, int, eGPUTextureFormat, float *) = delete; + bool ensure_2d(int, int, int, eGPUTextureFormat, float *) = delete; + bool ensure_2d_array(int, int, int, int, eGPUTextureFormat, float *) = delete; + bool ensure_3d(int, int, int, int, eGPUTextureFormat, float *) = delete; + bool ensure_cube(int, int, eGPUTextureFormat, float *) = delete; + bool ensure_cube_array(int, int, int, eGPUTextureFormat, float *) = delete; + void filter_mode(bool) = delete; + void free() = delete; + GPUTexture *mip_view(int) = delete; + GPUTexture *layer_view(int) = delete; + GPUTexture *stencil_view() = delete; +}; + /** * Dummy type to bind texture as image. * It is just a GPUTexture in disguise. diff --git a/source/blender/draw/intern/draw_attributes.cc b/source/blender/draw/intern/draw_attributes.cc index 011d72e9e8f..cc7c9959850 100644 --- a/source/blender/draw/intern/draw_attributes.cc +++ b/source/blender/draw/intern/draw_attributes.cc @@ -44,13 +44,10 @@ void drw_attributes_clear(DRW_Attributes *attributes) memset(attributes, 0, sizeof(DRW_Attributes)); } -void drw_attributes_merge(DRW_Attributes *dst, - const DRW_Attributes *src, - ThreadMutex *render_mutex) +void drw_attributes_merge(DRW_Attributes *dst, const DRW_Attributes *src, std::mutex &render_mutex) { - BLI_mutex_lock(render_mutex); + std::lock_guard lock{render_mutex}; drw_attributes_merge_requests(src, dst); - BLI_mutex_unlock(render_mutex); } bool drw_attributes_overlap(const DRW_Attributes *a, const DRW_Attributes *b) diff --git a/source/blender/draw/intern/draw_attributes.h b/source/blender/draw/intern/draw_attributes.h index 786301d0164..00621c711bf 100644 --- a/source/blender/draw/intern/draw_attributes.h +++ b/source/blender/draw/intern/draw_attributes.h @@ -9,6 +9,10 @@ #pragma once +#ifdef __cplusplus +# include <mutex> +#endif + #include "DNA_customdata_types.h" #include "DNA_meshdata_types.h" @@ -56,9 +60,11 @@ BLI_STATIC_ASSERT(sizeof(DRW_MeshCDMask) <= sizeof(uint32_t), "DRW_MeshCDMask ex void drw_attributes_clear(DRW_Attributes *attributes); +#ifdef __cplusplus void drw_attributes_merge(DRW_Attributes *dst, const DRW_Attributes *src, - ThreadMutex *render_mutex); + std::mutex &render_mutex); +#endif /* Return true if all requests in b are in a. */ bool drw_attributes_overlap(const DRW_Attributes *a, const DRW_Attributes *b); diff --git a/source/blender/draw/intern/draw_cache.h b/source/blender/draw/intern/draw_cache.h index 058f28f094d..45154b41670 100644 --- a/source/blender/draw/intern/draw_cache.h +++ b/source/blender/draw/intern/draw_cache.h @@ -13,6 +13,7 @@ extern "C" { struct GPUBatch; struct GPUMaterial; +struct GPUVertBuf; struct ModifierData; struct Object; struct PTCacheEdit; @@ -257,14 +258,17 @@ struct GPUBatch *DRW_cache_volume_selection_surface_get(struct Object *ob); /* GPencil */ -struct GPUBatch *DRW_cache_gpencil_strokes_get(struct Object *ob, int cfra); -struct GPUBatch *DRW_cache_gpencil_fills_get(struct Object *ob, int cfra); +struct GPUBatch *DRW_cache_gpencil_get(struct Object *ob, int cfra); +struct GPUVertBuf *DRW_cache_gpencil_position_buffer_get(struct Object *ob, int cfra); +struct GPUVertBuf *DRW_cache_gpencil_color_buffer_get(struct Object *ob, int cfra); struct GPUBatch *DRW_cache_gpencil_edit_lines_get(struct Object *ob, int cfra); struct GPUBatch *DRW_cache_gpencil_edit_points_get(struct Object *ob, int cfra); struct GPUBatch *DRW_cache_gpencil_edit_curve_handles_get(struct Object *ob, int cfra); struct GPUBatch *DRW_cache_gpencil_edit_curve_points_get(struct Object *ob, int cfra); -struct GPUBatch *DRW_cache_gpencil_sbuffer_stroke_get(struct Object *ob); -struct GPUBatch *DRW_cache_gpencil_sbuffer_fill_get(struct Object *ob); +struct GPUBatch *DRW_cache_gpencil_sbuffer_get(struct Object *ob, bool show_fill); +struct GPUVertBuf *DRW_cache_gpencil_sbuffer_position_buffer_get(struct Object *ob, + bool show_fill); +struct GPUVertBuf *DRW_cache_gpencil_sbuffer_color_buffer_get(struct Object *ob, bool show_fill); int DRW_gpencil_material_count_get(struct bGPdata *gpd); struct GPUBatch *DRW_cache_gpencil_face_wireframe_get(struct Object *ob); diff --git a/source/blender/draw/intern/draw_cache_extract_mesh.cc b/source/blender/draw/intern/draw_cache_extract_mesh.cc index d3170f4c776..f533904f355 100644 --- a/source/blender/draw/intern/draw_cache_extract_mesh.cc +++ b/source/blender/draw/intern/draw_cache_extract_mesh.cc @@ -686,7 +686,7 @@ void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph, MeshRenderData *mr = mesh_render_data_create( object, me, is_editmode, is_paint_mode, is_mode_active, obmat, do_final, do_uvedit, ts); mr->use_hide = use_hide; - mr->use_subsurf_fdots = mr->me && mr->me->runtime.subsurf_face_dot_tags != nullptr; + mr->use_subsurf_fdots = mr->me && mr->me->runtime->subsurf_face_dot_tags != nullptr; mr->use_final_mesh = do_final; #ifdef DEBUG_TIME diff --git a/source/blender/draw/intern/draw_cache_extract_mesh_render_data.cc b/source/blender/draw/intern/draw_cache_extract_mesh_render_data.cc index f554e9e67c3..f606701ed09 100644 --- a/source/blender/draw/intern/draw_cache_extract_mesh_render_data.cc +++ b/source/blender/draw/intern/draw_cache_extract_mesh_render_data.cc @@ -463,7 +463,7 @@ MeshRenderData *mesh_render_data_create(Object *object, mr->bm = me->edit_mesh->bm; mr->edit_bmesh = me->edit_mesh; mr->me = (do_final) ? editmesh_eval_final : editmesh_eval_cage; - mr->edit_data = is_mode_active ? mr->me->runtime.edit_data : nullptr; + mr->edit_data = is_mode_active ? mr->me->runtime->edit_data : nullptr; if (mr->edit_data) { EditMeshData *emd = mr->edit_data; @@ -499,8 +499,8 @@ MeshRenderData *mesh_render_data_create(Object *object, /* Use bmesh directly when the object is in edit mode unchanged by any modifiers. * For non-final UVs, always use original bmesh since the UV editor does not support * using the cage mesh with deformed coordinates. */ - if ((is_mode_active && mr->me->runtime.is_original_bmesh && - mr->me->runtime.wrapper_type == ME_WRAPPER_TYPE_BMESH) || + if ((is_mode_active && mr->me->runtime->is_original_bmesh && + mr->me->runtime->wrapper_type == ME_WRAPPER_TYPE_BMESH) || (do_uvedit && !do_final)) { mr->extract_type = MR_EXTRACT_BMESH; } diff --git a/source/blender/draw/intern/draw_cache_impl_curves.cc b/source/blender/draw/intern/draw_cache_impl_curves.cc index 33b97388620..85dd9ca8695 100644 --- a/source/blender/draw/intern/draw_cache_impl_curves.cc +++ b/source/blender/draw/intern/draw_cache_impl_curves.cc @@ -14,9 +14,9 @@ #include "BLI_listbase.h" #include "BLI_math_base.h" #include "BLI_math_vec_types.hh" -#include "BLI_math_vector.h" #include "BLI_math_vector.hh" #include "BLI_span.hh" +#include "BLI_task.hh" #include "BLI_utildefines.h" #include "DNA_curves_types.h" @@ -60,7 +60,7 @@ struct CurvesBatchCache { * some locking would be necessary because multiple objects can use the same curves data with * different materials, etc. This is a placeholder to make multi-threading easier in the future. */ - ThreadMutex render_mutex; + std::mutex render_mutex; }; static bool curves_batch_cache_valid(const Curves &curves) @@ -74,15 +74,13 @@ static void curves_batch_cache_init(Curves &curves) CurvesBatchCache *cache = static_cast<CurvesBatchCache *>(curves.batch_cache); if (!cache) { - cache = MEM_cnew<CurvesBatchCache>(__func__); + cache = MEM_new<CurvesBatchCache>(__func__); curves.batch_cache = cache; } else { - memset(cache, 0, sizeof(*cache)); + cache->curves_cache = {}; } - BLI_mutex_init(&cache->render_mutex); - cache->is_dirty = false; } @@ -172,9 +170,8 @@ void DRW_curves_batch_cache_dirty_tag(Curves *curves, int mode) void DRW_curves_batch_cache_free(Curves *curves) { curves_batch_cache_clear(*curves); - CurvesBatchCache *cache = static_cast<CurvesBatchCache *>(curves->batch_cache); - BLI_mutex_end(&cache->render_mutex); - MEM_SAFE_FREE(curves->batch_cache); + MEM_delete(static_cast<CurvesBatchCache *>(curves->batch_cache)); + curves->batch_cache = nullptr; } void DRW_curves_batch_cache_free_old(Curves *curves, int ctime) @@ -226,38 +223,38 @@ static void curves_batch_cache_fill_segments_proc_pos( MutableSpan<PositionAndParameter> posTime_data, MutableSpan<float> hairLength_data) { + using namespace blender; /* TODO: use hair radius layer if available. */ - const int curve_num = curves_id.geometry.curve_num; - const blender::bke::CurvesGeometry &curves = blender::bke::CurvesGeometry::wrap( - curves_id.geometry); - Span<float3> positions = curves.positions(); + const bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id.geometry); + const Span<float3> positions = curves.positions(); - for (const int i_curve : IndexRange(curve_num)) { - const IndexRange points = curves.points_for_curve(i_curve); + threading::parallel_for(curves.curves_range(), 1024, [&](const IndexRange range) { + for (const int i_curve : range) { + const IndexRange points = curves.points_for_curve(i_curve); - Span<float3> curve_positions = positions.slice(points); - MutableSpan<PositionAndParameter> curve_posTime_data = posTime_data.slice(points); - - float total_len = 0.0f; - for (const int i_point : curve_positions.index_range()) { - if (i_point > 0) { - total_len += blender::math::distance(curve_positions[i_point - 1], - curve_positions[i_point]); - } - curve_posTime_data[i_point].position = curve_positions[i_point]; - curve_posTime_data[i_point].parameter = total_len; - } - hairLength_data[i_curve] = total_len; + Span<float3> curve_positions = positions.slice(points); + MutableSpan<PositionAndParameter> curve_posTime_data = posTime_data.slice(points); - /* Assign length value. */ - if (total_len > 0.0f) { - const float factor = 1.0f / total_len; - /* Divide by total length to have a [0-1] number. */ + float total_len = 0.0f; for (const int i_point : curve_positions.index_range()) { - curve_posTime_data[i_point].parameter *= factor; + if (i_point > 0) { + total_len += math::distance(curve_positions[i_point - 1], curve_positions[i_point]); + } + curve_posTime_data[i_point].position = curve_positions[i_point]; + curve_posTime_data[i_point].parameter = total_len; + } + hairLength_data[i_curve] = total_len; + + /* Assign length value. */ + if (total_len > 0.0f) { + const float factor = 1.0f / total_len; + /* Divide by total length to have a [0-1] number. */ + for (const int i_point : curve_positions.index_range()) { + curve_posTime_data[i_point].parameter *= factor; + } } } - } + }); } static void curves_batch_cache_ensure_procedural_pos(const Curves &curves, @@ -275,7 +272,7 @@ static void curves_batch_cache_ensure_procedural_pos(const Curves &curves, GPU_vertbuf_data_alloc(cache.proc_point_buf, cache.point_len); MutableSpan posTime_data{ - reinterpret_cast<PositionAndParameter *>(GPU_vertbuf_get_data(cache.proc_point_buf)), + static_cast<PositionAndParameter *>(GPU_vertbuf_get_data(cache.proc_point_buf)), cache.point_len}; GPUVertFormat length_format = {0}; @@ -285,8 +282,8 @@ static void curves_batch_cache_ensure_procedural_pos(const Curves &curves, &length_format, GPU_USAGE_STATIC | GPU_USAGE_FLAG_BUFFER_TEXTURE_ONLY); GPU_vertbuf_data_alloc(cache.proc_length_buf, cache.strands_len); - MutableSpan hairLength_data{ - reinterpret_cast<float *>(GPU_vertbuf_get_data(cache.proc_length_buf)), cache.strands_len}; + MutableSpan hairLength_data{static_cast<float *>(GPU_vertbuf_get_data(cache.proc_length_buf)), + cache.strands_len}; curves_batch_cache_fill_segments_proc_pos(curves, posTime_data, hairLength_data); @@ -310,15 +307,15 @@ static void curves_batch_cache_ensure_procedural_pos(const Curves &curves, static void curves_batch_cache_ensure_data_edit_points(const Curves &curves_id, CurvesEvalCache &cache) { - const blender::bke::CurvesGeometry &curves = blender::bke::CurvesGeometry::wrap( - curves_id.geometry); + using namespace blender; + const bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id.geometry); static GPUVertFormat format_data = {0}; uint data = GPU_vertformat_attr_add(&format_data, "data", GPU_COMP_U8, 1, GPU_FETCH_INT); GPU_vertbuf_init_with_format(cache.data_edit_points, &format_data); GPU_vertbuf_data_alloc(cache.data_edit_points, curves.points_num()); - blender::VArray<float> selection; + VArray<float> selection; switch (curves_id.selection_domain) { case ATTR_DOMAIN_POINT: selection = curves.selection_point_float(); @@ -380,6 +377,7 @@ static void curves_batch_ensure_attribute(const Curves &curves, const int subdiv, const int index) { + using namespace blender; GPU_VERTBUF_DISCARD_SAFE(cache.proc_attributes_buf[index]); DRW_TEXTURE_FREE_SAFE(cache.proc_attributes_tex[index]); @@ -399,15 +397,15 @@ static void curves_batch_ensure_attribute(const Curves &curves, request.domain == ATTR_DOMAIN_POINT ? curves.geometry.point_num : curves.geometry.curve_num); - const blender::bke::AttributeAccessor attributes = - blender::bke::CurvesGeometry::wrap(curves.geometry).attributes(); + const bke::AttributeAccessor attributes = + bke::CurvesGeometry::wrap(curves.geometry).attributes(); /* TODO(@kevindietrich): float4 is used for scalar attributes as the implicit conversion done * by OpenGL to vec4 for a scalar `s` will produce a `vec4(s, 0, 0, 1)`. However, following * the Blender convention, it should be `vec4(s, s, s, 1)`. This could be resolved using a * similar texture state swizzle to map the attribute correctly as for volume attributes, so we * can control the conversion ourselves. */ - blender::VArray<ColorGeometry4f> attribute = attributes.lookup_or_default<ColorGeometry4f>( + VArray<ColorGeometry4f> attribute = attributes.lookup_or_default<ColorGeometry4f>( request.attribute_name, request.domain, {0.0f, 0.0f, 0.0f, 1.0f}); MutableSpan<ColorGeometry4f> vbo_span{ @@ -554,7 +552,6 @@ static bool curves_ensure_attributes(const Curves &curves, GPUMaterial *gpu_material, int subdiv) { - ThreadMutex *render_mutex = &cache.render_mutex; const CustomData *cd_curve = &curves.geometry.curve_data; const CustomData *cd_point = &curves.geometry.point_data; CurvesEvalFinalCache &final_cache = cache.curves_cache.final[subdiv]; @@ -588,9 +585,9 @@ static bool curves_ensure_attributes(const Curves &curves, GPU_VERTBUF_DISCARD_SAFE(cache.curves_cache.proc_attributes_buf[i]); DRW_TEXTURE_FREE_SAFE(cache.curves_cache.proc_attributes_tex[i]); } - drw_attributes_merge(&final_cache.attr_used, &attrs_needed, render_mutex); + drw_attributes_merge(&final_cache.attr_used, &attrs_needed, cache.render_mutex); } - drw_attributes_merge(&final_cache.attr_used_over_time, &attrs_needed, render_mutex); + drw_attributes_merge(&final_cache.attr_used_over_time, &attrs_needed, cache.render_mutex); } bool need_tf_update = false; @@ -689,7 +686,7 @@ static void request_attribute(Curves &curves, const char *name) drw_attributes_add_request( &attributes, name, type, CustomData_get_named_layer(&custom_data, type, name), domain); - drw_attributes_merge(&final_cache.attr_used, &attributes, &cache.render_mutex); + drw_attributes_merge(&final_cache.attr_used, &attributes, cache.render_mutex); } GPUTexture **DRW_curves_texture_for_evaluated_attribute(Curves *curves, diff --git a/source/blender/draw/intern/draw_cache_impl_gpencil.c b/source/blender/draw/intern/draw_cache_impl_gpencil.cc index 7a43c7ee2e6..667ae380ae4 100644 --- a/source/blender/draw/intern/draw_cache_impl_gpencil.c +++ b/source/blender/draw/intern/draw_cache_impl_gpencil.cc @@ -23,12 +23,14 @@ #include "DEG_depsgraph_query.h" #include "BLI_hash.h" +#include "BLI_math_vec_types.hh" #include "BLI_polyfill_2d.h" #include "draw_cache.h" #include "draw_cache_impl.h" #include "../engines/gpencil/gpencil_defines.h" +#include "../engines/gpencil/gpencil_shader_shared.h" #define BEZIER_HANDLE (1 << 3) #define COLOR_SHIFT 5 @@ -41,11 +43,13 @@ typedef struct GpencilBatchCache { /** Instancing Data */ GPUVertBuf *vbo; GPUVertBuf *vbo_col; - /** Fill Topology */ + /** Indices in material order, then stroke order with fill first. + * Strokes can be individually rendered using `gps->runtime.stroke_start` and + * `gps->runtime.fill_start`. */ GPUIndexBuf *ibo; - /** Instancing Batches */ - GPUBatch *stroke_batch; - GPUBatch *fill_batch; + /** Batches */ + GPUBatch *geom_batch; + /** Stroke lines only */ GPUBatch *lines_batch; /** Edit Mode */ @@ -97,7 +101,8 @@ static GpencilBatchCache *gpencil_batch_cache_init(Object *ob, int cfra) GpencilBatchCache *cache = gpd->runtime.gpencil_cache; if (!cache) { - cache = gpd->runtime.gpencil_cache = MEM_callocN(sizeof(*cache), __func__); + cache = gpd->runtime.gpencil_cache = (GpencilBatchCache *)MEM_callocN(sizeof(*cache), + __func__); } else { memset(cache, 0, sizeof(*cache)); @@ -116,8 +121,7 @@ static void gpencil_batch_cache_clear(GpencilBatchCache *cache) } GPU_BATCH_DISCARD_SAFE(cache->lines_batch); - GPU_BATCH_DISCARD_SAFE(cache->fill_batch); - GPU_BATCH_DISCARD_SAFE(cache->stroke_batch); + GPU_BATCH_DISCARD_SAFE(cache->geom_batch); GPU_VERTBUF_DISCARD_SAFE(cache->vbo); GPU_VERTBUF_DISCARD_SAFE(cache->vbo_col); GPU_INDEXBUF_DISCARD_SAFE(cache->ibo); @@ -172,9 +176,10 @@ void DRW_gpencil_batch_cache_free(bGPdata *gpd) /* MUST match the format below. */ typedef struct gpStrokeVert { - int32_t mat, stroke_id, point_id, packed_asp_hard_rot; /** Position and thickness packed in the same attribute. */ float pos[3], thickness; + /** Material Index, Stroke Index, Point Index, Packed aspect + hardness + rotation. */ + int32_t mat, stroke_id, point_id, packed_asp_hard_rot; /** UV and strength packed in the same attribute. */ float uv_fill[2], u_stroke, strength; } gpStrokeVert; @@ -183,12 +188,9 @@ static GPUVertFormat *gpencil_stroke_format(void) { static GPUVertFormat format = {0}; if (format.attr_len == 0) { - GPU_vertformat_attr_add(&format, "ma", GPU_COMP_I32, 4, GPU_FETCH_INT); GPU_vertformat_attr_add(&format, "pos", GPU_COMP_F32, 4, GPU_FETCH_FLOAT); + GPU_vertformat_attr_add(&format, "ma", GPU_COMP_I32, 4, GPU_FETCH_INT); GPU_vertformat_attr_add(&format, "uv", GPU_COMP_F32, 4, GPU_FETCH_FLOAT); - /* IMPORTANT: This means having only 4 attributes - * to fit into GPU module limit of 16 attributes. */ - GPU_vertformat_multiload_enable(&format, 4); } return &format; } @@ -238,9 +240,6 @@ static GPUVertFormat *gpencil_color_format(void) if (format.attr_len == 0) { GPU_vertformat_attr_add(&format, "col", GPU_COMP_F32, 4, GPU_FETCH_FLOAT); GPU_vertformat_attr_add(&format, "fcol", GPU_COMP_F32, 4, GPU_FETCH_FLOAT); - /* IMPORTANT: This means having only 4 attributes - * to fit into GPU module limit of 16 attributes. */ - GPU_vertformat_multiload_enable(&format, 4); } return &format; } @@ -295,7 +294,8 @@ BLI_INLINE int32_t pack_rotation_aspect_hardness(float rot, float asp, float har return packed; } -static void gpencil_buffer_add_point(gpStrokeVert *verts, +static void gpencil_buffer_add_point(GPUIndexBufBuilder *ibo, + gpStrokeVert *verts, gpColorVert *cols, const bGPDstroke *gps, const bGPDspoint *pt, @@ -319,7 +319,7 @@ static void gpencil_buffer_add_point(gpStrokeVert *verts, vert->strength = (round_cap0) ? pt->strength : -pt->strength; vert->u_stroke = pt->uv_fac; - vert->stroke_id = gps->runtime.stroke_start; + vert->stroke_id = gps->runtime.vertex_start; vert->point_id = v; vert->thickness = max_ff(0.0f, gps->thickness * pt->pressure) * (round_cap1 ? 1.0f : -1.0f); /* Tag endpoint material to -1 so they get discarded by vertex shader. */ @@ -329,27 +329,36 @@ static void gpencil_buffer_add_point(gpStrokeVert *verts, vert->packed_asp_hard_rot = pack_rotation_aspect_hardness( pt->uv_rot, aspect_ratio, gps->hardeness); + + if (!is_endpoint) { + /* Issue a Quad per point. */ + /* The attribute loading uses a different shader and will undo this bit packing. */ + int v_mat = (v << GP_VERTEX_ID_SHIFT) | GP_IS_STROKE_VERTEX_BIT; + GPU_indexbuf_add_tri_verts(ibo, v_mat + 0, v_mat + 1, v_mat + 2); + GPU_indexbuf_add_tri_verts(ibo, v_mat + 2, v_mat + 1, v_mat + 3); + } } -static void gpencil_buffer_add_stroke(gpStrokeVert *verts, +static void gpencil_buffer_add_stroke(GPUIndexBufBuilder *ibo, + gpStrokeVert *verts, gpColorVert *cols, const bGPDstroke *gps) { const bGPDspoint *pts = gps->points; int pts_len = gps->totpoints; bool is_cyclic = gpencil_stroke_is_cyclic(gps); - int v = gps->runtime.stroke_start; + int v = gps->runtime.vertex_start; /* First point for adjacency (not drawn). */ int adj_idx = (is_cyclic) ? (pts_len - 1) : min_ii(pts_len - 1, 1); - gpencil_buffer_add_point(verts, cols, gps, &pts[adj_idx], v++, true); + gpencil_buffer_add_point(ibo, verts, cols, gps, &pts[adj_idx], v++, true); for (int i = 0; i < pts_len; i++) { - gpencil_buffer_add_point(verts, cols, gps, &pts[i], v++, false); + gpencil_buffer_add_point(ibo, verts, cols, gps, &pts[i], v++, false); } /* Draw line to first point to complete the loop for cyclic strokes. */ if (is_cyclic) { - gpencil_buffer_add_point(verts, cols, gps, &pts[0], v, false); + gpencil_buffer_add_point(ibo, verts, cols, gps, &pts[0], v, false); /* UV factor needs to be adjusted for the last point to not be equal to the UV factor of the * first point. It should be the factor of the last point plus the distance from the last point * to the first. @@ -360,16 +369,20 @@ static void gpencil_buffer_add_stroke(gpStrokeVert *verts, } /* Last adjacency point (not drawn). */ adj_idx = (is_cyclic) ? 1 : max_ii(0, pts_len - 2); - gpencil_buffer_add_point(verts, cols, gps, &pts[adj_idx], v++, true); + gpencil_buffer_add_point(ibo, verts, cols, gps, &pts[adj_idx], v++, true); } static void gpencil_buffer_add_fill(GPUIndexBufBuilder *ibo, const bGPDstroke *gps) { int tri_len = gps->tot_triangles; - int v = gps->runtime.stroke_start; + int v = gps->runtime.vertex_start + 1; for (int i = 0; i < tri_len; i++) { uint *tri = gps->triangles[i].verts; - GPU_indexbuf_add_tri_verts(ibo, v + tri[0], v + tri[1], v + tri[2]); + /* The attribute loading uses a different shader and will undo this bit packing. */ + GPU_indexbuf_add_tri_verts(ibo, + (v + tri[0]) << GP_VERTEX_ID_SHIFT, + (v + tri[1]) << GP_VERTEX_ID_SHIFT, + (v + tri[2]) << GP_VERTEX_ID_SHIFT); } } @@ -379,10 +392,10 @@ static void gpencil_stroke_iter_cb(bGPDlayer *UNUSED(gpl), void *thunk) { gpIterData *iter = (gpIterData *)thunk; - gpencil_buffer_add_stroke(iter->verts, iter->cols, gps); if (gps->tot_triangles > 0) { gpencil_buffer_add_fill(&iter->ibo, gps); } + gpencil_buffer_add_stroke(&iter->ibo, iter->verts, iter->cols, gps); } static void gpencil_object_verts_count_cb(bGPDlayer *UNUSED(gpl), @@ -391,12 +404,15 @@ static void gpencil_object_verts_count_cb(bGPDlayer *UNUSED(gpl), void *thunk) { gpIterData *iter = (gpIterData *)thunk; - - /* Store first index offset */ - gps->runtime.stroke_start = iter->vert_len; + int stroke_vert_len = gps->totpoints + gpencil_stroke_is_cyclic(gps); + gps->runtime.vertex_start = iter->vert_len; + /* Add additional padding at the start and end. */ + iter->vert_len += 1 + stroke_vert_len + 1; + /* Store first index offset. */ gps->runtime.fill_start = iter->tri_len; - iter->vert_len += gps->totpoints + 2 + gpencil_stroke_is_cyclic(gps); iter->tri_len += gps->tot_triangles; + gps->runtime.stroke_start = iter->tri_len; + iter->tri_len += stroke_vert_len * 2; } static void gpencil_batches_ensure(Object *ob, GpencilBatchCache *cache, int cfra) @@ -406,7 +422,7 @@ static void gpencil_batches_ensure(Object *ob, GpencilBatchCache *cache, int cfr if (cache->vbo == NULL) { /* Should be discarded together. */ BLI_assert(cache->vbo == NULL && cache->ibo == NULL); - BLI_assert(cache->fill_batch == NULL && cache->stroke_batch == NULL); + BLI_assert(cache->geom_batch == NULL); /* TODO/PERF: Could be changed to only do it if needed. * For now it's simpler to assume we always need it * since multiple viewport could or could not need it. @@ -415,29 +431,29 @@ static void gpencil_batches_ensure(Object *ob, GpencilBatchCache *cache, int cfr bool do_onion = true; /* First count how many vertices and triangles are needed for the whole object. */ - gpIterData iter = { - .gpd = gpd, - .verts = NULL, - .ibo = {0}, - .vert_len = 1, /* Start at 1 for the gl_InstanceID trick to work (see vert shader). */ - .tri_len = 0, - .curve_len = 0, - }; + gpIterData iter = {}; + iter.gpd = gpd; + iter.verts = NULL; + iter.ibo = {0}; + iter.vert_len = 0; + iter.tri_len = 0; + iter.curve_len = 0; BKE_gpencil_visible_stroke_advanced_iter( NULL, ob, NULL, gpencil_object_verts_count_cb, &iter, do_onion, cfra); + GPUUsageType vbo_flag = GPU_USAGE_STATIC | GPU_USAGE_FLAG_BUFFER_TEXTURE_ONLY; /* Create VBOs. */ GPUVertFormat *format = gpencil_stroke_format(); GPUVertFormat *format_col = gpencil_color_format(); - cache->vbo = GPU_vertbuf_create_with_format(format); - cache->vbo_col = GPU_vertbuf_create_with_format(format_col); + cache->vbo = GPU_vertbuf_create_with_format_ex(format, vbo_flag); + cache->vbo_col = GPU_vertbuf_create_with_format_ex(format_col, vbo_flag); /* Add extra space at the end of the buffer because of quad load. */ GPU_vertbuf_data_alloc(cache->vbo, iter.vert_len + 2); GPU_vertbuf_data_alloc(cache->vbo_col, iter.vert_len + 2); iter.verts = (gpStrokeVert *)GPU_vertbuf_get_data(cache->vbo); iter.cols = (gpColorVert *)GPU_vertbuf_get_data(cache->vbo_col); /* Create IBO. */ - GPU_indexbuf_init(&iter.ibo, GPU_PRIM_TRIS, iter.tri_len, iter.vert_len); + GPU_indexbuf_init(&iter.ibo, GPU_PRIM_TRIS, iter.tri_len, 0xFFFFFFFFu); /* Fill buffers with data. */ BKE_gpencil_visible_stroke_advanced_iter( @@ -452,33 +468,39 @@ static void gpencil_batches_ensure(Object *ob, GpencilBatchCache *cache, int cfr /* Finish the IBO. */ cache->ibo = GPU_indexbuf_build(&iter.ibo); - /* Create the batches */ - cache->fill_batch = GPU_batch_create(GPU_PRIM_TRIS, cache->vbo, cache->ibo); - GPU_batch_vertbuf_add(cache->fill_batch, cache->vbo_col); - cache->stroke_batch = GPU_batch_create(GPU_PRIM_TRI_STRIP, gpencil_dummy_buffer_get(), NULL); - GPU_batch_instbuf_add_ex(cache->stroke_batch, cache->vbo, 0); - GPU_batch_instbuf_add_ex(cache->stroke_batch, cache->vbo_col, 0); + cache->geom_batch = GPU_batch_create(GPU_PRIM_TRIS, cache->vbo, cache->ibo); + /* Allow creation of buffer texture. */ + GPU_vertbuf_use(cache->vbo); + GPU_vertbuf_use(cache->vbo_col); gpd->flag &= ~GP_DATA_CACHE_IS_DIRTY; cache->is_dirty = false; } } -GPUBatch *DRW_cache_gpencil_strokes_get(Object *ob, int cfra) +GPUBatch *DRW_cache_gpencil_get(Object *ob, int cfra) +{ + GpencilBatchCache *cache = gpencil_batch_cache_get(ob, cfra); + gpencil_batches_ensure(ob, cache, cfra); + + return cache->geom_batch; +} + +GPUVertBuf *DRW_cache_gpencil_position_buffer_get(Object *ob, int cfra) { GpencilBatchCache *cache = gpencil_batch_cache_get(ob, cfra); gpencil_batches_ensure(ob, cache, cfra); - return cache->stroke_batch; + return cache->vbo; } -GPUBatch *DRW_cache_gpencil_fills_get(Object *ob, int cfra) +GPUVertBuf *DRW_cache_gpencil_color_buffer_get(Object *ob, int cfra) { GpencilBatchCache *cache = gpencil_batch_cache_get(ob, cfra); gpencil_batches_ensure(ob, cache, cfra); - return cache->fill_batch; + return cache->vbo_col; } static void gpencil_lines_indices_cb(bGPDlayer *UNUSED(gpl), @@ -489,7 +511,7 @@ static void gpencil_lines_indices_cb(bGPDlayer *UNUSED(gpl), gpIterData *iter = (gpIterData *)thunk; int pts_len = gps->totpoints + gpencil_stroke_is_cyclic(gps); - int start = gps->runtime.stroke_start + 1; + int start = gps->runtime.vertex_start + 1; int end = start + pts_len; for (int i = start; i < end; i++) { GPU_indexbuf_add_generic_vert(&iter->ibo, i); @@ -508,10 +530,9 @@ GPUBatch *DRW_cache_gpencil_face_wireframe_get(Object *ob) if (cache->lines_batch == NULL) { GPUVertBuf *vbo = cache->vbo; - gpIterData iter = { - .gpd = ob->data, - .ibo = {0}, - }; + gpIterData iter = {}; + iter.gpd = (bGPdata *)ob->data; + iter.ibo = {0}; uint vert_len = GPU_vertbuf_get_vertex_len(vbo); GPU_indexbuf_init_ex(&iter.ibo, GPU_PRIM_LINE_STRIP, vert_len, vert_len); @@ -540,7 +561,7 @@ bGPDstroke *DRW_cache_gpencil_sbuffer_stroke_data_get(Object *ob) Brush *brush = gpd->runtime.sbuffer_brush; /* Convert the sbuffer to a bGPDstroke. */ if (gpd->runtime.sbuffer_gps == NULL) { - bGPDstroke *gps = MEM_callocN(sizeof(*gps), "bGPDstroke sbuffer"); + bGPDstroke *gps = (bGPDstroke *)MEM_callocN(sizeof(*gps), "bGPDstroke sbuffer"); gps->totpoints = gpd->runtime.sbuffer_used; gps->mat_nr = max_ii(0, gpd->runtime.matid - 1); gps->flag = gpd->runtime.sbuffer_sflag; @@ -553,7 +574,9 @@ bGPDstroke *DRW_cache_gpencil_sbuffer_stroke_data_get(Object *ob) gps->tot_triangles = max_ii(0, gpd->runtime.sbuffer_used - 2); gps->caps[0] = gps->caps[1] = GP_STROKE_CAP_ROUND; - gps->runtime.stroke_start = 1; /* Add one for the adjacency index. */ + gps->runtime.vertex_start = 0; + gps->runtime.fill_start = 0; + gps->runtime.stroke_start = 0; copy_v4_v4(gps->vert_color_fill, gpd->runtime.vert_color_fill); /* Caps. */ gps->caps[0] = gps->caps[1] = (short)brush->gpencil_settings->caps_type; @@ -563,17 +586,17 @@ bGPDstroke *DRW_cache_gpencil_sbuffer_stroke_data_get(Object *ob) return gpd->runtime.sbuffer_gps; } -static void gpencil_sbuffer_stroke_ensure(bGPdata *gpd, bool do_stroke, bool do_fill) +static void gpencil_sbuffer_stroke_ensure(bGPdata *gpd, bool do_fill) { - tGPspoint *tpoints = gpd->runtime.sbuffer; + tGPspoint *tpoints = (tGPspoint *)gpd->runtime.sbuffer; bGPDstroke *gps = gpd->runtime.sbuffer_gps; int vert_len = gpd->runtime.sbuffer_used; /* DRW_cache_gpencil_sbuffer_stroke_data_get need to have been called previously. */ BLI_assert(gps != NULL); - if (do_stroke && (gpd->runtime.sbuffer_stroke_batch == NULL)) { - gps->points = MEM_mallocN(vert_len * sizeof(*gps->points), __func__); + if (gpd->runtime.sbuffer_batch == NULL) { + gps->points = (bGPDspoint *)MEM_mallocN(vert_len * sizeof(*gps->points), __func__); const DRWContextState *draw_ctx = DRW_context_state_get(); Scene *scene = draw_ctx->scene; @@ -596,36 +619,25 @@ static void gpencil_sbuffer_stroke_ensure(bGPdata *gpd, bool do_stroke, bool do_ /* Calc uv data along the stroke. */ BKE_gpencil_stroke_uv_update(gps); + int tri_len = gps->tot_triangles + (gps->totpoints + gpencil_stroke_is_cyclic(gps)) * 2; + /* Create IBO. */ + GPUIndexBufBuilder ibo_builder; + GPU_indexbuf_init(&ibo_builder, GPU_PRIM_TRIS, tri_len, 0xFFFFFFFFu); /* Create VBO. */ + GPUUsageType vbo_flag = GPU_USAGE_STATIC | GPU_USAGE_FLAG_BUFFER_TEXTURE_ONLY; GPUVertFormat *format = gpencil_stroke_format(); GPUVertFormat *format_color = gpencil_color_format(); - GPUVertBuf *vbo = GPU_vertbuf_create_with_format(format); - GPUVertBuf *vbo_col = GPU_vertbuf_create_with_format(format_color); - /* Add extra space at the end (and start) of the buffer because of quad load and cyclic. */ - GPU_vertbuf_data_alloc(vbo, 1 + vert_len + 1 + 2); - GPU_vertbuf_data_alloc(vbo_col, 1 + vert_len + 1 + 2); + GPUVertBuf *vbo = GPU_vertbuf_create_with_format_ex(format, vbo_flag); + GPUVertBuf *vbo_col = GPU_vertbuf_create_with_format_ex(format_color, vbo_flag); + /* Add extra space at the end the buffer because of quad load and cyclic. */ + GPU_vertbuf_data_alloc(vbo, vert_len + 2); + GPU_vertbuf_data_alloc(vbo_col, vert_len + 2); gpStrokeVert *verts = (gpStrokeVert *)GPU_vertbuf_get_data(vbo); gpColorVert *cols = (gpColorVert *)GPU_vertbuf_get_data(vbo_col); - /* Fill buffers with data. */ - gpencil_buffer_add_stroke(verts, cols, gps); - - GPUBatch *batch = GPU_batch_create(GPU_PRIM_TRI_STRIP, gpencil_dummy_buffer_get(), NULL); - GPU_batch_instbuf_add_ex(batch, vbo, true); - GPU_batch_instbuf_add_ex(batch, vbo_col, true); - - gpd->runtime.sbuffer_stroke_batch = batch; - - MEM_freeN(gps->points); - } - - if (do_fill && (gpd->runtime.sbuffer_fill_batch == NULL)) { - /* Create IBO. */ - GPUIndexBufBuilder ibo_builder; - GPU_indexbuf_init(&ibo_builder, GPU_PRIM_TRIS, gps->tot_triangles, vert_len); - - if (gps->tot_triangles > 0) { - float(*tpoints2d)[2] = MEM_mallocN(sizeof(*tpoints2d) * vert_len, __func__); + /* Create fill indices. */ + if (do_fill && gps->tot_triangles > 0) { + float(*tpoints2d)[2] = (float(*)[2])MEM_mallocN(sizeof(*tpoints2d) * vert_len, __func__); /* Triangulate in 2D. */ for (int i = 0; i < vert_len; i++) { copy_v2_v2(tpoints2d[i], tpoints[i].m_xy); @@ -633,51 +645,72 @@ static void gpencil_sbuffer_stroke_ensure(bGPdata *gpd, bool do_stroke, bool do_ /* Compute directly inside the IBO data buffer. */ /* OPTI: This is a bottleneck if the stroke is very long. */ BLI_polyfill_calc(tpoints2d, (uint)vert_len, 0, (uint(*)[3])ibo_builder.data); - /* Add stroke start offset. */ + /* Add stroke start offset and shift. */ for (int i = 0; i < gps->tot_triangles * 3; i++) { - ibo_builder.data[i] += gps->runtime.stroke_start; + ibo_builder.data[i] = (ibo_builder.data[i] + 1) << GP_VERTEX_ID_SHIFT; } /* HACK since we didn't use the builder API to avoid another malloc and copy, * we need to set the number of indices manually. */ ibo_builder.index_len = gps->tot_triangles * 3; + ibo_builder.index_min = 0; + /* For this case, do not allow index compaction to avoid yet another preprocessing step. */ + ibo_builder.index_max = 0xFFFFFFFFu - 1u; + + gps->runtime.stroke_start = gps->tot_triangles; MEM_freeN(tpoints2d); } - GPUIndexBuf *ibo = GPU_indexbuf_build(&ibo_builder); - GPUVertBuf *vbo = gpd->runtime.sbuffer_stroke_batch->inst[0]; - GPUVertBuf *vbo_col = gpd->runtime.sbuffer_stroke_batch->inst[1]; + /* Fill buffers with data. */ + gpencil_buffer_add_stroke(&ibo_builder, verts, cols, gps); - GPUBatch *batch = GPU_batch_create_ex(GPU_PRIM_TRIS, vbo, ibo, GPU_BATCH_OWNS_INDEX); - GPU_batch_vertbuf_add(batch, vbo_col); + GPUBatch *batch = GPU_batch_create_ex(GPU_PRIM_TRIS, + gpencil_dummy_buffer_get(), + GPU_indexbuf_build(&ibo_builder), + GPU_BATCH_OWNS_INDEX); - gpd->runtime.sbuffer_fill_batch = batch; + gpd->runtime.sbuffer_position_buf = vbo; + gpd->runtime.sbuffer_color_buf = vbo_col; + gpd->runtime.sbuffer_batch = batch; + + MEM_freeN(gps->points); } } -GPUBatch *DRW_cache_gpencil_sbuffer_stroke_get(Object *ob) +GPUBatch *DRW_cache_gpencil_sbuffer_get(Object *ob, bool show_fill) { bGPdata *gpd = (bGPdata *)ob->data; - gpencil_sbuffer_stroke_ensure(gpd, true, false); + /* Fill batch also need stroke batch to be created (vbo is shared). */ + gpencil_sbuffer_stroke_ensure(gpd, show_fill); - return gpd->runtime.sbuffer_stroke_batch; + return gpd->runtime.sbuffer_batch; } -GPUBatch *DRW_cache_gpencil_sbuffer_fill_get(Object *ob) +GPUVertBuf *DRW_cache_gpencil_sbuffer_position_buffer_get(Object *ob, bool show_fill) { bGPdata *gpd = (bGPdata *)ob->data; /* Fill batch also need stroke batch to be created (vbo is shared). */ - gpencil_sbuffer_stroke_ensure(gpd, true, true); + gpencil_sbuffer_stroke_ensure(gpd, show_fill); - return gpd->runtime.sbuffer_fill_batch; + return gpd->runtime.sbuffer_position_buf; +} + +GPUVertBuf *DRW_cache_gpencil_sbuffer_color_buffer_get(Object *ob, bool show_fill) +{ + bGPdata *gpd = (bGPdata *)ob->data; + /* Fill batch also need stroke batch to be created (vbo is shared). */ + gpencil_sbuffer_stroke_ensure(gpd, show_fill); + + return gpd->runtime.sbuffer_color_buf; } void DRW_cache_gpencil_sbuffer_clear(Object *ob) { bGPdata *gpd = (bGPdata *)ob->data; MEM_SAFE_FREE(gpd->runtime.sbuffer_gps); - GPU_BATCH_DISCARD_SAFE(gpd->runtime.sbuffer_fill_batch); - GPU_BATCH_DISCARD_SAFE(gpd->runtime.sbuffer_stroke_batch); + GPU_BATCH_DISCARD_SAFE(gpd->runtime.sbuffer_batch); + GPU_VERTBUF_DISCARD_SAFE(gpd->runtime.sbuffer_position_buf); + GPU_VERTBUF_DISCARD_SAFE(gpd->runtime.sbuffer_color_buf); } /** \} */ @@ -728,7 +761,7 @@ static void gpencil_edit_stroke_iter_cb(bGPDlayer *gpl, { gpEditIterData *iter = (gpEditIterData *)thunk; const int v_len = gps->totpoints; - const int v = gps->runtime.stroke_start + 1; + const int v = gps->runtime.vertex_start + 1; MDeformVert *dvert = ((iter->vgindex > -1) && gps->dvert) ? gps->dvert : NULL; gpEditVert *vert_ptr = iter->verts + v; @@ -743,9 +776,12 @@ static void gpencil_edit_stroke_iter_cb(bGPDlayer *gpl, vert_ptr->weight = gpencil_point_edit_weight(dvert, i, iter->vgindex); vert_ptr++; } - /* Draw line to first point to complete the loop for cyclic strokes. */ - vert_ptr->vflag = sflag | gpencil_point_edit_flag(layer_lock, &gps->points[0], 0, v_len); - vert_ptr->weight = gpencil_point_edit_weight(dvert, 0, iter->vgindex); + + if (gpencil_stroke_is_cyclic(gps)) { + /* Draw line to first point to complete the loop for cyclic strokes. */ + vert_ptr->vflag = sflag | gpencil_point_edit_flag(layer_lock, &gps->points[0], 0, v_len); + vert_ptr->weight = gpencil_point_edit_weight(dvert, 0, iter->vgindex); + } } static void gpencil_edit_curve_stroke_count_cb(bGPDlayer *gpl, @@ -876,14 +912,13 @@ static void gpencil_edit_batches_ensure(Object *ob, GpencilBatchCache *cache, in /* Curve Handles and Points for Editing. */ if (cache->edit_curve_vbo == NULL) { - gpIterData iterdata = { - .gpd = gpd, - .verts = NULL, - .ibo = {0}, - .vert_len = 0, - .tri_len = 0, - .curve_len = 0, - }; + gpIterData iterdata = {}; + iterdata.gpd = gpd; + iterdata.verts = NULL; + iterdata.ibo = {0}; + iterdata.vert_len = 0; + iterdata.tri_len = 0; + iterdata.curve_len = 0; /* Create VBO. */ GPUVertFormat *format = gpencil_edit_curve_format(); diff --git a/source/blender/draw/intern/draw_cache_impl_mesh.cc b/source/blender/draw/intern/draw_cache_impl_mesh.cc index 5a041493a6a..5ce658abfe4 100644 --- a/source/blender/draw/intern/draw_cache_impl_mesh.cc +++ b/source/blender/draw/intern/draw_cache_impl_mesh.cc @@ -554,22 +554,13 @@ BLI_INLINE void mesh_batch_cache_add_request(MeshBatchCache *cache, DRWBatchFlag static bool mesh_batch_cache_valid(Object *object, Mesh *me) { - MeshBatchCache *cache = static_cast<MeshBatchCache *>(me->runtime.batch_cache); + MeshBatchCache *cache = static_cast<MeshBatchCache *>(me->runtime->batch_cache); if (cache == nullptr) { return false; } - if (object->sculpt && object->sculpt->pbvh) { - if (cache->pbvh_is_drawing != BKE_pbvh_is_drawing(object->sculpt->pbvh)) { - return false; - } - - if (BKE_pbvh_is_drawing(object->sculpt->pbvh) && - BKE_pbvh_draw_cache_invalid(object->sculpt->pbvh)) { - return false; - } - } + /* Note: PBVH draw data should not be checked here. */ if (cache->is_editmode != (me->edit_mesh != nullptr)) { return false; @@ -588,11 +579,11 @@ static bool mesh_batch_cache_valid(Object *object, Mesh *me) static void mesh_batch_cache_init(Object *object, Mesh *me) { - MeshBatchCache *cache = static_cast<MeshBatchCache *>(me->runtime.batch_cache); + MeshBatchCache *cache = static_cast<MeshBatchCache *>(me->runtime->batch_cache); if (!cache) { - me->runtime.batch_cache = MEM_cnew<MeshBatchCache>(__func__); - cache = static_cast<MeshBatchCache *>(me->runtime.batch_cache); + me->runtime->batch_cache = MEM_cnew<MeshBatchCache>(__func__); + cache = static_cast<MeshBatchCache *>(me->runtime->batch_cache); } else { memset(cache, 0, sizeof(*cache)); @@ -634,7 +625,7 @@ void DRW_mesh_batch_cache_validate(Object *object, Mesh *me) static MeshBatchCache *mesh_batch_cache_get(Mesh *me) { - return static_cast<MeshBatchCache *>(me->runtime.batch_cache); + return static_cast<MeshBatchCache *>(me->runtime->batch_cache); } static void mesh_batch_cache_check_vertex_group(MeshBatchCache *cache, @@ -742,7 +733,7 @@ static void mesh_batch_cache_discard_uvedit_select(MeshBatchCache *cache) void DRW_mesh_batch_cache_dirty_tag(Mesh *me, eMeshBatchDirtyMode mode) { - MeshBatchCache *cache = static_cast<MeshBatchCache *>(me->runtime.batch_cache); + MeshBatchCache *cache = static_cast<MeshBatchCache *>(me->runtime->batch_cache); if (cache == nullptr) { return; } @@ -830,7 +821,7 @@ static void mesh_batch_cache_free_subdiv_cache(MeshBatchCache *cache) static void mesh_batch_cache_clear(Mesh *me) { - MeshBatchCache *cache = static_cast<MeshBatchCache *>(me->runtime.batch_cache); + MeshBatchCache *cache = static_cast<MeshBatchCache *>(me->runtime->batch_cache); if (!cache) { return; } @@ -862,7 +853,7 @@ static void mesh_batch_cache_clear(Mesh *me) void DRW_mesh_batch_cache_free(Mesh *me) { mesh_batch_cache_clear(me); - MEM_SAFE_FREE(me->runtime.batch_cache); + MEM_SAFE_FREE(me->runtime->batch_cache); } /** \} */ @@ -1017,8 +1008,7 @@ GPUBatch **DRW_mesh_batch_cache_get_surface_shaded(Object *object, BLI_assert(gpumat_array_len == cache->mat_len); mesh_cd_layers_type_merge(&cache->cd_needed, cd_needed); - ThreadMutex *mesh_render_mutex = (ThreadMutex *)me->runtime.render_mutex; - drw_attributes_merge(&cache->attr_needed, &attrs_needed, mesh_render_mutex); + drw_attributes_merge(&cache->attr_needed, &attrs_needed, me->runtime->render_mutex); mesh_batch_cache_request_surface_batches(cache); return cache->surface_per_mat; } @@ -1046,8 +1036,7 @@ GPUBatch *DRW_mesh_batch_cache_get_surface_vertpaint(Object *object, Mesh *me) DRW_Attributes attrs_needed{}; request_active_and_default_color_attributes(*object, *me, attrs_needed); - ThreadMutex *mesh_render_mutex = (ThreadMutex *)me->runtime.render_mutex; - drw_attributes_merge(&cache->attr_needed, &attrs_needed, mesh_render_mutex); + drw_attributes_merge(&cache->attr_needed, &attrs_needed, me->runtime->render_mutex); mesh_batch_cache_request_surface_batches(cache); return cache->batch.surface; @@ -1060,8 +1049,7 @@ GPUBatch *DRW_mesh_batch_cache_get_surface_sculpt(Object *object, Mesh *me) DRW_Attributes attrs_needed{}; request_active_and_default_color_attributes(*object, *me, attrs_needed); - ThreadMutex *mesh_render_mutex = (ThreadMutex *)me->runtime.render_mutex; - drw_attributes_merge(&cache->attr_needed, &attrs_needed, mesh_render_mutex); + drw_attributes_merge(&cache->attr_needed, &attrs_needed, me->runtime->render_mutex); mesh_batch_cache_request_surface_batches(cache); return cache->batch.surface; @@ -1300,7 +1288,7 @@ GPUBatch *DRW_mesh_batch_cache_get_surface_edges(Object *object, Mesh *me) void DRW_mesh_batch_cache_free_old(Mesh *me, int ctime) { - MeshBatchCache *cache = static_cast<MeshBatchCache *>(me->runtime.batch_cache); + MeshBatchCache *cache = static_cast<MeshBatchCache *>(me->runtime->batch_cache); if (cache == nullptr) { return; @@ -1446,8 +1434,6 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph, } } - ThreadMutex *mesh_render_mutex = (ThreadMutex *)me->runtime.render_mutex; - /* Verify that all surface batches have needed attribute layers. */ /* TODO(fclem): We could be a bit smarter here and only do it per @@ -1485,12 +1471,13 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph, cache->batch_ready &= ~(MBC_SURFACE); mesh_cd_layers_type_merge(&cache->cd_used, cache->cd_needed); - drw_attributes_merge(&cache->attr_used, &cache->attr_needed, mesh_render_mutex); + drw_attributes_merge(&cache->attr_used, &cache->attr_needed, me->runtime->render_mutex); } mesh_cd_layers_type_merge(&cache->cd_used_over_time, cache->cd_needed); mesh_cd_layers_type_clear(&cache->cd_needed); - drw_attributes_merge(&cache->attr_used_over_time, &cache->attr_needed, mesh_render_mutex); + drw_attributes_merge( + &cache->attr_used_over_time, &cache->attr_needed, me->runtime->render_mutex); drw_attributes_clear(&cache->attr_needed); } @@ -1537,7 +1524,7 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph, const bool do_update_sculpt_normals = ob->sculpt && ob->sculpt->pbvh; if (do_update_sculpt_normals) { Mesh *mesh = static_cast<Mesh *>(ob->data); - BKE_pbvh_update_normals(ob->sculpt->pbvh, mesh->runtime.subdiv_ccg); + BKE_pbvh_update_normals(ob->sculpt->pbvh, mesh->runtime->subdiv_ccg); } cache->batch_ready |= batch_requested; @@ -1548,8 +1535,8 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph, Mesh *editmesh_eval_cage = BKE_object_get_editmesh_eval_cage(ob); do_cage = editmesh_eval_final != editmesh_eval_cage; - do_uvcage = !(editmesh_eval_final->runtime.is_original_bmesh && - editmesh_eval_final->runtime.wrapper_type == ME_WRAPPER_TYPE_BMESH); + do_uvcage = !(editmesh_eval_final->runtime->is_original_bmesh && + editmesh_eval_final->runtime->wrapper_type == ME_WRAPPER_TYPE_BMESH); } const bool do_subdivision = BKE_subsurf_modifier_has_gpu_subdiv(me); diff --git a/source/blender/draw/intern/draw_cache_impl_subdivision.cc b/source/blender/draw/intern/draw_cache_impl_subdivision.cc index 9f445be9750..6a9e6c126e9 100644 --- a/source/blender/draw/intern/draw_cache_impl_subdivision.cc +++ b/source/blender/draw/intern/draw_cache_impl_subdivision.cc @@ -2036,7 +2036,7 @@ static bool draw_subdiv_create_requested_buffers(Object *ob, const bool use_hide, OpenSubdiv_EvaluatorCache *evaluator_cache) { - SubsurfRuntimeData *runtime_data = mesh->runtime.subsurf_runtime_data; + SubsurfRuntimeData *runtime_data = mesh->runtime->subsurf_runtime_data; BLI_assert(runtime_data && runtime_data->has_gpu_subdiv); if (runtime_data->settings.level == 0) { diff --git a/source/blender/draw/intern/draw_cache_impl_volume.c b/source/blender/draw/intern/draw_cache_impl_volume.cc index 18a4c81514b..ac5e6fa05b9 100644 --- a/source/blender/draw/intern/draw_cache_impl_volume.c +++ b/source/blender/draw/intern/draw_cache_impl_volume.cc @@ -7,7 +7,7 @@ * \brief Volume API for render engines */ -#include <string.h> +#include <cstring> #include "MEM_guardedalloc.h" @@ -39,7 +39,7 @@ static void volume_batch_cache_clear(Volume *volume); /* ---------------------------------------------------------------------- */ /* Volume GPUBatch Cache */ -typedef struct VolumeBatchCache { +struct VolumeBatchCache { /* 3D textures */ ListBase grids; @@ -54,22 +54,22 @@ typedef struct VolumeBatchCache { /* settings to determine if cache is invalid */ bool is_dirty; -} VolumeBatchCache; +}; /* GPUBatch cache management. */ static bool volume_batch_cache_valid(Volume *volume) { - VolumeBatchCache *cache = volume->batch_cache; + VolumeBatchCache *cache = static_cast<VolumeBatchCache *>(volume->batch_cache); return (cache && cache->is_dirty == false); } static void volume_batch_cache_init(Volume *volume) { - VolumeBatchCache *cache = volume->batch_cache; + VolumeBatchCache *cache = static_cast<VolumeBatchCache *>(volume->batch_cache); if (!cache) { - cache = volume->batch_cache = MEM_callocN(sizeof(*cache), __func__); + volume->batch_cache = cache = MEM_cnew<VolumeBatchCache>(__func__); } else { memset(cache, 0, sizeof(*cache)); @@ -89,13 +89,13 @@ void DRW_volume_batch_cache_validate(Volume *volume) static VolumeBatchCache *volume_batch_cache_get(Volume *volume) { DRW_volume_batch_cache_validate(volume); - return volume->batch_cache; + return static_cast<VolumeBatchCache *>(volume->batch_cache); } void DRW_volume_batch_cache_dirty_tag(Volume *volume, int mode) { - VolumeBatchCache *cache = volume->batch_cache; - if (cache == NULL) { + VolumeBatchCache *cache = static_cast<VolumeBatchCache *>(volume->batch_cache); + if (cache == nullptr) { return; } switch (mode) { @@ -109,7 +109,7 @@ void DRW_volume_batch_cache_dirty_tag(Volume *volume, int mode) static void volume_batch_cache_clear(Volume *volume) { - VolumeBatchCache *cache = volume->batch_cache; + VolumeBatchCache *cache = static_cast<VolumeBatchCache *>(volume->batch_cache); if (!cache) { return; } @@ -130,18 +130,18 @@ void DRW_volume_batch_cache_free(Volume *volume) volume_batch_cache_clear(volume); MEM_SAFE_FREE(volume->batch_cache); } -typedef struct VolumeWireframeUserData { +struct VolumeWireframeUserData { Volume *volume; Scene *scene; -} VolumeWireframeUserData; +}; static void drw_volume_wireframe_cb( void *userdata, const float (*verts)[3], const int (*edges)[2], int totvert, int totedge) { - VolumeWireframeUserData *data = userdata; + VolumeWireframeUserData *data = static_cast<VolumeWireframeUserData *>(userdata); Scene *scene = data->scene; Volume *volume = data->volume; - VolumeBatchCache *cache = volume->batch_cache; + VolumeBatchCache *cache = static_cast<VolumeBatchCache *>(volume->batch_cache); const bool do_hq_normals = (scene->r.perf_flag & SCE_PERF_HQ_NORMALS) != 0 || GPU_use_hq_normals_workaround(); @@ -181,7 +181,7 @@ static void drw_volume_wireframe_cb( if (volume->display.wireframe_type == VOLUME_WIREFRAME_POINTS) { /* Create batch. */ cache->face_wire.batch = GPU_batch_create( - GPU_PRIM_POINTS, cache->face_wire.pos_nor_in_order, NULL); + GPU_PRIM_POINTS, cache->face_wire.pos_nor_in_order, nullptr); } else { /* Create edge index buffer. */ @@ -203,15 +203,15 @@ static void drw_volume_wireframe_cb( GPUBatch *DRW_volume_batch_cache_get_wireframes_face(Volume *volume) { if (volume->display.wireframe_type == VOLUME_WIREFRAME_NONE) { - return NULL; + return nullptr; } VolumeBatchCache *cache = volume_batch_cache_get(volume); - if (cache->face_wire.batch == NULL) { + if (cache->face_wire.batch == nullptr) { const VolumeGrid *volume_grid = BKE_volume_grid_active_get_for_read(volume); - if (volume_grid == NULL) { - return NULL; + if (volume_grid == nullptr) { + return nullptr; } /* Create wireframe from OpenVDB tree. */ @@ -228,8 +228,8 @@ GPUBatch *DRW_volume_batch_cache_get_wireframes_face(Volume *volume) static void drw_volume_selection_surface_cb( void *userdata, float (*verts)[3], int (*tris)[3], int totvert, int tottris) { - Volume *volume = userdata; - VolumeBatchCache *cache = volume->batch_cache; + Volume *volume = static_cast<Volume *>(userdata); + VolumeBatchCache *cache = static_cast<VolumeBatchCache *>(volume->batch_cache); static GPUVertFormat format = {0}; static uint pos_id; @@ -257,10 +257,10 @@ static void drw_volume_selection_surface_cb( GPUBatch *DRW_volume_batch_cache_get_selection_surface(Volume *volume) { VolumeBatchCache *cache = volume_batch_cache_get(volume); - if (cache->selection_surface == NULL) { + if (cache->selection_surface == nullptr) { const VolumeGrid *volume_grid = BKE_volume_grid_active_get_for_read(volume); - if (volume_grid == NULL) { - return NULL; + if (volume_grid == nullptr) { + return nullptr; } BKE_volume_grid_selection_surface( volume, volume_grid, drw_volume_selection_surface_cb, volume); @@ -275,15 +275,14 @@ static DRWVolumeGrid *volume_grid_cache_get(const Volume *volume, const char *name = BKE_volume_grid_name(grid); /* Return cached grid. */ - DRWVolumeGrid *cache_grid; - for (cache_grid = cache->grids.first; cache_grid; cache_grid = cache_grid->next) { + LISTBASE_FOREACH (DRWVolumeGrid *, cache_grid, &cache->grids) { if (STREQ(cache_grid->name, name)) { return cache_grid; } } /* Allocate new grid. */ - cache_grid = MEM_callocN(sizeof(DRWVolumeGrid), __func__); + DRWVolumeGrid *cache_grid = MEM_cnew<DRWVolumeGrid>(__func__); cache_grid->name = BLI_strdup(name); BLI_addtail(&cache->grids, cache_grid); @@ -316,7 +315,7 @@ static DRWVolumeGrid *volume_grid_cache_get(const Volume *volume, dense_grid.voxels); /* The texture can be null if the resolution along one axis is larger than * GL_MAX_3D_TEXTURE_SIZE. */ - if (cache_grid->texture != NULL) { + if (cache_grid->texture != nullptr) { GPU_texture_swizzle_set(cache_grid->texture, (channels == 3) ? "rgb1" : "rrr1"); GPU_texture_wrap_mode(cache_grid->texture, false, false); BKE_volume_dense_float_grid_clear(&dense_grid); @@ -339,7 +338,7 @@ DRWVolumeGrid *DRW_volume_batch_cache_get_grid(Volume *volume, const VolumeGrid { VolumeBatchCache *cache = volume_batch_cache_get(volume); DRWVolumeGrid *grid = volume_grid_cache_get(volume, volume_grid, cache); - return (grid->texture != NULL) ? grid : NULL; + return (grid->texture != nullptr) ? grid : nullptr; } int DRW_volume_material_count_get(Volume *volume) diff --git a/source/blender/draw/intern/draw_curves.cc b/source/blender/draw/intern/draw_curves.cc index a61769e7a63..8847e3f6016 100644 --- a/source/blender/draw/intern/draw_curves.cc +++ b/source/blender/draw/intern/draw_curves.cc @@ -394,6 +394,11 @@ DRWShadingGroup *DRW_shgroup_curves_create_sub(Object *object, DRW_shgroup_uniform_float_copy(shgrp, "hairRadRoot", hair_rad_root); DRW_shgroup_uniform_float_copy(shgrp, "hairRadTip", hair_rad_tip); DRW_shgroup_uniform_bool_copy(shgrp, "hairCloseTip", hair_close_tip); + if (gpu_material) { + /* \note: This needs to happen before the drawcall to allow correct attribute extraction. + * (see T101896) */ + DRW_shgroup_add_material_resources(shgrp, gpu_material); + } /* TODO(fclem): Until we have a better way to cull the curves and render with orco, bypass * culling test. */ GPUBatch *geom = curves_cache->final[subdiv].proc_hairs[thickness_res - 1]; diff --git a/source/blender/draw/intern/draw_debug.cc b/source/blender/draw/intern/draw_debug.cc index a78dc59cb7e..55de779d85c 100644 --- a/source/blender/draw/intern/draw_debug.cc +++ b/source/blender/draw/intern/draw_debug.cc @@ -558,7 +558,7 @@ void DebugDraw::display_prints() int slot = GPU_shader_get_builtin_ssbo(shader, GPU_STORAGE_BUFFER_DEBUG_PRINT); float f_viewport[4]; GPU_viewport_size_get_f(f_viewport); - GPU_shader_uniform_4fv(shader, "viewport_size", f_viewport); + GPU_shader_uniform_2fv(shader, "viewport_size", f_viewport); if (gpu_print_buf_used) { GPU_debug_group_begin("GPU"); diff --git a/source/blender/draw/intern/draw_hair.cc b/source/blender/draw/intern/draw_hair.cc index 4e44967e5e9..281e58ea230 100644 --- a/source/blender/draw/intern/draw_hair.cc +++ b/source/blender/draw/intern/draw_hair.cc @@ -293,6 +293,11 @@ DRWShadingGroup *DRW_shgroup_hair_create_sub(Object *object, DRW_shgroup_uniform_float_copy(shgrp, "hairRadRoot", hair_rad_root); DRW_shgroup_uniform_float_copy(shgrp, "hairRadTip", hair_rad_tip); DRW_shgroup_uniform_bool_copy(shgrp, "hairCloseTip", hair_close_tip); + if (gpu_material) { + /* \note: This needs to happen before the drawcall to allow correct attribute extraction. + * (see T101896) */ + DRW_shgroup_add_material_resources(shgrp, gpu_material); + } /* TODO(fclem): Until we have a better way to cull the hair and render with orco, bypass * culling test. */ GPUBatch *geom = hair_cache->final[subdiv].proc_hairs[thickness_res - 1]; diff --git a/source/blender/draw/intern/draw_manager.c b/source/blender/draw/intern/draw_manager.c index 28abbc6ab71..b9a9780e651 100644 --- a/source/blender/draw/intern/draw_manager.c +++ b/source/blender/draw/intern/draw_manager.c @@ -422,7 +422,8 @@ static void draw_prune_vlattrs(DRWData *drw_data) /* Forget known attributes after they are unused for a few frames. */ LISTBASE_FOREACH_MUTABLE (GPULayerAttr *, attr, &drw_data->vlattrs_name_list) { if (++attr->users > 10) { - BLI_ghash_remove(drw_data->vlattrs_name_cache, (void *)attr->hash_code, NULL, NULL); + BLI_ghash_remove( + drw_data->vlattrs_name_cache, POINTER_FROM_UINT(attr->hash_code), NULL, NULL); BLI_freelinkN(&drw_data->vlattrs_name_list, attr); } } @@ -1958,20 +1959,6 @@ void DRW_render_gpencil(struct RenderEngine *engine, struct Depsgraph *depsgraph DST.buffer_finish_called = false; } -/* Callback function for RE_engine_update_render_passes to ensure all - * render passes are registered. */ -static void draw_render_result_ensure_pass_cb(void *user_data, - struct Scene *UNUSED(scene), - struct ViewLayer *view_layer, - const char *name, - int channels, - const char *chanid, - eNodeSocketDatatype UNUSED(type)) -{ - RenderEngine *engine = user_data; - RE_engine_add_pass(engine, name, channels, chanid, view_layer->name); -} - void DRW_render_to_image(RenderEngine *engine, struct Depsgraph *depsgraph) { Scene *scene = DEG_get_evaluated_scene(depsgraph); @@ -2022,10 +2009,6 @@ void DRW_render_to_image(RenderEngine *engine, struct Depsgraph *depsgraph) /* set default viewport */ GPU_viewport(0, 0, size[0], size[1]); - /* Update the render passes. This needs to be done before acquiring the render result. */ - RE_engine_update_render_passes( - engine, scene, view_layer, draw_render_result_ensure_pass_cb, engine); - /* Init render result. */ RenderResult *render_result = RE_engine_begin_result(engine, 0, diff --git a/source/blender/draw/intern/draw_manager.h b/source/blender/draw/intern/draw_manager.h index ce316a31cf9..52129049269 100644 --- a/source/blender/draw/intern/draw_manager.h +++ b/source/blender/draw/intern/draw_manager.h @@ -697,7 +697,7 @@ void drw_uniform_attrs_pool_update(struct GHash *table, struct Object *dupli_parent, struct DupliObject *dupli_source); -GPUUniformBuf *drw_ensure_layer_attribute_buffer(); +GPUUniformBuf *drw_ensure_layer_attribute_buffer(void); double *drw_engine_data_cache_time_get(GPUViewport *viewport); void *drw_engine_data_engine_data_create(GPUViewport *viewport, void *engine_type); diff --git a/source/blender/draw/intern/draw_manager_data.cc b/source/blender/draw/intern/draw_manager_data.cc index 030b13177f1..b9e0db71122 100644 --- a/source/blender/draw/intern/draw_manager_data.cc +++ b/source/blender/draw/intern/draw_manager_data.cc @@ -589,7 +589,7 @@ void DRW_shgroup_buffer_texture(DRWShadingGroup *shgroup, const char *name, GPUVertBuf *vertex_buffer) { - int location = GPU_shader_get_ssbo(shgroup->shader, name); + int location = GPU_shader_get_texture_binding(shgroup->shader, name); if (location == -1) { return; } @@ -606,7 +606,7 @@ void DRW_shgroup_buffer_texture_ref(DRWShadingGroup *shgroup, const char *name, GPUVertBuf **vertex_buffer) { - int location = GPU_shader_get_ssbo(shgroup->shader, name); + int location = GPU_shader_get_texture_binding(shgroup->shader, name); if (location == -1) { return; } @@ -1347,7 +1347,7 @@ static void drw_sculpt_generate_calls(DRWSculptCallbackData *scd) } Mesh *mesh = static_cast<Mesh *>(scd->ob->data); - BKE_pbvh_update_normals(pbvh, mesh->runtime.subdiv_ccg); + BKE_pbvh_update_normals(pbvh, mesh->runtime->subdiv_ccg); BKE_pbvh_draw_cb(pbvh, update_only_visible, @@ -1714,23 +1714,32 @@ static void drw_shgroup_init(DRWShadingGroup *shgroup, GPUShader *shader) } #ifdef DEBUG - int debug_print_location = GPU_shader_get_builtin_ssbo(shader, GPU_STORAGE_BUFFER_DEBUG_PRINT); - if (debug_print_location != -1) { - GPUStorageBuf *buf = drw_debug_gpu_print_buf_get(); - drw_shgroup_uniform_create_ex( - shgroup, debug_print_location, DRW_UNIFORM_STORAGE_BLOCK, buf, GPU_SAMPLER_DEFAULT, 0, 1); + /* TODO(Metal): Support Shader debug print. + * This is not currently supported by Metal Backend. */ + if (GPU_backend_get_type() != GPU_BACKEND_METAL) { + int debug_print_location = GPU_shader_get_builtin_ssbo(shader, GPU_STORAGE_BUFFER_DEBUG_PRINT); + if (debug_print_location != -1) { + GPUStorageBuf *buf = drw_debug_gpu_print_buf_get(); + drw_shgroup_uniform_create_ex(shgroup, + debug_print_location, + DRW_UNIFORM_STORAGE_BLOCK, + buf, + GPU_SAMPLER_DEFAULT, + 0, + 1); # ifndef DISABLE_DEBUG_SHADER_PRINT_BARRIER - /* Add a barrier to allow multiple shader writing to the same buffer. */ - DRW_shgroup_barrier(shgroup, GPU_BARRIER_SHADER_STORAGE); + /* Add a barrier to allow multiple shader writing to the same buffer. */ + DRW_shgroup_barrier(shgroup, GPU_BARRIER_SHADER_STORAGE); # endif - } + } - int debug_draw_location = GPU_shader_get_builtin_ssbo(shader, GPU_STORAGE_BUFFER_DEBUG_VERTS); - if (debug_draw_location != -1) { - GPUStorageBuf *buf = drw_debug_gpu_draw_buf_get(); - drw_shgroup_uniform_create_ex( - shgroup, debug_draw_location, DRW_UNIFORM_STORAGE_BLOCK, buf, GPU_SAMPLER_DEFAULT, 0, 1); - /* NOTE(fclem): No barrier as ordering is not important. */ + int debug_draw_location = GPU_shader_get_builtin_ssbo(shader, GPU_STORAGE_BUFFER_DEBUG_VERTS); + if (debug_draw_location != -1) { + GPUStorageBuf *buf = drw_debug_gpu_draw_buf_get(); + drw_shgroup_uniform_create_ex( + shgroup, debug_draw_location, DRW_UNIFORM_STORAGE_BLOCK, buf, GPU_SAMPLER_DEFAULT, 0, 1); + /* NOTE(fclem): No barrier as ordering is not important. */ + } } #endif diff --git a/source/blender/draw/intern/draw_manager_shader.c b/source/blender/draw/intern/draw_manager_shader.c index 8b287e116bc..40b05dff51f 100644 --- a/source/blender/draw/intern/draw_manager_shader.c +++ b/source/blender/draw/intern/draw_manager_shader.c @@ -252,7 +252,7 @@ static void drw_register_shader_vlattrs(GPUMaterial *mat) GPULayerAttr **p_val; /* Add to the table and list if newly seen. */ - if (!BLI_ghash_ensure_p(hash, (void *)attr->hash_code, (void ***)&p_val)) { + if (!BLI_ghash_ensure_p(hash, POINTER_FROM_UINT(attr->hash_code), (void ***)&p_val)) { DST.vmempool->vlattrs_ubo_ready = false; GPULayerAttr *new_link = *p_val = MEM_dupallocN(attr); diff --git a/source/blender/draw/intern/draw_manager_text.cc b/source/blender/draw/intern/draw_manager_text.cc index d41127c3641..100ef528bc8 100644 --- a/source/blender/draw/intern/draw_manager_text.cc +++ b/source/blender/draw/intern/draw_manager_text.cc @@ -15,6 +15,7 @@ #include "BKE_editmesh.h" #include "BKE_editmesh_cache.h" #include "BKE_global.h" +#include "BKE_mesh.h" #include "BKE_unit.h" #include "DNA_mesh_types.h" @@ -233,8 +234,8 @@ void DRW_text_edit_mesh_measure_stats(ARegion *region, float clip_planes[4][4]; /* allow for displaying shape keys and deform mods */ BMIter iter; - const float(*vert_coords)[3] = (me->runtime.edit_data ? me->runtime.edit_data->vertexCos : - nullptr); + const float(*vert_coords)[3] = (me->runtime->edit_data ? me->runtime->edit_data->vertexCos : + nullptr); const bool use_coords = (vert_coords != nullptr); /* when 2 or more edge-info options are enabled, space apart */ @@ -339,8 +340,8 @@ void DRW_text_edit_mesh_measure_stats(ARegion *region, const float(*poly_normals)[3] = nullptr; if (use_coords) { BM_mesh_elem_index_ensure(em->bm, BM_VERT | BM_FACE); - BKE_editmesh_cache_ensure_poly_normals(em, me->runtime.edit_data); - poly_normals = me->runtime.edit_data->polyNos; + BKE_editmesh_cache_ensure_poly_normals(em, me->runtime->edit_data); + poly_normals = me->runtime->edit_data->polyNos; } BM_ITER_MESH (eed, &iter, em->bm, BM_EDGES_OF_MESH) { diff --git a/source/blender/draw/intern/draw_pass.hh b/source/blender/draw/intern/draw_pass.hh index ee2180712d1..24dfdd1b97b 100644 --- a/source/blender/draw/intern/draw_pass.hh +++ b/source/blender/draw/intern/draw_pass.hh @@ -174,9 +174,15 @@ class PassBase { /** * Reminders: - * - (compare_mask & reference) is what is tested against (compare_mask & stencil_value) + * - `compare_mask & reference` is what is tested against `compare_mask & stencil_value` * stencil_value being the value stored in the stencil buffer. - * - (write-mask & reference) is what gets written if the test condition is fulfilled. + * - `write-mask & reference` is what gets written if the test condition is fulfilled. + * + * This will modify the stencil state until another call to this function. + * If not specified before any draw-call, these states will be undefined. + * + * For more information see: + * https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkStencilOpState.html */ void state_stencil(uint8_t write_mask, uint8_t reference, uint8_t compare_mask); @@ -728,7 +734,7 @@ template<class T> inline void PassBase<T>::state_set(DRWState state) template<class T> inline void PassBase<T>::state_stencil(uint8_t write_mask, uint8_t reference, uint8_t compare_mask) { - create_command(Type::StencilSet).stencil_set = {write_mask, reference, compare_mask}; + create_command(Type::StencilSet).stencil_set = {write_mask, compare_mask, reference}; } template<class T> inline void PassBase<T>::shader_set(GPUShader *shader) diff --git a/source/blender/draw/intern/draw_pbvh.cc b/source/blender/draw/intern/draw_pbvh.cc index cab260f87ac..38fb6d55245 100644 --- a/source/blender/draw/intern/draw_pbvh.cc +++ b/source/blender/draw/intern/draw_pbvh.cc @@ -389,12 +389,19 @@ struct PBVHBatches { break; case CD_PBVH_MASK_TYPE: - foreach_grids([&](int /*x*/, int /*y*/, int /*grid_index*/, CCGElem *elems[4], int i) { - float *mask = CCG_elem_mask(&args->ccg_key, elems[i]); + if (args->ccg_key.has_mask) { + foreach_grids([&](int /*x*/, int /*y*/, int /*grid_index*/, CCGElem *elems[4], int i) { + float *mask = CCG_elem_mask(&args->ccg_key, elems[i]); - *static_cast<uchar *>(GPU_vertbuf_raw_step(&access)) = mask ? uchar(*mask * 255.0f) : - 255; - }); + *static_cast<uchar *>(GPU_vertbuf_raw_step(&access)) = uchar(*mask * 255.0f); + }); + } + else { + foreach_grids( + [&](int /*x*/, int /*y*/, int /*grid_index*/, CCGElem * /*elems*/[4], int /*i*/) { + *static_cast<uchar *>(GPU_vertbuf_raw_step(&access)) = 0; + }); + } break; case CD_PBVH_FSET_TYPE: { @@ -1158,8 +1165,17 @@ struct PBVHBatches { } for (PBVHBatch &batch : batches.values()) { - GPU_batch_elembuf_set(batch.tris, tri_index, false); - GPU_batch_elembuf_set(batch.lines, lines_index, false); + if (tri_index) { + GPU_batch_elembuf_set(batch.tris, tri_index, false); + } + else { + /* Still flag the batch as dirty even if we're using the default index layout. */ + batch.tris->flag |= GPU_BATCH_DIRTY; + } + + if (lines_index) { + GPU_batch_elembuf_set(batch.lines, lines_index, false); + } } } diff --git a/source/blender/draw/intern/draw_view.hh b/source/blender/draw/intern/draw_view.hh index 4fc74e3e890..94fb62508bb 100644 --- a/source/blender/draw/intern/draw_view.hh +++ b/source/blender/draw/intern/draw_view.hh @@ -79,6 +79,26 @@ class View { return -(data_.winmat[3][2] + 1.0f) / data_.winmat[2][2]; } + const float4x4 &viewmat() const + { + return data_.viewmat; + } + + const float4x4 &viewinv() const + { + return data_.viewinv; + } + + const float4x4 &winmat() const + { + return data_.winmat; + } + + const float4x4 &wininv() const + { + return data_.wininv; + } + private: /** Called from draw manager. */ void bind(); diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh.hh b/source/blender/draw/intern/mesh_extractors/extract_mesh.hh index d9bb8d1d2b4..c6230e2695e 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh.hh +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh.hh @@ -16,6 +16,8 @@ #include "BKE_customdata.h" #include "BKE_editmesh.h" +#include "BKE_editmesh_cache.h" +#include "BKE_mesh.h" #include "draw_cache_extract.hh" @@ -116,7 +118,7 @@ BLI_INLINE const Mesh *editmesh_final_or_this(const Object *object, const Mesh * BLI_INLINE const CustomData *mesh_cd_ldata_get_from_mesh(const Mesh *me) { - switch ((eMeshWrapperType)me->runtime.wrapper_type) { + switch (me->runtime->wrapper_type) { case ME_WRAPPER_TYPE_SUBD: case ME_WRAPPER_TYPE_MDATA: return &me->ldata; @@ -132,7 +134,7 @@ BLI_INLINE const CustomData *mesh_cd_ldata_get_from_mesh(const Mesh *me) BLI_INLINE const CustomData *mesh_cd_pdata_get_from_mesh(const Mesh *me) { - switch ((eMeshWrapperType)me->runtime.wrapper_type) { + switch (me->runtime->wrapper_type) { case ME_WRAPPER_TYPE_SUBD: case ME_WRAPPER_TYPE_MDATA: return &me->pdata; @@ -148,7 +150,7 @@ BLI_INLINE const CustomData *mesh_cd_pdata_get_from_mesh(const Mesh *me) BLI_INLINE const CustomData *mesh_cd_edata_get_from_mesh(const Mesh *me) { - switch ((eMeshWrapperType)me->runtime.wrapper_type) { + switch (me->runtime->wrapper_type) { case ME_WRAPPER_TYPE_SUBD: case ME_WRAPPER_TYPE_MDATA: return &me->edata; @@ -164,7 +166,7 @@ BLI_INLINE const CustomData *mesh_cd_edata_get_from_mesh(const Mesh *me) BLI_INLINE const CustomData *mesh_cd_vdata_get_from_mesh(const Mesh *me) { - switch ((eMeshWrapperType)me->runtime.wrapper_type) { + switch (me->runtime->wrapper_type) { case ME_WRAPPER_TYPE_SUBD: case ME_WRAPPER_TYPE_MDATA: return &me->vdata; diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_edituv.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_edituv.cc index be919b1af67..e40503a9707 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_edituv.cc +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_edituv.cc @@ -557,7 +557,7 @@ static void extract_edituv_fdots_iter_poly_mesh(const MeshRenderData *mr, const bool mp_select = (efa) ? BM_elem_flag_test_bool(efa, BM_ELEM_SELECT) : false; if (mr->use_subsurf_fdots) { - const BLI_bitmap *facedot_tags = mr->me->runtime.subsurf_face_dot_tags; + const BLI_bitmap *facedot_tags = mr->me->runtime->subsurf_face_dot_tags; const MLoop *mloop = mr->mloop; const int ml_index_end = mp->loopstart + mp->totloop; diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_fdots.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_fdots.cc index d964f608e52..1b552b01d6b 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_fdots.cc +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_fdots.cc @@ -46,7 +46,7 @@ static void extract_fdots_iter_poly_mesh(const MeshRenderData *mr, GPUIndexBufBuilder *elb = static_cast<GPUIndexBufBuilder *>(_userdata); if (mr->use_subsurf_fdots) { - const BLI_bitmap *facedot_tags = mr->me->runtime.subsurf_face_dot_tags; + const BLI_bitmap *facedot_tags = mr->me->runtime->subsurf_face_dot_tags; const MLoop *mloop = mr->mloop; const int ml_index_end = mp->loopstart + mp->totloop; diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_fdots_pos.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_fdots_pos.cc index 6d93a482623..d43eb6117df 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_fdots_pos.cc +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_fdots_pos.cc @@ -77,7 +77,7 @@ static void extract_fdots_pos_iter_poly_mesh(const MeshRenderData *mr, const MVert *mvert = mr->mvert; const MLoop *mloop = mr->mloop; - const BLI_bitmap *facedot_tags = mr->me->runtime.subsurf_face_dot_tags; + const BLI_bitmap *facedot_tags = mr->me->runtime->subsurf_face_dot_tags; const int ml_index_end = mp->loopstart + mp->totloop; for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_fdots_uv.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_fdots_uv.cc index 96240af2ee6..802f000cb43 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_fdots_uv.cc +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_fdots_uv.cc @@ -74,7 +74,7 @@ static void extract_fdots_uv_iter_poly_mesh(const MeshRenderData *mr, void *_data) { MeshExtract_FdotUV_Data *data = static_cast<MeshExtract_FdotUV_Data *>(_data); - const BLI_bitmap *facedot_tags = mr->me->runtime.subsurf_face_dot_tags; + const BLI_bitmap *facedot_tags = mr->me->runtime->subsurf_face_dot_tags; const MLoop *mloop = mr->mloop; const int ml_index_end = mp->loopstart + mp->totloop; diff --git a/source/blender/draw/intern/shaders/common_gpencil_lib.glsl b/source/blender/draw/intern/shaders/common_gpencil_lib.glsl index 123c493b572..def841b07aa 100644 --- a/source/blender/draw/intern/shaders/common_gpencil_lib.glsl +++ b/source/blender/draw/intern/shaders/common_gpencil_lib.glsl @@ -90,10 +90,15 @@ float gpencil_clamp_small_stroke_thickness(float thickness, vec4 ndc_pos) #ifdef GPU_VERTEX_SHADER -/* Trick to detect if a drawcall is stroke or fill. - * This does mean that we need to draw an empty stroke segment before starting - * to draw the real stroke segments. */ -# define GPENCIL_IS_STROKE_VERTEX (gl_InstanceID != 0) +int gpencil_stroke_point_id() +{ + return (gl_VertexID & ~GP_IS_STROKE_VERTEX_BIT) >> GP_VERTEX_ID_SHIFT; +} + +bool gpencil_is_stroke_vertex() +{ + return flag_test(gl_VertexID, GP_IS_STROKE_VERTEX_BIT); +} /** * Returns value of gl_Position. @@ -120,20 +125,7 @@ float gpencil_clamp_small_stroke_thickness(float thickness, vec4 ndc_pos) * WARNING: Max attribute count is actually 14 because OSX OpenGL implementation * considers gl_VertexID and gl_InstanceID as vertex attribute. (see T74536) */ -vec4 gpencil_vertex(ivec4 ma, - ivec4 ma1, - ivec4 ma2, - ivec4 ma3, - vec4 pos, - vec4 pos1, - vec4 pos2, - vec4 pos3, - vec4 uv1, - vec4 uv2, - vec4 col1, - vec4 col2, - vec4 fcol1, - vec4 viewport_size, +vec4 gpencil_vertex(vec4 viewport_size, gpMaterialFlag material_flags, vec2 alignment_rot, /* World Position. */ @@ -155,6 +147,24 @@ vec4 gpencil_vertex(ivec4 ma, /* Stroke hardness. */ out float out_hardness) { + int stroke_point_id = (gl_VertexID & ~GP_IS_STROKE_VERTEX_BIT) >> GP_VERTEX_ID_SHIFT; + + /* Attribute Loading. */ + vec4 pos = texelFetch(gp_pos_tx, (stroke_point_id - 1) * 3 + 0); + vec4 pos1 = texelFetch(gp_pos_tx, (stroke_point_id + 0) * 3 + 0); + vec4 pos2 = texelFetch(gp_pos_tx, (stroke_point_id + 1) * 3 + 0); + vec4 pos3 = texelFetch(gp_pos_tx, (stroke_point_id + 2) * 3 + 0); + ivec4 ma = floatBitsToInt(texelFetch(gp_pos_tx, (stroke_point_id - 1) * 3 + 1)); + ivec4 ma1 = floatBitsToInt(texelFetch(gp_pos_tx, (stroke_point_id + 0) * 3 + 1)); + ivec4 ma2 = floatBitsToInt(texelFetch(gp_pos_tx, (stroke_point_id + 1) * 3 + 1)); + ivec4 ma3 = floatBitsToInt(texelFetch(gp_pos_tx, (stroke_point_id + 2) * 3 + 1)); + vec4 uv1 = texelFetch(gp_pos_tx, (stroke_point_id + 0) * 3 + 2); + vec4 uv2 = texelFetch(gp_pos_tx, (stroke_point_id + 1) * 3 + 2); + + vec4 col1 = texelFetch(gp_col_tx, (stroke_point_id + 0) * 2 + 0); + vec4 col2 = texelFetch(gp_col_tx, (stroke_point_id + 1) * 2 + 0); + vec4 fcol1 = texelFetch(gp_col_tx, (stroke_point_id + 0) * 2 + 1); + # define thickness1 pos1.w # define thickness2 pos2.w # define strength1 uv1.w @@ -167,7 +177,7 @@ vec4 gpencil_vertex(ivec4 ma, vec4 out_ndc; - if (GPENCIL_IS_STROKE_VERTEX) { + if (gpencil_is_stroke_vertex()) { bool is_dot = flag_test(material_flags, GP_STROKE_ALIGNMENT); bool is_squares = !flag_test(material_flags, GP_STROKE_DOTS); @@ -177,13 +187,6 @@ vec4 gpencil_vertex(ivec4 ma, is_squares = false; } - /* Endpoints, we discard the vertices. */ - if (ma1.x == -1 || (!is_dot && ma2.x == -1)) { - /* We set the vertex at the camera origin to generate 0 fragments. */ - out_ndc = vec4(0.0, 0.0, -3e36, 0.0); - return out_ndc; - } - /* Avoid using a vertex attribute for quad positioning. */ float x = float(gl_VertexID & 1) * 2.0 - 1.0; /* [-1..1] */ float y = float(gl_VertexID & 2) - 1.0; /* [-1..1] */ @@ -336,8 +339,7 @@ vec4 gpencil_vertex(ivec4 ma, out_N = safe_normalize(N); /* Decode fill opacity. */ - out_color = vec4(fcol1.rgb, floor(fcol1.a / 10.0)); - out_color.a /= 10000.0; + out_color = vec4(fcol1.rgb, floor(fcol1.a / 10.0) / 10000.0); /* We still offset the fills a little to avoid overlaps */ out_ndc.z += 0.000002; @@ -355,20 +357,7 @@ vec4 gpencil_vertex(ivec4 ma, return out_ndc; } -vec4 gpencil_vertex(ivec4 ma, - ivec4 ma1, - ivec4 ma2, - ivec4 ma3, - vec4 pos, - vec4 pos1, - vec4 pos2, - vec4 pos3, - vec4 uv1, - vec4 uv2, - vec4 col1, - vec4 col2, - vec4 fcol1, - vec4 viewport_size, +vec4 gpencil_vertex(vec4 viewport_size, out vec3 out_P, out vec3 out_N, out vec4 out_color, @@ -379,20 +368,7 @@ vec4 gpencil_vertex(ivec4 ma, out vec2 out_thickness, out float out_hardness) { - return gpencil_vertex(ma, - ma1, - ma2, - ma3, - pos, - pos1, - pos2, - pos3, - uv1, - uv2, - col1, - col2, - fcol1, - viewport_size, + return gpencil_vertex(viewport_size, 0u, vec2(1.0, 0.0), out_P, diff --git a/source/blender/draw/intern/shaders/draw_view_info.hh b/source/blender/draw/intern/shaders/draw_view_info.hh index 7b500f66a68..114dbab799f 100644 --- a/source/blender/draw/intern/shaders/draw_view_info.hh +++ b/source/blender/draw/intern/shaders/draw_view_info.hh @@ -122,26 +122,15 @@ GPU_SHADER_CREATE_INFO(draw_volume).additional_info("draw_modelmat", "draw_resou GPU_SHADER_CREATE_INFO(draw_gpencil) .typedef_source("gpencil_shader_shared.h") .define("DRW_GPENCIL_INFO") - .vertex_in(0, Type::IVEC4, "ma") - .vertex_in(1, Type::IVEC4, "ma1") - .vertex_in(2, Type::IVEC4, "ma2") - .vertex_in(3, Type::IVEC4, "ma3") - .vertex_in(4, Type::VEC4, "pos") - .vertex_in(5, Type::VEC4, "pos1") - .vertex_in(6, Type::VEC4, "pos2") - .vertex_in(7, Type::VEC4, "pos3") - .vertex_in(8, Type::VEC4, "uv1") - .vertex_in(9, Type::VEC4, "uv2") - .vertex_in(10, Type::VEC4, "col1") - .vertex_in(11, Type::VEC4, "col2") - .vertex_in(12, Type::VEC4, "fcol1") + .sampler(0, ImageType::FLOAT_BUFFER, "gp_pos_tx") + .sampler(1, ImageType::FLOAT_BUFFER, "gp_col_tx") /* Per Object */ .push_constant(Type::FLOAT, "gpThicknessScale") /* TODO(fclem): Replace with object info. */ .push_constant(Type::FLOAT, "gpThicknessWorldScale") /* TODO(fclem): Same as above. */ .define("gpThicknessIsScreenSpace", "(gpThicknessWorldScale < 0.0)") /* Per Layer */ .push_constant(Type::FLOAT, "gpThicknessOffset") - .additional_info("draw_modelmat", "draw_resource_id_uniform", "draw_object_infos"); + .additional_info("draw_modelmat", "draw_object_infos"); /** \} */ diff --git a/source/blender/editors/armature/pose_transform.c b/source/blender/editors/armature/pose_transform.c index 2a23615caa3..9ed963f0c66 100644 --- a/source/blender/editors/armature/pose_transform.c +++ b/source/blender/editors/armature/pose_transform.c @@ -788,7 +788,7 @@ static int pose_copy_exec(bContext *C, wmOperator *op) * existing on its own. */ BKE_copybuffer_copy_tag_ID(&ob_copy.id); - BLI_join_dirfile(str, sizeof(str), BKE_tempdir_base(), "copybuffer_pose.blend"); + BLI_path_join(str, sizeof(str), BKE_tempdir_base(), "copybuffer_pose.blend"); BKE_copybuffer_copy_end(temp_bmain, str, op->reports); /* We clear the lists so no datablocks gets freed, * This is required because objects in temp bmain shares same pointers @@ -844,7 +844,7 @@ static int pose_paste_exec(bContext *C, wmOperator *op) Main *tmp_bmain = BKE_main_new(); STRNCPY(tmp_bmain->filepath, BKE_main_blendfile_path_from_global()); - BLI_join_dirfile(str, sizeof(str), BKE_tempdir_base(), "copybuffer_pose.blend"); + BLI_path_join(str, sizeof(str), BKE_tempdir_base(), "copybuffer_pose.blend"); if (!BKE_copybuffer_read(tmp_bmain, str, op->reports, FILTER_ID_OB)) { BKE_report(op->reports, RPT_ERROR, "Copy buffer is empty"); BKE_main_free(tmp_bmain); diff --git a/source/blender/editors/asset/intern/asset_list.cc b/source/blender/editors/asset/intern/asset_list.cc index b0ff5c86520..7fdb924d769 100644 --- a/source/blender/editors/asset/intern/asset_list.cc +++ b/source/blender/editors/asset/intern/asset_list.cc @@ -488,7 +488,7 @@ std::string ED_assetlist_asset_filepath_get(const bContext *C, const char *asset_relpath = asset_handle.file_data->relpath; char path[FILE_MAX_LIBEXTRA]; - BLI_join_dirfile(path, sizeof(path), library_path, asset_relpath); + BLI_path_join(path, sizeof(path), library_path, asset_relpath); return path; } diff --git a/source/blender/editors/asset/intern/asset_ops.cc b/source/blender/editors/asset/intern/asset_ops.cc index 08259090e0c..d1c46a8259f 100644 --- a/source/blender/editors/asset/intern/asset_ops.cc +++ b/source/blender/editors/asset/intern/asset_ops.cc @@ -860,7 +860,7 @@ static bool set_filepath_for_asset_lib(const Main *bmain, struct wmOperator *op) } char file_path[PATH_MAX]; - BLI_join_dirfile(file_path, sizeof(file_path), lib->path, blend_filename); + BLI_path_join(file_path, sizeof(file_path), lib->path, blend_filename); RNA_string_set(op->ptr, "filepath", file_path); return true; diff --git a/source/blender/editors/gpencil/annotate_paint.c b/source/blender/editors/gpencil/annotate_paint.c index 677bf1bb392..2fd58a9cee0 100644 --- a/source/blender/editors/gpencil/annotate_paint.c +++ b/source/blender/editors/gpencil/annotate_paint.c @@ -13,9 +13,6 @@ #include "MEM_guardedalloc.h" -#include "BLI_blenlib.h" -#include "BLI_math.h" -#include "BLI_math_geom.h" #include "BLI_utildefines.h" #include "BLT_translation.h" diff --git a/source/blender/editors/gpencil/gpencil_data.c b/source/blender/editors/gpencil/gpencil_data.c index 340288b2d74..c6d7eb294fb 100644 --- a/source/blender/editors/gpencil/gpencil_data.c +++ b/source/blender/editors/gpencil/gpencil_data.c @@ -623,6 +623,7 @@ void GPENCIL_OT_layer_duplicate_object(wmOperatorType *ot) true, "Only Active", "Copy only active Layer, uncheck to append all layers"); + RNA_def_property_translation_context(prop, BLT_I18NCONTEXT_ID_GPENCIL); RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); } @@ -3686,6 +3687,7 @@ void GPENCIL_OT_materials_copy_to_object(wmOperatorType *ot) true, "Only Active", "Append only active material, uncheck to append all materials"); + RNA_def_property_translation_context(prop, BLT_I18NCONTEXT_ID_GPENCIL); RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); } diff --git a/source/blender/editors/include/ED_node.h b/source/blender/editors/include/ED_node.h index c30b8c5ec6a..24e14fdce72 100644 --- a/source/blender/editors/include/ED_node.h +++ b/source/blender/editors/include/ED_node.h @@ -87,17 +87,6 @@ void ED_node_tag_update_id(struct ID *id); float ED_node_grid_size(void); -/* node_relationships.cc */ - -/** - * Test == 0, clear all intersect flags. - */ -void ED_node_link_intersect_test(struct ScrArea *area, int test); -/** - * Assumes link with #NODE_LINKFLAG_HILITE set. - */ -void ED_node_link_insert(struct Main *bmain, struct ScrArea *area); - /* node_edit.cc */ void ED_node_set_tree_type(struct SpaceNode *snode, struct bNodeTreeType *typeinfo); @@ -186,3 +175,20 @@ bool ED_space_node_color_sample(struct Main *bmain, #ifdef __cplusplus } #endif + +#ifdef __cplusplus + +/* node_relationships.cc */ + +namespace blender::ed::space_node { + +void node_insert_on_link_flags_set(SpaceNode &snode, const ARegion ®ion); +/** + * Assumes link with #NODE_LINKFLAG_HILITE set. + */ +void node_insert_on_link_flags(Main &bmain, SpaceNode &snode); +void node_insert_on_link_flags_clear(bNodeTree &node_tree); + +} // namespace blender::ed::space_node + +#endif
\ No newline at end of file diff --git a/source/blender/editors/include/ED_uvedit.h b/source/blender/editors/include/ED_uvedit.h index b499ae0ce59..b97cd6a9099 100644 --- a/source/blender/editors/include/ED_uvedit.h +++ b/source/blender/editors/include/ED_uvedit.h @@ -339,12 +339,22 @@ bool ED_uvedit_udim_params_from_image_space(const struct SpaceImage *sima, bool use_active, struct UVMapUDIM_Params *udim_params); +typedef enum { + ED_UVPACK_MARGIN_SCALED = 0, /* Use scale of existing UVs to multiply margin. */ + ED_UVPACK_MARGIN_ADD, /* Just add the margin, ignoring any UV scale. */ + ED_UVPACK_MARGIN_FRACTION, /* Specify a precise fraction of final UV output. */ +} eUVPackIsland_MarginMethod; + +/** See also #UnwrapOptions. */ struct UVPackIsland_Params { uint rotate : 1; uint only_selected_uvs : 1; uint only_selected_faces : 1; uint use_seams : 1; uint correct_aspect : 1; + bool ignore_pinned; /* Ignore islands which have any pinned UVs. */ + eUVPackIsland_MarginMethod margin_method; /* Which formula to use when scaling island margin. */ + float margin; /* Additional space to add around each island. */ }; /** @@ -353,9 +363,24 @@ struct UVPackIsland_Params { bool uv_coords_isect_udim(const struct Image *image, const int udim_grid[2], const float coords[2]); + +/** + * Pack UV islands from multiple objects. + * + * \param scene: Scene containing the objects to be packed. + * \param objects: Array of Objects to pack. + * \param objects_len: Length of `objects` array. + * \param bmesh_override: BMesh array aligned with `objects`. + * Optional, when non-null this overrides object's BMesh. + * This is needed to perform UV packing on objects that aren't in edit-mode. + * \param udim_params: Parameters to specify UDIM target and UDIM source image. + * \param params: Parameters and options to pass to the packing engine. + * + */ void ED_uvedit_pack_islands_multi(const struct Scene *scene, Object **objects, uint objects_len, + struct BMesh **bmesh_override, const struct UVMapUDIM_Params *udim_params, const struct UVPackIsland_Params *params); diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h index 2a1941f0d9e..7e9422ff867 100644 --- a/source/blender/editors/include/UI_interface.h +++ b/source/blender/editors/include/UI_interface.h @@ -1683,6 +1683,7 @@ int UI_search_items_find_index(uiSearchItems *items, const char *name); * Adds a hint to the button which draws right aligned, grayed out and never clipped. */ void UI_but_hint_drawstr_set(uiBut *but, const char *string); +void UI_but_icon_indicator_number_set(uiBut *but, const int indicator_number); void UI_but_node_link_set(uiBut *but, struct bNodeSocket *socket, const float draw_color[4]); @@ -2788,7 +2789,8 @@ typedef struct uiPropertySplitWrapper { uiPropertySplitWrapper uiItemPropertySplitWrapperCreate(uiLayout *parent_layout); void uiItemL(uiLayout *layout, const char *name, int icon); /* label */ -void uiItemL_ex(uiLayout *layout, const char *name, int icon, bool highlight, bool redalert); +struct uiBut *uiItemL_ex( + uiLayout *layout, const char *name, int icon, bool highlight, bool redalert); /** * Helper to add a label and creates a property split layout if needed. */ diff --git a/source/blender/editors/include/UI_interface.hh b/source/blender/editors/include/UI_interface.hh index 6c756984203..fc03b0218c0 100644 --- a/source/blender/editors/include/UI_interface.hh +++ b/source/blender/editors/include/UI_interface.hh @@ -35,6 +35,7 @@ struct ContextPathItem { std::string name; /* #BIFIconID */ int icon; + int icon_indicator_number; }; void context_path_add_generic(Vector<ContextPathItem> &path, diff --git a/source/blender/editors/include/UI_interface_icons.h b/source/blender/editors/include/UI_interface_icons.h index a1a98a4b08c..9669e242dac 100644 --- a/source/blender/editors/include/UI_interface_icons.h +++ b/source/blender/editors/include/UI_interface_icons.h @@ -27,6 +27,12 @@ typedef struct IconFile { int index; } IconFile; +typedef struct IconTextOverlay { + char text[5]; +} IconTextOverlay; + +#define UI_NO_ICON_OVERLAY_TEXT NULL + #define ICON_DEFAULT_HEIGHT 16 #define ICON_DEFAULT_WIDTH 16 @@ -105,7 +111,8 @@ void UI_icon_draw_ex(float x, float alpha, float desaturate, const uchar mono_color[4], - bool mono_border); + bool mono_border, + const struct IconTextOverlay *text_overlay); void UI_icons_free(void); void UI_icons_free_drawinfo(void *drawinfo); @@ -124,6 +131,9 @@ int UI_icon_from_library(const struct ID *id); int UI_icon_from_object_mode(int mode); int UI_icon_color_from_collection(const struct Collection *collection); +void UI_icon_text_overlay_init_from_count(struct IconTextOverlay *text_overlay, + const int icon_indicator_number); + #ifdef __cplusplus } #endif diff --git a/source/blender/editors/include/UI_view2d.h b/source/blender/editors/include/UI_view2d.h index c357b67722d..f0bf04ed408 100644 --- a/source/blender/editors/include/UI_view2d.h +++ b/source/blender/editors/include/UI_view2d.h @@ -103,7 +103,7 @@ enum eView2D_CommonViewTypes { /** \} */ /* -------------------------------------------------------------------- */ -/** \name Foeard Declarations +/** \name Forward Declarations * \{ */ struct View2D; diff --git a/source/blender/editors/interface/interface.cc b/source/blender/editors/interface/interface.cc index 422fc34aa50..1f88d25af2b 100644 --- a/source/blender/editors/interface/interface.cc +++ b/source/blender/editors/interface/interface.cc @@ -6452,6 +6452,11 @@ void UI_but_hint_drawstr_set(uiBut *but, const char *string) ui_but_add_shortcut(but, string, false); } +void UI_but_icon_indicator_number_set(uiBut *but, const int indicator_number) +{ + UI_icon_text_overlay_init_from_count(&but->icon_overlay_text, indicator_number); +} + void UI_but_node_link_set(uiBut *but, bNodeSocket *socket, const float draw_color[4]) { but->flag |= UI_BUT_NODE_LINK; diff --git a/source/blender/editors/interface/interface_context_path.cc b/source/blender/editors/interface/interface_context_path.cc index e8f552e26a3..91b2f9613de 100644 --- a/source/blender/editors/interface/interface_context_path.cc +++ b/source/blender/editors/interface/interface_context_path.cc @@ -17,6 +17,8 @@ #include "UI_interface.hh" #include "UI_resources.h" +#include "RNA_prototypes.h" + #include "WM_api.h" namespace blender::ui { @@ -41,7 +43,13 @@ void context_path_add_generic(Vector<ContextPathItem> &path, static_cast<BIFIconID>(RNA_struct_ui_icon(rna_ptr.type)) : icon_override; - path.append({name, int(icon)}); + if (&rna_type == &RNA_NodeTree) { + ID *id = (ID *)ptr; + path.append({name, int(icon), id->us}); + } + else { + path.append({name, int(icon), 1}); + } } /* -------------------------------------------------------------------- */ @@ -60,7 +68,9 @@ void template_breadcrumbs(uiLayout &layout, Span<ContextPathItem> context_path) if (i > 0) { uiItemL(sub_row, "", ICON_RIGHTARROW_THIN); } - uiItemL(sub_row, context_path[i].name.c_str(), context_path[i].icon); + uiBut *but = uiItemL_ex( + sub_row, context_path[i].name.c_str(), context_path[i].icon, false, false); + UI_but_icon_indicator_number_set(but, context_path[i].icon_indicator_number); } } diff --git a/source/blender/editors/interface/interface_dropboxes.cc b/source/blender/editors/interface/interface_dropboxes.cc index ebfde05f516..60e1c0abfa1 100644 --- a/source/blender/editors/interface/interface_dropboxes.cc +++ b/source/blender/editors/interface/interface_dropboxes.cc @@ -89,7 +89,7 @@ static void ui_drop_material_copy(bContext * /*C*/, wmDrag *drag, wmDropBox *dro static char *ui_drop_material_tooltip(bContext *C, wmDrag *drag, - const int UNUSED(xy[2]), + const int /*xy*/[2], struct wmDropBox * /*drop*/) { PointerRNA rna_ptr = CTX_data_pointer_get_type(C, "object", &RNA_Object); diff --git a/source/blender/editors/interface/interface_icons.c b/source/blender/editors/interface/interface_icons.c index c080dce0f08..9a4f98ebcd6 100644 --- a/source/blender/editors/interface/interface_icons.c +++ b/source/blender/editors/interface/interface_icons.c @@ -415,8 +415,15 @@ static void vicon_collection_color_draw( const float aspect = (float)ICON_DEFAULT_WIDTH / (float)w; - UI_icon_draw_ex( - x, y, ICON_OUTLINER_COLLECTION, aspect, 1.0f, 0.0f, collection_color->color, true); + UI_icon_draw_ex(x, + y, + ICON_OUTLINER_COLLECTION, + aspect, + 1.0f, + 0.0f, + collection_color->color, + true, + UI_NO_ICON_OVERLAY_TEXT); } # define DEF_ICON_COLLECTION_COLOR_DRAW(index, color) \ @@ -444,7 +451,8 @@ static void vicon_strip_color_draw( const float aspect = (float)ICON_DEFAULT_WIDTH / (float)w; - UI_icon_draw_ex(x, y, ICON_SNAP_FACE, aspect, 1.0f, 0.0f, strip_color->color, true); + UI_icon_draw_ex( + x, y, ICON_SNAP_FACE, aspect, 1.0f, 0.0f, strip_color->color, true, UI_NO_ICON_OVERLAY_TEXT); } # define DEF_ICON_STRIP_COLOR_DRAW(index, color) \ @@ -472,8 +480,15 @@ static void vicon_strip_color_draw_library_data_indirect( { const float aspect = (float)ICON_DEFAULT_WIDTH / (float)w; - UI_icon_draw_ex( - x, y, ICON_LIBRARY_DATA_DIRECT, aspect, ICON_INDIRECT_DATA_ALPHA * alpha, 0.0f, NULL, false); + UI_icon_draw_ex(x, + y, + ICON_LIBRARY_DATA_DIRECT, + aspect, + ICON_INDIRECT_DATA_ALPHA * alpha, + 0.0f, + NULL, + false, + UI_NO_ICON_OVERLAY_TEXT); } static void vicon_strip_color_draw_library_data_override_noneditable( @@ -488,7 +503,8 @@ static void vicon_strip_color_draw_library_data_override_noneditable( ICON_INDIRECT_DATA_ALPHA * alpha * 0.75f, 0.0f, NULL, - false); + false, + UI_NO_ICON_OVERLAY_TEXT); } /* Dynamically render icon instead of rendering a plain color to a texture/buffer @@ -923,7 +939,7 @@ static void init_internal_icons(void) char iconfilestr[FILE_MAX]; if (icondir) { - BLI_join_dirfile(iconfilestr, sizeof(iconfilestr), icondir, btheme->tui.iconfile); + BLI_path_join(iconfilestr, sizeof(iconfilestr), icondir, btheme->tui.iconfile); /* if the image is missing bbuf will just be NULL */ bbuf = IMB_loadiffname(iconfilestr, IB_rect, NULL); @@ -1047,7 +1063,7 @@ static void init_iconfile_list(struct ListBase *list) /* check to see if the image is the right size, continue if not */ /* copying strings here should go ok, assuming that we never get back * a complete path to file longer than 256 chars */ - BLI_join_dirfile(iconfilestr, sizeof(iconfilestr), icondir, filename); + BLI_path_join(iconfilestr, sizeof(iconfilestr), icondir, filename); bbuf = IMB_loadiffname(iconfilestr, IB_rect); if (bbuf) { @@ -1716,9 +1732,47 @@ static void icon_draw_texture(float x, int ih, float alpha, const float rgb[3], - bool with_border) -{ - if (g_icon_draw_cache.enabled) { + bool with_border, + const IconTextOverlay *text_overlay) +{ + const float zoom_factor = w / UI_DPI_ICON_SIZE; + float text_width = 0.0f; + + /* No need to show if too zoomed out, otherwise it just adds noise. */ + const bool show_indicator = (text_overlay && text_overlay->text[0] != '\0') && + (zoom_factor > 0.7f); + + if (show_indicator) { + /* Handle the little numbers on top of the icon. */ + uchar text_color[4]; + UI_GetThemeColor3ubv(TH_TEXT, text_color); + text_color[3] = 255; + + uiFontStyle fstyle_small = *UI_FSTYLE_WIDGET; + fstyle_small.points *= zoom_factor; + fstyle_small.points *= 0.8f; + + rcti text_rect = { + .xmax = x + UI_UNIT_X * zoom_factor, + .xmin = x, + .ymax = y, + .ymin = y, + }; + + UI_fontstyle_draw(&fstyle_small, + &text_rect, + text_overlay->text, + sizeof(text_overlay->text), + text_color, + &(struct uiFontStyleDraw_Params){ + .align = UI_STYLE_TEXT_RIGHT, + }); + text_width = (float)UI_fontstyle_string_width(&fstyle_small, text_overlay->text) / UI_UNIT_X / + zoom_factor; + } + + /* Draw the actual icon. */ + if (!show_indicator && g_icon_draw_cache.enabled) { icon_draw_texture_cached(x, y, w, h, ix, iy, iw, ih, alpha, rgb, with_border); return; } @@ -1735,7 +1789,7 @@ static void icon_draw_texture(float x, GPUTexture *texture = with_border ? icongltex.tex[1] : icongltex.tex[0]; - GPUShader *shader = GPU_shader_get_builtin_shader(GPU_SHADER_2D_IMAGE_RECT_COLOR); + GPUShader *shader = GPU_shader_get_builtin_shader(GPU_SHADER_ICON); GPU_shader_bind(shader); const int img_binding = GPU_shader_get_texture_binding(shader, "image"); @@ -1752,6 +1806,7 @@ static void icon_draw_texture(float x, GPU_shader_uniform_vector(shader, rect_tex_loc, 4, 1, (float[4]){x1, y1, x2, y2}); GPU_shader_uniform_vector(shader, rect_geom_loc, 4, 1, (float[4]){x, y, x + w, y + h}); + GPU_shader_uniform_1f(shader, "text_width", text_width); GPU_texture_bind_ex(texture, GPU_SAMPLER_ICON, img_binding, false); @@ -1786,7 +1841,8 @@ static void icon_draw_size(float x, int draw_size, const float desaturate, const uchar mono_rgba[4], - const bool mono_border) + const bool mono_border, + const IconTextOverlay *text_overlay) { bTheme *btheme = UI_GetTheme(); const float fdraw_size = (float)draw_size; @@ -1874,7 +1930,8 @@ static void icon_draw_size(float x, di->data.texture.h, alpha, NULL, - false); + false, + text_overlay); } else if (di->type == ICON_TYPE_MONO_TEXTURE) { /* Monochrome icon that uses text or theme color. */ @@ -1908,7 +1965,8 @@ static void icon_draw_size(float x, di->data.texture.h + 2 * border_texel, color[3], color, - with_border); + with_border, + text_overlay); } else if (di->type == ICON_TYPE_BUFFER) { @@ -2425,17 +2483,27 @@ int UI_icon_color_from_collection(const Collection *collection) void UI_icon_draw(float x, float y, int icon_id) { - UI_icon_draw_ex(x, y, icon_id, U.inv_dpi_fac, 1.0f, 0.0f, NULL, false); + UI_icon_draw_ex(x, y, icon_id, U.inv_dpi_fac, 1.0f, 0.0f, NULL, false, UI_NO_ICON_OVERLAY_TEXT); } void UI_icon_draw_alpha(float x, float y, int icon_id, float alpha) { - UI_icon_draw_ex(x, y, icon_id, U.inv_dpi_fac, alpha, 0.0f, NULL, false); + UI_icon_draw_ex(x, y, icon_id, U.inv_dpi_fac, alpha, 0.0f, NULL, false, UI_NO_ICON_OVERLAY_TEXT); } void UI_icon_draw_preview(float x, float y, int icon_id, float aspect, float alpha, int size) { - icon_draw_size(x, y, icon_id, aspect, alpha, ICON_SIZE_PREVIEW, size, false, NULL, false); + icon_draw_size(x, + y, + icon_id, + aspect, + alpha, + ICON_SIZE_PREVIEW, + size, + false, + NULL, + false, + UI_NO_ICON_OVERLAY_TEXT); } void UI_icon_draw_ex(float x, @@ -2445,7 +2513,8 @@ void UI_icon_draw_ex(float x, float alpha, float desaturate, const uchar mono_color[4], - const bool mono_border) + const bool mono_border, + const IconTextOverlay *text_overlay) { const int draw_size = get_draw_size(ICON_SIZE_ICON); icon_draw_size(x, @@ -2457,7 +2526,19 @@ void UI_icon_draw_ex(float x, draw_size, desaturate, mono_color, - mono_border); + mono_border, + text_overlay); +} + +void UI_icon_text_overlay_init_from_count(IconTextOverlay *text_overlay, + const int icon_indicator_number) +{ + /* The icon indicator is used as an aggregator, no need to show if it is 1. */ + if (icon_indicator_number < 2) { + text_overlay->text[0] = '\0'; + return; + } + BLI_str_format_integer_unit(text_overlay->text, icon_indicator_number); } /* ********** Alert Icons ********** */ diff --git a/source/blender/editors/interface/interface_intern.h b/source/blender/editors/interface/interface_intern.h index 6ef7d346418..6ef81ad897e 100644 --- a/source/blender/editors/interface/interface_intern.h +++ b/source/blender/editors/interface/interface_intern.h @@ -23,6 +23,7 @@ struct ARegion; struct AnimationEvalContext; struct CurveMapping; struct CurveProfile; +struct IconTextOverlay; struct ID; struct ImBuf; struct Main; @@ -275,6 +276,9 @@ struct uiBut { uiButPushedStateFunc pushed_state_func; const void *pushed_state_arg; + /** Little indicator (e.g., counter) displayed on top of some icons. */ + struct IconTextOverlay icon_overlay_text; + /* pointer back */ uiBlock *block; }; diff --git a/source/blender/editors/interface/interface_layout.c b/source/blender/editors/interface/interface_layout.c index c906a5b36f1..496f72c089a 100644 --- a/source/blender/editors/interface/interface_layout.c +++ b/source/blender/editors/interface/interface_layout.c @@ -3235,7 +3235,7 @@ static uiBut *uiItemL_(uiLayout *layout, const char *name, int icon) return but; } -void uiItemL_ex( +uiBut *uiItemL_ex( uiLayout *layout, const char *name, int icon, const bool highlight, const bool redalert) { uiBut *but = uiItemL_(layout, name, icon); @@ -3248,6 +3248,8 @@ void uiItemL_ex( if (redalert) { UI_but_flag_enable(but, UI_BUT_REDALERT); } + + return but; } void uiItemL(uiLayout *layout, const char *name, int icon) diff --git a/source/blender/editors/interface/interface_ops.cc b/source/blender/editors/interface/interface_ops.cc index 8b29f1075b8..e089642963d 100644 --- a/source/blender/editors/interface/interface_ops.cc +++ b/source/blender/editors/interface/interface_ops.cc @@ -1859,7 +1859,7 @@ static void edittranslation_find_po_file(const char *root, /* First, full lang code. */ BLI_snprintf(tstr, sizeof(tstr), "%s.po", uilng); - BLI_join_dirfile(path, maxlen, root, uilng); + BLI_path_join(path, maxlen, root, uilng); BLI_path_append(path, maxlen, tstr); if (BLI_is_file(path)) { return; @@ -1885,7 +1885,7 @@ static void edittranslation_find_po_file(const char *root, BLI_strncpy(tstr + szt, tc, sizeof(tstr) - szt); } - BLI_join_dirfile(path, maxlen, root, tstr); + BLI_path_join(path, maxlen, root, tstr); strcat(tstr, ".po"); BLI_path_append(path, maxlen, tstr); if (BLI_is_file(path)) { diff --git a/source/blender/editors/interface/interface_panel.cc b/source/blender/editors/interface/interface_panel.cc index 7a69e2f9b2f..24d8281aad8 100644 --- a/source/blender/editors/interface/interface_panel.cc +++ b/source/blender/editors/interface/interface_panel.cc @@ -1112,7 +1112,8 @@ static void panel_draw_aligned_widgets(const uiStyle *style, 0.7f, 0.0f, title_color, - false); + false, + UI_NO_ICON_OVERLAY_TEXT); GPU_blend(GPU_BLEND_NONE); } @@ -1140,7 +1141,8 @@ static void panel_draw_aligned_widgets(const uiStyle *style, 1.0f, 0.0f, title_color, - false); + false, + UI_NO_ICON_OVERLAY_TEXT); GPU_blend(GPU_BLEND_NONE); } diff --git a/source/blender/editors/interface/interface_region_menu_pie.cc b/source/blender/editors/interface/interface_region_menu_pie.cc index 9dd6f95e190..f443dd43a3a 100644 --- a/source/blender/editors/interface/interface_region_menu_pie.cc +++ b/source/blender/editors/interface/interface_region_menu_pie.cc @@ -222,7 +222,7 @@ int UI_pie_menu_invoke(struct bContext *C, const char *idname, const wmEvent *ev return (OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH); } - pie = UI_pie_menu_begin(C, IFACE_(mt->label), ICON_NONE, event); + pie = UI_pie_menu_begin(C, CTX_IFACE_(mt->translation_context, mt->label), ICON_NONE, event); layout = UI_pie_menu_layout(pie); UI_menutype_draw(C, mt, layout); diff --git a/source/blender/editors/interface/interface_region_menu_popup.cc b/source/blender/editors/interface/interface_region_menu_popup.cc index f88cabb2b70..569f657a544 100644 --- a/source/blender/editors/interface/interface_region_menu_popup.cc +++ b/source/blender/editors/interface/interface_region_menu_popup.cc @@ -175,11 +175,7 @@ struct uiPopupMenu { static uiBlock *ui_block_func_POPUP(bContext *C, uiPopupBlockHandle *handle, void *arg_pup) { - uiBlock *block; uiPopupMenu *pup = static_cast<uiPopupMenu *>(arg_pup); - int minwidth, width, height; - char direction; - bool flip; if (pup->menu_func) { pup->block->handle = handle; @@ -188,6 +184,7 @@ static uiBlock *ui_block_func_POPUP(bContext *C, uiPopupBlockHandle *handle, voi } /* Find block minimum width. */ + int minwidth; if (uiLayoutGetUnitsX(pup->layout) != 0.0f) { /* Use the minimum width from the layout if it's set. */ minwidth = uiLayoutGetUnitsX(pup->layout) * UI_UNIT_X; @@ -207,6 +204,7 @@ static uiBlock *ui_block_func_POPUP(bContext *C, uiPopupBlockHandle *handle, voi } /* Find block direction. */ + char direction; if (pup->but) { if (pup->block->direction != 0) { /* allow overriding the direction from menu_func */ @@ -220,9 +218,9 @@ static uiBlock *ui_block_func_POPUP(bContext *C, uiPopupBlockHandle *handle, voi direction = UI_DIR_DOWN; } - flip = (direction == UI_DIR_DOWN); + bool flip = (direction == UI_DIR_DOWN); - block = pup->block; + uiBlock *block = pup->block; /* in some cases we create the block before the region, * so we set it delayed here if necessary */ @@ -232,6 +230,7 @@ static uiBlock *ui_block_func_POPUP(bContext *C, uiPopupBlockHandle *handle, voi block->direction = direction; + int width, height; UI_block_layout_resolve(block, &width, &height); UI_block_flag_enable(block, UI_BLOCK_MOVEMOUSE_QUIT); @@ -318,7 +317,6 @@ uiPopupBlockHandle *ui_popup_menu_create( { wmWindow *window = CTX_wm_window(C); const uiStyle *style = UI_style_get_dpi(); - uiPopupBlockHandle *handle; uiPopupMenu *pup = MEM_cnew<uiPopupMenu>(__func__); pup->block = UI_block_begin(C, nullptr, __func__, UI_EMBOSS_PULLDOWN); @@ -357,7 +355,8 @@ uiPopupBlockHandle *ui_popup_menu_create( pup->menu_func = menu_func; pup->menu_arg = arg; - handle = ui_popup_block_create(C, butregion, but, nullptr, ui_block_func_POPUP, pup, nullptr); + uiPopupBlockHandle *handle = ui_popup_block_create( + C, butregion, but, nullptr, ui_block_func_POPUP, pup, nullptr); if (!but) { handle->popup = true; @@ -384,7 +383,6 @@ uiPopupMenu *UI_popup_menu_begin_ex(bContext *C, { const uiStyle *style = UI_style_get_dpi(); uiPopupMenu *pup = MEM_cnew<uiPopupMenu>(__func__); - uiBut *but; pup->block = UI_block_begin(C, nullptr, block_name, UI_EMBOSS_PULLDOWN); pup->block->flag |= UI_BLOCK_POPUP_MEMORY | UI_BLOCK_IS_FLIP; @@ -423,7 +421,7 @@ uiPopupMenu *UI_popup_menu_begin_ex(bContext *C, ""); } else { - but = uiDefBut( + uiBut *but = uiDefBut( pup->block, UI_BTYPE_LABEL, 0, title, 0, 0, 200, UI_UNIT_Y, nullptr, 0.0, 0.0, 0, 0, ""); but->drawflag = UI_BUT_TEXT_LEFT; } @@ -448,20 +446,20 @@ void UI_popup_menu_but_set(uiPopupMenu *pup, struct ARegion *butregion, uiBut *b void UI_popup_menu_end(bContext *C, uiPopupMenu *pup) { wmWindow *window = CTX_wm_window(C); - uiPopupBlockHandle *menu; - uiBut *but = nullptr; - ARegion *butregion = nullptr; pup->popup = true; pup->mx = window->eventstate->xy[0]; pup->my = window->eventstate->xy[1]; + uiBut *but = nullptr; + ARegion *butregion = nullptr; if (pup->but) { but = pup->but; butregion = pup->butregion; } - menu = ui_popup_block_create(C, butregion, but, nullptr, ui_block_func_POPUP, pup, nullptr); + uiPopupBlockHandle *menu = ui_popup_block_create( + C, butregion, but, nullptr, ui_block_func_POPUP, pup, nullptr); menu->popup = true; UI_popup_handlers_add(C, &window->modalhandlers, menu, 0); @@ -545,8 +543,6 @@ void UI_popup_menu_reports(bContext *C, ReportList *reports) int UI_popup_menu_invoke(bContext *C, const char *idname, ReportList *reports) { - uiPopupMenu *pup; - uiLayout *layout; MenuType *mt = WM_menutype_find(idname, true); if (mt == nullptr) { @@ -559,8 +555,9 @@ int UI_popup_menu_invoke(bContext *C, const char *idname, ReportList *reports) return (OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH); } - pup = UI_popup_menu_begin(C, IFACE_(mt->label), ICON_NONE); - layout = UI_popup_menu_layout(pup); + uiPopupMenu *pup = UI_popup_menu_begin( + C, CTX_IFACE_(mt->translation_context, mt->label), ICON_NONE); + uiLayout *layout = UI_popup_menu_layout(pup); UI_menutype_draw(C, mt, layout); @@ -579,9 +576,9 @@ void UI_popup_block_invoke_ex( bContext *C, uiBlockCreateFunc func, void *arg, uiFreeArgFunc arg_free, bool can_refresh) { wmWindow *window = CTX_wm_window(C); - uiPopupBlockHandle *handle; - handle = ui_popup_block_create(C, nullptr, nullptr, func, nullptr, arg, arg_free); + uiPopupBlockHandle *handle = ui_popup_block_create( + C, nullptr, nullptr, func, nullptr, arg, arg_free); handle->popup = true; /* It can be useful to disable refresh (even though it will work) @@ -607,9 +604,9 @@ void UI_popup_block_ex(bContext *C, wmOperator *op) { wmWindow *window = CTX_wm_window(C); - uiPopupBlockHandle *handle; - handle = ui_popup_block_create(C, nullptr, nullptr, func, nullptr, arg, nullptr); + uiPopupBlockHandle *handle = ui_popup_block_create( + C, nullptr, nullptr, func, nullptr, arg, nullptr); handle->popup = true; handle->retvalue = 1; handle->can_refresh = true; diff --git a/source/blender/editors/interface/interface_region_popup.cc b/source/blender/editors/interface/interface_region_popup.cc index e93bc4c4bfe..e574cb30b23 100644 --- a/source/blender/editors/interface/interface_region_popup.cc +++ b/source/blender/editors/interface/interface_region_popup.cc @@ -69,7 +69,6 @@ static void ui_popup_block_position(wmWindow *window, /* Compute button position in window coordinates using the source * button region/block, to position the popup attached to it. */ rctf butrct; - if (!handle->refresh) { ui_block_to_window_rctf(butregion, but->block, &butrct, &but->rect); @@ -417,14 +416,13 @@ static void ui_popup_block_clip(wmWindow *window, uiBlock *block) { const float xmin_orig = block->rect.xmin; const int margin = UI_SCREEN_MARGIN; - int winx, winy; if (block->flag & UI_BLOCK_NO_WIN_CLIP) { return; } - winx = WM_window_pixels_x(window); - winy = WM_window_pixels_y(window); + const int winx = WM_window_pixels_x(window); + const int winy = WM_window_pixels_y(window); /* shift to left if outside of view */ if (block->rect.xmax > winx - margin) { @@ -549,7 +547,6 @@ uiBlock *ui_popup_block_refresh(bContext *C, void *arg = handle->popup_create_vars.arg; uiBlock *block_old = static_cast<uiBlock *>(region->uiblocks.first); - uiBlock *block; handle->refresh = (block_old != nullptr); @@ -561,6 +558,7 @@ uiBlock *ui_popup_block_refresh(bContext *C, #endif /* create ui block */ + uiBlock *block; if (create_func) { block = create_func(C, region, arg); } @@ -618,16 +616,14 @@ uiBlock *ui_popup_block_refresh(bContext *C, if (block->flag & UI_BLOCK_RADIAL) { const int win_width = UI_SCREEN_MARGIN; - int winx, winy; - - int x_offset = 0, y_offset = 0; - winx = WM_window_pixels_x(window); - winy = WM_window_pixels_y(window); + const int winx = WM_window_pixels_x(window); + const int winy = WM_window_pixels_y(window); copy_v2_v2(block->pie_data.pie_center_init, block->pie_data.pie_center_spawned); /* only try translation if area is large enough */ + int x_offset = 0; if (BLI_rctf_size_x(&block->rect) < winx - (2.0f * win_width)) { if (block->rect.xmin < win_width) { x_offset += win_width - block->rect.xmin; @@ -637,6 +633,7 @@ uiBlock *ui_popup_block_refresh(bContext *C, } } + int y_offset = 0; if (BLI_rctf_size_y(&block->rect) < winy - (2.0f * win_width)) { if (block->rect.ymin < win_width) { y_offset += win_width - block->rect.ymin; @@ -756,9 +753,6 @@ uiPopupBlockHandle *ui_popup_block_create(bContext *C, { wmWindow *window = CTX_wm_window(C); uiBut *activebut = UI_context_active_but_get(C); - static ARegionType type; - ARegion *region; - uiBlock *block; /* disable tooltips from buttons below */ if (activebut) { @@ -787,9 +781,10 @@ uiPopupBlockHandle *ui_popup_block_create(bContext *C, handle->can_refresh = false; /* create area region */ - region = ui_region_temp_add(CTX_wm_screen(C)); + ARegion *region = ui_region_temp_add(CTX_wm_screen(C)); handle->region = region; + static ARegionType type; memset(&type, 0, sizeof(ARegionType)); type.draw = ui_block_region_draw; type.layout = ui_block_region_refresh; @@ -798,7 +793,7 @@ uiPopupBlockHandle *ui_popup_block_create(bContext *C, UI_region_handlers_add(®ion->handlers); - block = ui_popup_block_refresh(C, handle, butregion, but); + uiBlock *block = ui_popup_block_refresh(C, handle, butregion, but); handle = block->handle; /* keep centered on window resizing */ diff --git a/source/blender/editors/interface/interface_templates.c b/source/blender/editors/interface/interface_templates.c index d21f969e431..a3259831c9f 100644 --- a/source/blender/editors/interface/interface_templates.c +++ b/source/blender/editors/interface/interface_templates.c @@ -3269,7 +3269,7 @@ void uiTemplatePreview(uiLayout *layout, uiDefButS(block, UI_BTYPE_ROW, B_MATPRV, - IFACE_("World"), + CTX_IFACE_(BLT_I18NCONTEXT_ID_WORLD, "World"), 0, 0, UI_UNIT_X * 10, diff --git a/source/blender/editors/interface/interface_widgets.c b/source/blender/editors/interface/interface_widgets.c index 6ba80e2e0d9..1bad9b34a72 100644 --- a/source/blender/editors/interface/interface_widgets.c +++ b/source/blender/editors/interface/interface_widgets.c @@ -1420,21 +1420,25 @@ static void widget_draw_icon( /* to indicate draggable */ if (ui_but_drag_is_draggable(but) && (but->flag & UI_ACTIVE)) { - UI_icon_draw_ex(xs, ys, icon, aspect, 1.25f, 0.0f, color, has_theme); + UI_icon_draw_ex( + xs, ys, icon, aspect, 1.25f, 0.0f, color, has_theme, &but->icon_overlay_text); } else if (but->flag & (UI_ACTIVE | UI_SELECT | UI_SELECT_DRAW)) { - UI_icon_draw_ex(xs, ys, icon, aspect, alpha, 0.0f, color, has_theme); + UI_icon_draw_ex( + xs, ys, icon, aspect, alpha, 0.0f, color, has_theme, &but->icon_overlay_text); } else if (!((but->icon != ICON_NONE) && UI_but_is_tool(but))) { if (has_theme) { alpha *= 0.8f; } - UI_icon_draw_ex(xs, ys, icon, aspect, alpha, 0.0f, color, has_theme); + UI_icon_draw_ex( + xs, ys, icon, aspect, alpha, 0.0f, color, has_theme, &but->icon_overlay_text); } else { const bTheme *btheme = UI_GetTheme(); const float desaturate = 1.0 - btheme->tui.icon_saturation; - UI_icon_draw_ex(xs, ys, icon, aspect, alpha, desaturate, color, has_theme); + UI_icon_draw_ex( + xs, ys, icon, aspect, alpha, desaturate, color, has_theme, &but->icon_overlay_text); } } @@ -5426,7 +5430,8 @@ void ui_draw_menu_item(const uiFontStyle *fstyle, GPU_blend(GPU_BLEND_ALPHA); /* XXX scale weak get from fstyle? */ - UI_icon_draw_ex(xs, ys, iconid, aspect, 1.0f, 0.0f, wt->wcol.text, false); + UI_icon_draw_ex( + xs, ys, iconid, aspect, 1.0f, 0.0f, wt->wcol.text, false, UI_NO_ICON_OVERLAY_TEXT); GPU_blend(GPU_BLEND_NONE); } diff --git a/source/blender/editors/io/io_alembic.c b/source/blender/editors/io/io_alembic.c index d4855f470ff..100d56a6b0d 100644 --- a/source/blender/editors/io/io_alembic.c +++ b/source/blender/editors/io/io_alembic.c @@ -658,7 +658,7 @@ void WM_OT_alembic_import(wmOperatorType *ot) ot->name = "Import Alembic"; ot->description = "Load an Alembic archive"; ot->idname = "WM_OT_alembic_import"; - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_PRESET; ot->invoke = wm_alembic_import_invoke; ot->exec = wm_alembic_import_exec; diff --git a/source/blender/editors/io/io_collada.c b/source/blender/editors/io/io_collada.c index 1048d0eca32..a630f150e0e 100644 --- a/source/blender/editors/io/io_collada.c +++ b/source/blender/editors/io/io_collada.c @@ -770,14 +770,12 @@ void WM_OT_collada_import(wmOperatorType *ot) ot->name = "Import COLLADA"; ot->description = "Load a Collada file"; ot->idname = "WM_OT_collada_import"; - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_PRESET; ot->invoke = WM_operator_filesel; ot->exec = wm_collada_import_exec; ot->poll = WM_operator_winactive; - // ot->flag = OPTYPE_PRESET; - ot->ui = wm_collada_import_draw; WM_operator_properties_filesel(ot, diff --git a/source/blender/editors/io/io_gpencil_import.c b/source/blender/editors/io/io_gpencil_import.c index c7a6b20af7b..5325965e9a5 100644 --- a/source/blender/editors/io/io_gpencil_import.c +++ b/source/blender/editors/io/io_gpencil_import.c @@ -111,7 +111,7 @@ static int wm_gpencil_import_svg_exec(bContext *C, wmOperator *op) char file_path[FILE_MAX]; RNA_PROP_BEGIN (op->ptr, itemptr, prop) { char *filename = RNA_string_get_alloc(&itemptr, "name", NULL, 0, NULL); - BLI_join_dirfile(file_path, sizeof(file_path), directory, filename); + BLI_path_join(file_path, sizeof(file_path), directory, filename); MEM_freeN(filename); /* Do Import. */ diff --git a/source/blender/editors/io/io_obj.c b/source/blender/editors/io/io_obj.c index cb8eafeb52d..27994af8fe7 100644 --- a/source/blender/editors/io/io_obj.c +++ b/source/blender/editors/io/io_obj.c @@ -81,7 +81,7 @@ static int wm_obj_export_exec(bContext *C, wmOperator *op) export_params.forward_axis = RNA_enum_get(op->ptr, "forward_axis"); export_params.up_axis = RNA_enum_get(op->ptr, "up_axis"); - export_params.scaling_factor = RNA_float_get(op->ptr, "scaling_factor"); + export_params.global_scale = RNA_float_get(op->ptr, "global_scale"); export_params.apply_modifiers = RNA_boolean_get(op->ptr, "apply_modifiers"); export_params.export_eval_mode = RNA_enum_get(op->ptr, "export_eval_mode"); @@ -122,7 +122,7 @@ static void ui_obj_export_settings(uiLayout *layout, PointerRNA *imfptr) col = uiLayoutColumn(box, false); sub = uiLayoutColumnWithHeading(col, false, IFACE_("Limit to")); uiItemR(sub, imfptr, "export_selected_objects", 0, IFACE_("Selected Only"), ICON_NONE); - uiItemR(sub, imfptr, "scaling_factor", 0, NULL, ICON_NONE); + uiItemR(sub, imfptr, "global_scale", 0, NULL, ICON_NONE); row = uiLayoutRow(box, false); uiItemR(row, imfptr, "forward_axis", UI_ITEM_R_EXPAND, IFACE_("Forward Axis"), ICON_NONE); @@ -301,15 +301,16 @@ void WM_OT_obj_export(struct wmOperatorType *ot) RNA_def_property_update_runtime(prop, (void *)forward_axis_update); prop = RNA_def_enum(ot->srna, "up_axis", io_transform_axis, IO_AXIS_Y, "Up Axis", ""); RNA_def_property_update_runtime(prop, (void *)up_axis_update); - RNA_def_float(ot->srna, - "scaling_factor", - 1.0f, - 0.001f, - 10000.0f, - "Scale", - "Upscale the object by this factor", - 0.01, - 1000.0f); + RNA_def_float( + ot->srna, + "global_scale", + 1.0f, + 0.0001f, + 10000.0f, + "Scale", + "Value by which to enlarge or shrink the objects with respect to the world's origin", + 0.0001f, + 10000.0f); /* File Writer options. */ RNA_def_boolean( ot->srna, "apply_modifiers", true, "Apply Modifiers", "Apply modifiers to exported meshes"); @@ -405,6 +406,7 @@ static int wm_obj_import_exec(bContext *C, wmOperator *op) { struct OBJImportParams import_params; RNA_string_get(op->ptr, "filepath", import_params.filepath); + import_params.global_scale = RNA_float_get(op->ptr, "global_scale"); import_params.clamp_size = RNA_float_get(op->ptr, "clamp_size"); import_params.forward_axis = RNA_enum_get(op->ptr, "forward_axis"); import_params.up_axis = RNA_enum_get(op->ptr, "up_axis"); @@ -425,8 +427,7 @@ static int wm_obj_import_exec(bContext *C, wmOperator *op) for (int i = 0; i < files_len; i++) { RNA_property_collection_lookup_int(op->ptr, prop, i, &fileptr); RNA_string_get(&fileptr, "name", file_only); - BLI_join_dirfile( - import_params.filepath, sizeof(import_params.filepath), dir_only, file_only); + BLI_path_join(import_params.filepath, sizeof(import_params.filepath), dir_only, file_only); import_params.clear_selection = (i == 0); OBJ_import(C, &import_params); } @@ -459,6 +460,7 @@ static void ui_obj_import_settings(uiLayout *layout, PointerRNA *imfptr) uiItemL(box, IFACE_("Transform"), ICON_OBJECT_DATA); uiLayout *col = uiLayoutColumn(box, false); uiLayout *sub = uiLayoutColumn(col, false); + uiItemR(sub, imfptr, "global_scale", 0, NULL, ICON_NONE); uiItemR(sub, imfptr, "clamp_size", 0, NULL, ICON_NONE); sub = uiLayoutColumn(col, false); @@ -489,7 +491,7 @@ void WM_OT_obj_import(struct wmOperatorType *ot) ot->name = "Import Wavefront OBJ"; ot->description = "Load a Wavefront OBJ scene"; ot->idname = "WM_OT_obj_import"; - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_PRESET; ot->invoke = wm_obj_import_invoke; ot->exec = wm_obj_import_exec; @@ -506,6 +508,16 @@ void WM_OT_obj_import(struct wmOperatorType *ot) FILE_SORT_DEFAULT); RNA_def_float( ot->srna, + "global_scale", + 1.0f, + 0.0001f, + 10000.0f, + "Scale", + "Value by which to enlarge or shrink the objects with respect to the world's origin", + 0.0001f, + 10000.0f); + RNA_def_float( + ot->srna, "clamp_size", 0.0f, 0.0f, diff --git a/source/blender/editors/io/io_stl_ops.c b/source/blender/editors/io/io_stl_ops.c index c98e5beaf3b..bbdb494e48a 100644 --- a/source/blender/editors/io/io_stl_ops.c +++ b/source/blender/editors/io/io_stl_ops.c @@ -49,7 +49,7 @@ static int wm_stl_import_execute(bContext *C, wmOperator *op) for (int i = 0; i < files_len; i++) { RNA_property_collection_lookup_int(op->ptr, prop, i, &fileptr); RNA_string_get(&fileptr, "name", file_only); - BLI_join_dirfile(params.filepath, sizeof(params.filepath), dir_only, file_only); + BLI_path_join(params.filepath, sizeof(params.filepath), dir_only, file_only); STL_import(C, ¶ms); } } @@ -95,7 +95,7 @@ void WM_OT_stl_import(struct wmOperatorType *ot) ot->exec = wm_stl_import_execute; ot->poll = WM_operator_winactive; ot->check = wm_stl_import_check; - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_PRESET; WM_operator_properties_filesel(ot, FILE_TYPE_FOLDER, diff --git a/source/blender/editors/io/io_usd.c b/source/blender/editors/io/io_usd.c index eb80cabcd7f..c776fbf0dd7 100644 --- a/source/blender/editors/io/io_usd.c +++ b/source/blender/editors/io/io_usd.c @@ -500,7 +500,7 @@ void WM_OT_usd_import(struct wmOperatorType *ot) ot->poll = WM_operator_winactive; ot->ui = wm_usd_import_draw; - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_PRESET; WM_operator_properties_filesel(ot, FILE_TYPE_FOLDER | FILE_TYPE_USD, diff --git a/source/blender/editors/mesh/editmesh_knife.c b/source/blender/editors/mesh/editmesh_knife.c index e9897021b4a..a147594b25b 100644 --- a/source/blender/editors/mesh/editmesh_knife.c +++ b/source/blender/editors/mesh/editmesh_knife.c @@ -100,7 +100,7 @@ typedef struct KnifeColors { /* Knife-tool Operator. */ typedef struct KnifeVert { Object *ob; - uint base_index; + uint ob_index; BMVert *v; /* Non-NULL if this is an original vert. */ ListBase edges; ListBase faces; @@ -142,7 +142,7 @@ typedef struct KnifeLineHit { KnifeVert *v; BMFace *f; Object *ob; - uint base_index; + uint ob_index; } KnifeLineHit; typedef struct KnifePosData { @@ -156,7 +156,7 @@ typedef struct KnifePosData { KnifeEdge *edge; BMFace *bmface; Object *ob; /* Object of the vert, edge or bmface. */ - uint base_index; + uint ob_index; /* When true, the cursor isn't over a face. */ bool is_space; @@ -182,7 +182,7 @@ typedef struct KnifeBVH { BVHTree *tree; /* Knife Custom BVH Tree. */ BMLoop *(*looptris)[3]; /* Used by #knife_bvh_raycast_cb to store the intersecting looptri. */ float uv[2]; /* Used by #knife_bvh_raycast_cb to store the intersecting uv. */ - uint base_index; + uint ob_index; /* Use #bm_ray_cast_cb_elem_not_in_face_check. */ bool (*filter_cb)(BMFace *f, void *userdata); @@ -218,6 +218,7 @@ typedef struct KnifeTool_OpData { /* Used for swapping current object when in multi-object edit mode. */ Object **objects; uint objects_len; + bool objects_free; /** Array `objects_len` length of additional per-object data. */ KnifeObjectInfo *objects_info; @@ -1158,11 +1159,11 @@ static void knife_update_header(bContext *C, wmOperator *op, KnifeTool_OpData *k * \{ */ static const int *knife_bm_tri_index_get(const KnifeTool_OpData *kcd, - int base_index, + int ob_index, int tri_index, int tri_index_buf[3]) { - const KnifeObjectInfo *obinfo = &kcd->objects_info[base_index]; + const KnifeObjectInfo *obinfo = &kcd->objects_info[ob_index]; if (obinfo->tri_indices) { return obinfo->tri_indices[tri_index]; } @@ -1173,25 +1174,25 @@ static const int *knife_bm_tri_index_get(const KnifeTool_OpData *kcd, } static void knife_bm_tri_cagecos_get(const KnifeTool_OpData *kcd, - int base_index, + int ob_index, int tri_index, float cos[3][3]) { - const KnifeObjectInfo *obinfo = &kcd->objects_info[base_index]; + const KnifeObjectInfo *obinfo = &kcd->objects_info[ob_index]; int tri_ind_buf[3]; - const int *tri_ind = knife_bm_tri_index_get(kcd, base_index, tri_index, tri_ind_buf); + const int *tri_ind = knife_bm_tri_index_get(kcd, ob_index, tri_index, tri_ind_buf); for (int i = 0; i < 3; i++) { copy_v3_v3(cos[i], obinfo->cagecos[tri_ind[i]]); } } static void knife_bm_tri_cagecos_get_worldspace(const KnifeTool_OpData *kcd, - int base_index, + int ob_index, int tri_index, float cos[3][3]) { - knife_bm_tri_cagecos_get(kcd, base_index, tri_index, cos); - const Object *ob = kcd->objects[base_index]; + knife_bm_tri_cagecos_get(kcd, ob_index, tri_index, cos); + const Object *ob = kcd->objects[ob_index]; for (int i = 0; i < 3; i++) { mul_m4_v3(ob->obmat, cos[i]); } @@ -1236,9 +1237,9 @@ static void knife_bvh_init(KnifeTool_OpData *kcd) bool test_fn_ret = false; /* Calculate tottri. */ - for (uint b = 0; b < kcd->objects_len; b++) { + for (uint ob_index = 0; ob_index < kcd->objects_len; ob_index++) { ob_tottri = 0; - ob = kcd->objects[b]; + ob = kcd->objects[ob_index]; em = BKE_editmesh_from_object(ob); for (int i = 0; i < em->tottri; i++) { @@ -1268,8 +1269,8 @@ static void knife_bvh_init(KnifeTool_OpData *kcd) * Don't forget to update #knife_bvh_intersect_plane! */ tottri = 0; - for (uint b = 0; b < kcd->objects_len; b++) { - ob = kcd->objects[b]; + for (uint ob_index = 0; ob_index < kcd->objects_len; ob_index++) { + ob = kcd->objects[ob_index]; em = BKE_editmesh_from_object(ob); looptris = em->looptris; @@ -1286,7 +1287,7 @@ static void knife_bvh_init(KnifeTool_OpData *kcd) } float tri_cos[3][3]; - knife_bm_tri_cagecos_get_worldspace(kcd, b, i, tri_cos); + knife_bm_tri_cagecos_get_worldspace(kcd, ob_index, i, tri_cos); BLI_bvhtree_insert(kcd->bvh.tree, i + tottri, &tri_cos[0][0], 3); } @@ -1324,10 +1325,10 @@ static void knife_bvh_raycast_cb(void *userdata, int tottri; tottri = 0; - uint b = 0; - for (; b < kcd->objects_len; b++) { + uint ob_index = 0; + for (; ob_index < kcd->objects_len; ob_index++) { index -= tottri; - ob = kcd->objects[b]; + ob = kcd->objects[ob_index]; em = BKE_editmesh_from_object(ob); tottri = em->tottri; if (index < tottri) { @@ -1343,7 +1344,7 @@ static void knife_bvh_raycast_cb(void *userdata, } float tri_cos[3][3]; - knife_bm_tri_cagecos_get_worldspace(kcd, b, index, tri_cos); + knife_bm_tri_cagecos_get_worldspace(kcd, ob_index, index, tri_cos); isect = (ray->radius > 0.0f ? isect_ray_tri_epsilon_v3( ray->origin, ray->direction, UNPACK3(tri_cos), &dist, uv, ray->radius) : @@ -1370,7 +1371,7 @@ static void knife_bvh_raycast_cb(void *userdata, kcd->bvh.looptris = em->looptris; copy_v2_v2(kcd->bvh.uv, uv); - kcd->bvh.base_index = b; + kcd->bvh.ob_index = ob_index; } } @@ -1382,7 +1383,7 @@ static BMFace *knife_bvh_raycast(KnifeTool_OpData *kcd, float *r_dist, float r_hitout[3], float r_cagehit[3], - uint *r_base_index) + uint *r_ob_index) { BMFace *face; BVHTreeRayHit hit; @@ -1399,7 +1400,7 @@ static BMFace *knife_bvh_raycast(KnifeTool_OpData *kcd, /* Hits returned in world space. */ if (r_hitout) { float tri_cos[3][3]; - knife_bm_tri_cagecos_get_worldspace(kcd, kcd->bvh.base_index, hit.index, tri_cos); + knife_bm_tri_cagecos_get_worldspace(kcd, kcd->bvh.ob_index, hit.index, tri_cos); interp_v3_v3v3v3_uv(r_hitout, UNPACK3(tri_cos), kcd->bvh.uv); if (r_cagehit) { @@ -1411,8 +1412,8 @@ static BMFace *knife_bvh_raycast(KnifeTool_OpData *kcd, *r_dist = hit.dist; } - if (r_base_index) { - *r_base_index = kcd->bvh.base_index; + if (r_ob_index) { + *r_ob_index = kcd->bvh.ob_index; } return face; @@ -1428,7 +1429,7 @@ static BMFace *knife_bvh_raycast_filter(KnifeTool_OpData *kcd, float *r_dist, float r_hitout[3], float r_cagehit[3], - uint *r_base_index, + uint *r_ob_index, bool (*filter_cb)(BMFace *f, void *userdata), void *filter_userdata) { @@ -1453,7 +1454,7 @@ static BMFace *knife_bvh_raycast_filter(KnifeTool_OpData *kcd, /* Hits returned in world space. */ if (r_hitout) { float tri_cos[3][3]; - knife_bm_tri_cagecos_get_worldspace(kcd, kcd->bvh.base_index, hit.index, tri_cos); + knife_bm_tri_cagecos_get_worldspace(kcd, kcd->bvh.ob_index, hit.index, tri_cos); interp_v3_v3v3v3_uv(r_hitout, UNPACK3(tri_cos), kcd->bvh.uv); if (r_cagehit) { @@ -1465,8 +1466,8 @@ static BMFace *knife_bvh_raycast_filter(KnifeTool_OpData *kcd, *r_dist = hit.dist; } - if (r_base_index) { - *r_base_index = kcd->bvh.base_index; + if (r_ob_index) { + *r_ob_index = kcd->bvh.ob_index; } return face; @@ -1726,7 +1727,7 @@ static KnifeEdge *new_knife_edge(KnifeTool_OpData *kcd) } /* Get a KnifeVert wrapper for an existing BMVert. */ -static KnifeVert *get_bm_knife_vert(KnifeTool_OpData *kcd, BMVert *v, Object *ob, uint base_index) +static KnifeVert *get_bm_knife_vert(KnifeTool_OpData *kcd, BMVert *v, Object *ob, uint ob_index) { KnifeVert *kfv = BLI_ghash_lookup(kcd->origvertmap, v); const float *cageco; @@ -1736,7 +1737,7 @@ static KnifeVert *get_bm_knife_vert(KnifeTool_OpData *kcd, BMVert *v, Object *ob BMFace *f; if (BM_elem_index_get(v) >= 0) { - cageco = kcd->objects_info[base_index].cagecos[BM_elem_index_get(v)]; + cageco = kcd->objects_info[ob_index].cagecos[BM_elem_index_get(v)]; } else { cageco = v->co; @@ -1748,7 +1749,7 @@ static KnifeVert *get_bm_knife_vert(KnifeTool_OpData *kcd, BMVert *v, Object *ob kfv = new_knife_vert(kcd, v->co, cageco_ws); kfv->v = v; kfv->ob = ob; - kfv->base_index = base_index; + kfv->ob_index = ob_index; BLI_ghash_insert(kcd->origvertmap, v, kfv); BM_ITER_ELEM (f, &bmiter, v, BM_FACES_OF_VERT) { @@ -1760,7 +1761,7 @@ static KnifeVert *get_bm_knife_vert(KnifeTool_OpData *kcd, BMVert *v, Object *ob } /* Get a KnifeEdge wrapper for an existing BMEdge. */ -static KnifeEdge *get_bm_knife_edge(KnifeTool_OpData *kcd, BMEdge *e, Object *ob, uint base_index) +static KnifeEdge *get_bm_knife_edge(KnifeTool_OpData *kcd, BMEdge *e, Object *ob, uint ob_index) { KnifeEdge *kfe = BLI_ghash_lookup(kcd->origedgemap, e); if (!kfe) { @@ -1769,8 +1770,8 @@ static KnifeEdge *get_bm_knife_edge(KnifeTool_OpData *kcd, BMEdge *e, Object *ob kfe = new_knife_edge(kcd); kfe->e = e; - kfe->v1 = get_bm_knife_vert(kcd, e->v1, ob, base_index); - kfe->v2 = get_bm_knife_vert(kcd, e->v2, ob, base_index); + kfe->v1 = get_bm_knife_vert(kcd, e->v1, ob, ob_index); + kfe->v2 = get_bm_knife_vert(kcd, e->v2, ob, ob_index); knife_add_to_vert_edges(kcd, kfe); @@ -1784,10 +1785,7 @@ static KnifeEdge *get_bm_knife_edge(KnifeTool_OpData *kcd, BMEdge *e, Object *ob return kfe; } -static ListBase *knife_get_face_kedges(KnifeTool_OpData *kcd, - Object *ob, - uint base_index, - BMFace *f) +static ListBase *knife_get_face_kedges(KnifeTool_OpData *kcd, Object *ob, uint ob_index, BMFace *f) { ListBase *list = BLI_ghash_lookup(kcd->kedgefacemap, f); @@ -1798,7 +1796,7 @@ static ListBase *knife_get_face_kedges(KnifeTool_OpData *kcd, list = knife_empty_list(kcd); BM_ITER_ELEM (e, &bmiter, f, BM_EDGES_OF_FACE) { - knife_append_list(kcd, list, get_bm_knife_edge(kcd, e, ob, base_index)); + knife_append_list(kcd, list, get_bm_knife_edge(kcd, e, ob, ob_index)); } BLI_ghash_insert(kcd->kedgefacemap, f, list); @@ -1809,7 +1807,7 @@ static ListBase *knife_get_face_kedges(KnifeTool_OpData *kcd, static void knife_edge_append_face(KnifeTool_OpData *kcd, KnifeEdge *kfe, BMFace *f) { - knife_append_list(kcd, knife_get_face_kedges(kcd, kfe->v1->ob, kfe->v1->base_index, f), kfe); + knife_append_list(kcd, knife_get_face_kedges(kcd, kfe->v1->ob, kfe->v1->ob_index, f), kfe); knife_append_list(kcd, &kfe->faces, f); } @@ -1826,7 +1824,7 @@ static KnifeVert *knife_split_edge(KnifeTool_OpData *kcd, newkfe->v1 = kfe->v1; newkfe->v2 = new_knife_vert(kcd, co, cageco); newkfe->v2->ob = kfe->v1->ob; - newkfe->v2->base_index = kfe->v1->base_index; + newkfe->v2->ob_index = kfe->v1->ob_index; newkfe->v2->is_cut = true; if (kfe->e) { knife_add_edge_faces_to_vert(kcd, newkfe->v2, kfe->e); @@ -2123,7 +2121,7 @@ static void knife_add_single_cut(KnifeTool_OpData *kcd, BLI_assert(lh1->f); kfe->v1 = new_knife_vert(kcd, lh1->hit, lh1->cagehit); kfe->v1->ob = lh1->ob; - kfe->v1->base_index = lh1->base_index; + kfe->v1->ob_index = lh1->ob_index; kfe->v1->is_cut = true; kfe->v1->is_face = true; knife_append_list(kcd, &kfe->v1->faces, lh1->f); @@ -2141,7 +2139,7 @@ static void knife_add_single_cut(KnifeTool_OpData *kcd, BLI_assert(lh2->f); kfe->v2 = new_knife_vert(kcd, lh2->hit, lh2->cagehit); kfe->v2->ob = lh2->ob; - kfe->v2->base_index = lh2->base_index; + kfe->v2->ob_index = lh2->ob_index; kfe->v2->is_cut = true; kfe->v2->is_face = true; knife_append_list(kcd, &kfe->v2->faces, lh2->f); @@ -2567,7 +2565,7 @@ static bool knife_ray_intersect_face(KnifeTool_OpData *kcd, const float v1[3], const float v2[3], Object *ob, - uint base_index, + uint ob_index, BMFace *f, const float face_tol_sq, float hit_co[3], @@ -2600,7 +2598,7 @@ static bool knife_ray_intersect_face(KnifeTool_OpData *kcd, break; } - knife_bm_tri_cagecos_get_worldspace(kcd, base_index, tri_i, tri_cos); + knife_bm_tri_cagecos_get_worldspace(kcd, ob_index, tri_i, tri_cos); /* Using epsilon test in case ray is directly through an internal * tessellation edge and might not hit either tessellation tri with @@ -2617,7 +2615,7 @@ static bool knife_ray_intersect_face(KnifeTool_OpData *kcd, } interp_v3_v3v3v3_uv(hit_cageco, UNPACK3(tri_cos), ray_tri_uv); /* Now check that far enough away from verts and edges. */ - list = knife_get_face_kedges(kcd, ob, base_index, f); + list = knife_get_face_kedges(kcd, ob, ob_index, f); for (ref = list->first; ref; ref = ref->next) { kfe = ref->ref; if (kfe->is_invalid) { @@ -2651,11 +2649,11 @@ static void calc_ortho_extent(KnifeTool_OpData *kcd) float ws[3]; INIT_MINMAX(min, max); - for (uint b = 0; b < kcd->objects_len; b++) { - ob = kcd->objects[b]; + for (uint ob_index = 0; ob_index < kcd->objects_len; ob_index++) { + ob = kcd->objects[ob_index]; em = BKE_editmesh_from_object(ob); - const float(*cagecos)[3] = kcd->objects_info[b].cagecos; + const float(*cagecos)[3] = kcd->objects_info[ob_index].cagecos; if (cagecos) { for (int i = 0; i < em->bm->totvert; i++) { copy_v3_v3(ws, cagecos[i]); @@ -2930,11 +2928,11 @@ static void knife_find_line_hits(KnifeTool_OpData *kcd) Object *ob; BMEditMesh *em; - uint b = 0; for (i = 0, result = results; i < tot; i++, result++) { - for (b = 0; b < kcd->objects_len; b++) { - ob = kcd->objects[b]; + uint ob_index = 0; + for (ob_index = 0; ob_index < kcd->objects_len; ob_index++) { + ob = kcd->objects[ob_index]; em = BKE_editmesh_from_object(ob); if (*result >= 0 && *result < em->tottri) { ls = (BMLoop **)em->looptris[*result]; @@ -2956,9 +2954,9 @@ static void knife_find_line_hits(KnifeTool_OpData *kcd) } /* Don't care what the value is except that it is non-NULL, for iterator. */ BLI_smallhash_insert(&faces, (uintptr_t)f, f); - BLI_smallhash_insert(&fobs, (uintptr_t)f, (void *)(uintptr_t)b); + BLI_smallhash_insert(&fobs, (uintptr_t)f, (void *)(uintptr_t)ob_index); - list = knife_get_face_kedges(kcd, ob, b, f); + list = knife_get_face_kedges(kcd, ob, ob_index, f); for (ref = list->first; ref; ref = ref->next) { kfe = ref->ref; if (kfe->is_invalid) { @@ -3033,7 +3031,7 @@ static void knife_find_line_hits(KnifeTool_OpData *kcd) } hit.ob = v->ob; - hit.base_index = v->base_index; + hit.ob_index = v->ob_index; copy_v3_v3(hit.hit, v->co); copy_v3_v3(hit.cagehit, v->cageco); copy_v2_v2(hit.schit, s); @@ -3109,7 +3107,7 @@ static void knife_find_line_hits(KnifeTool_OpData *kcd) transform_point_by_seg_v3( hit.hit, p_cage, kfe->v1->co, kfe->v2->co, kfe->v1->cageco, kfe->v2->cageco); hit.ob = kfe->v1->ob; - hit.base_index = kfe->v1->base_index; + hit.ob_index = kfe->v1->ob_index; copy_v3_v3(hit.cagehit, p_cage); copy_v2_v2(hit.schit, sint); hit.perc = lambda; @@ -3129,16 +3127,16 @@ static void knife_find_line_hits(KnifeTool_OpData *kcd) val = BLI_smallhash_iternext(&hiter, (uintptr_t *)&f)) { float p[3], p_cage[3]; - uint base_index = (uint)(uintptr_t)BLI_smallhash_lookup(&fobs, (uintptr_t)f); - ob = kcd->objects[base_index]; + uint ob_index = (uint)(uintptr_t)BLI_smallhash_lookup(&fobs, (uintptr_t)f); + ob = kcd->objects[ob_index]; if (use_hit_prev && - knife_ray_intersect_face(kcd, s1, v1, v3, ob, base_index, f, face_tol_sq, p, p_cage)) { + knife_ray_intersect_face(kcd, s1, v1, v3, ob, ob_index, f, face_tol_sq, p, p_cage)) { if (point_is_visible(kcd, p_cage, s1, (BMElem *)f)) { memset(&hit, 0, sizeof(hit)); hit.f = f; hit.ob = ob; - hit.base_index = base_index; + hit.ob_index = ob_index; copy_v3_v3(hit.hit, p); copy_v3_v3(hit.cagehit, p_cage); copy_v2_v2(hit.schit, s1); @@ -3148,12 +3146,12 @@ static void knife_find_line_hits(KnifeTool_OpData *kcd) } if (use_hit_curr && - knife_ray_intersect_face(kcd, s2, v2, v4, ob, base_index, f, face_tol_sq, p, p_cage)) { + knife_ray_intersect_face(kcd, s2, v2, v4, ob, ob_index, f, face_tol_sq, p, p_cage)) { if (point_is_visible(kcd, p_cage, s2, (BMElem *)f)) { memset(&hit, 0, sizeof(hit)); hit.f = f; hit.ob = ob; - hit.base_index = base_index; + hit.ob_index = ob_index; copy_v3_v3(hit.hit, p); copy_v3_v3(hit.cagehit, p_cage); copy_v2_v2(hit.schit, s2); @@ -3205,7 +3203,7 @@ static void knife_pos_data_clear(KnifePosData *kpd) static BMFace *knife_find_closest_face(KnifeTool_OpData *kcd, Object **r_ob, - uint *r_base_index, + uint *r_ob_index, bool *is_space, float r_co[3], float r_cageco[3]) @@ -3221,7 +3219,7 @@ static BMFace *knife_find_closest_face(KnifeTool_OpData *kcd, sub_v3_v3v3(ray, origin_ofs, origin); normalize_v3_v3(ray_normal, ray); - f = knife_bvh_raycast(kcd, origin, ray_normal, 0.0f, NULL, r_co, r_cageco, r_base_index); + f = knife_bvh_raycast(kcd, origin, ray_normal, 0.0f, NULL, r_co, r_cageco, r_ob_index); if (f && kcd->only_select && BM_elem_flag_test(f, BM_ELEM_SELECT) == 0) { f = NULL; @@ -3232,7 +3230,7 @@ static BMFace *knife_find_closest_face(KnifeTool_OpData *kcd, } if (f) { - *r_ob = kcd->objects[*r_base_index]; + *r_ob = kcd->objects[*r_ob_index]; } else { if (kcd->is_interactive) { @@ -3267,7 +3265,7 @@ static BMFace *knife_find_closest_face(KnifeTool_OpData *kcd, static int knife_sample_screen_density_from_closest_face(KnifeTool_OpData *kcd, const float radius, Object *ob, - uint base_index, + uint ob_index, BMFace *f, const float cageco[3]) { @@ -3280,7 +3278,7 @@ static int knife_sample_screen_density_from_closest_face(KnifeTool_OpData *kcd, knife_project_v2(kcd, cageco, sco); - list = knife_get_face_kedges(kcd, ob, base_index, f); + list = knife_get_face_kedges(kcd, ob, ob_index, f); for (ref = list->first; ref; ref = ref->next) { KnifeEdge *kfe = ref->ref; int i; @@ -3329,7 +3327,7 @@ static float knife_snap_size(KnifeTool_OpData *kcd, float maxsize) if (!kcd->curr.is_space) { density = (float)knife_sample_screen_density_from_closest_face( - kcd, maxsize * 2.0f, kcd->curr.ob, kcd->curr.base_index, kcd->curr.bmface, kcd->curr.cage); + kcd, maxsize * 2.0f, kcd->curr.ob, kcd->curr.ob_index, kcd->curr.bmface, kcd->curr.cage); } return density ? min_ff(maxsize / ((float)density * 0.5f), maxsize) : maxsize; @@ -3388,7 +3386,7 @@ static void knife_interp_v3_v3v3(const KnifeTool_OpData *kcd, /* p is closest point on edge to the mouse cursor. */ static KnifeEdge *knife_find_closest_edge_of_face( - KnifeTool_OpData *kcd, Object *ob, uint base_index, BMFace *f, float p[3], float cagep[3]) + KnifeTool_OpData *kcd, Object *ob, uint ob_index, BMFace *f, float p[3], float cagep[3]) { float sco[2]; float maxdist; @@ -3414,7 +3412,7 @@ static KnifeEdge *knife_find_closest_edge_of_face( knife_project_v2(kcd, cagep, sco); /* Look through all edges associated with this face. */ - list = knife_get_face_kedges(kcd, ob, base_index, f); + list = knife_get_face_kedges(kcd, ob, ob_index, f); for (ref = list->first; ref; ref = ref->next) { KnifeEdge *kfe = ref->ref; float kfv1_sco[2], kfv2_sco[2], test_cagep[3]; @@ -3479,7 +3477,7 @@ static KnifeEdge *knife_find_closest_edge_of_face( * this is important for angle snap, which uses the previous mouse position. */ edgesnap = new_knife_vert(kcd, p, cagep); edgesnap->ob = ob; - edgesnap->base_index = base_index; + edgesnap->ob_index = ob_index; knife_project_v2(kcd, edgesnap->cageco, kcd->curr.mval); } @@ -3906,14 +3904,14 @@ static bool knife_snap_update_from_mval(KnifeTool_OpData *kcd, const float mval[ kcd->curr.ob = kcd->vc.obedit; kcd->curr.bmface = knife_find_closest_face(kcd, &kcd->curr.ob, - &kcd->curr.base_index, + &kcd->curr.ob_index, &kcd->curr.is_space, kcd->curr.co, kcd->curr.cage); if (kcd->curr.bmface) { kcd->curr.edge = knife_find_closest_edge_of_face( - kcd, kcd->curr.ob, kcd->curr.base_index, kcd->curr.bmface, kcd->curr.co, kcd->curr.cage); + kcd, kcd->curr.ob, kcd->curr.ob_index, kcd->curr.bmface, kcd->curr.co, kcd->curr.cage); } if (kcd->curr.edge) { @@ -4017,7 +4015,7 @@ static void knifetool_undo(KnifeTool_OpData *kcd) static void knifetool_init_obinfo(KnifeTool_OpData *kcd, Object *ob, - uint base_index, + uint ob_index, bool use_tri_indices) { @@ -4027,7 +4025,7 @@ static void knifetool_init_obinfo(KnifeTool_OpData *kcd, BM_mesh_elem_index_ensure(em_eval->bm, BM_VERT); - KnifeObjectInfo *obinfo = &kcd->objects_info[base_index]; + KnifeObjectInfo *obinfo = &kcd->objects_info[ob_index]; obinfo->em = em_eval; obinfo->cagecos = (const float(*)[3])BKE_editmesh_vert_coords_alloc( kcd->vc.depsgraph, em_eval, scene_eval, obedit_eval, NULL); @@ -4045,10 +4043,10 @@ static void knifetool_init_obinfo(KnifeTool_OpData *kcd, } } -static void knifetool_free_obinfo(KnifeTool_OpData *kcd, uint base_index) +static void knifetool_free_obinfo(KnifeTool_OpData *kcd, uint ob_index) { - MEM_SAFE_FREE(kcd->objects_info[base_index].cagecos); - MEM_SAFE_FREE(kcd->objects_info[base_index].tri_indices); + MEM_SAFE_FREE(kcd->objects_info[ob_index].cagecos); + MEM_SAFE_FREE(kcd->objects_info[ob_index].tri_indices); } /** \} */ @@ -4081,6 +4079,8 @@ static void knife_init_colors(KnifeColors *colors) /* called when modal loop selection gets set up... */ static void knifetool_init(ViewContext *vc, KnifeTool_OpData *kcd, + Object **objects, + const int objects_len, const bool only_select, const bool cut_through, const bool xray, @@ -4101,16 +4101,24 @@ static void knifetool_init(ViewContext *vc, kcd->scene = scene; kcd->region = vc->region; - kcd->objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data( - vc->scene, vc->view_layer, vc->v3d, &kcd->objects_len); + if (objects) { + kcd->objects = objects; + kcd->objects_len = objects_len; + kcd->objects_free = false; + } + else { + kcd->objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data( + vc->scene, vc->view_layer, vc->v3d, &kcd->objects_len); + kcd->objects_free = true; + } Object *ob; BMEditMesh *em; kcd->objects_info = MEM_callocN(sizeof(*kcd->objects_info) * kcd->objects_len, "knife cagecos"); - for (uint b = 0; b < kcd->objects_len; b++) { - ob = kcd->objects[b]; + for (uint ob_index = 0; ob_index < kcd->objects_len; ob_index++) { + ob = kcd->objects[ob_index]; em = BKE_editmesh_from_object(ob); - knifetool_init_obinfo(kcd, ob, b, use_tri_indices); + knifetool_init_obinfo(kcd, ob, ob_index, use_tri_indices); /* Can't usefully select resulting edges in face mode. */ kcd->select_result = (em->selectmode != SCE_SELECT_FACE); @@ -4225,7 +4233,9 @@ static void knifetool_exit_ex(KnifeTool_OpData *kcd) } /* Free object bases. */ - MEM_freeN(kcd->objects); + if (kcd->objects_free) { + MEM_freeN(kcd->objects); + } /* Destroy kcd itself. */ MEM_freeN(kcd); @@ -4318,9 +4328,15 @@ static void knifetool_finish_single_post(KnifeTool_OpData *UNUSED(kcd), Object * /* Called on tool confirmation. */ static void knifetool_finish_ex(KnifeTool_OpData *kcd) { - for (uint b = 0; b < kcd->objects_len; b++) { - Object *ob = kcd->objects[b]; + /* Separate pre/post passes are needed because `em->looptris` recalculation from the 'post' pass + * causes causes triangle indices in #KnifeTool_OpData.bvh to get out of sync. + * So perform all the cuts before doing any mesh recalculation, see: T101721. */ + for (uint ob_index = 0; ob_index < kcd->objects_len; ob_index++) { + Object *ob = kcd->objects[ob_index]; knifetool_finish_single_pre(kcd, ob); + } + for (uint ob_index = 0; ob_index < kcd->objects_len; ob_index++) { + Object *ob = kcd->objects[ob_index]; knifetool_finish_single_post(kcd, ob); } } @@ -4789,6 +4805,8 @@ static int knifetool_invoke(bContext *C, wmOperator *op, const wmEvent *event) knifetool_init(&vc, kcd, + NULL, + 0, only_select, cut_through, xray, @@ -4802,8 +4820,8 @@ static int knifetool_invoke(bContext *C, wmOperator *op, const wmEvent *event) BMEditMesh *em; bool faces_selected = false; - for (uint b = 0; b < kcd->objects_len; b++) { - obedit = kcd->objects[b]; + for (uint ob_index = 0; ob_index < kcd->objects_len; ob_index++) { + obedit = kcd->objects[ob_index]; em = BKE_editmesh_from_object(obedit); if (em->bm->totfacesel != 0) { faces_selected = true; @@ -4942,7 +4960,12 @@ static bool edbm_mesh_knife_point_isect(LinkNode *polys, const float cent_ss[2]) return false; } -void EDBM_mesh_knife(ViewContext *vc, LinkNode *polys, bool use_tag, bool cut_through) +void EDBM_mesh_knife(ViewContext *vc, + Object **objects, + const int objects_len, + LinkNode *polys, + bool use_tag, + bool cut_through) { KnifeTool_OpData *kcd; @@ -4959,6 +4982,8 @@ void EDBM_mesh_knife(ViewContext *vc, LinkNode *polys, bool use_tag, bool cut_th knifetool_init(vc, kcd, + objects, + objects_len, only_select, cut_through, xray, @@ -5000,18 +5025,21 @@ void EDBM_mesh_knife(ViewContext *vc, LinkNode *polys, bool use_tag, bool cut_th /* Finish. */ { - Object *ob; - BMEditMesh *em; - for (uint b = 0; b < kcd->objects_len; b++) { - - ob = kcd->objects[b]; - em = BKE_editmesh_from_object(ob); + /* See #knifetool_finish_ex for why multiple passes are needed. */ + for (uint ob_index = 0; ob_index < kcd->objects_len; ob_index++) { + Object *ob = kcd->objects[ob_index]; + BMEditMesh *em = BKE_editmesh_from_object(ob); if (use_tag) { BM_mesh_elem_hflag_enable_all(em->bm, BM_EDGE, BM_ELEM_TAG, false); } knifetool_finish_single_pre(kcd, ob); + } + + for (uint ob_index = 0; ob_index < kcd->objects_len; ob_index++) { + Object *ob = kcd->objects[ob_index]; + BMEditMesh *em = BKE_editmesh_from_object(ob); /* Tag faces inside! */ if (use_tag) { @@ -5104,9 +5132,12 @@ void EDBM_mesh_knife(ViewContext *vc, LinkNode *polys, bool use_tag, bool cut_th #undef F_ISECT_SET_UNKNOWN #undef F_ISECT_SET_OUTSIDE } + } + for (uint ob_index = 0; ob_index < kcd->objects_len; ob_index++) { /* Defer freeing data until the BVH tree is finished with, see: #point_is_visible and * the doc-string for #knifetool_finish_single_post. */ + Object *ob = kcd->objects[ob_index]; knifetool_finish_single_post(kcd, ob); } diff --git a/source/blender/editors/mesh/editmesh_knife_project.c b/source/blender/editors/mesh/editmesh_knife_project.c index e27d19ab000..6004a2943a2 100644 --- a/source/blender/editors/mesh/editmesh_knife_project.c +++ b/source/blender/editors/mesh/editmesh_knife_project.c @@ -132,22 +132,21 @@ static int knifeproject_exec(bContext *C, wmOperator *op) ViewContext vc; em_setup_viewcontext(C, &vc); - /* TODO: Ideally meshes would occlude each other, currently they don't - * since each knife-project runs as a separate operation. */ uint objects_len; Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data( vc.scene, vc.view_layer, vc.v3d, &objects_len); + + EDBM_mesh_knife(&vc, objects, objects_len, polys, true, cut_through); + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { Object *obedit = objects[ob_index]; ED_view3d_viewcontext_init_object(&vc, obedit); BMEditMesh *em = BKE_editmesh_from_object(obedit); - EDBM_mesh_knife(&vc, polys, true, cut_through); - /* select only tagged faces */ BM_mesh_elem_hflag_disable_all(em->bm, BM_VERT | BM_EDGE | BM_FACE, BM_ELEM_SELECT, false); - EDBM_selectmode_disable_multi(C, SCE_SELECT_VERTEX, SCE_SELECT_EDGE); + EDBM_selectmode_disable(scene, em, SCE_SELECT_VERTEX, SCE_SELECT_EDGE); BM_mesh_elem_hflag_enable_test(em->bm, BM_FACE, BM_ELEM_SELECT, true, false, BM_ELEM_TAG); diff --git a/source/blender/editors/mesh/editmesh_mask_extract.c b/source/blender/editors/mesh/editmesh_mask_extract.c index fdf4746ab09..070f748c78e 100644 --- a/source/blender/editors/mesh/editmesh_mask_extract.c +++ b/source/blender/editors/mesh/editmesh_mask_extract.c @@ -88,7 +88,7 @@ static int geometry_extract_apply(bContext *C, ED_object_sculptmode_exit(C, depsgraph); - BKE_sculpt_mask_layers_ensure(ob, NULL); + BKE_sculpt_mask_layers_ensure(depsgraph, bmain, ob, NULL); /* Ensures that deformation from sculpt mode is taken into account before duplicating the mesh to * extract the geometry. */ @@ -481,7 +481,7 @@ static int paint_mask_slice_exec(bContext *C, wmOperator *op) Object *ob = CTX_data_active_object(C); View3D *v3d = CTX_wm_view3d(C); - BKE_sculpt_mask_layers_ensure(ob, NULL); + BKE_sculpt_mask_layers_ensure(NULL, NULL, ob, NULL); Mesh *mesh = ob->data; Mesh *new_mesh = (Mesh *)BKE_id_copy(bmain, &mesh->id); diff --git a/source/blender/editors/mesh/editmesh_select.cc b/source/blender/editors/mesh/editmesh_select.cc index 0d1e3c08d84..76d0bab8a52 100644 --- a/source/blender/editors/mesh/editmesh_select.cc +++ b/source/blender/editors/mesh/editmesh_select.cc @@ -22,7 +22,9 @@ #include "BKE_customdata.h" #include "BKE_deform.h" #include "BKE_editmesh.h" +#include "BKE_editmesh_cache.h" #include "BKE_layer.h" +#include "BKE_mesh.h" #include "BKE_report.h" #include "WM_api.h" @@ -1065,8 +1067,8 @@ bool EDBM_unified_findnearest_from_raycast(ViewContext *vc, { Mesh *me_eval = (Mesh *)DEG_get_evaluated_id(vc->depsgraph, static_cast<ID *>(obedit->data)); - if (me_eval->runtime.edit_data) { - coords = me_eval->runtime.edit_data->vertexCos; + if (me_eval->runtime->edit_data) { + coords = me_eval->runtime->edit_data->vertexCos; } } diff --git a/source/blender/editors/mesh/editmesh_undo.cc b/source/blender/editors/mesh/editmesh_undo.cc index f5056021f7d..5c837e61a79 100644 --- a/source/blender/editors/mesh/editmesh_undo.cc +++ b/source/blender/editors/mesh/editmesh_undo.cc @@ -101,7 +101,7 @@ struct UndoMesh { int shapenr; #ifdef USE_ARRAY_STORE - /* N`ull arrays are considered empty */ + /* Null arrays are considered empty. */ struct { /* most data is stored as 'custom' data */ BArrayCustomData *vdata, *edata, *ldata, *pdata; BArrayState **keyblocks; @@ -231,7 +231,7 @@ static void um_arraystore_cd_compact(CustomData *cdata, } bcd->states[i] = BLI_array_store_state_add( - bs, layer->data, (size_t)data_len * stride, state_reference); + bs, layer->data, size_t(data_len) * stride, state_reference); } else { bcd->states[i] = nullptr; @@ -334,7 +334,7 @@ static void um_arraystore_compact_ex(UndoMesh *um, const UndoMesh *um_ref, bool um_ref->store.keyblocks[i] : nullptr; um->store.keyblocks[i] = BLI_array_store_state_add( - bs, keyblock->data, (size_t)keyblock->totelem * stride, state_reference); + bs, keyblock->data, size_t(keyblock->totelem) * stride, state_reference); } if (keyblock->data) { @@ -352,7 +352,7 @@ static void um_arraystore_compact_ex(UndoMesh *um, const UndoMesh *um_ref, bool BArrayStore *bs = BLI_array_store_at_size_ensure( &um_arraystore.bs_stride, stride, ARRAY_CHUNK_SIZE); um->store.mselect = BLI_array_store_state_add( - bs, me->mselect, (size_t)me->totselect * stride, state_reference); + bs, me->mselect, size_t(me->totselect) * stride, state_reference); } /* keep me->totselect for validation */ @@ -597,6 +597,11 @@ static void *undomesh_from_editmesh(UndoMesh *um, BMEditMesh *em, Key *key, Undo * on it. Necessary to use the attribute API. */ strcpy(um->me.id.name, "MEundomesh_from_editmesh"); + /* Runtime data is necessary for some asserts in other code, and the overhead of creating it for + * undo meshes should be low. */ + BLI_assert(um->me.runtime == nullptr); + um->me.runtime = new blender::bke::MeshRuntime(); + CustomData_MeshMasks cd_mask_extra{}; cd_mask_extra.vmask = CD_MASK_SHAPE_KEYINDEX; BMeshToMeshParams params{}; diff --git a/source/blender/editors/mesh/mesh_intern.h b/source/blender/editors/mesh/mesh_intern.h index e6505715324..0e20bb18595 100644 --- a/source/blender/editors/mesh/mesh_intern.h +++ b/source/blender/editors/mesh/mesh_intern.h @@ -18,6 +18,7 @@ struct BMElem; struct BMOperator; struct EnumPropertyItem; struct LinkNode; +struct Object; struct bContext; struct wmKeyConfig; struct wmKeyMap; @@ -177,6 +178,8 @@ void MESH_OT_knife_project(struct wmOperatorType *ot); * \param use_tag: When set, tag all faces inside the polylines. */ void EDBM_mesh_knife(struct ViewContext *vc, + struct Object **objects, + int objects_len, struct LinkNode *polys, bool use_tag, bool cut_through); diff --git a/source/blender/editors/mesh/meshtools.cc b/source/blender/editors/mesh/meshtools.cc index 2210cc67bff..4d75ab7f041 100644 --- a/source/blender/editors/mesh/meshtools.cc +++ b/source/blender/editors/mesh/meshtools.cc @@ -1335,7 +1335,7 @@ struct VertPickData { static void ed_mesh_pick_vert__mapFunc(void *userData, int index, const float co[3], - const float UNUSED(no[3])) + const float /*no*/[3]) { VertPickData *data = static_cast<VertPickData *>(userData); if (data->hide_vert && data->hide_vert[index]) { diff --git a/source/blender/editors/object/object_add.cc b/source/blender/editors/object/object_add.cc index 3ce39b695e0..f6eee7c0c9e 100644 --- a/source/blender/editors/object/object_add.cc +++ b/source/blender/editors/object/object_add.cc @@ -3556,11 +3556,62 @@ void OBJECT_OT_convert(wmOperatorType *ot) /** \name Duplicate Object Operator * \{ */ +static void object_add_sync_base_collection( + Main *bmain, Scene *scene, ViewLayer *view_layer, Base *base_src, Object *object_new) +{ + if ((base_src != nullptr) && (base_src->flag & BASE_ENABLED_AND_MAYBE_VISIBLE_IN_VIEWPORT)) { + BKE_collection_object_add_from(bmain, scene, base_src->object, object_new); + } + else { + LayerCollection *layer_collection = BKE_layer_collection_get_active(view_layer); + BKE_collection_object_add(bmain, layer_collection->collection, object_new); + } +} + +static void object_add_sync_local_view(Base *base_src, Base *base_new) +{ + base_new->local_view_bits = base_src->local_view_bits; +} + +static void object_add_sync_rigid_body(Main *bmain, Object *object_src, Object *object_new) +{ + /* 1) duplis should end up in same collection as the original + * 2) Rigid Body sim participants MUST always be part of a collection... + */ + /* XXX: is 2) really a good measure here? */ + if (object_src->rigidbody_object || object_src->rigidbody_constraint) { + LISTBASE_FOREACH (Collection *, collection, &bmain->collections) { + if (BKE_collection_has_object(collection, object_src)) { + BKE_collection_object_add(bmain, collection, object_new); + } + } + } +} + /** * - Assumes `id.new` is correct. * - Leaves selection of base/object unaltered. * - Sets #ID.newid pointers. */ +static void object_add_duplicate_internal(Main *bmain, + Object *ob, + const eDupli_ID_Flags dupflag, + const eLibIDDuplicateFlags duplicate_options, + Object **r_ob_new) +{ + if (ob->mode & OB_MODE_POSE) { + return; + } + + Object *obn = static_cast<Object *>( + ID_NEW_SET(ob, BKE_object_duplicate(bmain, ob, dupflag, duplicate_options))); + if (r_ob_new) { + *r_ob_new = obn; + } + DEG_id_tag_update(&obn->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); + return; +} + static Base *object_add_duplicate_internal(Main *bmain, Scene *scene, ViewLayer *view_layer, @@ -3569,49 +3620,25 @@ static Base *object_add_duplicate_internal(Main *bmain, const eLibIDDuplicateFlags duplicate_options, Object **r_ob_new) { - Base *base, *basen = nullptr; - Object *obn; - - if (ob->mode & OB_MODE_POSE) { - /* nothing? */ + Object *object_new = nullptr; + object_add_duplicate_internal(bmain, ob, dupflag, duplicate_options, &object_new); + if (r_ob_new) { + *r_ob_new = object_new; + } + if (object_new == nullptr) { + return nullptr; } - else { - obn = static_cast<Object *>( - ID_NEW_SET(ob, BKE_object_duplicate(bmain, ob, dupflag, duplicate_options))); - if (r_ob_new) { - *r_ob_new = obn; - } - DEG_id_tag_update(&obn->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); - - BKE_view_layer_synced_ensure(scene, view_layer); - base = BKE_view_layer_base_find(view_layer, ob); - if ((base != nullptr) && (base->flag & BASE_ENABLED_AND_MAYBE_VISIBLE_IN_VIEWPORT)) { - BKE_collection_object_add_from(bmain, scene, ob, obn); - } - else { - LayerCollection *layer_collection = BKE_layer_collection_get_active(view_layer); - BKE_collection_object_add(bmain, layer_collection->collection, obn); - } - - BKE_view_layer_synced_ensure(scene, view_layer); - basen = BKE_view_layer_base_find(view_layer, obn); - if (base != nullptr && basen != nullptr) { - basen->local_view_bits = base->local_view_bits; - } - /* 1) duplis should end up in same collection as the original - * 2) Rigid Body sim participants MUST always be part of a collection... - */ - /* XXX: is 2) really a good measure here? */ - if (ob->rigidbody_object || ob->rigidbody_constraint) { - LISTBASE_FOREACH (Collection *, collection, &bmain->collections) { - if (BKE_collection_has_object(collection, ob)) { - BKE_collection_object_add(bmain, collection, obn); - } - } - } + BKE_view_layer_synced_ensure(scene, view_layer); + Base *base_src = BKE_view_layer_base_find(view_layer, ob); + object_add_sync_base_collection(bmain, scene, view_layer, base_src, object_new); + BKE_view_layer_synced_ensure(scene, view_layer); + Base *base_new = BKE_view_layer_base_find(view_layer, object_new); + if (base_src && base_new) { + object_add_sync_local_view(base_src, base_new); } - return basen; + object_add_sync_rigid_body(bmain, ob, object_new); + return base_new; } Base *ED_object_add_duplicate( @@ -3665,70 +3692,70 @@ static int duplicate_exec(bContext *C, wmOperator *op) * we also want to remap pointers between those... */ BKE_main_id_newptr_and_tag_clear(bmain); - /* Do not do collection re-syncs for each object; will do it once afterwards. - * However this means we can't get to new duplicated Base's immediately, will - * have to process them after the sync. */ - BKE_layer_collection_resync_forbid(); - /* Duplicate the selected objects, remember data needed to process - * after the sync (the base of the original object, and the copy of the - * original object). */ - blender::Vector<std::pair<Base *, Object *>> source_bases_new_objects; - Object *ob_new_active = nullptr; + * after the sync. */ + struct DuplicateObjectLink { + Base *base_src = nullptr; + Object *object_new = nullptr; + + DuplicateObjectLink(Base *base_src) : base_src(base_src) + { + } + }; + blender::Vector<DuplicateObjectLink> object_base_links; CTX_DATA_BEGIN (C, Base *, base, selected_bases) { - Object *ob_new = nullptr; + object_base_links.append(DuplicateObjectLink(base)); + } + CTX_DATA_END; + + bool new_objects_created = false; + for (DuplicateObjectLink &link : object_base_links) { object_add_duplicate_internal(bmain, - scene, - view_layer, - base->object, + link.base_src->object, dupflag, LIB_ID_DUPLICATE_IS_SUBPROCESS | LIB_ID_DUPLICATE_IS_ROOT_ID, - &ob_new); - if (ob_new == nullptr) { - continue; - } - source_bases_new_objects.append({base, ob_new}); - - /* note that this is safe to do with this context iterator, - * the list is made in advance */ - ED_object_base_select(base, BA_DESELECT); - - /* new object will become active */ - BKE_view_layer_synced_ensure(scene, view_layer); - if (BKE_view_layer_active_base_get(view_layer) == base) { - ob_new_active = ob_new; + &link.object_new); + if (link.object_new) { + new_objects_created = true; } } - CTX_DATA_END; - BKE_layer_collection_resync_allow(); - if (source_bases_new_objects.is_empty()) { + if (!new_objects_created) { return OPERATOR_CANCELLED; } - /* Sync the collection now, after everything is duplicated. */ - BKE_main_collection_sync(bmain); + /* Sync that could tag the view_layer out of sync. */ + for (DuplicateObjectLink &link : object_base_links) { + /* note that this is safe to do with this context iterator, + * the list is made in advance */ + ED_object_base_select(link.base_src, BA_DESELECT); + if (link.object_new) { + object_add_sync_base_collection(bmain, scene, view_layer, link.base_src, link.object_new); + object_add_sync_rigid_body(bmain, link.base_src->object, link.object_new); + } + } - /* After sync we can get to the new Base data, process it here. */ - for (const auto &item : source_bases_new_objects) { - Object *ob_new = item.second; - Base *base_source = item.first; - BKE_view_layer_synced_ensure(scene, view_layer); - Base *base_new = BKE_view_layer_base_find(view_layer, ob_new); - if (base_new == nullptr) { + /* Sync the view layer. Everything else should not tag the view_layer out of sync. */ + BKE_view_layer_synced_ensure(scene, view_layer); + const Base *active_base = BKE_view_layer_active_base_get(view_layer); + for (DuplicateObjectLink &link : object_base_links) { + if (!link.object_new) { continue; } + + Base *base_new = BKE_view_layer_base_find(view_layer, link.object_new); + BLI_assert(base_new); ED_object_base_select(base_new, BA_SELECT); - if (ob_new == ob_new_active) { + if (active_base == link.base_src) { ED_object_base_activate(C, base_new); } - if (base_new->object->data) { - DEG_id_tag_update(static_cast<ID *>(base_new->object->data), 0); + + if (link.object_new->data) { + DEG_id_tag_update(static_cast<ID *>(link.object_new->data), 0); } - /* #object_add_duplicate_internal will not have done this, since - * before the collection sync it would not have found the new base yet. */ - base_new->local_view_bits = base_source->local_view_bits; + + object_add_sync_local_view(link.base_src, base_new); } /* Note that this will also clear newid pointers and tags. */ diff --git a/source/blender/editors/object/object_modifier.cc b/source/blender/editors/object/object_modifier.cc index 872d2367332..85a35861329 100644 --- a/source/blender/editors/object/object_modifier.cc +++ b/source/blender/editors/object/object_modifier.cc @@ -21,6 +21,7 @@ #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" #include "DNA_object_force_types.h" +#include "DNA_pointcloud_types.h" #include "DNA_scene_types.h" #include "DNA_space_types.h" @@ -217,7 +218,7 @@ ModifierData *ED_object_modifier_add( if (ob->mode & OB_MODE_SCULPT) { /* ensure that grid paint mask layer is created */ - BKE_sculpt_mask_layers_ensure(ob, (MultiresModifierData *)new_md); + BKE_sculpt_mask_layers_ensure(nullptr, nullptr, ob, (MultiresModifierData *)new_md); } } else if (type == eModifierType_Skin) { @@ -788,7 +789,9 @@ static bool modifier_apply_obdata( if (ELEM(mti->type, eModifierTypeType_Constructive, eModifierTypeType_Nonconstructive)) { BKE_report( - reports, RPT_ERROR, "Transform curve to mesh in order to apply constructive modifiers"); + reports, + RPT_ERROR, + "Cannot apply constructive modifiers on curve. Convert curve to mesh in order to apply"); return false; } @@ -855,8 +858,38 @@ static bool modifier_apply_obdata( Main *bmain = DEG_get_bmain(depsgraph); BKE_object_material_from_eval_data(bmain, ob, &curves_eval.id); } + else if (ob->type == OB_POINTCLOUD) { + PointCloud &points = *static_cast<PointCloud *>(ob->data); + if (mti->modifyGeometrySet == nullptr) { + BLI_assert_unreachable(); + return false; + } + + /* Create a temporary geometry set and component. */ + GeometrySet geometry_set; + geometry_set.get_component_for_write<PointCloudComponent>().replace( + &points, GeometryOwnershipType::ReadOnly); + + ModifierEvalContext mectx = {depsgraph, ob, (ModifierApplyFlag)0}; + mti->modifyGeometrySet(md_eval, &mectx, &geometry_set); + if (!geometry_set.has_pointcloud()) { + BKE_report( + reports, RPT_ERROR, "Evaluated geometry from modifier does not contain a point cloud"); + return false; + } + PointCloud *pointcloud_eval = + geometry_set.get_component_for_write<PointCloudComponent>().release(); + + /* Anonymous attributes shouldn't be available on the applied geometry. */ + pointcloud_eval->attributes_for_write().remove_anonymous(); + + /* Copy the relevant information to the original. */ + Main *bmain = DEG_get_bmain(depsgraph); + BKE_object_material_from_eval_data(bmain, ob, &pointcloud_eval->id); + BKE_pointcloud_nomain_to_pointcloud(pointcloud_eval, &points, true); + } else { - /* TODO: implement for point clouds and volumes. */ + /* TODO: implement for volumes. */ BKE_report(reports, RPT_ERROR, "Cannot apply modifier for this object type"); return false; } @@ -2008,7 +2041,8 @@ static int multires_subdivide_exec(bContext *C, wmOperator *op) if (object->mode & OB_MODE_SCULPT) { /* ensure that grid paint mask layer is created */ - BKE_sculpt_mask_layers_ensure(object, mmd); + BKE_sculpt_mask_layers_ensure( + CTX_data_ensure_evaluated_depsgraph(C), CTX_data_main(C), object, mmd); } return OPERATOR_FINISHED; diff --git a/source/blender/editors/object/object_relations.c b/source/blender/editors/object/object_relations.c index 2f18922f4ee..5da19d76259 100644 --- a/source/blender/editors/object/object_relations.c +++ b/source/blender/editors/object/object_relations.c @@ -2318,26 +2318,39 @@ static int make_override_library_exec(bContext *C, wmOperator *op) id_root = &collection->id; user_overrides_from_selected_objects = true; } - /* Else, poll func ensures us that ID_IS_LINKED(obact) is true. */ + /* Else, poll func ensures us that ID_IS_LINKED(obact) is true, or that it is already an existing + * liboverride. */ else { + BLI_assert(ID_IS_LINKED(obact) || ID_IS_OVERRIDE_LIBRARY_REAL(obact)); id_root = &obact->id; user_overrides_from_selected_objects = true; } - const bool do_fully_editable = !user_overrides_from_selected_objects; - - GSet *user_overrides_objects_uids = do_fully_editable ? NULL : - BLI_gset_new(BLI_ghashutil_inthash_p, - BLI_ghashutil_intcmp, - __func__); - /* Make already existing selected liboverrides editable. */ + bool is_active_override = false; FOREACH_SELECTED_OBJECT_BEGIN (view_layer, CTX_wm_view3d(C), ob_iter) { if (ID_IS_OVERRIDE_LIBRARY_REAL(ob_iter) && !ID_IS_LINKED(ob_iter)) { ob_iter->id.override_library->flag &= ~IDOVERRIDE_LIBRARY_FLAG_SYSTEM_DEFINED; + is_active_override = is_active_override || (&ob_iter->id == id_root); + DEG_id_tag_update(&ob_iter->id, ID_RECALC_COPY_ON_WRITE); } } FOREACH_SELECTED_OBJECT_END; + /* If the active object is a liboverride, there is no point going further, since in the weird + * case where some other selected objects would be linked ones, there is no way to properly + * create overrides for them currently. + * + * Could be added later if really needed, but would rather avoid that extra complexity here. */ + if (is_active_override) { + return OPERATOR_FINISHED; + } + + const bool do_fully_editable = !user_overrides_from_selected_objects; + + GSet *user_overrides_objects_uids = do_fully_editable ? NULL : + BLI_gset_new(BLI_ghashutil_inthash_p, + BLI_ghashutil_intcmp, + __func__); if (do_fully_editable) { /* Pass. */ diff --git a/source/blender/editors/object/object_vgroup.cc b/source/blender/editors/object/object_vgroup.cc index a63e06d6866..d874226f04e 100644 --- a/source/blender/editors/object/object_vgroup.cc +++ b/source/blender/editors/object/object_vgroup.cc @@ -2376,22 +2376,6 @@ void ED_vgroup_mirror(Object *ob, /* TODO: vgroup locking. * TODO: face masking. */ -#define VGROUP_MIRR_OP \ - dvert_mirror_op(dvert, \ - dvert_mirr, \ - sel, \ - sel_mirr, \ - flip_map, \ - flip_map_len, \ - mirror_weights, \ - flip_vgroups, \ - all_vgroups, \ - def_nr) - - BMVert *eve, *eve_mirr; - MDeformVert *dvert_mirr; - char sel, sel_mirr; - int *flip_map = nullptr, flip_map_len; const int def_nr = BKE_object_defgroup_active_index_get(ob) - 1; int totmirr = 0, totfail = 0; @@ -2404,6 +2388,8 @@ void ED_vgroup_mirror(Object *ob, return; } + int *flip_map = nullptr; + int flip_map_len; if (flip_vgroups) { flip_map = all_vgroups ? BKE_object_defgroup_flip_map(ob, false, &flip_map_len) : BKE_object_defgroup_flip_map_single(ob, false, def_nr, &flip_map_len); @@ -2438,21 +2424,27 @@ void ED_vgroup_mirror(Object *ob, BM_mesh_elem_hflag_disable_all(em->bm, BM_VERT, BM_ELEM_TAG, false); /* Go through the list of edit-vertices and assign them. */ + BMVert *eve, *eve_mirr; BM_ITER_MESH (eve, &iter, em->bm, BM_VERTS_OF_MESH) { if (!BM_elem_flag_test(eve, BM_ELEM_TAG)) { if ((eve_mirr = EDBM_verts_mirror_get(em, eve))) { if (eve_mirr != eve) { if (!BM_elem_flag_test(eve_mirr, BM_ELEM_TAG)) { - sel = BM_elem_flag_test(eve, BM_ELEM_SELECT); - sel_mirr = BM_elem_flag_test(eve_mirr, BM_ELEM_SELECT); + const bool sel = BM_elem_flag_test(eve, BM_ELEM_SELECT); + const bool sel_mirr = BM_elem_flag_test(eve_mirr, BM_ELEM_SELECT); if ((sel || sel_mirr) && (eve != eve_mirr)) { - MDeformVert *dvert = static_cast<MDeformVert *>( - BM_ELEM_CD_GET_VOID_P(eve, cd_dvert_offset)); - dvert_mirr = static_cast<MDeformVert *>( - BM_ELEM_CD_GET_VOID_P(eve_mirr, cd_dvert_offset)); - - VGROUP_MIRR_OP; + dvert_mirror_op( + static_cast<MDeformVert *>(BM_ELEM_CD_GET_VOID_P(eve, cd_dvert_offset)), + static_cast<MDeformVert *>(BM_ELEM_CD_GET_VOID_P(eve_mirr, cd_dvert_offset)), + sel, + sel_mirr, + flip_map, + flip_map_len, + mirror_weights, + flip_vgroups, + all_vgroups, + def_nr); totmirr++; } @@ -2471,39 +2463,38 @@ void ED_vgroup_mirror(Object *ob, } else { /* object mode / weight paint */ - int vidx, vidx_mirr; const bool use_vert_sel = (me->editflag & ME_EDIT_PAINT_VERT_SEL) != 0; if (me->deform_verts().is_empty()) { goto cleanup; } - if (!use_vert_sel) { - sel = sel_mirr = true; - } - BLI_bitmap *vert_tag = BLI_BITMAP_NEW(me->totvert, __func__); MutableSpan<MDeformVert> dverts = me->deform_verts_for_write(); const bke::AttributeAccessor attributes = me->attributes(); const VArray<bool> select_vert = attributes.lookup_or_default<bool>( ".select_vert", ATTR_DOMAIN_POINT, false); - for (vidx = 0; vidx < me->totvert; vidx++) { + for (int vidx = 0; vidx < me->totvert; vidx++) { if (!BLI_BITMAP_TEST(vert_tag, vidx)) { + int vidx_mirr; if ((vidx_mirr = mesh_get_x_mirror_vert(ob, nullptr, vidx, use_topology)) != -1) { if (vidx != vidx_mirr) { if (!BLI_BITMAP_TEST(vert_tag, vidx_mirr)) { - - if (use_vert_sel) { - sel = select_vert[vidx]; - sel_mirr = select_vert[vidx_mirr]; - } + const bool sel = use_vert_sel ? select_vert[vidx] : true; + const bool sel_mirr = use_vert_sel ? select_vert[vidx_mirr] : true; if (sel || sel_mirr) { - MDeformVert *dvert = &dverts[vidx]; - dvert_mirr = &dvert[vidx_mirr]; - - VGROUP_MIRR_OP; + dvert_mirror_op(&dverts[vidx], + &dverts[vidx_mirr], + sel, + sel_mirr, + flip_map, + flip_map_len, + mirror_weights, + flip_vgroups, + all_vgroups, + def_nr); totmirr++; } @@ -2523,9 +2514,6 @@ void ED_vgroup_mirror(Object *ob, } else if (ob->type == OB_LATTICE) { Lattice *lt = vgroup_edit_lattice(ob); - int i1, i2; - int u, v, w; - int pntsu_half; /* half but found up odd value */ if (lt->pntsu == 1 || lt->dvert == nullptr) { @@ -2535,29 +2523,33 @@ void ED_vgroup_mirror(Object *ob, /* unlike editmesh we know that by only looping over the first half of * the 'u' indices it will cover all points except the middle which is * ok in this case */ - pntsu_half = lt->pntsu / 2; + int pntsu_half = lt->pntsu / 2; - for (w = 0; w < lt->pntsw; w++) { - for (v = 0; v < lt->pntsv; v++) { - for (u = 0; u < pntsu_half; u++) { + for (int w = 0; w < lt->pntsw; w++) { + for (int v = 0; v < lt->pntsv; v++) { + for (int u = 0; u < pntsu_half; u++) { int u_inv = (lt->pntsu - 1) - u; if (u != u_inv) { - BPoint *bp, *bp_mirr; + const int i1 = BKE_lattice_index_from_uvw(lt, u, v, w); + const int i2 = BKE_lattice_index_from_uvw(lt, u_inv, v, w); - i1 = BKE_lattice_index_from_uvw(lt, u, v, w); - i2 = BKE_lattice_index_from_uvw(lt, u_inv, v, w); + const BPoint *bp = <->def[i1]; + const BPoint *bp_mirr = <->def[i2]; - bp = <->def[i1]; - bp_mirr = <->def[i2]; - - sel = bp->f1 & SELECT; - sel_mirr = bp_mirr->f1 & SELECT; + const bool sel = bp->f1 & SELECT; + const bool sel_mirr = bp_mirr->f1 & SELECT; if (sel || sel_mirr) { - MDeformVert *dvert = <->dvert[i1]; - dvert_mirr = <->dvert[i2]; - - VGROUP_MIRR_OP; + dvert_mirror_op(<->dvert[i1], + <->dvert[i2], + sel, + sel_mirr, + flip_map, + flip_map_len, + mirror_weights, + flip_vgroups, + all_vgroups, + def_nr); totmirr++; } } diff --git a/source/blender/editors/physics/dynamicpaint_ops.c b/source/blender/editors/physics/dynamicpaint_ops.c index 1ce90849a88..41238eb171b 100644 --- a/source/blender/editors/physics/dynamicpaint_ops.c +++ b/source/blender/editors/physics/dynamicpaint_ops.c @@ -405,7 +405,7 @@ static void dynamicPaint_bakeImageSequence(DynamicPaintBakeJob *job) /* primary output layer */ if (surface->flags & MOD_DPAINT_OUT1) { /* set filepath */ - BLI_join_dirfile( + BLI_path_join( filepath, sizeof(filepath), surface->image_output_path, surface->output_name); BLI_path_frame(filepath, frame, 4); @@ -415,7 +415,7 @@ static void dynamicPaint_bakeImageSequence(DynamicPaintBakeJob *job) /* secondary output */ if (surface->flags & MOD_DPAINT_OUT2 && surface->type == MOD_DPAINT_SURFACE_T_PAINT) { /* set filepath */ - BLI_join_dirfile( + BLI_path_join( filepath, sizeof(filepath), surface->image_output_path, surface->output_name2); BLI_path_frame(filepath, frame, 4); diff --git a/source/blender/editors/physics/particle_edit.c b/source/blender/editors/physics/particle_edit.c index 761a6702c6f..e6d0aca7902 100644 --- a/source/blender/editors/physics/particle_edit.c +++ b/source/blender/editors/physics/particle_edit.c @@ -3533,7 +3533,7 @@ static void PE_mirror_x(Depsgraph *depsgraph, Scene *scene, Object *ob, int tagg } const bool use_dm_final_indices = (psys->part->use_modifier_stack && - !psmd_eval->mesh_final->runtime.deformed_only); + !BKE_mesh_is_deformed_only(psmd_eval->mesh_final)); /* NOTE: this is not nice to use tessfaces but hard to avoid since pa->num uses tessfaces */ BKE_mesh_tessface_ensure(me); @@ -4353,7 +4353,7 @@ static void brush_add_count_iter(void *__restrict iter_data_v, 0, 0, 0)) { - if (psys->part->use_modifier_stack && !psmd_eval->mesh_final->runtime.deformed_only) { + if (psys->part->use_modifier_stack && !BKE_mesh_is_deformed_only(psmd_eval->mesh_final)) { add_pars[iter].num = add_pars[iter].num_dmcache; add_pars[iter].num_dmcache = DMCACHE_ISCHILD; } @@ -4430,7 +4430,7 @@ static int brush_add(const bContext *C, PEData *data, short number) timestep = psys_get_timestep(&sim); - if (psys->part->use_modifier_stack || psmd_eval->mesh_final->runtime.deformed_only) { + if (psys->part->use_modifier_stack || BKE_mesh_is_deformed_only(psmd_eval->mesh_final)) { mesh = psmd_eval->mesh_final; } else { diff --git a/source/blender/editors/physics/particle_object.c b/source/blender/editors/physics/particle_object.c index 08db03db0e9..210757173eb 100644 --- a/source/blender/editors/physics/particle_object.c +++ b/source/blender/editors/physics/particle_object.c @@ -732,7 +732,7 @@ static bool remap_hair_emitter(Depsgraph *depsgraph, invert_m4_m4(to_imat, to_mat); const bool use_dm_final_indices = (target_psys->part->use_modifier_stack && - !target_psmd->mesh_final->runtime.deformed_only); + !BKE_mesh_is_deformed_only(target_psmd->mesh_final)); if (use_dm_final_indices || !target_psmd->mesh_original) { mesh = target_psmd->mesh_final; @@ -1204,9 +1204,7 @@ static bool copy_particle_systems_to_object(const bContext *C, #undef PSYS_FROM_FIRST #undef PSYS_FROM_NEXT - if (duplicate_settings) { - DEG_relations_tag_update(bmain); - } + DEG_relations_tag_update(bmain); DEG_id_tag_update(&ob_to->id, ID_RECALC_GEOMETRY); WM_main_add_notifier(NC_OBJECT | ND_PARTICLE | NA_EDITED, ob_to); return true; @@ -1269,8 +1267,7 @@ static int copy_particle_systems_exec(bContext *C, wmOperator *op) CTX_DATA_END; if (changed_tot > 0) { - Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C); - DEG_graph_tag_relations_update(depsgraph); + DEG_relations_tag_update(CTX_data_main(C)); } if ((changed_tot == 0 && fail == 0) || fail) { diff --git a/source/blender/editors/physics/physics_fluid.c b/source/blender/editors/physics/physics_fluid.c index 07e8f5c5743..314796e96e4 100644 --- a/source/blender/editors/physics/physics_fluid.c +++ b/source/blender/editors/physics/physics_fluid.c @@ -368,7 +368,7 @@ static void fluid_bake_startjob(void *customdata, short *stop, short *do_update, BKE_spacedata_draw_locks(true); if (fluid_is_bake_noise(job) || fluid_is_bake_all(job)) { - BLI_path_join(temp_dir, sizeof(temp_dir), fds->cache_directory, FLUID_DOMAIN_DIR_NOISE, NULL); + BLI_path_join(temp_dir, sizeof(temp_dir), fds->cache_directory, FLUID_DOMAIN_DIR_NOISE); BLI_path_abs(temp_dir, relbase); BLI_dir_create_recursive(temp_dir); /* Create 'noise' subdir if it does not exist already */ fds->cache_flag &= ~(FLUID_DOMAIN_BAKED_NOISE | FLUID_DOMAIN_OUTDATED_NOISE); @@ -376,7 +376,7 @@ static void fluid_bake_startjob(void *customdata, short *stop, short *do_update, job->pause_frame = &fds->cache_frame_pause_noise; } if (fluid_is_bake_mesh(job) || fluid_is_bake_all(job)) { - BLI_path_join(temp_dir, sizeof(temp_dir), fds->cache_directory, FLUID_DOMAIN_DIR_MESH, NULL); + BLI_path_join(temp_dir, sizeof(temp_dir), fds->cache_directory, FLUID_DOMAIN_DIR_MESH); BLI_path_abs(temp_dir, relbase); BLI_dir_create_recursive(temp_dir); /* Create 'mesh' subdir if it does not exist already */ fds->cache_flag &= ~(FLUID_DOMAIN_BAKED_MESH | FLUID_DOMAIN_OUTDATED_MESH); @@ -384,8 +384,7 @@ static void fluid_bake_startjob(void *customdata, short *stop, short *do_update, job->pause_frame = &fds->cache_frame_pause_mesh; } if (fluid_is_bake_particle(job) || fluid_is_bake_all(job)) { - BLI_path_join( - temp_dir, sizeof(temp_dir), fds->cache_directory, FLUID_DOMAIN_DIR_PARTICLES, NULL); + BLI_path_join(temp_dir, sizeof(temp_dir), fds->cache_directory, FLUID_DOMAIN_DIR_PARTICLES); BLI_path_abs(temp_dir, relbase); /* Create 'particles' subdir if it does not exist already */ @@ -396,7 +395,7 @@ static void fluid_bake_startjob(void *customdata, short *stop, short *do_update, job->pause_frame = &fds->cache_frame_pause_particles; } if (fluid_is_bake_guiding(job) || fluid_is_bake_all(job)) { - BLI_path_join(temp_dir, sizeof(temp_dir), fds->cache_directory, FLUID_DOMAIN_DIR_GUIDE, NULL); + BLI_path_join(temp_dir, sizeof(temp_dir), fds->cache_directory, FLUID_DOMAIN_DIR_GUIDE); BLI_path_abs(temp_dir, relbase); BLI_dir_create_recursive(temp_dir); /* Create 'guiding' subdir if it does not exist already */ fds->cache_flag &= ~(FLUID_DOMAIN_BAKED_GUIDE | FLUID_DOMAIN_OUTDATED_GUIDE); @@ -404,11 +403,11 @@ static void fluid_bake_startjob(void *customdata, short *stop, short *do_update, job->pause_frame = &fds->cache_frame_pause_guide; } if (fluid_is_bake_data(job) || fluid_is_bake_all(job)) { - BLI_path_join(temp_dir, sizeof(temp_dir), fds->cache_directory, FLUID_DOMAIN_DIR_CONFIG, NULL); + BLI_path_join(temp_dir, sizeof(temp_dir), fds->cache_directory, FLUID_DOMAIN_DIR_CONFIG); BLI_path_abs(temp_dir, relbase); BLI_dir_create_recursive(temp_dir); /* Create 'config' subdir if it does not exist already */ - BLI_path_join(temp_dir, sizeof(temp_dir), fds->cache_directory, FLUID_DOMAIN_DIR_DATA, NULL); + BLI_path_join(temp_dir, sizeof(temp_dir), fds->cache_directory, FLUID_DOMAIN_DIR_DATA); BLI_path_abs(temp_dir, relbase); BLI_dir_create_recursive(temp_dir); /* Create 'data' subdir if it does not exist already */ fds->cache_flag &= ~(FLUID_DOMAIN_BAKED_DATA | FLUID_DOMAIN_OUTDATED_DATA); @@ -416,8 +415,7 @@ static void fluid_bake_startjob(void *customdata, short *stop, short *do_update, job->pause_frame = &fds->cache_frame_pause_data; if (fds->flags & FLUID_DOMAIN_EXPORT_MANTA_SCRIPT) { - BLI_path_join( - temp_dir, sizeof(temp_dir), fds->cache_directory, FLUID_DOMAIN_DIR_SCRIPT, NULL); + BLI_path_join(temp_dir, sizeof(temp_dir), fds->cache_directory, FLUID_DOMAIN_DIR_SCRIPT); BLI_path_abs(temp_dir, relbase); BLI_dir_create_recursive(temp_dir); /* Create 'script' subdir if it does not exist already */ } diff --git a/source/blender/editors/render/render_preview.cc b/source/blender/editors/render/render_preview.cc index ad24f46f923..140199209da 100644 --- a/source/blender/editors/render/render_preview.cc +++ b/source/blender/editors/render/render_preview.cc @@ -1325,7 +1325,7 @@ static ImBuf *icon_preview_imbuf_from_brush(Brush *brush) const char *brushicons_dir = BKE_appdir_folder_id(BLENDER_DATAFILES, "brushicons"); /* Expected to be found, but don't crash if it's not. */ if (brushicons_dir) { - BLI_join_dirfile(filepath, sizeof(filepath), brushicons_dir, brush->icon_filepath); + BLI_path_join(filepath, sizeof(filepath), brushicons_dir, brush->icon_filepath); /* Use default color spaces. */ brush->icon_imbuf = IMB_loadiffname(filepath, flags, nullptr); diff --git a/source/blender/editors/render/render_shading.cc b/source/blender/editors/render/render_shading.cc index 25d5ceb8216..83ce447e8cf 100644 --- a/source/blender/editors/render/render_shading.cc +++ b/source/blender/editors/render/render_shading.cc @@ -879,7 +879,7 @@ static int new_world_exec(bContext *C, wmOperator * /*op*/) wo = new_wo; } else { - wo = BKE_world_add(bmain, DATA_("World")); + wo = BKE_world_add(bmain, CTX_DATA_(BLT_I18NCONTEXT_ID_WORLD, "World")); ED_node_shader_default(C, &wo->id); wo->use_nodes = true; } diff --git a/source/blender/editors/scene/scene_edit.c b/source/blender/editors/scene/scene_edit.c index f19017cb723..01877deb716 100644 --- a/source/blender/editors/scene/scene_edit.c +++ b/source/blender/editors/scene/scene_edit.c @@ -286,6 +286,7 @@ static void SCENE_OT_new(wmOperatorType *ot) /* properties */ ot->prop = RNA_def_enum(ot->srna, "type", scene_new_items, SCE_COPY_NEW, "Type", ""); + RNA_def_property_translation_context(ot->prop, BLT_I18NCONTEXT_ID_SCENE); } /** \} */ diff --git a/source/blender/editors/screen/area.c b/source/blender/editors/screen/area.c index 39e3c7a2f0a..8db968cbb8a 100644 --- a/source/blender/editors/screen/area.c +++ b/source/blender/editors/screen/area.c @@ -187,7 +187,8 @@ static void area_draw_azone_fullscreen( min_ff(alpha, 0.75f), 0.0f, NULL, - false); + false, + UI_NO_ICON_OVERLAY_TEXT); } /** diff --git a/source/blender/editors/screen/workspace_edit.c b/source/blender/editors/screen/workspace_edit.c index 9a6bdc98d76..8cf11583b3a 100644 --- a/source/blender/editors/screen/workspace_edit.c +++ b/source/blender/editors/screen/workspace_edit.c @@ -405,7 +405,7 @@ static WorkspaceConfigFileData *workspace_config_file_read(const char *app_templ char startup_file_path[FILE_MAX] = {0}; if (cfgdir) { - BLI_join_dirfile(startup_file_path, sizeof(startup_file_path), cfgdir, BLENDER_STARTUP_FILE); + BLI_path_join(startup_file_path, sizeof(startup_file_path), cfgdir, BLENDER_STARTUP_FILE); } bool has_path = BLI_exists(startup_file_path); @@ -425,8 +425,7 @@ static WorkspaceConfigFileData *workspace_system_file_read(const char *app_templ } char startup_file_path[FILE_MAX]; - BLI_join_dirfile( - startup_file_path, sizeof(startup_file_path), template_dir, BLENDER_STARTUP_FILE); + BLI_path_join(startup_file_path, sizeof(startup_file_path), template_dir, BLENDER_STARTUP_FILE); bool has_path = BLI_exists(startup_file_path); return (has_path) ? BKE_blendfile_workspace_config_read(startup_file_path, NULL, 0, NULL) : NULL; diff --git a/source/blender/editors/sculpt_paint/paint_image_2d.c b/source/blender/editors/sculpt_paint/paint_image_2d.c index 79316361e53..b7ce4b2973c 100644 --- a/source/blender/editors/sculpt_paint/paint_image_2d.c +++ b/source/blender/editors/sculpt_paint/paint_image_2d.c @@ -402,7 +402,8 @@ static ImBuf *brush_painter_imbuf_new( if (is_texbrush) { brush_imbuf_tex_co(&tex_mapping, x, y, texco); - BKE_brush_sample_tex_3d(scene, brush, texco, rgba, thread, pool); + const MTex *mtex = &brush->mtex; + BKE_brush_sample_tex_3d(scene, brush, mtex, texco, rgba, thread, pool); /* TODO(sergey): Support texture paint color space. */ if (!use_float) { IMB_colormanagement_scene_linear_to_display_v3(rgba, display); @@ -446,6 +447,7 @@ static void brush_painter_imbuf_update(BrushPainter *painter, { Scene *scene = painter->scene; Brush *brush = painter->brush; + const MTex *mtex = &brush->mtex; BrushPainterCache *cache = &tile->cache; const char *display_device = scene->display_settings.display_device; @@ -485,7 +487,7 @@ static void brush_painter_imbuf_update(BrushPainter *painter, if (!use_texture_old) { if (is_texbrush) { brush_imbuf_tex_co(&tex_mapping, x, y, texco); - BKE_brush_sample_tex_3d(scene, brush, texco, rgba, thread, pool); + BKE_brush_sample_tex_3d(scene, brush, mtex, texco, rgba, thread, pool); /* TODO(sergey): Support texture paint color space. */ if (!use_float) { IMB_colormanagement_scene_linear_to_display_v3(rgba, display); diff --git a/source/blender/editors/sculpt_paint/paint_image_ops_paint.cc b/source/blender/editors/sculpt_paint/paint_image_ops_paint.cc index ced7949c69f..d5c5aa5cebd 100644 --- a/source/blender/editors/sculpt_paint/paint_image_ops_paint.cc +++ b/source/blender/editors/sculpt_paint/paint_image_ops_paint.cc @@ -74,11 +74,8 @@ class AbstractPaintMode { class ImagePaintMode : public AbstractPaintMode { public: - void *paint_new_stroke(bContext *C, - wmOperator *op, - Object * /*ob*/, - const float UNUSED(mouse[2]), - int mode) override + void *paint_new_stroke( + bContext *C, wmOperator *op, Object * /*ob*/, const float /*mouse*/[2], int mode) override { return paint_2d_new_stroke(C, op, mode); } diff --git a/source/blender/editors/sculpt_paint/paint_image_proj.c b/source/blender/editors/sculpt_paint/paint_image_proj.c index 8ffa78cd457..d9aa11a2847 100644 --- a/source/blender/editors/sculpt_paint/paint_image_proj.c +++ b/source/blender/editors/sculpt_paint/paint_image_proj.c @@ -4074,7 +4074,7 @@ static bool proj_paint_state_mesh_eval_init(const bContext *C, ProjPaintState *p ps->totloop_eval = ps->me_eval->totloop; ps->mlooptri_eval = BKE_mesh_runtime_looptri_ensure(ps->me_eval); - ps->totlooptri_eval = ps->me_eval->runtime.looptris.len; + ps->totlooptri_eval = BKE_mesh_runtime_looptri_len(ps->me_eval); ps->poly_to_loop_uv = MEM_mallocN(ps->totpoly_eval * sizeof(MLoopUV *), "proj_paint_mtfaces"); @@ -5370,7 +5370,7 @@ static void do_projectpaint_thread(TaskPool *__restrict UNUSED(pool), void *ph_v /* Color texture (alpha used as mask). */ if (ps->is_texbrush) { - MTex *mtex = &brush->mtex; + const MTex *mtex = BKE_brush_color_texture_get(brush, OB_MODE_TEXTURE_PAINT); float samplecos[3]; float texrgba[4]; @@ -5386,7 +5386,8 @@ static void do_projectpaint_thread(TaskPool *__restrict UNUSED(pool), void *ph_v /* NOTE: for clone and smear, * we only use the alpha, could be a special function */ - BKE_brush_sample_tex_3d(ps->scene, brush, samplecos, texrgba, thread_index, pool); + BKE_brush_sample_tex_3d( + ps->scene, brush, mtex, samplecos, texrgba, thread_index, pool); copy_v3_v3(texrgb, texrgba); custom_mask *= texrgba[3]; diff --git a/source/blender/editors/sculpt_paint/paint_mask.c b/source/blender/editors/sculpt_paint/paint_mask.c index e1fd731a4b7..571ebd79764 100644 --- a/source/blender/editors/sculpt_paint/paint_mask.c +++ b/source/blender/editors/sculpt_paint/paint_mask.c @@ -31,6 +31,7 @@ #include "BKE_multires.h" #include "BKE_paint.h" #include "BKE_pbvh.h" +#include "BKE_scene.h" #include "BKE_subsurf.h" #include "DEG_depsgraph.h" @@ -146,7 +147,7 @@ static int mask_flood_fill_exec(bContext *C, wmOperator *op) value = RNA_float_get(op->ptr, "value"); MultiresModifierData *mmd = BKE_sculpt_multires_active(scene, ob); - BKE_sculpt_mask_layers_ensure(ob, mmd); + BKE_sculpt_mask_layers_ensure(depsgraph, CTX_data_main(C), ob, mmd); BKE_sculpt_update_object_for_edit(depsgraph, ob, false, true, false); pbvh = ob->sculpt->pbvh; @@ -861,7 +862,9 @@ static void sculpt_gesture_mask_end(bContext *C, SculptGestureContext *sgcontext BKE_pbvh_update_vertex_data(sgcontext->ss->pbvh, PBVH_UpdateMask); } -static void sculpt_gesture_init_mask_properties(SculptGestureContext *sgcontext, wmOperator *op) +static void sculpt_gesture_init_mask_properties(bContext *C, + SculptGestureContext *sgcontext, + wmOperator *op) { sgcontext->operation = MEM_callocN(sizeof(SculptGestureMaskOperation), "Mask Operation"); @@ -869,7 +872,8 @@ static void sculpt_gesture_init_mask_properties(SculptGestureContext *sgcontext, Object *object = sgcontext->vc.obact; MultiresModifierData *mmd = BKE_sculpt_multires_active(sgcontext->vc.scene, object); - BKE_sculpt_mask_layers_ensure(sgcontext->vc.obact, mmd); + BKE_sculpt_mask_layers_ensure( + CTX_data_depsgraph_pointer(C), CTX_data_main(C), sgcontext->vc.obact, mmd); mask_operation->op.sculpt_gesture_begin = sculpt_gesture_mask_begin; mask_operation->op.sculpt_gesture_apply_for_symmetry_pass = @@ -1514,7 +1518,7 @@ static int paint_mask_gesture_box_exec(bContext *C, wmOperator *op) if (!sgcontext) { return OPERATOR_CANCELLED; } - sculpt_gesture_init_mask_properties(sgcontext, op); + sculpt_gesture_init_mask_properties(C, sgcontext, op); sculpt_gesture_apply(C, sgcontext, op); sculpt_gesture_context_free(sgcontext); return OPERATOR_FINISHED; @@ -1526,7 +1530,7 @@ static int paint_mask_gesture_lasso_exec(bContext *C, wmOperator *op) if (!sgcontext) { return OPERATOR_CANCELLED; } - sculpt_gesture_init_mask_properties(sgcontext, op); + sculpt_gesture_init_mask_properties(C, sgcontext, op); sculpt_gesture_apply(C, sgcontext, op); sculpt_gesture_context_free(sgcontext); return OPERATOR_FINISHED; @@ -1538,7 +1542,7 @@ static int paint_mask_gesture_line_exec(bContext *C, wmOperator *op) if (!sgcontext) { return OPERATOR_CANCELLED; } - sculpt_gesture_init_mask_properties(sgcontext, op); + sculpt_gesture_init_mask_properties(C, sgcontext, op); sculpt_gesture_apply(C, sgcontext, op); sculpt_gesture_context_free(sgcontext); return OPERATOR_FINISHED; diff --git a/source/blender/editors/sculpt_paint/paint_utils.c b/source/blender/editors/sculpt_paint/paint_utils.c index cb981a3bfb1..ce4a5151a20 100644 --- a/source/blender/editors/sculpt_paint/paint_utils.c +++ b/source/blender/editors/sculpt_paint/paint_utils.c @@ -286,7 +286,7 @@ static void imapaint_pick_uv( const ePaintCanvasSource mode = scene->toolsettings->imapaint.mode; const MLoopTri *lt = BKE_mesh_runtime_looptri_ensure(me_eval); - const int tottri = me_eval->runtime.looptris.len; + const int tottri = BKE_mesh_runtime_looptri_len(me_eval); const MVert *mvert = BKE_mesh_verts(me_eval); const MLoop *mloop = BKE_mesh_loops(me_eval); diff --git a/source/blender/editors/sculpt_paint/paint_vertex.cc b/source/blender/editors/sculpt_paint/paint_vertex.cc index 6e3504332ed..acd8b1a6bb1 100644 --- a/source/blender/editors/sculpt_paint/paint_vertex.cc +++ b/source/blender/editors/sculpt_paint/paint_vertex.cc @@ -433,9 +433,10 @@ static void paint_and_tex_color_alpha_intern(VPaint *vp, float r_rgba[4]) { const Brush *brush = BKE_paint_brush(&vp->paint); - BLI_assert(brush->mtex.tex != nullptr); - if (brush->mtex.brush_map_mode == MTEX_MAP_MODE_3D) { - BKE_brush_sample_tex_3d(vc->scene, brush, co, r_rgba, 0, nullptr); + const MTex *mtex = BKE_brush_mask_texture_get(brush, OB_MODE_SCULPT); + BLI_assert(mtex->tex != nullptr); + if (mtex->brush_map_mode == MTEX_MAP_MODE_3D) { + BKE_brush_sample_tex_3d(vc->scene, brush, mtex, co, r_rgba, 0, nullptr); } else { float co_ss[2]; /* screenspace */ @@ -445,7 +446,7 @@ static void paint_and_tex_color_alpha_intern(VPaint *vp, co_ss, (eV3DProjTest)(V3D_PROJ_TEST_CLIP_BB | V3D_PROJ_TEST_CLIP_NEAR)) == V3D_PROJ_RET_OK) { const float co_ss_3d[3] = {co_ss[0], co_ss[1], 0.0f}; /* we need a 3rd empty value */ - BKE_brush_sample_tex_3d(vc->scene, brush, co_ss_3d, r_rgba, 0, nullptr); + BKE_brush_sample_tex_3d(vc->scene, brush, mtex, co_ss_3d, r_rgba, 0, nullptr); } else { zero_v4(r_rgba); diff --git a/source/blender/editors/sculpt_paint/sculpt.c b/source/blender/editors/sculpt_paint/sculpt.c index 9e1b1cda0c3..95192114429 100644 --- a/source/blender/editors/sculpt_paint/sculpt.c +++ b/source/blender/editors/sculpt_paint/sculpt.c @@ -41,6 +41,7 @@ #include "BKE_paint.h" #include "BKE_pbvh.h" #include "BKE_report.h" +#include "BKE_scene.h" #include "BKE_subdiv_ccg.h" #include "BKE_subsurf.h" @@ -1981,7 +1982,7 @@ static void calc_area_normal_and_center_task_cb(void *__restrict userdata, int(*orco_tris)[3]; int orco_tris_num; - BKE_pbvh_node_get_bm_orco_data(data->nodes[n], &orco_tris, &orco_tris_num, &orco_coords); + BKE_pbvh_node_get_bm_orco_data(data->nodes[n], &orco_tris, &orco_tris_num, &orco_coords, NULL); for (int i = 0; i < orco_tris_num; i++) { const float *co_tri[3] = { @@ -2457,7 +2458,7 @@ float SCULPT_brush_strength_factor(SculptSession *ss, { StrokeCache *cache = ss->cache; const Scene *scene = cache->vc->scene; - const MTex *mtex = &br->mtex; + const MTex *mtex = BKE_brush_mask_texture_get(br, OB_MODE_SCULPT); float avg = 1.0f; float rgba[4]; float point[3]; @@ -2469,7 +2470,7 @@ float SCULPT_brush_strength_factor(SculptSession *ss, } else if (mtex->brush_map_mode == MTEX_MAP_MODE_3D) { /* Get strength by feeding the vertex location directly into a texture. */ - avg = BKE_brush_sample_tex_3d(scene, br, point, rgba, 0, ss->tex_pool); + avg = BKE_brush_sample_tex_3d(scene, br, mtex, point, rgba, 0, ss->tex_pool); } else { float symm_point[3], point_2d[2]; @@ -2498,19 +2499,19 @@ float SCULPT_brush_strength_factor(SculptSession *ss, x = symm_point[0]; y = symm_point[1]; - x *= br->mtex.size[0]; - y *= br->mtex.size[1]; + x *= mtex->size[0]; + y *= mtex->size[1]; - x += br->mtex.ofs[0]; - y += br->mtex.ofs[1]; + x += mtex->ofs[0]; + y += mtex->ofs[1]; - avg = paint_get_tex_pixel(&br->mtex, x, y, ss->tex_pool, thread_id); + avg = paint_get_tex_pixel(mtex, x, y, ss->tex_pool, thread_id); avg += br->texture_sample_bias; } else { const float point_3d[3] = {point_2d[0], point_2d[1], 0.0f}; - avg = BKE_brush_sample_tex_3d(scene, br, point_3d, rgba, 0, ss->tex_pool); + avg = BKE_brush_sample_tex_3d(scene, br, mtex, point_3d, rgba, 0, ss->tex_pool); } } @@ -3265,7 +3266,7 @@ static void sculpt_topology_update(Sculpt *sd, if (BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) { BKE_pbvh_node_mark_topology_update(nodes[n]); - BKE_pbvh_bmesh_node_save_orig(ss->bm, nodes[n]); + BKE_pbvh_bmesh_node_save_orig(ss->bm, ss->bm_log, nodes[n], false); } } @@ -4272,7 +4273,8 @@ static void sculpt_update_cache_invariants( bContext *C, Sculpt *sd, SculptSession *ss, wmOperator *op, const float mval[2]) { StrokeCache *cache = MEM_callocN(sizeof(StrokeCache), "stroke cache"); - UnifiedPaintSettings *ups = &CTX_data_tool_settings(C)->unified_paint_settings; + ToolSettings *tool_settings = CTX_data_tool_settings(C); + UnifiedPaintSettings *ups = &tool_settings->unified_paint_settings; Brush *brush = BKE_paint_brush(&sd->paint); ViewContext *vc = paint_stroke_view_context(op->customdata); Object *ob = CTX_data_active_object(C); @@ -4407,6 +4409,13 @@ static void sculpt_update_cache_invariants( } } + /* Original coordinates require the sculpt undo system, which isn't used + * for image brushes. It's also not necessary, just disable it. */ + if (brush && brush->sculpt_tool == SCULPT_TOOL_PAINT && + SCULPT_use_image_paint_brush(&tool_settings->paint_mode, ob)) { + cache->original = false; + } + cache->first_time = true; #define PIXEL_INPUT_THRESHHOLD 5 @@ -5658,7 +5667,7 @@ static int sculpt_brush_stroke_invoke(bContext *C, wmOperator *op, const wmEvent } if (SCULPT_tool_is_mask(brush->sculpt_tool)) { MultiresModifierData *mmd = BKE_sculpt_multires_active(ss->scene, ob); - BKE_sculpt_mask_layers_ensure(ob, mmd); + BKE_sculpt_mask_layers_ensure(CTX_data_depsgraph_pointer(C), CTX_data_main(C), ob, mmd); } if (SCULPT_tool_is_face_sets(brush->sculpt_tool)) { Mesh *mesh = BKE_object_get_original_mesh(ob); @@ -5749,15 +5758,14 @@ static int sculpt_brush_stroke_modal(bContext *C, wmOperator *op, const wmEvent if (!started && ELEM(retval, OPERATOR_FINISHED, OPERATOR_CANCELLED)) { /* Did the stroke never start? If so push a blank sculpt undo * step to prevent a global undo step (which is triggered by the - * OPTYPE_UNDO flag in SCULPT_OT_brush_stroke). + * #OPTYPE_UNDO flag in #SCULPT_OT_brush_stroke). * * Having blank global undo steps interleaved with sculpt steps * corrupts the DynTopo undo stack. * See T101430. * - * Note: simply returning OPERATOR_CANCELLED was not - * sufficient to prevent this. - */ + * NOTE: simply returning #OPERATOR_CANCELLED was not + * sufficient to prevent this. */ Sculpt *sd = CTX_data_tool_settings(C)->sculpt; Brush *brush = BKE_paint_brush(&sd->paint); diff --git a/source/blender/editors/sculpt_paint/sculpt_automasking.cc b/source/blender/editors/sculpt_paint/sculpt_automasking.cc index 5a0fcd8bc27..505440c9272 100644 --- a/source/blender/editors/sculpt_paint/sculpt_automasking.cc +++ b/source/blender/editors/sculpt_paint/sculpt_automasking.cc @@ -503,7 +503,7 @@ float SCULPT_automasking_factor_get(AutomaskingCache *automasking, PBVHVertRef vert, AutomaskingNodeData *automask_data) { - if (!automasking) { + if (!automasking || vert.i == PBVH_REF_NONE) { return 1.0f; } diff --git a/source/blender/editors/sculpt_paint/sculpt_brush_types.c b/source/blender/editors/sculpt_paint/sculpt_brush_types.c index 041efbb357c..92541d10a59 100644 --- a/source/blender/editors/sculpt_paint/sculpt_brush_types.c +++ b/source/blender/editors/sculpt_paint/sculpt_brush_types.c @@ -2149,6 +2149,8 @@ static void do_elastic_deform_brush_task_cb_ex(void *__restrict userdata, BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { SCULPT_orig_vert_data_update(&orig_data, &vd); + SCULPT_automasking_node_update(ss, &automask_data, &vd); + float final_disp[3]; switch (brush->elastic_deform_type) { case BRUSH_ELASTIC_DEFORM_GRAB: diff --git a/source/blender/editors/sculpt_paint/sculpt_expand.c b/source/blender/editors/sculpt_paint/sculpt_expand.c index 90279cb339c..3133bb2007e 100644 --- a/source/blender/editors/sculpt_paint/sculpt_expand.c +++ b/source/blender/editors/sculpt_paint/sculpt_expand.c @@ -27,6 +27,7 @@ #include "BKE_paint.h" #include "BKE_pbvh.h" #include "BKE_report.h" +#include "BKE_scene.h" #include "BKE_subdiv_ccg.h" #include "DEG_depsgraph.h" @@ -166,15 +167,16 @@ static float sculpt_expand_falloff_value_vertex_get(SculptSession *ss, if (expand_cache->texture_distortion_strength == 0.0f) { return expand_cache->vert_falloff[v_i]; } - - if (!expand_cache->brush->mtex.tex) { + const Brush *brush = expand_cache->brush; + const MTex *mtex = BKE_brush_mask_texture_get(brush, OB_MODE_SCULPT); + if (!mtex->tex) { return expand_cache->vert_falloff[v_i]; } float rgba[4]; const float *vertex_co = SCULPT_vertex_co_get(ss, v); const float avg = BKE_brush_sample_tex_3d( - expand_cache->scene, expand_cache->brush, vertex_co, rgba, 0, ss->tex_pool); + expand_cache->scene, brush, mtex, vertex_co, rgba, 0, ss->tex_pool); const float distortion = (avg - 0.5f) * expand_cache->texture_distortion_strength * expand_cache->max_vert_falloff; @@ -2107,6 +2109,11 @@ static int sculpt_expand_invoke(bContext *C, wmOperator *op, const wmEvent *even depsgraph = CTX_data_ensure_evaluated_depsgraph(C); } + if (ss->expand_cache->target == SCULPT_EXPAND_TARGET_MASK) { + MultiresModifierData *mmd = BKE_sculpt_multires_active(ss->scene, ob); + BKE_sculpt_mask_layers_ensure(depsgraph, CTX_data_main(C), ob, mmd); + } + BKE_sculpt_update_object_for_edit(depsgraph, ob, true, true, needs_colors); /* Do nothing when the mesh has 0 vertices. */ @@ -2121,11 +2128,6 @@ static int sculpt_expand_invoke(bContext *C, wmOperator *op, const wmEvent *even ss->face_sets = BKE_sculpt_face_sets_ensure(mesh); } - if (ss->expand_cache->target == SCULPT_EXPAND_TARGET_MASK) { - MultiresModifierData *mmd = BKE_sculpt_multires_active(ss->scene, ob); - BKE_sculpt_mask_layers_ensure(ob, mmd); - } - /* Face Set operations are not supported in dyntopo. */ if (ss->expand_cache->target == SCULPT_EXPAND_TARGET_FACE_SETS && BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) { diff --git a/source/blender/editors/sculpt_paint/sculpt_face_set.cc b/source/blender/editors/sculpt_paint/sculpt_face_set.cc index 15a91e4eaab..8fb4dea668e 100644 --- a/source/blender/editors/sculpt_paint/sculpt_face_set.cc +++ b/source/blender/editors/sculpt_paint/sculpt_face_set.cc @@ -18,6 +18,7 @@ #include "BLI_math_vector.hh" #include "BLI_span.hh" #include "BLI_task.h" +#include "BLI_task.hh" #include "DNA_brush_types.h" #include "DNA_customdata_types.h" @@ -198,8 +199,8 @@ static void do_relax_face_sets_brush_task_cb_ex(void *__restrict userdata, const bool relax_face_sets = !(ss->cache->iteration_count % 3 == 0); /* This operations needs a strength tweak as the relax deformation is too weak by default. */ - if (relax_face_sets) { - bstrength *= 2.0f; + if (relax_face_sets && data->iteration < 2) { + bstrength *= 1.5f; } const int thread_id = BLI_task_parallel_thread_id(tls); @@ -261,6 +262,7 @@ void SCULPT_do_draw_face_sets_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, in if (ss->cache->alt_smooth) { SCULPT_boundary_info_ensure(ob); for (int i = 0; i < 4; i++) { + data.iteration = i; BLI_task_parallel_range(0, totnode, &data, do_relax_face_sets_brush_task_cb_ex, &settings); } } @@ -312,6 +314,7 @@ static EnumPropertyItem prop_sculpt_face_set_create_types[] = { static int sculpt_face_set_create_exec(bContext *C, wmOperator *op) { + using namespace blender; Object *ob = CTX_data_active_object(C); SculptSession *ss = ob->sculpt; Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C); @@ -395,25 +398,16 @@ static int sculpt_face_set_create_exec(bContext *C, wmOperator *op) } if (mode == SCULPT_FACE_SET_SELECTION) { - BMesh *bm; - const BMAllocTemplate allocsize = BMALLOC_TEMPLATE_FROM_ME(mesh); - BMeshCreateParams create_params{}; - create_params.use_toolflags = true; - bm = BM_mesh_create(&allocsize, &create_params); - - BMeshFromMeshParams convert_params{}; - convert_params.calc_vert_normal = true; - convert_params.calc_face_normal = true; - BM_mesh_bm_from_me(bm, mesh, &convert_params); - - BMIter iter; - BMFace *f; - BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) { - if (BM_elem_flag_test(f, BM_ELEM_SELECT)) { - ss->face_sets[BM_elem_index_get(f)] = next_face_set; + const bke::AttributeAccessor attributes = mesh->attributes(); + const VArraySpan<bool> select_poly = attributes.lookup_or_default<bool>( + ".select_poly", ATTR_DOMAIN_FACE, false); + threading::parallel_for(IndexRange(mesh->totvert), 4096, [&](const IndexRange range) { + for (const int i : range) { + if (select_poly[i]) { + ss->face_sets[i] = next_face_set; + } } - } - BM_mesh_free(bm); + }); } for (int i = 0; i < totnode; i++) { @@ -827,9 +821,6 @@ static int sculpt_face_sets_change_visibility_exec(bContext *C, wmOperator *op) const int mode = RNA_enum_get(op->ptr, "mode"); const int tot_vert = SCULPT_vertex_count_get(ss); - const int active_face_set = SCULPT_active_face_set_get(ss); - - SCULPT_undo_push_begin(ob, op); PBVH *pbvh = ob->sculpt->pbvh; PBVHNode **nodes; @@ -842,62 +833,86 @@ static int sculpt_face_sets_change_visibility_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } - SCULPT_undo_push_node(ob, nodes[0], SCULPT_UNDO_FACE_SETS); - - if (mode == SCULPT_FACE_SET_VISIBILITY_TOGGLE) { - bool hidden_vertex = false; + const int active_face_set = SCULPT_active_face_set_get(ss); - /* This can fail with regular meshes with non-manifold geometry as the visibility state can't - * be synced from face sets to non-manifold vertices. */ - if (BKE_pbvh_type(ss->pbvh) == PBVH_GRIDS) { - for (int i = 0; i < tot_vert; i++) { - PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); + SCULPT_undo_push_begin(ob, op); + for (int i = 0; i < totnode; i++) { + SCULPT_undo_push_node(ob, nodes[i], SCULPT_UNDO_HIDDEN); + } - if (!SCULPT_vertex_visible_get(ss, vertex)) { - hidden_vertex = true; - break; + switch (mode) { + case SCULPT_FACE_SET_VISIBILITY_TOGGLE: { + bool hidden_vertex = false; + + /* This can fail with regular meshes with non-manifold geometry as the visibility state can't + * be synced from face sets to non-manifold vertices. */ + if (BKE_pbvh_type(ss->pbvh) == PBVH_GRIDS) { + for (int i = 0; i < tot_vert; i++) { + PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); + + if (!SCULPT_vertex_visible_get(ss, vertex)) { + hidden_vertex = true; + break; + } } } - } - if (ss->hide_poly) { - for (int i = 0; i < ss->totfaces; i++) { - if (ss->hide_poly[i]) { - hidden_vertex = true; - break; + if (ss->hide_poly) { + for (int i = 0; i < ss->totfaces; i++) { + if (ss->hide_poly[i]) { + hidden_vertex = true; + break; + } } } - } - ss->hide_poly = BKE_sculpt_hide_poly_ensure(mesh); + ss->hide_poly = BKE_sculpt_hide_poly_ensure(mesh); - if (hidden_vertex) { - SCULPT_face_visibility_all_set(ss, true); - } - else { - SCULPT_face_visibility_all_set(ss, false); - SCULPT_face_set_visibility_set(ss, active_face_set, true); + if (hidden_vertex) { + SCULPT_face_visibility_all_set(ss, true); + } + else { + if (ss->face_sets) { + SCULPT_face_visibility_all_set(ss, false); + SCULPT_face_set_visibility_set(ss, active_face_set, true); + } + else { + SCULPT_face_visibility_all_set(ss, true); + } + } + break; } - } + case SCULPT_FACE_SET_VISIBILITY_SHOW_ACTIVE: + ss->hide_poly = BKE_sculpt_hide_poly_ensure(mesh); - if (mode == SCULPT_FACE_SET_VISIBILITY_SHOW_ACTIVE) { - ss->hide_poly = BKE_sculpt_hide_poly_ensure(mesh); - SCULPT_face_visibility_all_set(ss, false); - SCULPT_face_set_visibility_set(ss, active_face_set, true); - } + if (ss->face_sets) { + SCULPT_face_visibility_all_set(ss, false); + SCULPT_face_set_visibility_set(ss, active_face_set, true); + } + else { + SCULPT_face_set_visibility_set(ss, active_face_set, true); + } + break; + case SCULPT_FACE_SET_VISIBILITY_HIDE_ACTIVE: + ss->hide_poly = BKE_sculpt_hide_poly_ensure(mesh); - if (mode == SCULPT_FACE_SET_VISIBILITY_HIDE_ACTIVE) { - ss->hide_poly = BKE_sculpt_hide_poly_ensure(mesh); - SCULPT_face_set_visibility_set(ss, active_face_set, false); - } + if (ss->face_sets) { + SCULPT_face_set_visibility_set(ss, active_face_set, false); + } + else { + SCULPT_face_visibility_all_set(ss, false); + } - if (mode == SCULPT_FACE_SET_VISIBILITY_INVERT) { - ss->hide_poly = BKE_sculpt_hide_poly_ensure(mesh); - SCULPT_face_visibility_all_invert(ss); + break; + case SCULPT_FACE_SET_VISIBILITY_INVERT: + ss->hide_poly = BKE_sculpt_hide_poly_ensure(mesh); + SCULPT_face_visibility_all_invert(ss); + break; } /* For modes that use the cursor active vertex, update the rotation origin for viewport - * navigation. */ + * navigation. + */ if (ELEM(mode, SCULPT_FACE_SET_VISIBILITY_TOGGLE, SCULPT_FACE_SET_VISIBILITY_SHOW_ACTIVE)) { UnifiedPaintSettings *ups = &CTX_data_tool_settings(C)->unified_paint_settings; float location[3]; @@ -912,7 +927,6 @@ static int sculpt_face_sets_change_visibility_exec(bContext *C, wmOperator *op) SCULPT_visibility_sync_all_from_faces(ob); SCULPT_undo_push_end(ob); - for (int i = 0; i < totnode; i++) { BKE_pbvh_node_mark_update_visibility(nodes[i]); } diff --git a/source/blender/editors/sculpt_paint/sculpt_filter_mask.c b/source/blender/editors/sculpt_paint/sculpt_filter_mask.c index 69eac9a6168..8e199a72858 100644 --- a/source/blender/editors/sculpt_paint/sculpt_filter_mask.c +++ b/source/blender/editors/sculpt_paint/sculpt_filter_mask.c @@ -16,6 +16,7 @@ #include "BKE_context.h" #include "BKE_paint.h" #include "BKE_pbvh.h" +#include "BKE_scene.h" #include "DEG_depsgraph.h" @@ -166,7 +167,7 @@ static int sculpt_mask_filter_exec(bContext *C, wmOperator *op) int filter_type = RNA_enum_get(op->ptr, "filter_type"); MultiresModifierData *mmd = BKE_sculpt_multires_active(scene, ob); - BKE_sculpt_mask_layers_ensure(ob, mmd); + BKE_sculpt_mask_layers_ensure(CTX_data_depsgraph_pointer(C), CTX_data_main(C), ob, mmd); BKE_sculpt_update_object_for_edit(depsgraph, ob, true, true, false); diff --git a/source/blender/editors/sculpt_paint/sculpt_intern.h b/source/blender/editors/sculpt_paint/sculpt_intern.h index edb681466b5..bf47b64d176 100644 --- a/source/blender/editors/sculpt_paint/sculpt_intern.h +++ b/source/blender/editors/sculpt_paint/sculpt_intern.h @@ -332,7 +332,7 @@ typedef struct SculptThreadedTaskData { int mask_init_seed; ThreadMutex mutex; - + int iteration; } SculptThreadedTaskData; /*************** Brush testing declarations ****************/ diff --git a/source/blender/editors/sculpt_paint/sculpt_mask_expand.c b/source/blender/editors/sculpt_paint/sculpt_mask_expand.c index 56ddb99fe28..1b8cc5347ac 100644 --- a/source/blender/editors/sculpt_paint/sculpt_mask_expand.c +++ b/source/blender/editors/sculpt_paint/sculpt_mask_expand.c @@ -14,6 +14,7 @@ #include "DNA_brush_types.h" #include "DNA_meshdata_types.h" +#include "DNA_modifier_types.h" #include "DNA_object_types.h" #include "BKE_ccg.h" @@ -331,6 +332,11 @@ static int sculpt_mask_expand_invoke(bContext *C, wmOperator *op, const wmEvent SculptCursorGeometryInfo sgi; const float mval_fl[2] = {UNPACK2(event->mval)}; + MultiresModifierData *mmd = BKE_sculpt_multires_active(CTX_data_scene(C), ob); + BKE_sculpt_mask_layers_ensure(depsgraph, CTX_data_main(C), ob, mmd); + + BKE_sculpt_update_object_for_edit(depsgraph, ob, true, true, false); + SCULPT_vertex_random_access_ensure(ss); op->customdata = MEM_mallocN(sizeof(float[2]), "initial mouse position"); @@ -338,8 +344,6 @@ static int sculpt_mask_expand_invoke(bContext *C, wmOperator *op, const wmEvent SCULPT_cursor_geometry_info_update(C, &sgi, mval_fl, false); - BKE_sculpt_update_object_for_edit(depsgraph, ob, true, true, false); - int vertex_count = SCULPT_vertex_count_get(ss); ss->filter_cache = MEM_callocN(sizeof(FilterCache), "filter cache"); diff --git a/source/blender/editors/sculpt_paint/sculpt_mask_init.c b/source/blender/editors/sculpt_paint/sculpt_mask_init.c index 5d7ef9aab15..99a7bb8a926 100644 --- a/source/blender/editors/sculpt_paint/sculpt_mask_init.c +++ b/source/blender/editors/sculpt_paint/sculpt_mask_init.c @@ -15,6 +15,7 @@ #include "DNA_brush_types.h" #include "DNA_meshdata_types.h" +#include "DNA_modifier_types.h" #include "DNA_object_types.h" #include "BKE_ccg.h" @@ -110,6 +111,9 @@ static int sculpt_mask_init_exec(bContext *C, wmOperator *op) const int mode = RNA_enum_get(op->ptr, "mode"); + MultiresModifierData *mmd = BKE_sculpt_multires_active(CTX_data_scene(C), ob); + BKE_sculpt_mask_layers_ensure(depsgraph, CTX_data_main(C), ob, mmd); + BKE_sculpt_update_object_for_edit(depsgraph, ob, true, true, false); PBVH *pbvh = ob->sculpt->pbvh; diff --git a/source/blender/editors/sculpt_paint/sculpt_ops.c b/source/blender/editors/sculpt_paint/sculpt_ops.c index 1042356489b..a740ec2773b 100644 --- a/source/blender/editors/sculpt_paint/sculpt_ops.c +++ b/source/blender/editors/sculpt_paint/sculpt_ops.c @@ -21,6 +21,7 @@ #include "DNA_listBase.h" #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" +#include "DNA_modifier_types.h" #include "DNA_node_types.h" #include "DNA_object_types.h" #include "DNA_scene_types.h" @@ -1041,12 +1042,12 @@ static int sculpt_bake_cavity_exec(bContext *C, wmOperator *op) Sculpt *sd = CTX_data_tool_settings(C)->sculpt; Brush *brush = BKE_paint_brush(&sd->paint); + MultiresModifierData *mmd = BKE_sculpt_multires_active(CTX_data_scene(C), ob); + BKE_sculpt_mask_layers_ensure(depsgraph, CTX_data_main(C), ob, mmd); + BKE_sculpt_update_object_for_edit(depsgraph, ob, true, true, false); SCULPT_vertex_random_access_ensure(ss); - MultiresModifierData *mmd = BKE_sculpt_multires_active(CTX_data_scene(C), ob); - BKE_sculpt_mask_layers_ensure(ob, mmd); - SCULPT_undo_push_begin(ob, op); CavityBakeMixMode mode = RNA_enum_get(op->ptr, "mix_mode"); @@ -1116,10 +1117,7 @@ static int sculpt_bake_cavity_exec(bContext *C, wmOperator *op) SCULPT_undo_push_end(ob); SCULPT_flush_update_done(C, ob, SCULPT_UPDATE_MASK); - - /* Unlike other operators we do not tag the ID for update here; - * it triggers a PBVH rebuild which is too slow and ruins - * the interactivity of the tool. */ + SCULPT_tag_update_overlays(C); return OPERATOR_FINISHED; } @@ -1294,11 +1292,10 @@ static int sculpt_reveal_all_exec(bContext *C, wmOperator *op) SCULPT_visibility_sync_all_from_faces(ob); - /* Note: SCULPT_visibility_sync_all_from_faces may have deleted - * pbvh->hide_vert if hide_poly did not exist, which is why - * we call BKE_pbvh_update_hide_attributes_from_mesh here instead of - * after CustomData_free_layer_named above. - */ + /* NOTE: #SCULPT_visibility_sync_all_from_faces may have deleted + * `pbvh->hide_vert` if hide_poly did not exist, which is why + * we call #BKE_pbvh_update_hide_attributes_from_mesh here instead of + * after #CustomData_free_layer_named above. */ if (!with_bmesh) { BKE_pbvh_update_hide_attributes_from_mesh(ss->pbvh); } diff --git a/source/blender/editors/space_clip/clip_ops.c b/source/blender/editors/space_clip/clip_ops.c index f276c2acd1a..486f9f9ccb0 100644 --- a/source/blender/editors/space_clip/clip_ops.c +++ b/source/blender/editors/space_clip/clip_ops.c @@ -201,7 +201,7 @@ static int open_exec(bContext *C, wmOperator *op) RNA_property_collection_lookup_int(op->ptr, prop, 0, &fileptr); RNA_string_get(&fileptr, "name", file_only); - BLI_join_dirfile(str, sizeof(str), dir_only, file_only); + BLI_path_join(str, sizeof(str), dir_only, file_only); } else { BKE_report(op->reports, RPT_ERROR, "No files selected to be opened"); @@ -1611,7 +1611,9 @@ void CLIP_OT_mode_set(wmOperatorType *ot) ot->poll = ED_space_clip_poll; /* properties */ - RNA_def_enum(ot->srna, "mode", rna_enum_clip_editor_mode_items, SC_MODE_TRACKING, "Mode", ""); + ot->prop = RNA_def_enum( + ot->srna, "mode", rna_enum_clip_editor_mode_items, SC_MODE_TRACKING, "Mode", ""); + RNA_def_property_translation_context(ot->prop, BLT_I18NCONTEXT_ID_MOVIECLIP); } /** \} */ @@ -1639,14 +1641,14 @@ static int clip_view_ndof_invoke(bContext *C, wmOperator *UNUSED(op), const wmEv float pan_vec[3]; const wmNDOFMotionData *ndof = event->customdata; - const float speed = NDOF_PIXELS_PER_SECOND; + const float pan_speed = NDOF_PIXELS_PER_SECOND; WM_event_ndof_pan_get(ndof, pan_vec, true); - mul_v2_fl(pan_vec, (speed * ndof->dt) / sc->zoom); - pan_vec[2] *= -ndof->dt; + mul_v3_fl(pan_vec, ndof->dt); + mul_v2_fl(pan_vec, pan_speed / sc->zoom); - sclip_zoom_set_factor(C, 1.0f + pan_vec[2], NULL, false); + sclip_zoom_set_factor(C, max_ff(0.0f, 1.0f - pan_vec[2]), NULL, false); sc->xof += pan_vec[0]; sc->yof += pan_vec[1]; diff --git a/source/blender/editors/space_file/file_draw.c b/source/blender/editors/space_file/file_draw.c index 554b628ba0f..240901318b5 100644 --- a/source/blender/editors/space_file/file_draw.c +++ b/source/blender/editors/space_file/file_draw.c @@ -410,8 +410,15 @@ static void file_draw_preview(const SpaceFile *sfile, } icon_x = xco + (ex / 2.0f) - (icon_size / 2.0f); icon_y = yco + (ey / 2.0f) - (icon_size * ((file->typeflag & FILE_TYPE_DIR) ? 0.78f : 0.75f)); - UI_icon_draw_ex( - icon_x, icon_y, icon, icon_aspect / U.dpi_fac, icon_opacity, 0.0f, icon_color, false); + UI_icon_draw_ex(icon_x, + icon_y, + icon, + icon_aspect / U.dpi_fac, + icon_opacity, + 0.0f, + icon_color, + false, + UI_NO_ICON_OVERLAY_TEXT); } if (is_link || is_offline) { @@ -424,8 +431,24 @@ static void file_draw_preview(const SpaceFile *sfile, /* At very bottom-left if preview style. */ const uchar dark[4] = {0, 0, 0, 255}; const uchar light[4] = {255, 255, 255, 255}; - UI_icon_draw_ex(icon_x + 1, icon_y - 1, arrow, 1.0f / U.dpi_fac, 0.2f, 0.0f, dark, false); - UI_icon_draw_ex(icon_x, icon_y, arrow, 1.0f / U.dpi_fac, 0.6f, 0.0f, light, false); + UI_icon_draw_ex(icon_x + 1, + icon_y - 1, + arrow, + 1.0f / U.dpi_fac, + 0.2f, + 0.0f, + dark, + false, + UI_NO_ICON_OVERLAY_TEXT); + UI_icon_draw_ex(icon_x, + icon_y, + arrow, + 1.0f / U.dpi_fac, + 0.6f, + 0.0f, + light, + false, + UI_NO_ICON_OVERLAY_TEXT); } else { /* Link to folder or non-previewed file. */ @@ -433,8 +456,15 @@ static void file_draw_preview(const SpaceFile *sfile, UI_GetThemeColor4ubv(TH_BACK, icon_color); icon_x = xco + ((file->typeflag & FILE_TYPE_DIR) ? 0.14f : 0.23f) * scaledx; icon_y = yco + ((file->typeflag & FILE_TYPE_DIR) ? 0.24f : 0.14f) * scaledy; - UI_icon_draw_ex( - icon_x, icon_y, arrow, icon_aspect / U.dpi_fac * 1.8, 0.3f, 0.0f, icon_color, false); + UI_icon_draw_ex(icon_x, + icon_y, + arrow, + icon_aspect / U.dpi_fac * 1.8, + 0.3f, + 0.0f, + icon_color, + false, + UI_NO_ICON_OVERLAY_TEXT); } } else if (icon && !is_icon && !(file->typeflag & FILE_TYPE_FTFONT)) { @@ -444,8 +474,17 @@ static void file_draw_preview(const SpaceFile *sfile, const uchar light[4] = {255, 255, 255, 255}; icon_x = xco + (2.0f * UI_DPI_FAC); icon_y = yco + (2.0f * UI_DPI_FAC); - UI_icon_draw_ex(icon_x + 1, icon_y - 1, icon, 1.0f / U.dpi_fac, 0.2f, 0.0f, dark, false); - UI_icon_draw_ex(icon_x, icon_y, icon, 1.0f / U.dpi_fac, 0.6f, 0.0f, light, false); + UI_icon_draw_ex(icon_x + 1, + icon_y - 1, + icon, + 1.0f / U.dpi_fac, + 0.2f, + 0.0f, + dark, + false, + UI_NO_ICON_OVERLAY_TEXT); + UI_icon_draw_ex( + icon_x, icon_y, icon, 1.0f / U.dpi_fac, 0.6f, 0.0f, light, false, UI_NO_ICON_OVERLAY_TEXT); } const bool is_current_main_data = filelist_file_get_id(file) != NULL; @@ -456,7 +495,15 @@ static void file_draw_preview(const SpaceFile *sfile, const uchar light[4] = {255, 255, 255, 255}; icon_x = xco + ex - UI_UNIT_X; icon_y = yco + ey - UI_UNIT_Y; - UI_icon_draw_ex(icon_x, icon_y, ICON_CURRENT_FILE, 1.0f / U.dpi_fac, 0.6f, 0.0f, light, false); + UI_icon_draw_ex(icon_x, + icon_y, + ICON_CURRENT_FILE, + 1.0f / U.dpi_fac, + 0.6f, + 0.0f, + light, + false, + UI_NO_ICON_OVERLAY_TEXT); } /* Contrasting outline around some preview types. */ @@ -544,10 +591,10 @@ static void renamebutton_cb(bContext *C, void *UNUSED(arg1), char *oldname) ARegion *region = CTX_wm_region(C); FileSelectParams *params = ED_fileselect_get_active_params(sfile); - BLI_join_dirfile(orgname, sizeof(orgname), params->dir, oldname); + BLI_path_join(orgname, sizeof(orgname), params->dir, oldname); BLI_strncpy(filename, params->renamefile, sizeof(filename)); BLI_filename_make_safe(filename); - BLI_join_dirfile(newname, sizeof(newname), params->dir, filename); + BLI_path_join(newname, sizeof(newname), params->dir, filename); if (!STREQ(orgname, newname)) { if (!BLI_exists(newname)) { @@ -952,7 +999,7 @@ void file_draw_list(const bContext *C, ARegion *region) file = filelist_file(files, i); file_selflag = filelist_entry_select_get(sfile->files, file, CHECK_ALL); - BLI_join_dirfile(path, sizeof(path), root, file->relpath); + BLI_path_join(path, sizeof(path), root, file->relpath); if (!(file_selflag & FILE_SEL_EDITING)) { if ((params->highlight_file == i) || (file_selflag & FILE_SEL_HIGHLIGHTED) || diff --git a/source/blender/editors/space_file/file_ops.c b/source/blender/editors/space_file/file_ops.c index f7fd910d6e9..a4d4bf98474 100644 --- a/source/blender/editors/space_file/file_ops.c +++ b/source/blender/editors/space_file/file_ops.c @@ -386,7 +386,7 @@ static bool fsmenu_write_file_and_refresh_or_report_error(struct FSMenu *fsmenu, } char filepath[FILE_MAX]; - BLI_join_dirfile(filepath, sizeof(filepath), cfgdir, BLENDER_BOOKMARK_FILE); + BLI_path_join(filepath, sizeof(filepath), cfgdir, BLENDER_BOOKMARK_FILE); if (UNLIKELY(!fsmenu_write_file(fsmenu, filepath))) { BKE_reportf(reports, RPT_ERROR, "Unable to open or write bookmark file \"%s\"", filepath); return false; @@ -1564,7 +1564,7 @@ void file_sfile_to_operator_ex( PropertyRNA *prop; /* XXX, not real length */ - BLI_join_dirfile(filepath, FILE_MAX, params->dir, params->file); + BLI_path_join(filepath, FILE_MAX, params->dir, params->file); if ((prop = RNA_struct_find_property(op->ptr, "relative_path"))) { if (RNA_property_boolean_get(op->ptr, prop)) { @@ -1741,7 +1741,7 @@ bool file_draw_check_exists(SpaceFile *sfile) const FileSelectParams *params = ED_fileselect_get_active_params(sfile); if (params && (params->flag & FILE_CHECK_EXISTING)) { char filepath[FILE_MAX]; - BLI_join_dirfile(filepath, sizeof(filepath), params->dir, params->file); + BLI_path_join(filepath, sizeof(filepath), params->dir, params->file); if (BLI_is_file(filepath)) { return true; } @@ -2311,13 +2311,13 @@ static bool new_folder_path(const char *parent, char folder[FILE_MAX], char name int len = 0; BLI_strncpy(name, "New Folder", FILE_MAXFILE); - BLI_join_dirfile(folder, FILE_MAX, parent, name); + BLI_path_join(folder, FILE_MAX, parent, name); /* check whether folder with the name already exists, in this case * add number to the name. Check length of generated name to avoid * crazy case of huge number of folders each named 'New Folder (x)' */ while (BLI_exists(folder) && (len < FILE_MAXFILE)) { len = BLI_snprintf(name, FILE_MAXFILE, "New Folder(%d)", i); - BLI_join_dirfile(folder, FILE_MAX, parent, name); + BLI_path_join(folder, FILE_MAX, parent, name); i++; } @@ -2457,8 +2457,7 @@ static void file_expand_directory(bContext *C) else if (params->dir[0] == '~') { char tmpstr[sizeof(params->dir) - 1]; BLI_strncpy(tmpstr, params->dir + 1, sizeof(tmpstr)); - BLI_path_join( - params->dir, sizeof(params->dir), BKE_appdir_folder_default_or_root(), tmpstr, NULL); + BLI_path_join(params->dir, sizeof(params->dir), BKE_appdir_folder_default_or_root(), tmpstr); } else if (params->dir[0] == '\0') @@ -2622,7 +2621,7 @@ void file_filename_enter_handle(bContext *C, void *UNUSED(arg_unused), void *arg } if (matches == 1) { - BLI_join_dirfile(filepath, sizeof(params->dir), params->dir, params->file); + BLI_path_join(filepath, sizeof(params->dir), params->dir, params->file); /* if directory, open it and empty filename field */ if (filelist_is_dir(sfile->files, filepath)) { @@ -2850,7 +2849,7 @@ static bool file_delete_single(const FileSelectParams *params, const char **r_error_message) { char str[FILE_MAX]; - BLI_join_dirfile(str, sizeof(str), params->dir, file->relpath); + BLI_path_join(str, sizeof(str), params->dir, file->relpath); if (BLI_delete_soft(str, r_error_message) != 0 || BLI_exists(str)) { return false; } diff --git a/source/blender/editors/space_file/filelist.cc b/source/blender/editors/space_file/filelist.cc index c2dc8e9196d..f177eebf6f2 100644 --- a/source/blender/editors/space_file/filelist.cc +++ b/source/blender/editors/space_file/filelist.cc @@ -115,6 +115,9 @@ struct FileListInternEntry { * Owning pointer. */ AssetMetaData *imported_asset_data; + /* See #FILE_ENTRY_BLENDERLIB_NO_PREVIEW. */ + bool blenderlib_has_no_preview; + /** Defined in BLI_fileops.h */ eFileAttributes attributes; BLI_stat_t st; @@ -844,7 +847,7 @@ static bool is_filtered_lib_type(FileListInternEntry *file, { char path[FILE_MAX_LIBEXTRA], dir[FILE_MAX_LIBEXTRA], *group, *name; - BLI_join_dirfile(path, sizeof(path), root, file->relpath); + BLI_path_join(path, sizeof(path), root, file->relpath); if (BLO_library_path_explode(path, dir, &group, &name)) { return is_filtered_id_file_type(file, group, name, filter); @@ -1204,7 +1207,7 @@ static int filelist_geticon_ex(const FileDirEntry *file, target = file->redirection_path; } else if (root) { - BLI_join_dirfile(fullpath, sizeof(fullpath), root, file->relpath); + BLI_path_join(fullpath, sizeof(fullpath), root, file->relpath); BLI_path_slash_ensure(fullpath); } for (; tfsm; tfsm = tfsm->next) { @@ -1575,6 +1578,14 @@ static void filelist_cache_previews_push(FileList *filelist, FileDirEntry *entry return; } + /* If we know this is an external ID without a preview, skip loading the preview. Can save quite + * some time in heavy files, because otherwise for each missing preview and for each preview + * reload, we'd reopen the .blend to look for the preview. */ + if ((entry->typeflag & FILE_TYPE_BLENDERLIB) && + (entry->flags & FILE_ENTRY_BLENDERLIB_NO_PREVIEW)) { + return; + } + FileListInternEntry *intern_entry = filelist->filelist_intern.filtered[index]; PreviewImage *preview_in_memory = intern_entry->local_data.preview_image; if (preview_in_memory && !BKE_previewimg_is_finished(preview_in_memory, ICON_SIZE_PREVIEW)) { @@ -1606,7 +1617,7 @@ static void filelist_cache_previews_push(FileList *filelist, FileDirEntry *entry BLI_strncpy(preview->filepath, entry->redirection_path, FILE_MAXDIR); } else { - BLI_join_dirfile( + BLI_path_join( preview->filepath, sizeof(preview->filepath), filelist->filelist.root, entry->relpath); } // printf("%s: %d - %s\n", __func__, preview->index, preview->filepath); @@ -1894,7 +1905,7 @@ static char *fileentry_uiname(const char *root, if (typeflag & FILE_TYPE_FTFONT && !(typeflag & FILE_TYPE_BLENDERLIB)) { char abspath[FILE_MAX_LIBEXTRA]; - BLI_join_dirfile(abspath, sizeof(abspath), root, relpath); + BLI_path_join(abspath, sizeof(abspath), root, relpath); name = BLF_display_name_from_file(abspath); if (name) { /* Allocated string, so no need to #BLI_strdup. */ @@ -1906,7 +1917,7 @@ static char *fileentry_uiname(const char *root, char abspath[FILE_MAX_LIBEXTRA]; char *group; - BLI_join_dirfile(abspath, sizeof(abspath), root, relpath); + BLI_path_join(abspath, sizeof(abspath), root, relpath); BLO_library_path_explode(abspath, buff, &group, &name); if (!name) { name = group; @@ -2042,6 +2053,9 @@ static FileDirEntry *filelist_file_create_entry(FileList *filelist, const int in ret->preview_icon_id = BKE_icon_imbuf_create(ibuf); } } + if (entry->blenderlib_has_no_preview) { + ret->flags |= FILE_ENTRY_BLENDERLIB_NO_PREVIEW; + } BLI_addtail(&cache->cached_entries, ret); return ret; } @@ -2887,7 +2901,7 @@ static int filelist_readjob_list_dir(const char *root, entry->relpath = static_cast<char *>(MEM_dupallocN(files[i].relname)); entry->st = files[i].s; - BLI_join_dirfile(full_path, FILE_MAX, root, entry->relpath); + BLI_path_join(full_path, FILE_MAX, root, entry->relpath); char *target = full_path; /* Set initial file type and attributes. */ @@ -2995,10 +3009,15 @@ static void filelist_readjob_list_lib_add_datablock(ListBase *entries, entry->relpath = BLI_strdup(datablock_info->name); } entry->typeflag |= FILE_TYPE_BLENDERLIB; - if (datablock_info && datablock_info->asset_data) { - entry->typeflag |= FILE_TYPE_ASSET; - /* Moves ownership! */ - entry->imported_asset_data = datablock_info->asset_data; + + if (datablock_info) { + entry->blenderlib_has_no_preview = datablock_info->no_preview_found; + + if (datablock_info->asset_data) { + entry->typeflag |= FILE_TYPE_ASSET; + /* Moves ownership! */ + entry->imported_asset_data = datablock_info->asset_data; + } } entry->blentype = idcode; BLI_addtail(entries, entry); @@ -3525,7 +3544,7 @@ static void filelist_readjob_recursive_dir_add_items(const bool do_lib, /* When loading entries recursive, the rel_path should be relative from the root dir. * we combine the relative path to the subdir with the relative path of the entry. */ - BLI_join_dirfile(dir, sizeof(dir), rel_subdir, entry->relpath); + BLI_path_join(dir, sizeof(dir), rel_subdir, entry->relpath); MEM_freeN(entry->relpath); entry->relpath = BLI_strdup(dir + 2); /* + 2 to remove '//' * added by BLI_path_rel to rel_subdir. */ @@ -3535,7 +3554,7 @@ static void filelist_readjob_recursive_dir_add_items(const bool do_lib, if (filelist_readjob_should_recurse_into_entry( max_recursion, is_lib, recursion_level, entry)) { /* We have a directory we want to list, add it to todo list! */ - BLI_join_dirfile(dir, sizeof(dir), root, entry->relpath); + BLI_path_join(dir, sizeof(dir), root, entry->relpath); BLI_path_normalize_dir(job_params->main_name, dir); td_dir = static_cast<TodoDir *>(BLI_stack_push_r(todo_dirs)); td_dir->level = recursion_level + 1; diff --git a/source/blender/editors/space_file/filesel.c b/source/blender/editors/space_file/filesel.c index 9ebc4872544..df7e9702e85 100644 --- a/source/blender/editors/space_file/filesel.c +++ b/source/blender/editors/space_file/filesel.c @@ -1174,7 +1174,7 @@ int autocomplete_directory(struct bContext *C, char *str, void *UNUSED(arg_v)) char path[FILE_MAX]; BLI_stat_t status; - BLI_join_dirfile(path, sizeof(path), dirname, de->d_name); + BLI_path_join(path, sizeof(path), dirname, de->d_name); if (BLI_stat(path, &status) == 0) { if (S_ISDIR(status.st_mode)) { /* is subdir */ diff --git a/source/blender/editors/space_file/fsmenu.c b/source/blender/editors/space_file/fsmenu.c index 35ce7ef364c..cea53908d4f 100644 --- a/source/blender/editors/space_file/fsmenu.c +++ b/source/blender/editors/space_file/fsmenu.c @@ -113,10 +113,10 @@ static GHash *fsmenu_xdg_user_dirs_parse(const char *home) char filepath[FILE_MAX]; const char *xdg_config_home = getenv("XDG_CONFIG_HOME"); if (xdg_config_home != NULL) { - BLI_path_join(filepath, sizeof(filepath), xdg_config_home, "user-dirs.dirs", NULL); + BLI_path_join(filepath, sizeof(filepath), xdg_config_home, "user-dirs.dirs"); } else { - BLI_path_join(filepath, sizeof(filepath), home, ".config", "user-dirs.dirs", NULL); + BLI_path_join(filepath, sizeof(filepath), home, ".config", "user-dirs.dirs"); } fp = BLI_fopen(filepath, "r"); if (!fp) { @@ -147,7 +147,7 @@ static GHash *fsmenu_xdg_user_dirs_parse(const char *home) * Based on the 'user-dirs.dirs' man page, * there is no need to resolve arbitrary environment variables. */ if (STRPREFIX(l_value, "$HOME" SEP_STR)) { - BLI_path_join(l_value_expanded, sizeof(l_value_expanded), home, l_value + 6, NULL); + BLI_path_join(l_value_expanded, sizeof(l_value_expanded), home, l_value + 6); l_value_final = l_value_expanded; } @@ -186,7 +186,7 @@ static void fsmenu_xdg_insert_entry(GHash *xdg_map, char xdg_path_buf[FILE_MAXDIR]; const char *xdg_path = xdg_map ? BLI_ghash_lookup(xdg_map, key) : NULL; if (xdg_path == NULL) { - BLI_path_join(xdg_path_buf, sizeof(xdg_path_buf), home, default_path, NULL); + BLI_path_join(xdg_path_buf, sizeof(xdg_path_buf), home, default_path); xdg_path = xdg_path_buf; } fsmenu_insert_entry( @@ -254,10 +254,10 @@ void ED_fsmenu_entry_set_path(struct FSMenuEntry *fsentry, const char *path) fsentry->path = (path && path[0]) ? BLI_strdup(path) : NULL; - BLI_join_dirfile(tmp_name, - sizeof(tmp_name), - BKE_appdir_folder_id_create(BLENDER_USER_CONFIG, NULL), - BLENDER_BOOKMARK_FILE); + BLI_path_join(tmp_name, + sizeof(tmp_name), + BKE_appdir_folder_id_create(BLENDER_USER_CONFIG, NULL), + BLENDER_BOOKMARK_FILE); fsmenu_write_file(ED_fsmenu_get(), tmp_name); } } @@ -318,10 +318,10 @@ void ED_fsmenu_entry_set_name(struct FSMenuEntry *fsentry, const char *name) BLI_strncpy(fsentry->name, name, sizeof(fsentry->name)); } - BLI_join_dirfile(tmp_name, - sizeof(tmp_name), - BKE_appdir_folder_id_create(BLENDER_USER_CONFIG, NULL), - BLENDER_BOOKMARK_FILE); + BLI_path_join(tmp_name, + sizeof(tmp_name), + BKE_appdir_folder_id_create(BLENDER_USER_CONFIG, NULL), + BLENDER_BOOKMARK_FILE); fsmenu_write_file(ED_fsmenu_get(), tmp_name); } } @@ -983,7 +983,7 @@ void fsmenu_read_system(struct FSMenu *fsmenu, int read_bookmarks) if (xdg_runtime_dir != NULL) { struct direntry *dirs; char name[FILE_MAX]; - BLI_join_dirfile(name, sizeof(name), xdg_runtime_dir, "gvfs/"); + BLI_path_join(name, sizeof(name), xdg_runtime_dir, "gvfs/"); const uint dirs_num = BLI_filelist_dir_contents(name, &dirs); for (uint i = 0; i < dirs_num; i++) { if (dirs[i].type & S_IFDIR) { diff --git a/source/blender/editors/space_file/space_file.c b/source/blender/editors/space_file/space_file.c index a3182222263..74f1b8e838a 100644 --- a/source/blender/editors/space_file/space_file.c +++ b/source/blender/editors/space_file/space_file.c @@ -1157,7 +1157,7 @@ void ED_file_read_bookmarks(void) if (cfgdir) { char name[FILE_MAX]; - BLI_join_dirfile(name, sizeof(name), cfgdir, BLENDER_BOOKMARK_FILE); + BLI_path_join(name, sizeof(name), cfgdir, BLENDER_BOOKMARK_FILE); fsmenu_read_bookmarks(ED_fsmenu_get(), name); } } diff --git a/source/blender/editors/space_image/image_draw.c b/source/blender/editors/space_image/image_draw.c index 42ebed6ec3a..85b1e2b6707 100644 --- a/source/blender/editors/space_image/image_draw.c +++ b/source/blender/editors/space_image/image_draw.c @@ -589,15 +589,27 @@ void ED_space_image_grid_steps(SpaceImage *sima, float grid_steps_y[SI_GRID_STEPS_LEN], const int grid_dimension) { - const int flag = sima->flag; + const eSpaceImage_GridShapeSource grid_shape_source = sima->grid_shape_source; for (int step = 0; step < SI_GRID_STEPS_LEN; step++) { - if (flag & SI_CUSTOM_GRID) { - grid_steps_x[step] = 1.0f / sima->custom_grid_subdiv[0]; - grid_steps_y[step] = 1.0f / sima->custom_grid_subdiv[1]; - } - else { - grid_steps_x[step] = powf(grid_dimension, step - SI_GRID_STEPS_LEN); - grid_steps_y[step] = powf(grid_dimension, step - SI_GRID_STEPS_LEN); + switch (grid_shape_source) { + case SI_GRID_SHAPE_DYNAMIC: + grid_steps_x[step] = powf(grid_dimension, step - SI_GRID_STEPS_LEN); + grid_steps_y[step] = powf(grid_dimension, step - SI_GRID_STEPS_LEN); + break; + case SI_GRID_SHAPE_FIXED: + grid_steps_x[step] = 1.0f / sima->custom_grid_subdiv[0]; + grid_steps_y[step] = 1.0f / sima->custom_grid_subdiv[1]; + break; + case SI_GRID_SHAPE_PIXEL: { + int pixel_width = IMG_SIZE_FALLBACK; + int pixel_height = IMG_SIZE_FALLBACK; + ED_space_image_get_size(sima, &pixel_width, &pixel_height); + BLI_assert(pixel_width > 0 && pixel_height > 0); + grid_steps_x[step] = 1.0f / pixel_width; + grid_steps_y[step] = 1.0f / pixel_height; + } break; + default: + BLI_assert_unreachable(); } } } diff --git a/source/blender/editors/space_image/image_ops.c b/source/blender/editors/space_image/image_ops.c index bb47ad5c6c0..3503c4c8168 100644 --- a/source/blender/editors/space_image/image_ops.c +++ b/source/blender/editors/space_image/image_ops.c @@ -762,14 +762,14 @@ static int image_view_ndof_invoke(bContext *C, wmOperator *UNUSED(op), const wmE float pan_vec[3]; const wmNDOFMotionData *ndof = event->customdata; - const float speed = NDOF_PIXELS_PER_SECOND; + const float pan_speed = NDOF_PIXELS_PER_SECOND; WM_event_ndof_pan_get(ndof, pan_vec, true); - mul_v2_fl(pan_vec, (speed * ndof->dt) / sima->zoom); - pan_vec[2] *= -ndof->dt; + mul_v3_fl(pan_vec, ndof->dt); + mul_v2_fl(pan_vec, pan_speed / sima->zoom); - sima_zoom_set_factor(sima, region, 1.0f + pan_vec[2], NULL, false); + sima_zoom_set_factor(sima, region, max_ff(0.0f, 1.0f - pan_vec[2]), NULL, false); sima->xof += pan_vec[0]; sima->yof += pan_vec[1]; diff --git a/source/blender/editors/space_image/image_sequence.c b/source/blender/editors/space_image/image_sequence.c index 7108c258665..d2725652979 100644 --- a/source/blender/editors/space_image/image_sequence.c +++ b/source/blender/editors/space_image/image_sequence.c @@ -62,14 +62,14 @@ static void image_sequence_get_frame_ranges(wmOperator *op, ListBase *ranges) STREQLEN(base_tail, tail, FILE_MAX)) { /* Set filepath to first frame in the range. */ if (frame->framenr < range_first_frame) { - BLI_join_dirfile(range->filepath, sizeof(range->filepath), dir, filename); + BLI_path_join(range->filepath, sizeof(range->filepath), dir, filename); range_first_frame = frame->framenr; } } else { /* start a new frame range */ range = MEM_callocN(sizeof(*range), __func__); - BLI_join_dirfile(range->filepath, sizeof(range->filepath), dir, filename); + BLI_path_join(range->filepath, sizeof(range->filepath), dir, filename); BLI_addtail(ranges, range); BLI_strncpy(base_head, head, sizeof(base_head)); diff --git a/source/blender/editors/space_image/space_image.c b/source/blender/editors/space_image/space_image.c index 71cbcea1c1f..53e1bc0a1e5 100644 --- a/source/blender/editors/space_image/space_image.c +++ b/source/blender/editors/space_image/space_image.c @@ -5,6 +5,7 @@ * \ingroup spimage */ +#include "DNA_defaults.h" #include "DNA_gpencil_types.h" #include "DNA_image_types.h" #include "DNA_mask_types.h" @@ -117,6 +118,8 @@ static SpaceLink *image_create(const ScrArea *UNUSED(area), const Scene *UNUSED( simage->custom_grid_subdiv[0] = 10; simage->custom_grid_subdiv[1] = 10; + simage->mask_info = *DNA_struct_default_get(MaskSpaceInfo); + /* header */ region = MEM_callocN(sizeof(ARegion), "header for image"); diff --git a/source/blender/editors/space_info/info_stats.cc b/source/blender/editors/space_info/info_stats.cc index cac0dbb0067..e8b005b2b67 100644 --- a/source/blender/editors/space_info/info_stats.cc +++ b/source/blender/editors/space_info/info_stats.cc @@ -40,6 +40,7 @@ #include "BKE_key.h" #include "BKE_layer.h" #include "BKE_main.h" +#include "BKE_mesh.h" #include "BKE_object.h" #include "BKE_paint.h" #include "BKE_particle.h" @@ -94,8 +95,8 @@ static bool stats_mesheval(const Mesh *me_eval, bool is_selected, SceneStats *st int totvert, totedge, totface, totloop; - const SubdivCCG *subdiv_ccg = me_eval->runtime.subdiv_ccg; - const SubsurfRuntimeData *subsurf_runtime_data = me_eval->runtime.subsurf_runtime_data; + const SubdivCCG *subdiv_ccg = me_eval->runtime->subdiv_ccg; + const SubsurfRuntimeData *subsurf_runtime_data = me_eval->runtime->subsurf_runtime_data; if (subdiv_ccg != nullptr) { BKE_subdiv_ccg_topology_counters(subdiv_ccg, &totvert, &totedge, &totface, &totloop); diff --git a/source/blender/editors/space_info/textview.c b/source/blender/editors/space_info/textview.c index aee72860a0a..12ee6f45991 100644 --- a/source/blender/editors/space_info/textview.c +++ b/source/blender/editors/space_info/textview.c @@ -235,7 +235,8 @@ static bool textview_draw_string(TextViewDrawState *tds, 1.0f, 0.0f, icon_fg, - false); + false, + UI_NO_ICON_OVERLAY_TEXT); GPU_blend(GPU_BLEND_NONE); } diff --git a/source/blender/editors/space_nla/nla_edit.c b/source/blender/editors/space_nla/nla_edit.c index 9df25b1229e..8a3c6745259 100644 --- a/source/blender/editors/space_nla/nla_edit.c +++ b/source/blender/editors/space_nla/nla_edit.c @@ -2641,6 +2641,8 @@ static int nla_fmodifier_add_exec(bContext *C, wmOperator *op) void NLA_OT_fmodifier_add(wmOperatorType *ot) { + PropertyRNA *prop; + /* identifiers */ ot->name = "Add F-Modifier"; ot->idname = "NLA_OT_fmodifier_add"; @@ -2659,11 +2661,12 @@ void NLA_OT_fmodifier_add(wmOperatorType *ot) RNA_def_property_translation_context(ot->prop, BLT_I18NCONTEXT_ID_ACTION); RNA_def_enum_funcs(ot->prop, nla_fmodifier_itemf); - RNA_def_boolean(ot->srna, - "only_active", - true, - "Only Active", - "Only add a F-Modifier of the specified type to the active strip"); + prop = RNA_def_boolean(ot->srna, + "only_active", + true, + "Only Active", + "Only add a F-Modifier of the specified type to the active strip"); + RNA_def_property_translation_context(prop, BLT_I18NCONTEXT_ID_ACTION); } /** \} */ @@ -2832,8 +2835,10 @@ void NLA_OT_fmodifier_paste(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; /* properties */ - RNA_def_boolean( + ot->prop = RNA_def_boolean( ot->srna, "only_active", true, "Only Active", "Only paste F-Modifiers on active strip"); + RNA_def_property_translation_context(ot->prop, BLT_I18NCONTEXT_ID_ACTION); + RNA_def_boolean( ot->srna, "replace", diff --git a/source/blender/editors/space_node/add_node_search.cc b/source/blender/editors/space_node/add_node_search.cc index 819f0abac7c..28e18c20f46 100644 --- a/source/blender/editors/space_node/add_node_search.cc +++ b/source/blender/editors/space_node/add_node_search.cc @@ -182,6 +182,9 @@ static void gather_add_node_operations(const bContext &C, /* Skip the empty group type. */ continue; } + if (StringRefNull(node_type->ui_name).endswith("(Legacy)")) { + continue; + } AddNodeItem item{}; item.ui_name = IFACE_(node_type->ui_name); diff --git a/source/blender/editors/space_node/link_drag_search.cc b/source/blender/editors/space_node/link_drag_search.cc index 17410937d4c..ffa2d377056 100644 --- a/source/blender/editors/space_node/link_drag_search.cc +++ b/source/blender/editors/space_node/link_drag_search.cc @@ -274,7 +274,9 @@ static void gather_socket_link_operations(const bContext &C, if (!(node_type->poll && node_type->poll(node_type, &node_tree, &disabled_hint))) { continue; } - + if (StringRefNull(node_type->ui_name).endswith("(Legacy)")) { + continue; + } if (node_type->gather_link_search_ops) { nodes::GatherLinkSearchOpParams params{*node_type, node_tree, socket, search_link_ops}; node_type->gather_link_search_ops(params); diff --git a/source/blender/editors/space_node/node_draw.cc b/source/blender/editors/space_node/node_draw.cc index dc155f5e28d..98b2cacd162 100644 --- a/source/blender/editors/space_node/node_draw.cc +++ b/source/blender/editors/space_node/node_draw.cc @@ -2145,6 +2145,9 @@ static void node_draw_basis(const bContext &C, 0, ""); UI_but_func_set(but, node_toggle_button_cb, &node, (void *)"NODE_OT_group_edit"); + if (node.id) { + UI_but_icon_indicator_number_set(but, node.id->us); + } UI_block_emboss_set(&block, UI_EMBOSS); } if (node.type == NODE_CUSTOM && node.typeinfo->ui_icon != ICON_NONE) { @@ -3011,7 +3014,7 @@ static void node_draw_nodetree(const bContext &C, } } -/* Draw the breadcrumb on the bottom of the editor. */ +/* Draw the breadcrumb on the top of the editor. */ static void draw_tree_path(const bContext &C, ARegion ®ion) { using namespace blender; diff --git a/source/blender/editors/space_node/node_edit.cc b/source/blender/editors/space_node/node_edit.cc index 3a80733f06c..48b3d711bdf 100644 --- a/source/blender/editors/space_node/node_edit.cc +++ b/source/blender/editors/space_node/node_edit.cc @@ -1361,12 +1361,15 @@ static int node_duplicate_exec(bContext *C, wmOperator *op) SpaceNode *snode = CTX_wm_space_node(C); bNodeTree *ntree = snode->edittree; const bool keep_inputs = RNA_boolean_get(op->ptr, "keep_inputs"); + bool linked = RNA_boolean_get(op->ptr, "linked") || ((U.dupflag & USER_DUP_NTREE) == 0); + const bool dupli_node_tree = !linked; bool changed = false; ED_preview_kill_jobs(CTX_wm_manager(C), bmain); Map<const bNode *, bNode *> node_map; Map<const bNodeSocket *, bNodeSocket *> socket_map; + Map<const ID *, ID *> duplicated_node_groups; bNode *lastnode = (bNode *)ntree->nodes.last; LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { @@ -1374,6 +1377,18 @@ static int node_duplicate_exec(bContext *C, wmOperator *op) bNode *new_node = bke::node_copy_with_mapping( ntree, *node, LIB_ID_COPY_DEFAULT, true, socket_map); node_map.add_new(node, new_node); + + if (node->id && dupli_node_tree) { + ID *new_group = duplicated_node_groups.lookup_or_add_cb(node->id, [&]() { + ID *new_group = BKE_id_copy(bmain, node->id); + /* Remove user added by copying. */ + id_us_min(new_group); + return new_group; + }); + id_us_plus(new_group); + id_us_min(new_node->id); + new_node->id = new_group; + } changed = true; } @@ -1462,6 +1477,8 @@ static int node_duplicate_exec(bContext *C, wmOperator *op) void NODE_OT_duplicate(wmOperatorType *ot) { + PropertyRNA *prop; + /* identifiers */ ot->name = "Duplicate Nodes"; ot->description = "Duplicate selected nodes"; @@ -1476,6 +1493,13 @@ void NODE_OT_duplicate(wmOperatorType *ot) RNA_def_boolean( ot->srna, "keep_inputs", false, "Keep Inputs", "Keep the input links to duplicated nodes"); + + prop = RNA_def_boolean(ot->srna, + "linked", + true, + "Linked", + "Duplicate node but not node trees, linking to the original data"); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); } /* XXX: some code needing updating to operators. */ @@ -2275,6 +2299,7 @@ static int node_clipboard_copy_exec(bContext *C, wmOperator * /*op*/) newlink->tosock = socket_map.lookup(link->tosock); newlink->fromnode = node_map.lookup(link->fromnode); newlink->fromsock = socket_map.lookup(link->fromsock); + newlink->multi_input_socket_index = link->multi_input_socket_index; BKE_node_clipboard_add_link(newlink); } @@ -2396,11 +2421,19 @@ static int node_clipboard_paste_exec(bContext *C, wmOperator *op) } LISTBASE_FOREACH (bNodeLink *, link, clipboard_links_lb) { - nodeAddLink(ntree, - node_map.lookup(link->fromnode), - socket_map.lookup(link->fromsock), - node_map.lookup(link->tonode), - socket_map.lookup(link->tosock)); + bNodeLink *new_link = nodeAddLink(ntree, + node_map.lookup(link->fromnode), + socket_map.lookup(link->fromsock), + node_map.lookup(link->tonode), + socket_map.lookup(link->tosock)); + new_link->multi_input_socket_index = link->multi_input_socket_index; + } + + ntree->ensure_topology_cache(); + + for (bNode *new_node : node_map.values()) { + /* Update multi input socket indices in case all connected nodes weren't copied. */ + update_multi_input_indices_for_removed_links(*new_node); } Main *bmain = CTX_data_main(C); diff --git a/source/blender/editors/space_node/node_intern.hh b/source/blender/editors/space_node/node_intern.hh index 50c03489027..1c3026628a6 100644 --- a/source/blender/editors/space_node/node_intern.hh +++ b/source/blender/editors/space_node/node_intern.hh @@ -270,6 +270,8 @@ void NODE_OT_group_edit(wmOperatorType *ot); /* node_relationships.cc */ +void update_multi_input_indices_for_removed_links(bNode &node); + void NODE_OT_link(wmOperatorType *ot); void NODE_OT_link_make(wmOperatorType *ot); void NODE_OT_links_cut(wmOperatorType *ot); diff --git a/source/blender/editors/space_node/node_ops.cc b/source/blender/editors/space_node/node_ops.cc index 6c52dae5b86..d45c33a3c59 100644 --- a/source/blender/editors/space_node/node_ops.cc +++ b/source/blender/editors/space_node/node_ops.cc @@ -174,7 +174,17 @@ void ED_operatormacros_node() "Duplicate", "Duplicate selected nodes and move them", OPTYPE_UNDO | OPTYPE_REGISTER); - WM_operatortype_macro_define(ot, "NODE_OT_duplicate"); + mot = WM_operatortype_macro_define(ot, "NODE_OT_duplicate"); + RNA_boolean_set(mot->ptr, "linked", false); + WM_operatortype_macro_define(ot, "NODE_OT_translate_attach"); + + ot = WM_operatortype_append_macro( + "NODE_OT_duplicate_move_linked", + "Duplicate Linked", + "Duplicate selected nodes, but not their node trees, and move them", + OPTYPE_UNDO | OPTYPE_REGISTER); + mot = WM_operatortype_macro_define(ot, "NODE_OT_duplicate"); + RNA_boolean_set(mot->ptr, "linked", true); WM_operatortype_macro_define(ot, "NODE_OT_translate_attach"); /* modified operator call for duplicating with input links */ diff --git a/source/blender/editors/space_node/node_relationships.cc b/source/blender/editors/space_node/node_relationships.cc index 7cf18e2f828..637c795d4d7 100644 --- a/source/blender/editors/space_node/node_relationships.cc +++ b/source/blender/editors/space_node/node_relationships.cc @@ -64,6 +64,10 @@ struct NodeInsertOfsData { float offset_x; /* offset to apply to node chain */ }; +namespace blender::ed::space_node { + +bNodeSocket *get_main_socket(bNodeTree &ntree, bNode &node, eNodeSocketInOut in_out); + static void clear_picking_highlight(ListBase *links) { LISTBASE_FOREACH (bNodeLink *, link, links) { @@ -71,10 +75,6 @@ static void clear_picking_highlight(ListBase *links) } } -namespace blender::ed::space_node { - -void update_multi_input_indices_for_removed_links(bNode &node); - /* -------------------------------------------------------------------- */ /** \name Add Node * \{ */ @@ -815,7 +815,8 @@ static void draw_draglink_tooltip(const bContext * /*C*/, ARegion * /*region*/, nldrag->cursor[0]; const float y = nldrag->cursor[1] - 2.0f * UI_DPI_FAC; - UI_icon_draw_ex(x, y, ICON_ADD, U.inv_dpi_fac, 1.0f, 0.0f, text_col, false); + UI_icon_draw_ex( + x, y, ICON_ADD, U.inv_dpi_fac, 1.0f, 0.0f, text_col, false, UI_NO_ICON_OVERLAY_TEXT); } static void draw_draglink_tooltip_activate(const ARegion ®ion, bNodeLinkDrag &nldrag) @@ -1750,29 +1751,31 @@ static int node_attach_invoke(bContext *C, wmOperator * /*op*/, const wmEvent *e } LISTBASE_FOREACH_BACKWARD (bNode *, node, &ntree.nodes) { - if (node->flag & NODE_SELECT) { - if (node->parent == nullptr) { - /* disallow moving a parent into its child */ - if (nodeAttachNodeCheck(frame, node) == false) { - /* attach all unparented nodes */ - nodeAttachNode(node, frame); - } + if (!(node->flag & NODE_SELECT)) { + continue; + } + + if (node->parent == nullptr) { + /* disallow moving a parent into its child */ + if (nodeAttachNodeCheck(frame, node) == false) { + /* attach all unparented nodes */ + nodeAttachNode(node, frame); } - else { - /* attach nodes which share parent with the frame */ - bNode *parent; - for (parent = frame->parent; parent; parent = parent->parent) { - if (parent == node->parent) { - break; - } + } + else { + /* attach nodes which share parent with the frame */ + bNode *parent; + for (parent = frame->parent; parent; parent = parent->parent) { + if (parent == node->parent) { + break; } + } - if (parent) { - /* disallow moving a parent into its child */ - if (nodeAttachNodeCheck(frame, node) == false) { - nodeDetachNode(node); - nodeAttachNode(node, frame); - } + if (parent) { + /* disallow moving a parent into its child */ + if (nodeAttachNodeCheck(frame, node) == false) { + nodeDetachNode(node); + nodeAttachNode(node, frame); } } } @@ -1881,100 +1884,55 @@ void NODE_OT_detach(wmOperatorType *ot) /** \name Automatic Node Insert on Dragging * \{ */ -/* prevent duplicate testing code below */ -static bool ed_node_link_conditions(ScrArea *area, - bool test, - SpaceNode **r_snode, - bNode **r_select) +static bNode *get_selected_node_for_insertion(bNodeTree &node_tree) { - SpaceNode *snode = area ? (SpaceNode *)area->spacedata.first : nullptr; - - *r_snode = snode; - *r_select = nullptr; - - /* no unlucky accidents */ - if (area == nullptr || area->spacetype != SPACE_NODE) { - return false; - } - - if (!test) { - /* no need to look for a node */ - return true; - } - - bNode *node; - bNode *select = nullptr; - for (node = (bNode *)snode->edittree->nodes.first; node; node = node->next) { + bNode *selected_node = nullptr; + int selected_node_count = 0; + for (bNode *node : node_tree.all_nodes()) { if (node->flag & SELECT) { - if (select) { - break; - } - select = node; + selected_node = node; + selected_node_count++; + } + if (selected_node_count > 1) { + return nullptr; } } - /* only one selected */ - if (node || select == nullptr) { - return false; + if (!selected_node) { + return nullptr; } - - /* correct node */ - if (BLI_listbase_is_empty(&select->inputs) || BLI_listbase_is_empty(&select->outputs)) { - return false; + if (selected_node->input_sockets().is_empty() || selected_node->output_sockets().is_empty()) { + return nullptr; } - - ARegion *region = BKE_area_find_region_type(area, RGN_TYPE_WINDOW); - - /* test node for links */ - LISTBASE_FOREACH (bNodeLink *, link, &snode->edittree->links) { - if (node_link_is_hidden_or_dimmed(region->v2d, *link)) { - continue; - } - - if (link->tonode == select || link->fromnode == select) { - return false; - } + if (std::any_of(selected_node->input_sockets().begin(), + selected_node->input_sockets().end(), + [&](const bNodeSocket *socket) { return socket->is_directly_linked(); })) { + return nullptr; } - - *r_select = select; - return true; + if (std::any_of(selected_node->output_sockets().begin(), + selected_node->output_sockets().end(), + [&](const bNodeSocket *socket) { return socket->is_directly_linked(); })) { + return nullptr; + }; + return selected_node; } -/** \} */ - -} // namespace blender::ed::space_node - -/* -------------------------------------------------------------------- */ -/** \name Node Line Intersection Test - * \{ */ - -void ED_node_link_intersect_test(ScrArea *area, int test) +void node_insert_on_link_flags_set(SpaceNode &snode, const ARegion ®ion) { - using namespace blender; - using namespace blender::ed::space_node; - - bNode *select; - SpaceNode *snode; - if (!ed_node_link_conditions(area, test, &snode, &select)) { - return; - } + bNodeTree &node_tree = *snode.edittree; + node_tree.ensure_topology_cache(); - /* clear flags */ - LISTBASE_FOREACH (bNodeLink *, link, &snode->edittree->links) { - link->flag &= ~NODE_LINKFLAG_HILITE; - } + node_insert_on_link_flags_clear(node_tree); - if (test == 0) { + bNode *node_to_insert = get_selected_node_for_insertion(node_tree); + if (!node_to_insert) { return; } - ARegion *region = BKE_area_find_region_type(area, RGN_TYPE_WINDOW); - /* find link to select/highlight */ bNodeLink *selink = nullptr; float dist_best = FLT_MAX; - LISTBASE_FOREACH (bNodeLink *, link, &snode->edittree->links) { - - if (node_link_is_hidden_or_dimmed(region->v2d, *link)) { + LISTBASE_FOREACH (bNodeLink *, link, &node_tree.links) { + if (node_link_is_hidden_or_dimmed(region.v2d, *link)) { continue; } @@ -1986,10 +1944,10 @@ void ED_node_link_intersect_test(ScrArea *area, int test) * upper left node edge of a intersected line segment */ for (int i = 0; i < NODE_LINK_RESOL; i++) { /* Check if the node rectangle intersects the line from this point to next one. */ - if (BLI_rctf_isect_segment(&select->totr, coords[i], coords[i + 1])) { + if (BLI_rctf_isect_segment(&node_to_insert->totr, coords[i], coords[i + 1])) { /* store the shortest distance to the upper left edge * of all intersections found so far */ - const float node_xy[] = {select->totr.xmin, select->totr.ymax}; + const float node_xy[] = {node_to_insert->totr.xmin, node_to_insert->totr.ymax}; /* to be precise coords should be clipped by select->totr, * but not done since there's no real noticeable difference */ @@ -2009,9 +1967,89 @@ void ED_node_link_intersect_test(ScrArea *area, int test) } } -/** \} */ +void node_insert_on_link_flags_clear(bNodeTree &node_tree) +{ + LISTBASE_FOREACH (bNodeLink *, link, &node_tree.links) { + link->flag &= ~NODE_LINKFLAG_HILITE; + } +} -namespace blender::ed::space_node { +void node_insert_on_link_flags(Main &bmain, SpaceNode &snode) +{ + bNodeTree &node_tree = *snode.edittree; + node_tree.ensure_topology_cache(); + bNode *node_to_insert = get_selected_node_for_insertion(node_tree); + if (!node_to_insert) { + return; + } + + /* Find link to insert on. */ + bNodeTree &ntree = *snode.edittree; + bNodeLink *old_link = nullptr; + LISTBASE_FOREACH (bNodeLink *, link, &ntree.links) { + if (link->flag & NODE_LINKFLAG_HILITE) { + old_link = link; + break; + } + } + if (old_link == nullptr) { + return; + } + + old_link->flag &= ~NODE_LINKFLAG_HILITE; + + bNodeSocket *best_input = get_main_socket(ntree, *node_to_insert, SOCK_IN); + bNodeSocket *best_output = get_main_socket(ntree, *node_to_insert, SOCK_OUT); + + if (node_to_insert->type != NODE_REROUTE) { + /* Ignore main sockets when the types don't match. */ + if (best_input != nullptr && ntree.typeinfo->validate_link != nullptr && + !ntree.typeinfo->validate_link(static_cast<eNodeSocketDatatype>(old_link->fromsock->type), + static_cast<eNodeSocketDatatype>(best_input->type))) { + best_input = nullptr; + } + if (best_output != nullptr && ntree.typeinfo->validate_link != nullptr && + !ntree.typeinfo->validate_link(static_cast<eNodeSocketDatatype>(best_output->type), + static_cast<eNodeSocketDatatype>(old_link->tosock->type))) { + best_output = nullptr; + } + } + + bNode *from_node = old_link->fromnode; + bNodeSocket *from_socket = old_link->fromsock; + bNode *to_node = old_link->tonode; + + if (best_output != nullptr) { + /* Relink the "start" of the existing link to the newly inserted node. */ + old_link->fromnode = node_to_insert; + old_link->fromsock = best_output; + BKE_ntree_update_tag_link_changed(&ntree); + } + else { + nodeRemLink(&ntree, old_link); + } + + if (best_input != nullptr) { + /* Add a new link that connects the node on the left to the newly inserted node. */ + nodeAddLink(&ntree, from_node, from_socket, node_to_insert, best_input); + } + + /* Set up insert offset data, it needs stuff from here. */ + if ((snode.flag & SNODE_SKIP_INSOFFSET) == 0) { + BLI_assert(snode.runtime->iofsd == nullptr); + NodeInsertOfsData *iofsd = MEM_cnew<NodeInsertOfsData>(__func__); + + iofsd->insert = node_to_insert; + iofsd->prev = from_node; + iofsd->next = to_node; + + snode.runtime->iofsd = iofsd; + } + + ED_node_tree_propagate_change(nullptr, &bmain, &ntree); +} + +/** \} */ /* -------------------------------------------------------------------- */ /** \name Node Insert Offset Operator @@ -2048,7 +2086,7 @@ static int get_main_socket_priority(const bNodeSocket *socket) } /** Get the "main" socket based on the node declaration or an heuristic. */ -static bNodeSocket *get_main_socket(bNodeTree &ntree, bNode &node, eNodeSocketInOut in_out) +bNodeSocket *get_main_socket(bNodeTree &ntree, bNode &node, eNodeSocketInOut in_out) { ListBase *sockets = (in_out == SOCK_IN) ? &node.inputs : &node.outputs; @@ -2426,85 +2464,3 @@ void NODE_OT_insert_offset(wmOperatorType *ot) /** \} */ } // namespace blender::ed::space_node - -/* -------------------------------------------------------------------- */ -/** \name Note Link Insert - * \{ */ - -void ED_node_link_insert(Main *bmain, ScrArea *area) -{ - using namespace blender::ed::space_node; - - bNode *node_to_insert; - SpaceNode *snode; - if (!ed_node_link_conditions(area, true, &snode, &node_to_insert)) { - return; - } - - /* Find link to insert on. */ - bNodeTree &ntree = *snode->edittree; - bNodeLink *old_link = nullptr; - LISTBASE_FOREACH (bNodeLink *, link, &ntree.links) { - if (link->flag & NODE_LINKFLAG_HILITE) { - old_link = link; - break; - } - } - if (old_link == nullptr) { - return; - } - - old_link->flag &= ~NODE_LINKFLAG_HILITE; - - bNodeSocket *best_input = get_main_socket(ntree, *node_to_insert, SOCK_IN); - bNodeSocket *best_output = get_main_socket(ntree, *node_to_insert, SOCK_OUT); - - if (node_to_insert->type != NODE_REROUTE) { - /* Ignore main sockets when the types don't match. */ - if (best_input != nullptr && ntree.typeinfo->validate_link != nullptr && - !ntree.typeinfo->validate_link(static_cast<eNodeSocketDatatype>(old_link->fromsock->type), - static_cast<eNodeSocketDatatype>(best_input->type))) { - best_input = nullptr; - } - if (best_output != nullptr && ntree.typeinfo->validate_link != nullptr && - !ntree.typeinfo->validate_link(static_cast<eNodeSocketDatatype>(best_output->type), - static_cast<eNodeSocketDatatype>(old_link->tosock->type))) { - best_output = nullptr; - } - } - - bNode *from_node = old_link->fromnode; - bNodeSocket *from_socket = old_link->fromsock; - bNode *to_node = old_link->tonode; - - if (best_output != nullptr) { - /* Relink the "start" of the existing link to the newly inserted node. */ - old_link->fromnode = node_to_insert; - old_link->fromsock = best_output; - BKE_ntree_update_tag_link_changed(&ntree); - } - else { - nodeRemLink(&ntree, old_link); - } - - if (best_input != nullptr) { - /* Add a new link that connects the node on the left to the newly inserted node. */ - nodeAddLink(&ntree, from_node, from_socket, node_to_insert, best_input); - } - - /* Set up insert offset data, it needs stuff from here. */ - if ((snode->flag & SNODE_SKIP_INSOFFSET) == 0) { - BLI_assert(snode->runtime->iofsd == nullptr); - NodeInsertOfsData *iofsd = MEM_cnew<NodeInsertOfsData>(__func__); - - iofsd->insert = node_to_insert; - iofsd->prev = from_node; - iofsd->next = to_node; - - snode->runtime->iofsd = iofsd; - } - - ED_node_tree_propagate_change(nullptr, bmain, snode->edittree); -} - -/** \} */ diff --git a/source/blender/editors/space_outliner/outliner_dragdrop.cc b/source/blender/editors/space_outliner/outliner_dragdrop.cc index 259d879d76f..3b07c6da5fa 100644 --- a/source/blender/editors/space_outliner/outliner_dragdrop.cc +++ b/source/blender/editors/space_outliner/outliner_dragdrop.cc @@ -888,7 +888,7 @@ static bool datastack_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event) static char *datastack_drop_tooltip(bContext * /*C*/, wmDrag *drag, - const int UNUSED(xy[2]), + const int /*xy*/[2], struct wmDropBox * /*drop*/) { StackDropData *drop_data = static_cast<StackDropData *>(drag->poin); diff --git a/source/blender/editors/space_outliner/outliner_draw.cc b/source/blender/editors/space_outliner/outliner_draw.cc index 8a1119d5e66..699dd6d4844 100644 --- a/source/blender/editors/space_outliner/outliner_draw.cc +++ b/source/blender/editors/space_outliner/outliner_draw.cc @@ -2877,7 +2877,8 @@ static bool tselem_draw_icon(uiBlock *block, TreeStoreElem *tselem, TreeElement *te, float alpha, - const bool is_clickable) + const bool is_clickable, + const int num_elements) { TreeElementIcon data = tree_element_get_icon(tselem, te); if (data.icon == 0) { @@ -2885,6 +2886,8 @@ static bool tselem_draw_icon(uiBlock *block, } const bool is_collection = outliner_is_collection_tree_element(te); + IconTextOverlay text_overlay; + UI_icon_text_overlay_init_from_count(&text_overlay, num_elements); /* Collection colors and icons covered by restrict buttons. */ if (!is_clickable || x >= xmax || is_collection) { @@ -2904,7 +2907,8 @@ static bool tselem_draw_icon(uiBlock *block, alpha, 0.0f, btheme->collection_color[collection->color_tag].color, - true); + true, + &text_overlay); return true; } } @@ -2915,10 +2919,10 @@ static bool tselem_draw_icon(uiBlock *block, /* Restrict column clip. it has been coded by simply overdrawing, doesn't work for buttons. */ uchar color[4]; if (UI_icon_get_theme_color(data.icon, color)) { - UI_icon_draw_ex(x, y, data.icon, U.inv_dpi_fac, alpha, 0.0f, color, true); + UI_icon_draw_ex(x, y, data.icon, U.inv_dpi_fac, alpha, 0.0f, color, true, &text_overlay); } else { - UI_icon_draw_ex(x, y, data.icon, U.inv_dpi_fac, alpha, 0.0f, nullptr, false); + UI_icon_draw_ex(x, y, data.icon, U.inv_dpi_fac, alpha, 0.0f, nullptr, false, &text_overlay); } } else { @@ -2941,53 +2945,6 @@ static bool tselem_draw_icon(uiBlock *block, return true; } -/** - * For icon-only children of a collapsed tree, - * Draw small number over the icon to show how many items of this type are displayed. - */ -static void outliner_draw_iconrow_number(const uiFontStyle *fstyle, - int offsx, - int ys, - const int num_elements) -{ - const float color[4] = {0.0f, 0.0f, 0.0f, 1.0f}; - float ufac = 0.25f * UI_UNIT_X; - float offset_x = float(offsx) + UI_UNIT_X * 0.35f; - rctf rect{}; - BLI_rctf_init(&rect, - offset_x + ufac, - offset_x + UI_UNIT_X - ufac, - float(ys) - UI_UNIT_Y * 0.2f + ufac, - float(ys) - UI_UNIT_Y * 0.2f + UI_UNIT_Y - ufac); - - UI_draw_roundbox_corner_set(UI_CNR_ALL); - UI_draw_roundbox_aa(&rect, true, float(UI_UNIT_Y) / 2.0f - ufac, color); - - /* Now the numbers. */ - uchar text_col[4]; - - UI_GetThemeColor3ubv(TH_TEXT_HI, text_col); - text_col[3] = 255; - - uiFontStyle fstyle_small = *fstyle; - fstyle_small.points *= 0.8f; - - /* We treat +99 as 4 digits to make sure the (eyeballed) alignment looks nice. */ - int num_digits = 4; - char number_text[4] = "+99"; - if (num_elements < 100) { - BLI_snprintf(number_text, sizeof(number_text), "%d", num_elements); - num_digits = num_elements < 10 ? 1 : 2; - } - UI_fontstyle_draw_simple(&fstyle_small, - (offset_x + ufac + UI_UNIT_X * (2 - num_digits) * 0.12f), - float(ys) - UI_UNIT_Y * 0.095f + ufac, - number_text, - text_col); - UI_fontstyle_set(fstyle); - GPU_blend(GPU_BLEND_ALPHA); /* Round-box and text drawing disables. */ -} - static void outliner_icon_background_colors(float icon_color[4], float icon_border[4]) { float text[4]; @@ -3020,7 +2977,6 @@ static void outliner_draw_active_indicator(const float minx, static void outliner_draw_iconrow_doit(uiBlock *block, TreeElement *te, - const uiFontStyle *fstyle, int xmax, int *offsx, int ys, @@ -3049,13 +3005,13 @@ static void outliner_draw_iconrow_doit(uiBlock *block, if (tselem->flag & TSE_HIGHLIGHTED_ICON) { alpha_fac += 0.5; } - tselem_draw_icon(block, xmax, float(*offsx), float(ys), tselem, te, alpha_fac, false); + tselem_draw_icon( + block, xmax, float(*offsx), float(ys), tselem, te, alpha_fac, false, num_elements); te->xs = *offsx; te->ys = ys; te->xend = short(*offsx) + UI_UNIT_X; if (num_elements > 1) { - outliner_draw_iconrow_number(fstyle, *offsx, ys, num_elements); te->flag |= TE_ICONROW_MERGED; } else { @@ -3098,6 +3054,7 @@ static void outliner_draw_iconrow(bContext *C, int *offsx, int ys, float alpha_fac, + bool in_bone_hierarchy, MergedIconRow *merged) { eOLDrawState active = OL_DRAWSEL_NONE; @@ -3107,8 +3064,12 @@ static void outliner_draw_iconrow(bContext *C, te->flag &= ~(TE_ICONROW | TE_ICONROW_MERGED); /* object hierarchy always, further constrained on level */ + /* Bones are also hierarchies and get a merged count, but we only start recursing into them if + * an they are at the root level of a collapsed subtree (e.g. not "hidden" in a collapsed + * collection). */ + const bool is_bone = ELEM(tselem->type, TSE_BONE, TSE_EBONE, TSE_POSE_CHANNEL); if ((level < 1) || ((tselem->type == TSE_SOME_ID) && (te->idcode == ID_OB)) || - ELEM(tselem->type, TSE_BONE, TSE_EBONE, TSE_POSE_CHANNEL)) { + (in_bone_hierarchy && is_bone)) { /* active blocks get white circle */ if (tselem->type == TSE_SOME_ID) { if (te->idcode == ID_OB) { @@ -3139,7 +3100,7 @@ static void outliner_draw_iconrow(bContext *C, TSE_POSE_CHANNEL, TSE_POSEGRP, TSE_DEFGROUP)) { - outliner_draw_iconrow_doit(block, te, fstyle, xmax, offsx, ys, alpha_fac, active, 1); + outliner_draw_iconrow_doit(block, te, xmax, offsx, ys, alpha_fac, active, 1); } else { const int index = tree_element_id_type_to_index(te); @@ -3151,8 +3112,13 @@ static void outliner_draw_iconrow(bContext *C, } } - /* this tree element always has same amount of branches, so don't draw */ - if (tselem->type != TSE_R_LAYER) { + /* TSE_R_LAYER tree element always has same amount of branches, so don't draw. */ + /* Also only recurse into bone hierarchies if a direct child of the collapsed element to merge + * into. */ + const bool is_root_level_bone = is_bone && (level == 0); + in_bone_hierarchy |= is_root_level_bone; + if (!ELEM(tselem->type, TSE_R_LAYER, TSE_BONE, TSE_EBONE, TSE_POSE_CHANNEL) || + in_bone_hierarchy) { outliner_draw_iconrow(C, block, fstyle, @@ -3164,6 +3130,7 @@ static void outliner_draw_iconrow(bContext *C, offsx, ys, alpha_fac, + in_bone_hierarchy, merged); } } @@ -3181,7 +3148,6 @@ static void outliner_draw_iconrow(bContext *C, if (merged->num_elements[index] != 0) { outliner_draw_iconrow_doit(block, merged->tree_element[index], - fstyle, xmax, offsx, ys, @@ -3370,7 +3336,8 @@ static void outliner_draw_tree_element(bContext *C, tselem, te, (tselem->flag & TSE_HIGHLIGHTED_ICON) ? alpha_fac + 0.5f : alpha_fac, - true)) { + true, + 1)) { offsx += UI_UNIT_X + 4 * ufac; } else { @@ -3425,6 +3392,7 @@ static void outliner_draw_tree_element(bContext *C, &tempx, *starty, alpha_fac, + false, &merged); GPU_blend(GPU_BLEND_NONE); diff --git a/source/blender/editors/space_outliner/outliner_edit.cc b/source/blender/editors/space_outliner/outliner_edit.cc index 6eca6fffece..9a6a25fdbae 100644 --- a/source/blender/editors/space_outliner/outliner_edit.cc +++ b/source/blender/editors/space_outliner/outliner_edit.cc @@ -809,7 +809,7 @@ static int outliner_id_copy_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } - BLI_join_dirfile(str, sizeof(str), BKE_tempdir_base(), "copybuffer.blend"); + BLI_path_join(str, sizeof(str), BKE_tempdir_base(), "copybuffer.blend"); BKE_copybuffer_copy_end(bmain, str, op->reports); BKE_reportf(op->reports, RPT_INFO, "Copied %d selected data-block(s)", num_ids); @@ -843,7 +843,7 @@ static int outliner_id_paste_exec(bContext *C, wmOperator *op) char str[FILE_MAX]; const short flag = FILE_AUTOSELECT | FILE_ACTIVE_COLLECTION; - BLI_join_dirfile(str, sizeof(str), BKE_tempdir_base(), "copybuffer.blend"); + BLI_path_join(str, sizeof(str), BKE_tempdir_base(), "copybuffer.blend"); const int num_pasted = BKE_copybuffer_paste(C, str, flag, op->reports, 0); if (num_pasted == 0) { diff --git a/source/blender/editors/space_sequencer/sequencer_add.c b/source/blender/editors/space_sequencer/sequencer_add.c index 6ef8d7fd108..b17d0bfac4e 100644 --- a/source/blender/editors/space_sequencer/sequencer_add.c +++ b/source/blender/editors/space_sequencer/sequencer_add.c @@ -274,7 +274,7 @@ static void load_data_init_from_operator(SeqLoadData *load_data, bContext *C, wm RNA_PROP_BEGIN (op->ptr, itemptr, prop) { char *filename = RNA_string_get_alloc(&itemptr, "name", NULL, 0, NULL); BLI_strncpy(load_data->name, filename, sizeof(load_data->name)); - BLI_join_dirfile(load_data->path, sizeof(load_data->path), directory, filename); + BLI_path_join(load_data->path, sizeof(load_data->path), directory, filename); MEM_freeN(filename); break; } @@ -834,7 +834,7 @@ static void sequencer_add_movie_multiple_strips(bContext *C, char file_only[FILE_MAX]; RNA_string_get(op->ptr, "directory", dir_only); RNA_string_get(&itemptr, "name", file_only); - BLI_join_dirfile(load_data->path, sizeof(load_data->path), dir_only, file_only); + BLI_path_join(load_data->path, sizeof(load_data->path), dir_only, file_only); BLI_strncpy(load_data->name, file_only, sizeof(load_data->name)); Sequence *seq_movie = NULL; Sequence *seq_sound = NULL; @@ -1082,7 +1082,7 @@ static void sequencer_add_sound_multiple_strips(bContext *C, char file_only[FILE_MAX]; RNA_string_get(op->ptr, "directory", dir_only); RNA_string_get(&itemptr, "name", file_only); - BLI_join_dirfile(load_data->path, sizeof(load_data->path), dir_only, file_only); + BLI_path_join(load_data->path, sizeof(load_data->path), dir_only, file_only); BLI_strncpy(load_data->name, file_only, sizeof(load_data->name)); Sequence *seq = SEQ_add_sound_strip(bmain, scene, ed->seqbasep, load_data); if (seq == NULL) { diff --git a/source/blender/editors/space_sequencer/sequencer_draw.c b/source/blender/editors/space_sequencer/sequencer_draw.c index 201425dafab..a6916f9d031 100644 --- a/source/blender/editors/space_sequencer/sequencer_draw.c +++ b/source/blender/editors/space_sequencer/sequencer_draw.c @@ -804,7 +804,7 @@ static void draw_seq_text_get_source(Sequence *seq, char *r_source, size_t sourc switch (seq->type) { case SEQ_TYPE_IMAGE: case SEQ_TYPE_MOVIE: { - BLI_join_dirfile(r_source, source_len, seq->strip->dir, seq->strip->stripdata->name); + BLI_path_join(r_source, source_len, seq->strip->dir, seq->strip->stripdata->name); break; } case SEQ_TYPE_SOUND_RAM: { diff --git a/source/blender/editors/space_sequencer/sequencer_edit.c b/source/blender/editors/space_sequencer/sequencer_edit.c index 2938513f130..c0c7782c60c 100644 --- a/source/blender/editors/space_sequencer/sequencer_edit.c +++ b/source/blender/editors/space_sequencer/sequencer_edit.c @@ -2939,7 +2939,7 @@ static int sequencer_change_path_invoke(bContext *C, wmOperator *op, const wmEve Sequence *seq = SEQ_select_active_get(scene); char filepath[FILE_MAX]; - BLI_join_dirfile(filepath, sizeof(filepath), seq->strip->dir, seq->strip->stripdata->name); + BLI_path_join(filepath, sizeof(filepath), seq->strip->dir, seq->strip->stripdata->name); RNA_string_set(op->ptr, "directory", seq->strip->dir); RNA_string_set(op->ptr, "filepath", filepath); diff --git a/source/blender/editors/space_sequencer/space_sequencer.c b/source/blender/editors/space_sequencer/space_sequencer.c index 8b6d37caa41..1d20926d16c 100644 --- a/source/blender/editors/space_sequencer/space_sequencer.c +++ b/source/blender/editors/space_sequencer/space_sequencer.c @@ -129,7 +129,6 @@ static SpaceLink *sequencer_create(const ScrArea *UNUSED(area), const Scene *sce region->regiontype = RGN_TYPE_TOOLS; region->alignment = RGN_ALIGN_LEFT; region->flag = RGN_FLAG_HIDDEN; - region->v2d.flag |= V2D_VIEWSYNC_AREA_VERTICAL; /* Channels. */ region = MEM_callocN(sizeof(ARegion), "channels for sequencer"); @@ -137,6 +136,7 @@ static SpaceLink *sequencer_create(const ScrArea *UNUSED(area), const Scene *sce BLI_addtail(&sseq->regionbase, region); region->regiontype = RGN_TYPE_CHANNELS; region->alignment = RGN_ALIGN_LEFT; + region->v2d.flag |= V2D_VIEWSYNC_AREA_VERTICAL; /* Preview region. */ /* NOTE: if you change values here, also change them in sequencer_init_preview_region. */ diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_column.cc b/source/blender/editors/space_spreadsheet/spreadsheet_column.cc index 46e98acb8e8..af41225f42a 100644 --- a/source/blender/editors/space_spreadsheet/spreadsheet_column.cc +++ b/source/blender/editors/space_spreadsheet/spreadsheet_column.cc @@ -12,6 +12,7 @@ #include "BLI_string_ref.hh" #include "BKE_geometry_set.hh" +#include "BKE_instances.hh" #include "spreadsheet_column.hh" #include "spreadsheet_column_values.hh" @@ -44,7 +45,7 @@ eSpreadsheetColumnValueType cpp_type_to_column_type(const CPPType &type) if (type.is<std::string>()) { return SPREADSHEET_VALUE_TYPE_STRING; } - if (type.is<InstanceReference>()) { + if (type.is<bke::InstanceReference>()) { return SPREADSHEET_VALUE_TYPE_INSTANCES; } if (type.is<ColorGeometry4b>()) { 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 8af12590b0f..59a8daf4f4a 100644 --- a/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc +++ b/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc @@ -10,6 +10,7 @@ #include "BKE_editmesh.h" #include "BKE_geometry_fields.hh" #include "BKE_global.h" +#include "BKE_instances.hh" #include "BKE_lib_id.h" #include "BKE_mesh.h" #include "BKE_mesh_wrapper.h" @@ -143,29 +144,31 @@ std::unique_ptr<ColumnValues> GeometryDataSource::get_column_values( } if (component_->type() == GEO_COMPONENT_TYPE_INSTANCES) { - const InstancesComponent &instances = static_cast<const InstancesComponent &>(*component_); - if (STREQ(column_id.name, "Name")) { - Span<int> reference_handles = instances.instance_reference_handles(); - Span<InstanceReference> references = instances.references(); - return std::make_unique<ColumnValues>( - column_id.name, - VArray<InstanceReference>::ForFunc(domain_num, - [reference_handles, references](int64_t index) { - return references[reference_handles[index]]; - })); - } - Span<float4x4> transforms = instances.instance_transforms(); - if (STREQ(column_id.name, "Rotation")) { - return std::make_unique<ColumnValues>( - column_id.name, VArray<float3>::ForFunc(domain_num, [transforms](int64_t index) { - return transforms[index].to_euler(); - })); - } - if (STREQ(column_id.name, "Scale")) { - return std::make_unique<ColumnValues>( - column_id.name, VArray<float3>::ForFunc(domain_num, [transforms](int64_t index) { - return transforms[index].scale(); - })); + if (const bke::Instances *instances = + static_cast<const InstancesComponent &>(*component_).get_for_read()) { + if (STREQ(column_id.name, "Name")) { + Span<int> reference_handles = instances->reference_handles(); + Span<bke::InstanceReference> references = instances->references(); + return std::make_unique<ColumnValues>( + column_id.name, + VArray<bke::InstanceReference>::ForFunc( + domain_num, [reference_handles, references](int64_t index) { + return references[reference_handles[index]]; + })); + } + Span<float4x4> transforms = instances->transforms(); + if (STREQ(column_id.name, "Rotation")) { + return std::make_unique<ColumnValues>( + column_id.name, VArray<float3>::ForFunc(domain_num, [transforms](int64_t index) { + return transforms[index].to_euler(); + })); + } + if (STREQ(column_id.name, "Scale")) { + return std::make_unique<ColumnValues>( + column_id.name, VArray<float3>::ForFunc(domain_num, [transforms](int64_t index) { + return transforms[index].scale(); + })); + } } } else if (G.debug_value == 4001 && component_->type() == GEO_COMPONENT_TYPE_MESH) { @@ -487,37 +490,6 @@ GeometrySet spreadsheet_get_display_geometry_set(const SpaceSpreadsheet *sspread return geometry_set; } -class GeometryComponentCacheKey : public SpreadsheetCache::Key { - public: - /* Use the pointer to the geometry component as a key to detect when the geometry changed. */ - const GeometryComponent *component; - - GeometryComponentCacheKey(const GeometryComponent &component) : component(&component) - { - } - - uint64_t hash() const override - { - return get_default_hash(this->component); - } - - bool is_equal_to(const Key &other) const override - { - if (const GeometryComponentCacheKey *other_geo = - dynamic_cast<const GeometryComponentCacheKey *>(&other)) { - return this->component == other_geo->component; - } - return false; - } -}; - -class GeometryComponentCacheValue : public SpreadsheetCache::Value { - public: - /* Stores the result of fields evaluated on a geometry component. Without this, fields would have - * to be reevaluated on every redraw. */ - Map<std::pair<eAttrDomain, GField>, GArray<>> arrays; -}; - std::unique_ptr<DataSource> data_source_from_geometry(const bContext *C, Object *object_eval) { SpaceSpreadsheet *sspreadsheet = CTX_wm_space_spreadsheet(C); diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_layout.cc b/source/blender/editors/space_spreadsheet/spreadsheet_layout.cc index b4b8417c172..06eb338bd00 100644 --- a/source/blender/editors/space_spreadsheet/spreadsheet_layout.cc +++ b/source/blender/editors/space_spreadsheet/spreadsheet_layout.cc @@ -6,6 +6,7 @@ #include "BLI_math_vec_types.hh" #include "BKE_geometry_set.hh" +#include "BKE_instances.hh" #include "spreadsheet_column_values.hh" #include "spreadsheet_layout.hh" @@ -197,10 +198,10 @@ class SpreadsheetLayoutDrawer : public SpreadsheetDrawer { const ColorGeometry4b value = data.get<ColorGeometry4b>(real_index); this->draw_byte_color(params, value); } - else if (data.type().is<InstanceReference>()) { - const InstanceReference value = data.get<InstanceReference>(real_index); + else if (data.type().is<bke::InstanceReference>()) { + const bke::InstanceReference value = data.get<bke::InstanceReference>(real_index); switch (value.type()) { - case InstanceReference::Type::Object: { + case bke::InstanceReference::Type::Object: { const Object &object = value.object(); uiDefIconTextBut(params.block, UI_BTYPE_LABEL, @@ -219,7 +220,7 @@ class SpreadsheetLayoutDrawer : public SpreadsheetDrawer { nullptr); break; } - case InstanceReference::Type::Collection: { + case bke::InstanceReference::Type::Collection: { Collection &collection = value.collection(); uiDefIconTextBut(params.block, UI_BTYPE_LABEL, @@ -238,7 +239,7 @@ class SpreadsheetLayoutDrawer : public SpreadsheetDrawer { nullptr); break; } - case InstanceReference::Type::GeometrySet: { + case bke::InstanceReference::Type::GeometrySet: { uiDefIconTextBut(params.block, UI_BTYPE_LABEL, 0, @@ -256,7 +257,7 @@ class SpreadsheetLayoutDrawer : public SpreadsheetDrawer { nullptr); break; } - case InstanceReference::Type::None: { + case bke::InstanceReference::Type::None: { break; } } diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_row_filter.cc b/source/blender/editors/space_spreadsheet/spreadsheet_row_filter.cc index 96827692a25..3586389b00b 100644 --- a/source/blender/editors/space_spreadsheet/spreadsheet_row_filter.cc +++ b/source/blender/editors/space_spreadsheet/spreadsheet_row_filter.cc @@ -14,6 +14,8 @@ #include "RNA_access.h" +#include "BKE_instances.hh" + #include "spreadsheet_data_source_geometry.hh" #include "spreadsheet_intern.hh" #include "spreadsheet_layout.hh" @@ -280,22 +282,22 @@ static void apply_row_filter(const SpreadsheetRowFilter &row_filter, } } } - else if (column_data.type().is<InstanceReference>()) { + else if (column_data.type().is<bke::InstanceReference>()) { const StringRef value = row_filter.value_string; apply_filter_operation( - column_data.typed<InstanceReference>(), - [&](const InstanceReference cell) { + column_data.typed<bke::InstanceReference>(), + [&](const bke::InstanceReference cell) { switch (cell.type()) { - case InstanceReference::Type::Object: { + case bke::InstanceReference::Type::Object: { return value == (reinterpret_cast<ID &>(cell.object()).name + 2); } - case InstanceReference::Type::Collection: { + case bke::InstanceReference::Type::Collection: { return value == (reinterpret_cast<ID &>(cell.collection()).name + 2); } - case InstanceReference::Type::GeometrySet: { + case bke::InstanceReference::Type::GeometrySet: { return false; } - case InstanceReference::Type::None: { + case bke::InstanceReference::Type::None: { return false; } } diff --git a/source/blender/editors/space_view3d/drawobject.c b/source/blender/editors/space_view3d/drawobject.c index 36ced74a8b7..6370d56ae8c 100644 --- a/source/blender/editors/space_view3d/drawobject.c +++ b/source/blender/editors/space_view3d/drawobject.c @@ -17,6 +17,7 @@ #include "BKE_editmesh.h" #include "BKE_global.h" #include "BKE_mesh.h" +#include "BKE_mesh_runtime.h" #include "BKE_object.h" #include "DEG_depsgraph.h" @@ -94,8 +95,8 @@ void ED_draw_object_facemap(Depsgraph *depsgraph, const MPoly *mp; int i; - if (me->runtime.looptris.array) { - const MLoopTri *mlt = me->runtime.looptris.array; + if (BKE_mesh_runtime_looptri_ensure(me)) { + const MLoopTri *mlt = BKE_mesh_runtime_looptri_ensure(me); for (mp = polys, i = 0; i < mpoly_len; i++, mp++) { if (facemap_data[i] == facemap) { for (int j = 2; j < mp->totloop; j++) { diff --git a/source/blender/editors/space_view3d/space_view3d.cc b/source/blender/editors/space_view3d/space_view3d.cc index 4ac6f926818..635fbd75d74 100644 --- a/source/blender/editors/space_view3d/space_view3d.cc +++ b/source/blender/editors/space_view3d/space_view3d.cc @@ -631,7 +631,7 @@ static bool view3d_object_data_drop_poll(bContext *C, wmDrag *drag, const wmEven static char *view3d_object_data_drop_tooltip(bContext * /*C*/, wmDrag * /*drag*/, - const int UNUSED(xy[2]), + const int /*xy*/[2], wmDropBox * /*drop*/) { return BLI_strdup(TIP_("Create object instance from object-data")); diff --git a/source/blender/editors/space_view3d/view3d_gizmo_preselect_type.cc b/source/blender/editors/space_view3d/view3d_gizmo_preselect_type.cc index c61eac3c777..793ada4f577 100644 --- a/source/blender/editors/space_view3d/view3d_gizmo_preselect_type.cc +++ b/source/blender/editors/space_view3d/view3d_gizmo_preselect_type.cc @@ -19,8 +19,10 @@ #include "BKE_context.h" #include "BKE_editmesh.h" +#include "BKE_editmesh_cache.h" #include "BKE_global.h" #include "BKE_layer.h" +#include "BKE_mesh.h" #include "DEG_depsgraph.h" #include "DEG_depsgraph_query.h" @@ -234,8 +236,8 @@ static int gizmo_preselect_elem_test_select(bContext *C, wmGizmo *gz, const int Object *ob = gz_ele->bases[gz_ele->base_index]->object; Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); Mesh *me_eval = (Mesh *)DEG_get_evaluated_id(depsgraph, static_cast<ID *>(ob->data)); - if (me_eval->runtime.edit_data) { - coords = me_eval->runtime.edit_data->vertexCos; + if (me_eval->runtime->edit_data) { + coords = me_eval->runtime->edit_data->vertexCos; } } EDBM_preselect_elem_update_from_single(gz_ele->psel, bm, best.ele, coords); diff --git a/source/blender/editors/space_view3d/view3d_iterators.cc b/source/blender/editors/space_view3d/view3d_iterators.cc index 139ac9de6e4..932563863fe 100644 --- a/source/blender/editors/space_view3d/view3d_iterators.cc +++ b/source/blender/editors/space_view3d/view3d_iterators.cc @@ -262,7 +262,7 @@ struct foreachScreenFace_userData { static void meshobject_foreachScreenVert__mapFunc(void *userData, int index, const float co[3], - const float UNUSED(no[3])) + const float /*no*/[3]) { foreachScreenObjectVert_userData *data = static_cast<foreachScreenObjectVert_userData *>( userData); @@ -316,7 +316,7 @@ void meshobject_foreachScreenVert( static void mesh_foreachScreenVert__mapFunc(void *userData, int index, const float co[3], - const float UNUSED(no[3])) + const float /*no*/[3]) { foreachScreenVert_userData *data = static_cast<foreachScreenVert_userData *>(userData); BMVert *eve = BM_vert_at_index(data->vc.em->bm, index); @@ -538,7 +538,7 @@ void mesh_foreachScreenEdge_clip_bb_segment(ViewContext *vc, static void mesh_foreachScreenFace__mapFunc(void *userData, int index, const float cent[3], - const float UNUSED(no[3])) + const float /*no*/[3]) { foreachScreenFace_userData *data = static_cast<foreachScreenFace_userData *>(userData); BMFace *efa = BM_face_at_index(data->vc.em->bm, index); @@ -576,7 +576,7 @@ void mesh_foreachScreenFace( BM_mesh_elem_table_ensure(vc->em->bm, BM_FACE); - if (me->runtime.subsurf_face_dot_tags != nullptr) { + if (me->runtime->subsurf_face_dot_tags != nullptr) { BKE_mesh_foreach_mapped_subdiv_face_center( me, mesh_foreachScreenFace__mapFunc, &data, MESH_FOREACH_NOP); } diff --git a/source/blender/editors/space_view3d/view3d_navigate_ndof.c b/source/blender/editors/space_view3d/view3d_navigate_ndof.c index 29e63a72daf..9fb33013c4e 100644 --- a/source/blender/editors/space_view3d/view3d_navigate_ndof.c +++ b/source/blender/editors/space_view3d/view3d_navigate_ndof.c @@ -370,14 +370,17 @@ static int view3d_ndof_cameraview_pan_zoom(bContext *C, const wmEvent *event) return OPERATOR_PASS_THROUGH; } + const float pan_speed = NDOF_PIXELS_PER_SECOND; const bool has_translate = !is_zero_v2(ndof->tvec); const bool has_zoom = ndof->tvec[2] != 0.0f; float pan_vec[3]; WM_event_ndof_pan_get(ndof, pan_vec, true); - mul_v2_fl(pan_vec, ndof->dt); - pan_vec[2] *= -ndof->dt; + mul_v3_fl(pan_vec, ndof->dt); + /* NOTE: unlike image and clip views, the 2D pan doesn't have to be scaled by the zoom level. + * #ED_view3d_camera_view_pan already takes the zoom level into account. */ + mul_v2_fl(pan_vec, pan_speed); /* NOTE(@campbellbarton): In principle rotating could pass through to regular * non-camera NDOF behavior (exiting the camera-view and rotating). @@ -393,16 +396,14 @@ static int view3d_ndof_cameraview_pan_zoom(bContext *C, const wmEvent *event) bool changed = false; if (has_translate) { - const float speed = NDOF_PIXELS_PER_SECOND; - float event_ofs[2] = {pan_vec[0] * speed, pan_vec[1] * speed}; - if (ED_view3d_camera_view_pan(region, event_ofs)) { + /* Use the X & Y of `pan_vec`. */ + if (ED_view3d_camera_view_pan(region, pan_vec)) { changed = true; } } if (has_zoom) { - const float scale = 1.0f + pan_vec[2]; - if (ED_view3d_camera_view_zoom_scale(rv3d, scale)) { + if (ED_view3d_camera_view_zoom_scale(rv3d, max_ff(0.0f, 1.0f - pan_vec[2]))) { changed = true; } } diff --git a/source/blender/editors/space_view3d/view3d_navigate_walk.c b/source/blender/editors/space_view3d/view3d_navigate_walk.c index 3e0ce892b5a..fcb4f549353 100644 --- a/source/blender/editors/space_view3d/view3d_navigate_walk.c +++ b/source/blender/editors/space_view3d/view3d_navigate_walk.c @@ -849,11 +849,15 @@ static void walkEvent(WalkInfo *walk, const wmEvent *event) if (ret) { WalkTeleport *teleport = &walk->teleport; + + /* Store the current navigation mode if we are not already teleporting. */ + if (teleport->state == WALK_TELEPORT_STATE_OFF) { + teleport->navigation_mode = walk->navigation_mode; + } teleport->state = WALK_TELEPORT_STATE_ON; teleport->initial_time = PIL_check_seconds_timer(); teleport->duration = U.walk_navigation.teleport_time; - teleport->navigation_mode = walk->navigation_mode; walk_navigation_mode_set(walk, WALK_MODE_FREE); copy_v3_v3(teleport->origin, walk->rv3d->viewinv[3]); @@ -864,9 +868,7 @@ static void walkEvent(WalkInfo *walk, const wmEvent *event) sub_v3_v3v3(teleport->direction, loc, teleport->origin); } - else { - walk->teleport.state = WALK_TELEPORT_STATE_OFF; - } + break; } @@ -1229,11 +1231,11 @@ static int walkApply(bContext *C, WalkInfo *walk, bool is_confirm) /* keep moving if we were moving */ copy_v2_v2(dvec, walk->teleport.direction); - z_cur = walk->rv3d->viewinv[3][2]; - z_new = walk->teleport.origin[2] - getFreeFallDistance(walk->gravity, t) * walk->grid; + z_cur = walk->rv3d->viewinv[3][2] / walk->grid; + z_new = (walk->teleport.origin[2] / walk->grid) - getFreeFallDistance(walk->gravity, t); /* jump */ - z_new += t * walk->speed_jump * walk->grid; + z_new += t * walk->speed_jump; /* duration is the jump duration */ if (t > walk->teleport.duration) { diff --git a/source/blender/editors/space_view3d/view3d_ops.c b/source/blender/editors/space_view3d/view3d_ops.c index 6fbd553e17b..ad12aef6d67 100644 --- a/source/blender/editors/space_view3d/view3d_ops.c +++ b/source/blender/editors/space_view3d/view3d_ops.c @@ -59,7 +59,7 @@ static int view3d_copybuffer_exec(bContext *C, wmOperator *op) } CTX_DATA_END; - BLI_join_dirfile(str, sizeof(str), BKE_tempdir_base(), "copybuffer.blend"); + BLI_path_join(str, sizeof(str), BKE_tempdir_base(), "copybuffer.blend"); BKE_copybuffer_copy_end(bmain, str, op->reports); BKE_reportf(op->reports, RPT_INFO, "Copied %d selected object(s)", num_copied); @@ -91,7 +91,7 @@ static int view3d_pastebuffer_exec(bContext *C, wmOperator *op) flag |= FILE_ACTIVE_COLLECTION; } - BLI_join_dirfile(str, sizeof(str), BKE_tempdir_base(), "copybuffer.blend"); + BLI_path_join(str, sizeof(str), BKE_tempdir_base(), "copybuffer.blend"); const int num_pasted = BKE_copybuffer_paste(C, str, flag, op->reports, FILTER_ID_OB); if (num_pasted == 0) { diff --git a/source/blender/editors/transform/CMakeLists.txt b/source/blender/editors/transform/CMakeLists.txt index ec6f62e0f5b..3787a59c83c 100644 --- a/source/blender/editors/transform/CMakeLists.txt +++ b/source/blender/editors/transform/CMakeLists.txt @@ -40,7 +40,7 @@ set(SRC transform_convert_mesh_uv.c transform_convert_mesh_vert_cdata.c transform_convert_nla.c - transform_convert_node.c + transform_convert_node.cc transform_convert_object.c transform_convert_object_texspace.c transform_convert_paintcurve.c diff --git a/source/blender/editors/transform/transform.c b/source/blender/editors/transform/transform.c index 6f7eb317b42..5b194ae7237 100644 --- a/source/blender/editors/transform/transform.c +++ b/source/blender/editors/transform/transform.c @@ -60,8 +60,6 @@ * and being able to set it to zero is handy. */ /* #define USE_NUM_NO_ZERO */ -static void initSnapSpatial(TransInfo *t, float r_snap[2]); - bool transdata_check_local_islands(TransInfo *t, short around) { if (t->options & (CTX_CURSOR | CTX_TEXTURE_SPACE)) { @@ -1518,26 +1516,26 @@ void saveTransform(bContext *C, TransInfo *t, wmOperator *op) } } - bool use_prop_edit = false; - int prop_edit_flag = 0; - if (t->flag & T_PROP_EDIT_ALL) { - if (t->flag & T_PROP_EDIT) { - use_prop_edit = true; - } - if (t->flag & T_PROP_CONNECTED) { - prop_edit_flag |= PROP_EDIT_CONNECTED; - } - if (t->flag & T_PROP_PROJECTED) { - prop_edit_flag |= PROP_EDIT_PROJECTED; + /* Save proportional edit settings. + * Skip saving proportional edit if it was not actually used. */ + if (!(t->options & CTX_NO_PET)) { + bool use_prop_edit = false; + int prop_edit_flag = 0; + if (t->flag & T_PROP_EDIT_ALL) { + if (t->flag & T_PROP_EDIT) { + use_prop_edit = true; + } + if (t->flag & T_PROP_CONNECTED) { + prop_edit_flag |= PROP_EDIT_CONNECTED; + } + if (t->flag & T_PROP_PROJECTED) { + prop_edit_flag |= PROP_EDIT_PROJECTED; + } } - } - - /* If modal, save settings back in scene if not set as operator argument */ - if ((t->flag & T_MODAL) || (op->flag & OP_IS_REPEAT)) { - /* save settings if not set in operator */ - /* skip saving proportional edit if it was not actually used */ - if (!(t->options & CTX_NO_PET)) { + /* If modal, save settings back in scene if not set as operator argument */ + if ((t->flag & T_MODAL) || (op->flag & OP_IS_REPEAT)) { + /* save settings if not set in operator */ if ((prop = RNA_struct_find_property(op->ptr, "use_proportional_edit")) && !RNA_property_is_set(op->ptr, prop)) { BKE_view_layer_synced_ensure(t->scene, t->view_layer); @@ -1576,6 +1574,14 @@ void saveTransform(bContext *C, TransInfo *t, wmOperator *op) ts->prop_mode = t->prop_mode; } } + + if ((prop = RNA_struct_find_property(op->ptr, "use_proportional_edit"))) { + RNA_property_boolean_set(op->ptr, prop, use_prop_edit); + RNA_boolean_set(op->ptr, "use_proportional_connected", prop_edit_flag & PROP_EDIT_CONNECTED); + RNA_boolean_set(op->ptr, "use_proportional_projected", prop_edit_flag & PROP_EDIT_PROJECTED); + RNA_enum_set(op->ptr, "proportional_edit_falloff", t->prop_mode); + RNA_float_set(op->ptr, "proportional_size", t->prop_size); + } } /* Save snapping settings. */ @@ -1588,9 +1594,9 @@ void saveTransform(bContext *C, TransInfo *t, wmOperator *op) RNA_enum_set(op->ptr, "snap_target", t->tsnap.source_select); eSnapTargetSelect target = t->tsnap.target_select; - RNA_boolean_set(op->ptr, "use_snap_self", (target & SCE_SNAP_TARGET_NOT_ACTIVE) != 0); - RNA_boolean_set(op->ptr, "use_snap_edit", (target & SCE_SNAP_TARGET_NOT_EDITED) != 0); - RNA_boolean_set(op->ptr, "use_snap_nonedit", (target & SCE_SNAP_TARGET_NOT_NONEDITED) != 0); + RNA_boolean_set(op->ptr, "use_snap_self", (target & SCE_SNAP_TARGET_NOT_ACTIVE) == 0); + RNA_boolean_set(op->ptr, "use_snap_edit", (target & SCE_SNAP_TARGET_NOT_EDITED) == 0); + RNA_boolean_set(op->ptr, "use_snap_nonedit", (target & SCE_SNAP_TARGET_NOT_NONEDITED) == 0); RNA_boolean_set( op->ptr, "use_snap_selectable", (target & SCE_SNAP_TARGET_ONLY_SELECTABLE) != 0); } @@ -1635,14 +1641,6 @@ void saveTransform(bContext *C, TransInfo *t, wmOperator *op) } } - if ((prop = RNA_struct_find_property(op->ptr, "use_proportional_edit"))) { - RNA_property_boolean_set(op->ptr, prop, use_prop_edit); - RNA_boolean_set(op->ptr, "use_proportional_connected", prop_edit_flag & PROP_EDIT_CONNECTED); - RNA_boolean_set(op->ptr, "use_proportional_projected", prop_edit_flag & PROP_EDIT_PROJECTED); - RNA_enum_set(op->ptr, "proportional_edit_falloff", t->prop_mode); - RNA_float_set(op->ptr, "proportional_size", t->prop_size); - } - if ((prop = RNA_struct_find_property(op->ptr, "mirror"))) { RNA_property_boolean_set(op->ptr, prop, (t->flag & T_NO_MIRROR) == 0); } @@ -1723,13 +1721,18 @@ void saveTransform(bContext *C, TransInfo *t, wmOperator *op) } } -static void initSnapSpatial(TransInfo *t, float r_snap[2]) +static void initSnapSpatial(TransInfo *t, float r_snap[3], float *r_snap_precision) { + /* Default values. */ + r_snap[0] = r_snap[1] = 1.0f; + r_snap[1] = 0.0f; + *r_snap_precision = 0.1f; + if (t->spacetype == SPACE_VIEW3D) { if (t->region->regiondata) { View3D *v3d = t->area->spacedata.first; - r_snap[0] = ED_view3d_grid_view_scale(t->scene, v3d, t->region, NULL) * 1.0f; - r_snap[1] = r_snap[0] * 0.1f; + r_snap[0] = r_snap[1] = r_snap[2] = ED_view3d_grid_view_scale( + t->scene, v3d, t->region, NULL); } } else if (t->spacetype == SPACE_IMAGE) { @@ -1737,33 +1740,22 @@ static void initSnapSpatial(TransInfo *t, float r_snap[2]) View2D *v2d = &t->region->v2d; int grid_size = SI_GRID_STEPS_LEN; float zoom_factor = ED_space_image_zoom_level(v2d, grid_size); - float grid_steps[SI_GRID_STEPS_LEN]; + float grid_steps_x[SI_GRID_STEPS_LEN]; float grid_steps_y[SI_GRID_STEPS_LEN]; - ED_space_image_grid_steps(sima, grid_steps, grid_steps_y, grid_size); + ED_space_image_grid_steps(sima, grid_steps_x, grid_steps_y, grid_size); /* Snapping value based on what type of grid is used (adaptive-subdividing or custom-grid). */ - r_snap[0] = ED_space_image_increment_snap_value(grid_size, grid_steps, zoom_factor); - r_snap[1] = r_snap[0] / 2.0f; - - /* TODO: Implement snapping for custom grid sizes with `grid_steps[0] != grid_steps_y[0]`. - * r_snap_y[0] = ED_space_image_increment_snap_value(grid_size, grid_steps_y, zoom_factor); - * r_snap_y[1] = r_snap_y[0] / 2.0f; - */ + r_snap[0] = ED_space_image_increment_snap_value(grid_size, grid_steps_x, zoom_factor); + r_snap[1] = ED_space_image_increment_snap_value(grid_size, grid_steps_y, zoom_factor); + *r_snap_precision = 0.5f; } else if (t->spacetype == SPACE_CLIP) { - r_snap[0] = 0.125f; - r_snap[1] = 0.0625f; + r_snap[0] = r_snap[1] = 0.125f; + *r_snap_precision = 0.5f; } else if (t->spacetype == SPACE_NODE) { r_snap[0] = r_snap[1] = ED_node_grid_size(); } - else if (t->spacetype == SPACE_GRAPH) { - r_snap[0] = 1.0; - r_snap[1] = 0.1f; - } - else { - r_snap[0] = r_snap[1] = 1.0f; - } } bool initTransform(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *event, int mode) @@ -1903,7 +1895,7 @@ bool initTransform(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *eve initSnapping(t, op); /* Initialize snapping data AFTER mode flags */ - initSnapSpatial(t, t->snap_spatial); + initSnapSpatial(t, t->snap_spatial, &t->snap_spatial_precision); /* EVIL! posemode code can switch translation to rotate when 1 bone is selected. * will be removed (ton) */ diff --git a/source/blender/editors/transform/transform.h b/source/blender/editors/transform/transform.h index 09fc07f57f4..90f2795184b 100644 --- a/source/blender/editors/transform/transform.h +++ b/source/blender/editors/transform/transform.h @@ -19,6 +19,10 @@ #include "transform_data.h" +#ifdef __cplusplus +extern "C" { +#endif + /* use node center for transform instead of upper-left corner. * disabled since it makes absolute snapping not work so nicely */ @@ -141,6 +145,7 @@ typedef enum { /** No cursor wrapping on region bounds */ T_NO_CURSOR_WRAP = 1 << 23, } eTFlag; +ENUM_OPERATORS(eTFlag, T_NO_CURSOR_WRAP); #define T_ALL_RESTRICTIONS (T_NO_CONSTRAINT | T_NULL_ONE) #define T_PROP_EDIT_ALL (T_PROP_EDIT | T_PROP_CONNECTED | T_PROP_PROJECTED) @@ -550,7 +555,12 @@ typedef struct TransInfo { /** Snapping Gears. */ float snap[2]; /** Spatial snapping gears(even when rotating, scaling... etc). */ - float snap_spatial[2]; + float snap_spatial[3]; + /** + * Precision factor that is multiplied to snap_spatial when precision + * modifier is enabled for snap to grid or incremental snap. + */ + float snap_spatial_precision; /** Mouse side of the current frame, 'L', 'R' or 'B' */ char frame_side; @@ -864,3 +874,7 @@ bool checkUseAxisMatrix(TransInfo *t); th++, i++) /** \} */ + +#ifdef __cplusplus +} +#endif diff --git a/source/blender/editors/transform/transform_constraints.c b/source/blender/editors/transform/transform_constraints.c index fa56456d8e7..7abf0e5c00c 100644 --- a/source/blender/editors/transform/transform_constraints.c +++ b/source/blender/editors/transform/transform_constraints.c @@ -404,9 +404,11 @@ static void applyAxisConstraintVec(const TransInfo *t, } } + /* Fallback for when axes are aligned. */ + mul_m3_v3(t->con.pmtx, out); + if (is_snap_to_point) { - /* With snap points, a projection is alright, no adjustments needed. */ - mul_m3_v3(t->con.pmtx, out); + /* Pass. With snap points, a projection is alright, no adjustments needed. */ } else { const int dims = getConstraintSpaceDimension(t); @@ -422,14 +424,9 @@ static void applyAxisConstraintVec(const TransInfo *t, /* Disabled, as it has not proven to be really useful. (See T82386). */ // constraint_snap_plane_to_face(t, plane, out); } - else { + else if (!isPlaneProjectionViewAligned(t, plane)) { /* View alignment correction. */ - if (!isPlaneProjectionViewAligned(t, plane)) { - planeProjection(t, plane, in, out); - } - else { - mul_m3_v3(t->con.pmtx, out); - } + planeProjection(t, plane, in, out); } } } diff --git a/source/blender/editors/transform/transform_convert.h b/source/blender/editors/transform/transform_convert.h index f32bff6dcff..4798d666d70 100644 --- a/source/blender/editors/transform/transform_convert.h +++ b/source/blender/editors/transform/transform_convert.h @@ -10,6 +10,10 @@ #include "RE_engine.h" +#ifdef __cplusplus +extern "C" { +#endif + struct BMEditMesh; struct BMesh; struct BezTriple; @@ -222,8 +226,6 @@ void transform_convert_mesh_crazyspace_transdata_set(const float mtx[3][3], struct TransData *r_td); void transform_convert_mesh_crazyspace_free(struct TransMeshDataCrazySpace *r_crazyspace_data); -void special_aftertrans_update__mesh(bContext *C, TransInfo *t); - /* transform_convert_mesh_edge.c */ extern TransConvertTypeInfo TransConvertType_MeshEdge; @@ -244,7 +246,7 @@ extern TransConvertTypeInfo TransConvertType_MeshVertCData; extern TransConvertTypeInfo TransConvertType_NLA; -/* transform_convert_node.c */ +/* transform_convert_node.cc */ extern TransConvertTypeInfo TransConvertType_Node; @@ -279,3 +281,7 @@ extern TransConvertTypeInfo TransConvertType_SequencerImage; /* transform_convert_tracking.c */ extern TransConvertTypeInfo TransConvertType_Tracking; + +#ifdef __cplusplus +} +#endif diff --git a/source/blender/editors/transform/transform_convert_mesh.c b/source/blender/editors/transform/transform_convert_mesh.c index af7982f862a..7e237c9eb32 100644 --- a/source/blender/editors/transform/transform_convert_mesh.c +++ b/source/blender/editors/transform/transform_convert_mesh.c @@ -2068,7 +2068,7 @@ static void recalcData_mesh(TransInfo *t) /** \name Special After Transform Mesh * \{ */ -void special_aftertrans_update__mesh(bContext *UNUSED(C), TransInfo *t) +static void special_aftertrans_update__mesh(bContext *UNUSED(C), TransInfo *t) { const bool is_canceling = (t->state == TRANS_CANCEL); const bool use_automerge = !is_canceling && (t->flag & (T_AUTOMERGE | T_AUTOSPLIT)) != 0; diff --git a/source/blender/editors/transform/transform_convert_mesh_edge.c b/source/blender/editors/transform/transform_convert_mesh_edge.c index 7f26029850b..0ce4c592f53 100644 --- a/source/blender/editors/transform/transform_convert_mesh_edge.c +++ b/source/blender/editors/transform/transform_convert_mesh_edge.c @@ -125,5 +125,5 @@ TransConvertTypeInfo TransConvertType_MeshEdge = { /* flags */ T_EDIT, /* createTransData */ createTransEdge, /* recalcData */ recalcData_mesh_edge, - /* special_aftertrans_update */ special_aftertrans_update__mesh, + /* special_aftertrans_update */ NULL, }; diff --git a/source/blender/editors/transform/transform_convert_nla.c b/source/blender/editors/transform/transform_convert_nla.c index cfa933d1600..af5a51cbff6 100644 --- a/source/blender/editors/transform/transform_convert_nla.c +++ b/source/blender/editors/transform/transform_convert_nla.c @@ -56,6 +56,29 @@ typedef struct TransDataNla { } TransDataNla; /* -------------------------------------------------------------------- */ +/** \name Transform application to NLA strips + * \{ */ + +/** + * \brief Applies a translation to the given NlaStrip. + * \param strip_rna_ptr The RNA pointer of the NLA strip to modify. + * \param transdata The transformation info structure. + */ +static void applyTransformNLA_translation(PointerRNA *strip_rna_ptr, const TransDataNla *transdata) +{ + /* NOTE: we write these twice to avoid truncation errors which can arise when + * moving the strips a large distance using numeric input T33852. + */ + RNA_float_set(strip_rna_ptr, "frame_start", transdata->h1[0]); + RNA_float_set(strip_rna_ptr, "frame_end", transdata->h2[0]); + + RNA_float_set(strip_rna_ptr, "frame_start", transdata->h1[0]); + RNA_float_set(strip_rna_ptr, "frame_end", transdata->h2[0]); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ /** \name NLA Transform Creation * \{ */ @@ -329,15 +352,8 @@ static void recalcData_nla(TransInfo *t) * * this is done as a iterative procedure (done 5 times max for now) */ - NlaStrip *prev = strip->prev; - while (prev != NULL && (prev->type & NLASTRIP_TYPE_TRANSITION)) { - prev = prev->prev; - } - - NlaStrip *next = strip->next; - while (next != NULL && (next->type & NLASTRIP_TYPE_TRANSITION)) { - next = next->next; - } + NlaStrip *prev = BKE_nlastrip_prev_in_track(strip, true); + NlaStrip *next = BKE_nlastrip_next_in_track(strip, true); for (short iter = 0; iter < 5; iter++) { const bool pExceeded = (prev != NULL) && (tdn->h1[0] < prev->end); @@ -380,17 +396,10 @@ static void recalcData_nla(TransInfo *t) /* Use RNA to write the values to ensure that constraints on these are obeyed * (e.g. for transition strips, the values are taken from the neighbors) - * - * NOTE: we write these twice to avoid truncation errors which can arise when - * moving the strips a large distance using numeric input T33852. */ RNA_pointer_create(NULL, &RNA_NlaStrip, strip, &strip_ptr); - RNA_float_set(&strip_ptr, "frame_start", tdn->h1[0]); - RNA_float_set(&strip_ptr, "frame_end", tdn->h2[0]); - - RNA_float_set(&strip_ptr, "frame_start", tdn->h1[0]); - RNA_float_set(&strip_ptr, "frame_end", tdn->h2[0]); + applyTransformNLA_translation(&strip_ptr, tdn); /* flush transforms to child strips (since this should be a meta) */ BKE_nlameta_flush_transforms(strip); diff --git a/source/blender/editors/transform/transform_convert_node.c b/source/blender/editors/transform/transform_convert_node.cc index ed19789fdd8..6ab0e1fe701 100644 --- a/source/blender/editors/transform/transform_convert_node.c +++ b/source/blender/editors/transform/transform_convert_node.cc @@ -96,13 +96,13 @@ static bool is_node_parent_select(bNode *node) return false; } -static void createTransNodeData(bContext *UNUSED(C), TransInfo *t) +static void createTransNodeData(bContext * /*C*/, TransInfo *t) { const float dpi_fac = UI_DPI_FAC; - SpaceNode *snode = t->area->spacedata.first; + SpaceNode *snode = static_cast<SpaceNode *>(t->area->spacedata.first); /* Custom data to enable edge panning during the node transform */ - struct TransCustomDataNode *customdata = MEM_callocN(sizeof(*customdata), __func__); + TransCustomDataNode *customdata = MEM_cnew<TransCustomDataNode>(__func__); UI_view2d_edge_pan_init(t->context, &customdata->edgepan_data, NODE_EDGE_PAN_INSIDE_PAD, @@ -125,7 +125,7 @@ static void createTransNodeData(bContext *UNUSED(C), TransInfo *t) } /* Nodes don't support PET and probably never will. */ - t->flag &= ~T_PROP_EDIT_ALL; + t->flag = t->flag & ~T_PROP_EDIT_ALL; /* set transform flags on nodes */ LISTBASE_FOREACH (bNode *, node, &snode->edittree->nodes) { @@ -142,9 +142,8 @@ static void createTransNodeData(bContext *UNUSED(C), TransInfo *t) return; } - TransData *td = tc->data = MEM_callocN(tc->data_len * sizeof(TransData), "TransNode TransData"); - TransData2D *td2d = tc->data_2d = MEM_callocN(tc->data_len * sizeof(TransData2D), - "TransNode TransData2D"); + TransData *td = tc->data = MEM_cnew_array<TransData>(tc->data_len, __func__); + TransData2D *td2d = tc->data_2d = MEM_cnew_array<TransData2D>(tc->data_len, __func__); LISTBASE_FOREACH (bNode *, node, &snode->edittree->nodes) { if (node->flag & NODE_TRANSFORM) { @@ -156,14 +155,59 @@ static void createTransNodeData(bContext *UNUSED(C), TransInfo *t) /** \} */ /* -------------------------------------------------------------------- */ -/** \name Node Transform Creation +/** \name Flush Transform Nodes * \{ */ +static void node_snap_grid_apply(TransInfo *t) +{ + int i; + + if (!(activeSnap(t) && (t->tsnap.mode & (SCE_SNAP_MODE_INCREMENT | SCE_SNAP_MODE_GRID)))) { + return; + } + + float grid_size[2]; + copy_v2_v2(grid_size, t->snap_spatial); + if (t->modifiers & MOD_PRECISION) { + mul_v2_fl(grid_size, t->snap_spatial_precision); + } + + /* Early exit on unusable grid size. */ + if (is_zero_v2(grid_size)) { + return; + } + + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + TransData *td; + + for (i = 0, td = tc->data; i < tc->data_len; i++, td++) { + float iloc[2], loc[2], tvec[2]; + if (td->flag & TD_SKIP) { + continue; + } + + if ((t->flag & T_PROP_EDIT) && (td->factor == 0.0f)) { + continue; + } + + copy_v2_v2(iloc, td->loc); + + loc[0] = roundf(iloc[0] / grid_size[0]) * grid_size[0]; + loc[1] = roundf(iloc[1] / grid_size[1]) * grid_size[1]; + + sub_v2_v2v2(tvec, loc, iloc); + add_v2_v2(td->loc, tvec); + } + } +} + static void flushTransNodes(TransInfo *t) { + using namespace blender::ed; const float dpi_fac = UI_DPI_FAC; + SpaceNode *snode = static_cast<SpaceNode *>(t->area->spacedata.first); - struct TransCustomDataNode *customdata = (struct TransCustomDataNode *)t->custom.type.data; + TransCustomDataNode *customdata = (TransCustomDataNode *)t->custom.type.data; if (t->options & CTX_VIEW2D_EDGE_PAN) { if (t->state == TRANS_CANCEL) { @@ -190,13 +234,13 @@ static void flushTransNodes(TransInfo *t) } FOREACH_TRANS_DATA_CONTAINER (t, tc) { - applyGridAbsolute(t); + node_snap_grid_apply(t); /* flush to 2d vector from internally used 3d vector */ for (int i = 0; i < tc->data_len; i++) { TransData *td = &tc->data[i]; TransData2D *td2d = &tc->data_2d[i]; - bNode *node = td->extra; + bNode *node = static_cast<bNode *>(td->extra); float loc[2]; add_v2_v2v2(loc, td2d->loc, offset); @@ -221,7 +265,7 @@ static void flushTransNodes(TransInfo *t) /* handle intersection with noodles */ if (tc->data_len == 1) { - ED_node_link_intersect_test(t->area, 1); + space_node::node_insert_on_link_flags_set(*snode, *t->region); } } } @@ -234,13 +278,15 @@ static void flushTransNodes(TransInfo *t) static void special_aftertrans_update__node(bContext *C, TransInfo *t) { - struct Main *bmain = CTX_data_main(C); + using namespace blender::ed; + Main *bmain = CTX_data_main(C); + SpaceNode *snode = (SpaceNode *)t->area->spacedata.first; + bNodeTree *ntree = snode->edittree; + const bool canceled = (t->state == TRANS_CANCEL); - SpaceNode *snode = (SpaceNode *)t->area->spacedata.first; if (canceled && t->remove_on_cancel) { /* remove selected nodes on cancel */ - bNodeTree *ntree = snode->edittree; if (ntree) { LISTBASE_FOREACH_MUTABLE (bNode *, node, &ntree->nodes) { if (node->flag & NODE_SELECT) { @@ -253,11 +299,10 @@ static void special_aftertrans_update__node(bContext *C, TransInfo *t) if (!canceled) { ED_node_post_apply_transform(C, snode->edittree); - ED_node_link_insert(bmain, t->area); + space_node::node_insert_on_link_flags(*bmain, *snode); } - /* clear link line */ - ED_node_link_intersect_test(t->area, 0); + space_node::node_insert_on_link_flags_clear(*ntree); } /** \} */ diff --git a/source/blender/editors/transform/transform_mode_curveshrinkfatten.c b/source/blender/editors/transform/transform_mode_curveshrinkfatten.c index f7f9e14b8ac..0b87b45679a 100644 --- a/source/blender/editors/transform/transform_mode_curveshrinkfatten.c +++ b/source/blender/editors/transform/transform_mode_curveshrinkfatten.c @@ -8,6 +8,7 @@ #include <stdlib.h> #include "BLI_math.h" +#include "BLI_math_bits.h" #include "BLI_string.h" #include "BKE_context.h" @@ -62,7 +63,14 @@ static void applyCurveShrinkFatten(TransInfo *t, const int UNUSED(mval[2])) } if (td->val) { - *td->val = td->ival * ratio; + if (td->ival == 0.0f && ratio > 1.0f) { + /* Allow Shrink/Fatten for zero radius. */ + *td->val = (ratio - 1.0f) * uint_as_float(POINTER_AS_UINT(t->custom.mode.data)); + } + else { + *td->val = td->ival * ratio; + } + /* apply PET */ *td->val = interpf(*td->val, td->ival, td->factor); CLAMP_MIN(*td->val, 0.0f); @@ -92,6 +100,18 @@ void initCurveShrinkFatten(TransInfo *t) t->num.unit_type[0] = B_UNIT_NONE; t->flag |= T_NO_CONSTRAINT; + + float scale_factor = 0.0f; + if (((t->spacetype == SPACE_VIEW3D) && (t->region->regiontype == RGN_TYPE_WINDOW) && + (t->data_len_all == 1)) || + (t->data_len_all == 3 && TRANS_DATA_CONTAINER_FIRST_OK(t)->data[0].val == NULL)) { + /* For cases where only one point on the curve is being transformed and the radius of that + * point is zero, use the factor to multiply the offset of the ratio and allow scaling. + * Note that for bezier curves, 3 TransData equals 1 point in most cases. */ + RegionView3D *rv3d = t->region->regiondata; + scale_factor = rv3d->pixsize * t->mouse.factor * t->zfac; + } + t->custom.mode.data = POINTER_FROM_UINT(float_as_uint(scale_factor)); } /** \} */ diff --git a/source/blender/editors/transform/transform_mode_translate.c b/source/blender/editors/transform/transform_mode_translate.c index 8f6ec7bd98f..59d34c3918b 100644 --- a/source/blender/editors/transform/transform_mode_translate.c +++ b/source/blender/editors/transform/transform_mode_translate.c @@ -170,7 +170,7 @@ static void transdata_elem_translate_fn(void *__restrict iter_data_v, /** \} */ /* -------------------------------------------------------------------- */ -/** \name Transform (Translation) +/** \name Transform (Translation) Header * \{ */ static void translate_dist_to_str(char *r_str, @@ -341,6 +341,96 @@ static void headerTranslation(TransInfo *t, const float vec[3], char str[UI_MAX_ } } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Transform (Translation) Snapping + * \{ */ + +static void translate_snap_target_grid_ensure(TransInfo *t) +{ + /* Only need to calculate once. */ + if ((t->tsnap.status & TARGET_GRID_INIT) == 0) { + if (t->data_type == &TransConvertType_Cursor3D) { + /* Use a fallback when transforming the cursor. + * In this case the center is _not_ derived from the cursor which is being transformed. */ + copy_v3_v3(t->tsnap.snapTargetGrid, TRANS_DATA_CONTAINER_FIRST_SINGLE(t)->data->iloc); + } + else if (t->around == V3D_AROUND_CURSOR) { + /* Use a fallback for cursor selection, + * this isn't useful as a global center for absolute grid snapping + * since its not based on the position of the selection. */ + tranform_snap_target_median_calc(t, t->tsnap.snapTargetGrid); + } + else { + copy_v3_v3(t->tsnap.snapTargetGrid, t->center_global); + } + t->tsnap.status |= TARGET_GRID_INIT; + } +} + +static void translate_snap_grid_apply(TransInfo *t, + const int max_index, + const float grid_dist[3], + const float loc[3], + float r_out[3]) +{ + BLI_assert(max_index <= 2); + translate_snap_target_grid_ensure(t); + const float *center_global = t->tsnap.snapTargetGrid; + const float *asp = t->aspect; + + float in[3]; + if (t->con.mode & CON_APPLY) { + BLI_assert(t->tsnap.snapElem == SCE_SNAP_MODE_NONE); + t->con.applyVec(t, NULL, NULL, loc, in); + } + else { + copy_v3_v3(in, loc); + } + + for (int i = 0; i <= max_index; i++) { + const float iter_fac = grid_dist[i] * asp[i]; + r_out[i] = iter_fac * roundf((in[i] + center_global[i]) / iter_fac) - center_global[i]; + } +} + +static bool translate_snap_grid(TransInfo *t, float *val) +{ + if (!activeSnap(t)) { + return false; + } + + if (!(t->tsnap.mode & SCE_SNAP_MODE_GRID) || validSnap(t)) { + /* Don't do grid snapping if there is a valid snap point. */ + return false; + } + + /* Don't do grid snapping if not in 3D viewport or UV editor */ + if (!ELEM(t->spacetype, SPACE_VIEW3D, SPACE_IMAGE)) { + return false; + } + + if (t->mode != TFM_TRANSLATION) { + return false; + } + + float grid_dist[3]; + copy_v3_v3(grid_dist, t->snap_spatial); + if (t->modifiers & MOD_PRECISION) { + mul_v3_fl(grid_dist, t->snap_spatial_precision); + } + + /* Early bailing out if no need to snap */ + if (is_zero_v3(grid_dist)) { + return false; + } + + translate_snap_grid_apply(t, t->idx_max, grid_dist, val, val); + t->tsnap.snapElem = SCE_SNAP_MODE_GRID; + return true; +} + static void ApplySnapTranslation(TransInfo *t, float vec[3]) { float point[3]; @@ -372,6 +462,12 @@ static void ApplySnapTranslation(TransInfo *t, float vec[3]) } } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Transform (Translation) + * \{ */ + static void applyTranslationValue(TransInfo *t, const float vec[3]) { struct TranslateCustomData *custom_data = t->custom.mode.data; @@ -514,7 +610,7 @@ static void applyTranslation(TransInfo *t, const int UNUSED(mval[2])) t->tsnap.snapElem = SCE_SNAP_MODE_NONE; applySnappingAsGroup(t, global_dir); - transform_snap_grid(t, global_dir); + translate_snap_grid(t, global_dir); if (t->con.mode & CON_APPLY) { float in[3]; @@ -590,7 +686,8 @@ void initTranslation(TransInfo *t) t->num.flag = 0; t->num.idx_max = t->idx_max; - copy_v2_v2(t->snap, t->snap_spatial); + t->snap[0] = t->snap_spatial[0]; + t->snap[1] = t->snap_spatial[0] * t->snap_spatial_precision; copy_v3_fl(t->num.val_inc, t->snap[0]); t->num.unit_sys = t->scene->unit.system; diff --git a/source/blender/editors/transform/transform_ops.c b/source/blender/editors/transform/transform_ops.c index dbda9a26bbf..82791b2a9f5 100644 --- a/source/blender/editors/transform/transform_ops.c +++ b/source/blender/editors/transform/transform_ops.c @@ -523,9 +523,7 @@ static int transform_invoke(bContext *C, wmOperator *op, const wmEvent *event) return OPERATOR_RUNNING_MODAL; } -static bool transform_poll_property(const bContext *UNUSED(C), - wmOperator *op, - const PropertyRNA *prop) +static bool transform_poll_property(const bContext *C, wmOperator *op, const PropertyRNA *prop) { const char *prop_id = RNA_property_identifier(prop); @@ -559,12 +557,21 @@ static bool transform_poll_property(const bContext *UNUSED(C), } /* Proportional Editing. */ - { + if (STRPREFIX(prop_id, "proportional") || STRPREFIX(prop_id, "use_proportional")) { + ScrArea *area = CTX_wm_area(C); + if (area->spacetype == SPACE_NLA) { + /* Hide properties that are not supported in some spaces. */ + return false; + } + PropertyRNA *prop_pet = RNA_struct_find_property(op->ptr, "use_proportional_edit"); - if (prop_pet && (prop_pet != prop) && (RNA_property_boolean_get(op->ptr, prop_pet) == false)) { - if (STRPREFIX(prop_id, "proportional") || STRPREFIX(prop_id, "use_proportional")) { - return false; - } + if ((prop_pet != prop) && (RNA_property_boolean_get(op->ptr, prop_pet) == false)) { + /* If "use_proportional_edit" is false, hide: + * - "proportional_edit_falloff", + * - "proportional_size", + * - "use_proportional_connected", + * - "use_proportional_projected". */ + return false; } } diff --git a/source/blender/editors/transform/transform_snap.c b/source/blender/editors/transform/transform_snap.c index 3f9cca55138..672d947936d 100644 --- a/source/blender/editors/transform/transform_snap.c +++ b/source/blender/editors/transform/transform_snap.c @@ -511,56 +511,6 @@ void applySnappingIndividual(TransInfo *t) } } -void applyGridAbsolute(TransInfo *t) -{ - int i; - - if (!(activeSnap(t) && (t->tsnap.mode & (SCE_SNAP_MODE_INCREMENT | SCE_SNAP_MODE_GRID)))) { - return; - } - - float grid_size = (t->modifiers & MOD_PRECISION) ? t->snap_spatial[1] : t->snap_spatial[0]; - - /* early exit on unusable grid size */ - if (grid_size == 0.0f) { - return; - } - - FOREACH_TRANS_DATA_CONTAINER (t, tc) { - TransData *td; - - for (i = 0, td = tc->data; i < tc->data_len; i++, td++) { - float iloc[3], loc[3], tvec[3]; - if (td->flag & TD_SKIP) { - continue; - } - - if ((t->flag & T_PROP_EDIT) && (td->factor == 0.0f)) { - continue; - } - - copy_v3_v3(iloc, td->loc); - if (tc->use_local_mat) { - mul_m4_v3(tc->mat, iloc); - } - else if (t->options & CTX_OBJECT) { - BKE_object_eval_transform_all(t->depsgraph, t->scene, td->ob); - copy_v3_v3(iloc, td->ob->obmat[3]); - } - - mul_v3_v3fl(loc, iloc, 1.0f / grid_size); - loc[0] = roundf(loc[0]); - loc[1] = roundf(loc[1]); - loc[2] = roundf(loc[2]); - mul_v3_fl(loc, grid_size); - - sub_v3_v3v3(tvec, loc, iloc); - mul_m3_v3(td->smtx, tvec); - add_v3_v3(td->loc, tvec); - } - } -} - void applySnappingAsGroup(TransInfo *t, float *vec) { if (!activeSnap_SnappingAsGroup(t)) { @@ -1187,7 +1137,7 @@ static void snap_calc_sequencer_fn(TransInfo *t, float *UNUSED(vec)) /** \name Target * \{ */ -static void snap_target_median_impl(TransInfo *t, float r_median[3]) +void tranform_snap_target_median_calc(const TransInfo *t, float r_median[3]) { int i_accum = 0; @@ -1223,28 +1173,6 @@ static void snap_target_median_impl(TransInfo *t, float r_median[3]) // TargetSnapOffset(t, NULL); } -static void snap_target_grid_ensure(TransInfo *t) -{ - /* Only need to calculate once. */ - if ((t->tsnap.status & TARGET_GRID_INIT) == 0) { - if (t->data_type == &TransConvertType_Cursor3D) { - /* Use a fallback when transforming the cursor. - * In this case the center is _not_ derived from the cursor which is being transformed. */ - copy_v3_v3(t->tsnap.snapTargetGrid, TRANS_DATA_CONTAINER_FIRST_SINGLE(t)->data->iloc); - } - else if (t->around == V3D_AROUND_CURSOR) { - /* Use a fallback for cursor selection, - * this isn't useful as a global center for absolute grid snapping - * since its not based on the position of the selection. */ - snap_target_median_impl(t, t->tsnap.snapTargetGrid); - } - else { - copy_v3_v3(t->tsnap.snapTargetGrid, t->center_global); - } - t->tsnap.status |= TARGET_GRID_INIT; - } -} - static void TargetSnapOffset(TransInfo *t, TransData *td) { if (t->spacetype == SPACE_NODE && td != NULL) { @@ -1316,7 +1244,7 @@ static void TargetSnapMedian(TransInfo *t) { /* Only need to calculate once. */ if ((t->tsnap.status & TARGET_INIT) == 0) { - snap_target_median_impl(t, t->tsnap.snapTarget); + tranform_snap_target_median_calc(t, t->tsnap.snapTarget); t->tsnap.status |= TARGET_INIT; } } @@ -1654,61 +1582,6 @@ bool snapNodesTransform( /** \name snap Grid * \{ */ -static void snap_grid_apply( - TransInfo *t, const int max_index, const float grid_dist, const float loc[3], float r_out[3]) -{ - BLI_assert(max_index <= 2); - snap_target_grid_ensure(t); - const float *center_global = t->tsnap.snapTargetGrid; - const float *asp = t->aspect; - - float in[3]; - if (t->con.mode & CON_APPLY) { - BLI_assert(t->tsnap.snapElem == SCE_SNAP_MODE_NONE); - t->con.applyVec(t, NULL, NULL, loc, in); - } - else { - copy_v3_v3(in, loc); - } - - for (int i = 0; i <= max_index; i++) { - const float iter_fac = grid_dist * asp[i]; - r_out[i] = iter_fac * roundf((in[i] + center_global[i]) / iter_fac) - center_global[i]; - } -} - -bool transform_snap_grid(TransInfo *t, float *val) -{ - if (!activeSnap(t)) { - return false; - } - - if (!(t->tsnap.mode & SCE_SNAP_MODE_GRID) || validSnap(t)) { - /* Don't do grid snapping if there is a valid snap point. */ - return false; - } - - /* Don't do grid snapping if not in 3D viewport or UV editor */ - if (!ELEM(t->spacetype, SPACE_VIEW3D, SPACE_IMAGE)) { - return false; - } - - if (t->mode != TFM_TRANSLATION) { - return false; - } - - float grid_dist = (t->modifiers & MOD_PRECISION) ? t->snap[1] : t->snap[0]; - - /* Early bailing out if no need to snap */ - if (grid_dist == 0.0f) { - return false; - } - - snap_grid_apply(t, t->idx_max, grid_dist, val, val); - t->tsnap.snapElem = SCE_SNAP_MODE_GRID; - return true; -} - static void snap_increment_apply_ex(const TransInfo *UNUSED(t), const int max_index, const float increment_val, diff --git a/source/blender/editors/transform/transform_snap.h b/source/blender/editors/transform/transform_snap.h index 3672e76c778..16d9062e978 100644 --- a/source/blender/editors/transform/transform_snap.h +++ b/source/blender/editors/transform/transform_snap.h @@ -11,6 +11,10 @@ /* For enum. */ #include "DNA_space_types.h" +#ifdef __cplusplus +extern "C" { +#endif + bool peelObjectsTransform(struct TransInfo *t, const float mval[2], bool use_peel_object, @@ -34,10 +38,10 @@ bool snapNodesTransform(struct TransInfo *t, bool transformModeUseSnap(const TransInfo *t); +void tranform_snap_target_median_calc(const TransInfo *t, float r_median[3]); bool transform_snap_increment_ex(const TransInfo *t, bool use_local_space, float *r_val); bool transform_snap_increment(const TransInfo *t, float *val); float transform_snap_increment_get(const TransInfo *t); -bool transform_snap_grid(TransInfo *t, float *val); bool activeSnap(const TransInfo *t); bool activeSnap_SnappingIndividual(const TransInfo *t); @@ -48,7 +52,6 @@ bool validSnap(const TransInfo *t); void initSnapping(struct TransInfo *t, struct wmOperator *op); void freeSnapping(struct TransInfo *t); void applySnappingIndividual(TransInfo *t); -void applyGridAbsolute(TransInfo *t); void applySnappingAsGroup(TransInfo *t, float *vec); void resetSnapping(TransInfo *t); eRedrawFlag handleSnapping(TransInfo *t, const struct wmEvent *event); @@ -92,3 +95,7 @@ void transform_snap_anim_flush_data(TransInfo *t, TransData *td, eAnimEdit_AutoSnap autosnap, float *r_val_final); + +#ifdef __cplusplus +} +#endif diff --git a/source/blender/editors/transform/transform_snap_object.cc b/source/blender/editors/transform/transform_snap_object.cc index 90a13722b63..7971e1ca9af 100644 --- a/source/blender/editors/transform/transform_snap_object.cc +++ b/source/blender/editors/transform/transform_snap_object.cc @@ -105,7 +105,7 @@ struct SnapData_EditMesh { /* Looptris. */ BVHTreeFromEditMesh treedata_editmesh; - struct Mesh_Runtime *mesh_runtime; + blender::bke::MeshRuntime *mesh_runtime; float min[3], max[3]; void clear() @@ -189,14 +189,14 @@ static const Mesh *mesh_for_snap(Object *ob_eval, eSnapEditType edit_mode_type, const Mesh *editmesh_eval_cage = BKE_object_get_editmesh_eval_cage(ob_eval); if ((edit_mode_type == SNAP_GEOM_FINAL) && editmesh_eval_final) { - if (editmesh_eval_final->runtime.wrapper_type == ME_WRAPPER_TYPE_BMESH) { + if (editmesh_eval_final->runtime->wrapper_type == ME_WRAPPER_TYPE_BMESH) { return nullptr; } me_eval = editmesh_eval_final; use_hide = true; } else if ((edit_mode_type == SNAP_GEOM_CAGE) && editmesh_eval_cage) { - if (editmesh_eval_cage->runtime.wrapper_type == ME_WRAPPER_TYPE_BMESH) { + if (editmesh_eval_cage->runtime->wrapper_type == ME_WRAPPER_TYPE_BMESH) { return nullptr; } me_eval = editmesh_eval_cage; @@ -253,21 +253,21 @@ static SnapData_Mesh *snap_object_data_mesh_get(SnapObjectContext *sctx, sod = sod_p->get(); bool is_dirty = false; if (sod->treedata_mesh.tree && sod->treedata_mesh.cached && - !bvhcache_has_tree(me_eval->runtime.bvh_cache, sod->treedata_mesh.tree)) { + !bvhcache_has_tree(me_eval->runtime->bvh_cache, sod->treedata_mesh.tree)) { /* The tree is owned by the Mesh and may have been freed since we last used. */ is_dirty = true; } else if (sod->bvhtree[0] && sod->cached[0] && - !bvhcache_has_tree(me_eval->runtime.bvh_cache, sod->bvhtree[0])) { + !bvhcache_has_tree(me_eval->runtime->bvh_cache, sod->bvhtree[0])) { /* The tree is owned by the Mesh and may have been freed since we last used. */ is_dirty = true; } else if (sod->bvhtree[1] && sod->cached[1] && - !bvhcache_has_tree(me_eval->runtime.bvh_cache, sod->bvhtree[1])) { + !bvhcache_has_tree(me_eval->runtime->bvh_cache, sod->bvhtree[1])) { /* The tree is owned by the Mesh and may have been freed since we last used. */ is_dirty = true; } - else if (sod->treedata_mesh.looptri != me_eval->runtime.looptris.array) { + else if (sod->treedata_mesh.looptri != me_eval->looptris().data()) { is_dirty = true; } else if (sod->treedata_mesh.vert != verts.data()) { @@ -330,19 +330,19 @@ static SnapData_Mesh *snap_object_data_mesh_get(SnapObjectContext *sctx, /* Searches for the #Mesh_Runtime associated with the object that is most likely to be updated due * to changes in the `edit_mesh`. */ -static Mesh_Runtime *snap_object_data_editmesh_runtime_get(Object *ob_eval) +static blender::bke::MeshRuntime *snap_object_data_editmesh_runtime_get(Object *ob_eval) { Mesh *editmesh_eval_final = BKE_object_get_editmesh_eval_final(ob_eval); if (editmesh_eval_final) { - return &editmesh_eval_final->runtime; + return editmesh_eval_final->runtime; } Mesh *editmesh_eval_cage = BKE_object_get_editmesh_eval_cage(ob_eval); if (editmesh_eval_cage) { - return &editmesh_eval_cage->runtime; + return editmesh_eval_cage->runtime; } - return &((Mesh *)ob_eval->data)->runtime; + return ((Mesh *)ob_eval->data)->runtime; } static SnapData_EditMesh *snap_object_data_editmesh_get(SnapObjectContext *sctx, @@ -457,7 +457,7 @@ static BVHTreeFromEditMesh *snap_object_data_editmesh_treedata_get(SnapObjectCon 4, BVHTREE_FROM_EM_LOOPTRI, &sod->mesh_runtime->bvh_cache, - static_cast<ThreadMutex *>(sod->mesh_runtime->eval_mutex)); + &sod->mesh_runtime->eval_mutex); } } if (treedata == nullptr || treedata->tree == nullptr) { @@ -2923,7 +2923,7 @@ static eSnapMode snapEditMesh(SnapObjectContext *sctx, 2, BVHTREE_FROM_EM_VERTS, &sod->mesh_runtime->bvh_cache, - (ThreadMutex *)sod->mesh_runtime->eval_mutex); + &sod->mesh_runtime->eval_mutex); } sod->bvhtree[0] = treedata.tree; sod->cached[0] = treedata.cached; @@ -2955,7 +2955,7 @@ static eSnapMode snapEditMesh(SnapObjectContext *sctx, 2, BVHTREE_FROM_EM_EDGES, &sod->mesh_runtime->bvh_cache, - static_cast<ThreadMutex *>(sod->mesh_runtime->eval_mutex)); + &sod->mesh_runtime->eval_mutex); } sod->bvhtree[1] = treedata.tree; sod->cached[1] = treedata.cached; diff --git a/source/blender/editors/util/ed_util.c b/source/blender/editors/util/ed_util.c index 12e77c6ef00..92d65688bf1 100644 --- a/source/blender/editors/util/ed_util.c +++ b/source/blender/editors/util/ed_util.c @@ -374,7 +374,7 @@ void unpack_menu(bContext *C, char local_name[FILE_MAXDIR + FILE_MAX], fi[FILE_MAX]; BLI_split_file_part(abs_name, fi, sizeof(fi)); - BLI_path_join(local_name, sizeof(local_name), "//", folder, fi, NULL); + BLI_path_join(local_name, sizeof(local_name), "//", folder, fi); if (!STREQ(abs_name, local_name)) { switch (BKE_packedfile_compare_to_file(blendfile_path, local_name, pf)) { case PF_CMP_NOFILE: diff --git a/source/blender/editors/util/ed_viewer_path.cc b/source/blender/editors/util/ed_viewer_path.cc index 5c03367cba8..4da1559b726 100644 --- a/source/blender/editors/util/ed_viewer_path.cc +++ b/source/blender/editors/util/ed_viewer_path.cc @@ -249,6 +249,9 @@ bool is_active_geometry_nodes_viewer(const bContext &C, if (md->type != eModifierType_Nodes) { return false; } + if ((md->mode & eModifierMode_Realtime) == 0) { + return false; + } modifier = reinterpret_cast<const NodesModifierData *>(md); break; } diff --git a/source/blender/editors/uvedit/uvedit_islands.cc b/source/blender/editors/uvedit/uvedit_islands.cc index 2648ec5e2f6..92745667505 100644 --- a/source/blender/editors/uvedit/uvedit_islands.cc +++ b/source/blender/editors/uvedit/uvedit_islands.cc @@ -403,6 +403,219 @@ int bm_mesh_calc_uv_islands(const Scene *scene, /** \} */ +static float pack_islands_scale_margin(const blender::Vector<FaceIsland *> &island_vector, + BoxPack *box_array, + const float scale, + const float margin) +{ + for (const int index : island_vector.index_range()) { + FaceIsland *island = island_vector[index]; + BoxPack *box = &box_array[index]; + box->index = index; + box->w = BLI_rctf_size_x(&island->bounds_rect) * scale + 2 * margin; + box->h = BLI_rctf_size_y(&island->bounds_rect) * scale + 2 * margin; + } + float max_u, max_v; + BLI_box_pack_2d(box_array, island_vector.size(), &max_u, &max_v); + return max_ff(max_u, max_v); +} + +static float pack_islands_margin_fraction(const blender::Vector<FaceIsland *> &island_vector, + BoxPack *box_array, + const float margin_fraction) +{ + /* + * Root finding using a combined search / modified-secant method. + * First, use a robust search procedure to bracket the root within a factor of 10. + * Then, use a modified-secant method to converge. + * + * This is a specialized solver using domain knowledge to accelerate convergence. + */ + + float scale_low = 0.0f; + float value_low = 0.0f; + float scale_high = 0.0f; + float value_high = 0.0f; + float scale_last = 0.0f; + + /* Scaling smaller than `min_scale_roundoff` is unlikely to fit and + * will destroy information in existing UVs. */ + float min_scale_roundoff = 1e-5f; + + /* Certain inputs might have poor convergence properties. + * Use `max_iteration` to prevent an infinite loop. */ + int max_iteration = 25; + for (int iteration = 0; iteration < max_iteration; iteration++) { + float scale = 1.0f; + + if (iteration == 0) { + BLI_assert(iteration == 0); + BLI_assert(scale == 1.0f); + BLI_assert(scale_low == 0.0f); + BLI_assert(scale_high == 0.0f); + } + else if (scale_low == 0.0f) { + BLI_assert(scale_high > 0.0f); + /* Search mode, shrink layout until we can find a scale that fits. */ + scale = scale_high * 0.1f; + } + else if (scale_high == 0.0f) { + BLI_assert(scale_low > 0.0f); + /* Search mode, grow layout until we can find a scale that doesn't fit. */ + scale = scale_low * 10.0f; + } + else { + /* Bracket mode, use modified secant method to find root. */ + BLI_assert(scale_low > 0.0f); + BLI_assert(scale_high > 0.0f); + BLI_assert(value_low <= 0.0f); + BLI_assert(value_high >= 0.0f); + if (scale_high < scale_low * 1.0001f) { + /* Convergence. */ + break; + } + + /* Secant method for area. */ + scale = (sqrtf(scale_low) * value_high - sqrtf(scale_high) * value_low) / + (value_high - value_low); + scale = scale * scale; + + if (iteration & 1) { + /* Modified binary-search to improve robustness. */ + scale = sqrtf(scale * sqrtf(scale_low * scale_high)); + } + } + + scale = max_ff(scale, min_scale_roundoff); + + /* Evaluate our `f`. */ + scale_last = scale; + float max_uv = pack_islands_scale_margin( + island_vector, box_array, scale_last, margin_fraction); + float value = sqrtf(max_uv) - 1.0f; + + if (value <= 0.0f) { + scale_low = scale; + value_low = value; + } + else { + scale_high = scale; + value_high = value; + if (scale == min_scale_roundoff) { + /* Unable to pack without damaging UVs. */ + scale_low = scale; + break; + } + } + } + + const bool flush = true; + if (flush) { + /* Write back best pack as a side-effect. First get best pack. */ + if (scale_last != scale_low) { + scale_last = scale_low; + float max_uv = pack_islands_scale_margin( + island_vector, box_array, scale_last, margin_fraction); + UNUSED_VARS(max_uv); + /* TODO (?): `if (max_uv < 1.0f) { scale_last /= max_uv; }` */ + } + + /* Then expand FaceIslands by the correct amount. */ + for (const int index : island_vector.index_range()) { + BoxPack *box = &box_array[index]; + box->x /= scale_last; + box->y /= scale_last; + FaceIsland *island = island_vector[index]; + BLI_rctf_pad( + &island->bounds_rect, margin_fraction / scale_last, margin_fraction / scale_last); + } + } + return scale_last; +} + +static float calc_margin_from_aabb_length_sum(const blender::Vector<FaceIsland *> &island_vector, + const struct UVPackIsland_Params ¶ms) +{ + /* Logic matches behavior from #GEO_uv_parametrizer_pack. + * Attempt to give predictable results + * not dependent on current UV scale by using + * `aabb_length_sum` (was "`area`") to multiply + * the margin by the length (was "area"). + */ + double aabb_length_sum = 0.0f; + for (FaceIsland *island : island_vector) { + float w = BLI_rctf_size_x(&island->bounds_rect); + float h = BLI_rctf_size_y(&island->bounds_rect); + aabb_length_sum += sqrtf(w * h); + } + return params.margin * aabb_length_sum * 0.1f; +} + +static BoxPack *pack_islands_params(const blender::Vector<FaceIsland *> &island_vector, + const struct UVPackIsland_Params ¶ms, + float r_scale[2]) +{ + BoxPack *box_array = static_cast<BoxPack *>( + MEM_mallocN(sizeof(*box_array) * island_vector.size(), __func__)); + + if (params.margin == 0.0f) { + /* Special case for zero margin. Margin_method is ignored as all formulas give same result. */ + const float max_uv = pack_islands_scale_margin(island_vector, box_array, 1.0f, 0.0f); + r_scale[0] = 1.0f / max_uv; + r_scale[1] = r_scale[0]; + return box_array; + } + + if (params.margin_method == ED_UVPACK_MARGIN_FRACTION) { + /* Uses a line search on scale. ~10x slower than other method. */ + const float scale = pack_islands_margin_fraction(island_vector, box_array, params.margin); + r_scale[0] = scale; + r_scale[1] = scale; + /* pack_islands_margin_fraction will pad FaceIslands, return early. */ + return box_array; + } + + float margin = params.margin; + switch (params.margin_method) { + case ED_UVPACK_MARGIN_ADD: /* Default for Blender 2.8 and earlier. */ + break; /* Nothing to do. */ + case ED_UVPACK_MARGIN_SCALED: /* Default for Blender 3.3 and later. */ + margin = calc_margin_from_aabb_length_sum(island_vector, params); + break; + case ED_UVPACK_MARGIN_FRACTION: /* Added as an option in Blender 3.4. */ + BLI_assert_unreachable(); /* Handled above. */ + break; + default: + BLI_assert_unreachable(); + } + + const float max_uv = pack_islands_scale_margin(island_vector, box_array, 1.0f, margin); + r_scale[0] = 1.0f / max_uv; + r_scale[1] = r_scale[0]; + + for (int index = 0; index < island_vector.size(); index++) { + FaceIsland *island = island_vector[index]; + BLI_rctf_pad(&island->bounds_rect, margin, margin); + } + return box_array; +} + +static bool island_has_pins(FaceIsland *island) +{ + BMLoop *l; + BMIter iter; + const int cd_loop_uv_offset = island->cd_loop_uv_offset; + for (int i = 0; i < island->faces_len; i++) { + BM_ITER_ELEM (l, &iter, island->faces[i], BM_LOOPS_OF_FACE) { + MLoopUV *luv = static_cast<MLoopUV *>(BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset)); + if (luv->flag & MLOOPUV_PINNED) { + return true; + } + } + } + return false; +} + /* -------------------------------------------------------------------- */ /** \name Public UV Island Packing * @@ -412,6 +625,7 @@ int bm_mesh_calc_uv_islands(const Scene *scene, void ED_uvedit_pack_islands_multi(const Scene *scene, Object **objects, const uint objects_len, + BMesh **bmesh_override, const struct UVMapUDIM_Params *udim_params, const struct UVPackIsland_Params *params) { @@ -419,9 +633,17 @@ void ED_uvedit_pack_islands_multi(const Scene *scene, for (uint ob_index = 0; ob_index < objects_len; ob_index++) { Object *obedit = objects[ob_index]; - BMEditMesh *em = BKE_editmesh_from_object(obedit); - - const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV); + BMesh *bm = nullptr; + if (bmesh_override) { + /* Note: obedit is still required for aspect ratio and ID_RECALC_GEOMETRY. */ + bm = bmesh_override[ob_index]; + } + else { + BMEditMesh *em = BKE_editmesh_from_object(obedit); + bm = em->bm; + } + BLI_assert(bm); + const int cd_loop_uv_offset = CustomData_get_offset(&bm->ldata, CD_MLOOPUV); if (cd_loop_uv_offset == -1) { continue; } @@ -437,7 +659,7 @@ void ED_uvedit_pack_islands_multi(const Scene *scene, ListBase island_list = {nullptr}; bm_mesh_calc_uv_islands(scene, - em->bm, + bm, &island_list, params->only_selected_faces, params->only_selected_uvs, @@ -445,8 +667,14 @@ void ED_uvedit_pack_islands_multi(const Scene *scene, aspect_y, cd_loop_uv_offset); - int index; - LISTBASE_FOREACH_INDEX (struct FaceIsland *, island, &island_list, index) { + /* Remove from linked list and append to blender::Vector. */ + LISTBASE_FOREACH_MUTABLE (struct FaceIsland *, island, &island_list) { + BLI_remlink(&island_list, island); + if (params->ignore_pinned && island_has_pins(island)) { + MEM_freeN(island->faces); + MEM_freeN(island); + continue; + } island_vector.append(island); } } @@ -455,19 +683,12 @@ void ED_uvedit_pack_islands_multi(const Scene *scene, return; } - float margin = scene->toolsettings->uvcalc_margin; - double area = 0.0f; - - BoxPack *boxarray = static_cast<BoxPack *>( - MEM_mallocN(sizeof(*boxarray) * island_vector.size(), __func__)); - /* Coordinates of bounding box containing all selected UVs. */ float selection_min_co[2], selection_max_co[2]; INIT_MINMAX2(selection_min_co, selection_max_co); for (int index = 0; index < island_vector.size(); index++) { FaceIsland *island = island_vector[index]; - /* Skip calculation if using specified UDIM option. */ if (udim_params && (udim_params->use_target_udim == false)) { float bounds_min[2], bounds_max[2]; @@ -489,17 +710,6 @@ void ED_uvedit_pack_islands_multi(const Scene *scene, bm_face_array_calc_bounds( island->faces, island->faces_len, island->cd_loop_uv_offset, &island->bounds_rect); - - BoxPack *box = &boxarray[index]; - box->index = index; - box->x = 0.0f; - box->y = 0.0f; - box->w = BLI_rctf_size_x(&island->bounds_rect); - box->h = BLI_rctf_size_y(&island->bounds_rect); - - if (margin > 0.0f) { - area += double(sqrtf(box->w * box->h)); - } } /* Center of bounding box containing all selected UVs. */ @@ -509,28 +719,8 @@ void ED_uvedit_pack_islands_multi(const Scene *scene, selection_center[1] = (selection_min_co[1] + selection_max_co[1]) / 2.0f; } - if (margin > 0.0f) { - /* Logic matches behavior from #GEO_uv_parametrizer_pack, - * use area so multiply the margin by the area to give - * predictable results not dependent on UV scale. */ - margin = (margin * float(area)) * 0.1f; - for (int i = 0; i < island_vector.size(); i++) { - FaceIsland *island = island_vector[i]; - BoxPack *box = &boxarray[i]; - - BLI_rctf_pad(&island->bounds_rect, margin, margin); - box->w = BLI_rctf_size_x(&island->bounds_rect); - box->h = BLI_rctf_size_y(&island->bounds_rect); - } - } - - float boxarray_size[2]; - BLI_box_pack_2d(boxarray, island_vector.size(), &boxarray_size[0], &boxarray_size[1]); - - /* Don't change the aspect when scaling. */ - boxarray_size[0] = boxarray_size[1] = max_ff(boxarray_size[0], boxarray_size[1]); - - const float scale[2] = {1.0f / boxarray_size[0], 1.0f / boxarray_size[1]}; + float scale[2] = {1.0f, 1.0f}; + BoxPack *box_array = pack_islands_params(island_vector, *params, scale); /* Tile offset. */ float base_offset[2] = {0.0f, 0.0f}; @@ -580,14 +770,14 @@ void ED_uvedit_pack_islands_multi(const Scene *scene, } for (int i = 0; i < island_vector.size(); i++) { - FaceIsland *island = island_vector[boxarray[i].index]; + FaceIsland *island = island_vector[box_array[i].index]; const float pivot[2] = { island->bounds_rect.xmin, island->bounds_rect.ymin, }; const float offset[2] = { - ((boxarray[i].x * scale[0]) - island->bounds_rect.xmin) + base_offset[0], - ((boxarray[i].y * scale[1]) - island->bounds_rect.ymin) + base_offset[1], + ((box_array[i].x * scale[0]) - island->bounds_rect.xmin) + base_offset[0], + ((box_array[i].y * scale[1]) - island->bounds_rect.ymin) + base_offset[1], }; for (int j = 0; j < island->faces_len; j++) { BMFace *efa = island->faces[j]; @@ -607,7 +797,7 @@ void ED_uvedit_pack_islands_multi(const Scene *scene, MEM_freeN(island); } - MEM_freeN(boxarray); + MEM_freeN(box_array); } /** \} */ diff --git a/source/blender/editors/uvedit/uvedit_ops.c b/source/blender/editors/uvedit/uvedit_ops.c index 5e2d9097abd..b65f4889347 100644 --- a/source/blender/editors/uvedit/uvedit_ops.c +++ b/source/blender/editors/uvedit/uvedit_ops.c @@ -37,6 +37,7 @@ #include "BKE_node.h" #include "DEG_depsgraph.h" +#include "DEG_depsgraph_query.h" #include "ED_image.h" #include "ED_mesh.h" @@ -113,7 +114,8 @@ bool ED_object_get_active_image(Object *ob, bNode **r_node, bNodeTree **r_ntree) { - Material *ma = BKE_object_material_get(ob, mat_nr); + Material *ma = DEG_is_evaluated_object(ob) ? BKE_object_material_get_eval(ob, mat_nr) : + BKE_object_material_get(ob, mat_nr); bNodeTree *ntree = (ma && ma->use_nodes) ? ma->nodetree : NULL; bNode *node = (ntree) ? nodeGetActiveTexture(ntree) : NULL; diff --git a/source/blender/editors/uvedit/uvedit_unwrap_ops.c b/source/blender/editors/uvedit/uvedit_unwrap_ops.c index 661a2a1b05b..ecaba3234a7 100644 --- a/source/blender/editors/uvedit/uvedit_unwrap_ops.c +++ b/source/blender/editors/uvedit/uvedit_unwrap_ops.c @@ -1050,50 +1050,6 @@ void UV_OT_minimize_stretch(wmOperatorType *ot) /** \name Pack UV Islands Operator * \{ */ -static void uvedit_pack_islands(const Scene *scene, Object *ob, BMesh *bm) -{ - const UnwrapOptions options = { - .topology_from_uvs = true, - .only_selected_faces = false, - .only_selected_uvs = true, - .fill_holes = false, - .correct_aspect = false, - }; - - bool rotate = true; - bool ignore_pinned = false; - - ParamHandle *handle = construct_param_handle(scene, ob, bm, &options, NULL); - GEO_uv_parametrizer_pack(handle, scene->toolsettings->uvcalc_margin, rotate, ignore_pinned); - GEO_uv_parametrizer_flush(handle); - GEO_uv_parametrizer_delete(handle); -} - -/** - * \warning Since this uses #ParamHandle it doesn't work with non-manifold meshes (see T82637). - * Use #ED_uvedit_pack_islands_multi for a more general solution. - * - * TODO: remove this function, in favor of #ED_uvedit_pack_islands_multi. - */ -static void uvedit_pack_islands_multi(const Scene *scene, - Object **objects, - const uint objects_len, - const UnwrapOptions *options, - bool rotate, - bool ignore_pinned) -{ - ParamHandle *handle = construct_param_handle_multi(scene, objects, objects_len, options); - GEO_uv_parametrizer_pack(handle, scene->toolsettings->uvcalc_margin, rotate, ignore_pinned); - GEO_uv_parametrizer_flush(handle); - GEO_uv_parametrizer_delete(handle); - - for (uint ob_index = 0; ob_index < objects_len; ob_index++) { - Object *obedit = objects[ob_index]; - DEG_id_tag_update(obedit->data, ID_RECALC_GEOMETRY); - WM_main_add_notifier(NC_GEOM | ND_DATA, obedit->data); - } -} - /* Packing targets. */ enum { PACK_UDIM_SRC_CLOSEST = 0, @@ -1125,7 +1081,6 @@ static int pack_islands_exec(bContext *C, wmOperator *op) } /* RNA props */ - const bool rotate = RNA_boolean_get(op->ptr, "rotate"); const int udim_source = RNA_enum_get(op->ptr, "udim_source"); if (RNA_struct_property_is_set(op->ptr, "margin")) { scene->toolsettings->uvcalc_margin = RNA_float_get(op->ptr, "margin"); @@ -1139,21 +1094,42 @@ static int pack_islands_exec(bContext *C, wmOperator *op) const bool use_udim_params = ED_uvedit_udim_params_from_image_space( sima, use_active, &udim_params); + const struct UVPackIsland_Params pack_island_params = { + .rotate = RNA_boolean_get(op->ptr, "rotate"), + .only_selected_uvs = options.only_selected_uvs, + .only_selected_faces = options.only_selected_faces, + .use_seams = !options.topology_from_uvs || options.topology_from_uvs_use_seams, + .correct_aspect = options.correct_aspect, + .ignore_pinned = false, + .margin_method = RNA_enum_get(op->ptr, "margin_method"), + .margin = RNA_float_get(op->ptr, "margin"), + }; ED_uvedit_pack_islands_multi(scene, objects, objects_len, + NULL, use_udim_params ? &udim_params : NULL, - &(struct UVPackIsland_Params){ - .rotate = rotate, - .only_selected_uvs = true, - .only_selected_faces = true, - .correct_aspect = true, - }); + &pack_island_params); MEM_freeN(objects); return OPERATOR_FINISHED; } +const EnumPropertyItem pack_margin_method[] = { + {ED_UVPACK_MARGIN_SCALED, + "SCALED", + 0, + "Scaled", + "Use scale of existing UVs to multiply margin"}, + {ED_UVPACK_MARGIN_ADD, "ADD", 0, "Add", "Just add the margin, ignoring any UV scale"}, + {ED_UVPACK_MARGIN_FRACTION, + "FRACTION", + 0, + "Fraction", + "Specify a precise fraction of final UV output"}, + {0, NULL, 0, NULL, NULL}, +}; + void UV_OT_pack_islands(wmOperatorType *ot) { static const EnumPropertyItem pack_target[] = { @@ -1180,6 +1156,8 @@ void UV_OT_pack_islands(wmOperatorType *ot) /* properties */ RNA_def_enum(ot->srna, "udim_source", pack_target, PACK_UDIM_SRC_CLOSEST, "Pack to", ""); RNA_def_boolean(ot->srna, "rotate", true, "Rotate", "Rotate islands for best fit"); + RNA_def_enum( + ot->srna, "margin_method", pack_margin_method, ED_UVPACK_MARGIN_SCALED, "Margin Method", ""); RNA_def_float_factor( ot->srna, "margin", 0.001f, 0.0f, 1.0f, "Margin", "Space between islands", 0.0f, 1.0f); } @@ -1892,12 +1870,19 @@ void ED_uvedit_live_unwrap(const Scene *scene, Object **objects, int objects_len .fill_holes = (scene->toolsettings->uvcalc_flag & UVCALC_FILLHOLES) != 0, .correct_aspect = (scene->toolsettings->uvcalc_flag & UVCALC_NO_ASPECT_CORRECT) == 0, }; - - bool rotate = true; - bool ignore_pinned = true; - uvedit_unwrap_multi(scene, objects, objects_len, &options, NULL); - uvedit_pack_islands_multi(scene, objects, objects_len, &options, rotate, ignore_pinned); + + const struct UVPackIsland_Params pack_island_params = { + .rotate = true, + .only_selected_uvs = options.only_selected_uvs, + .only_selected_faces = options.only_selected_faces, + .use_seams = !options.topology_from_uvs || options.topology_from_uvs_use_seams, + .correct_aspect = options.correct_aspect, + .ignore_pinned = true, + .margin_method = ED_UVPACK_MARGIN_SCALED, + .margin = scene->toolsettings->uvcalc_margin, + }; + ED_uvedit_pack_islands_multi(scene, objects, objects_len, NULL, NULL, &pack_island_params); } } @@ -1929,8 +1914,6 @@ static int unwrap_exec(bContext *C, wmOperator *op) .correct_aspect = RNA_boolean_get(op->ptr, "correct_aspect"), }; - bool rotate = true; - bool ignore_pinned = true; if (CTX_wm_space_image(C)) { /* Inside the UV Editor, only unwrap selected UVs. */ options.only_selected_uvs = true; @@ -2035,7 +2018,18 @@ static int unwrap_exec(bContext *C, wmOperator *op) .count_failed = 0, }; uvedit_unwrap_multi(scene, objects, objects_len, &options, &result_info); - uvedit_pack_islands_multi(scene, objects, objects_len, &options, rotate, ignore_pinned); + + const struct UVPackIsland_Params pack_island_params = { + .rotate = true, + .only_selected_uvs = options.only_selected_uvs, + .only_selected_faces = options.only_selected_faces, + .use_seams = !options.topology_from_uvs || options.topology_from_uvs_use_seams, + .correct_aspect = options.correct_aspect, + .ignore_pinned = true, + .margin_method = RNA_enum_get(op->ptr, "margin_method"), + .margin = RNA_float_get(op->ptr, "margin"), + }; + ED_uvedit_pack_islands_multi(scene, objects, objects_len, NULL, NULL, &pack_island_params); MEM_freeN(objects); @@ -2098,6 +2092,8 @@ void UV_OT_unwrap(wmOperatorType *ot) 0, "Use Subdivision Surface", "Map UVs taking vertex position after Subdivision Surface modifier has been applied"); + RNA_def_enum( + ot->srna, "margin_method", pack_margin_method, ED_UVPACK_MARGIN_SCALED, "Margin Method", ""); RNA_def_float_factor( ot->srna, "margin", 0.001f, 0.0f, 1.0f, "Margin", "Space between islands", 0.0f, 1.0f); } @@ -2118,7 +2114,6 @@ typedef struct ThickFace { static int smart_uv_project_thickface_area_cmp_fn(const void *tf_a_p, const void *tf_b_p) { - const ThickFace *tf_a = (ThickFace *)tf_a_p; const ThickFace *tf_b = (ThickFace *)tf_b_p; @@ -2412,17 +2407,17 @@ static int smart_project_exec(bContext *C, wmOperator *op) /* Depsgraph refresh functions are called here. */ const bool correct_aspect = RNA_boolean_get(op->ptr, "correct_aspect"); - ED_uvedit_pack_islands_multi(scene, - objects_changed, - object_changed_len, - NULL, - &(struct UVPackIsland_Params){ - .rotate = true, - .only_selected_uvs = only_selected_uvs, - .only_selected_faces = true, - .correct_aspect = correct_aspect, - .use_seams = true, - }); + + const struct UVPackIsland_Params params = { + .rotate = true, + .only_selected_uvs = only_selected_uvs, + .only_selected_faces = true, + .correct_aspect = correct_aspect, + .use_seams = true, + .margin_method = RNA_enum_get(op->ptr, "margin_method"), + .margin = RNA_float_get(op->ptr, "island_margin"), + }; + ED_uvedit_pack_islands_multi(scene, objects_changed, object_changed_len, NULL, NULL, ¶ms); /* #ED_uvedit_pack_islands_multi only supports `per_face_aspect = false`. */ const bool per_face_aspect = false; @@ -2464,6 +2459,8 @@ void UV_OT_smart_project(wmOperatorType *ot) DEG2RADF(89.0f)); RNA_def_property_float_default(prop, DEG2RADF(66.0f)); + RNA_def_enum( + ot->srna, "margin_method", pack_margin_method, ED_UVPACK_MARGIN_SCALED, "Margin Method", ""); RNA_def_float(ot->srna, "island_margin", 0.0f, @@ -3161,13 +3158,24 @@ void ED_uvedit_add_simple_uvs(Main *bmain, const Scene *scene, Object *ob) .calc_face_normal = true, .calc_vert_normal = true, })); - /* select all uv loops first - pack parameters needs this to make sure charts are registered */ + /* Select all UVs for cube_project. */ ED_uvedit_select_all(bm); /* A cube size of 2.0 maps [-1..1] vertex coords to [0.0..1.0] in UV coords. */ uvedit_unwrap_cube_project(scene, bm, 2.0, false, false, NULL); - /* Set the margin really quickly before the packing operation. */ - scene->toolsettings->uvcalc_margin = 0.001f; - uvedit_pack_islands(scene, ob, bm); + + /* Pack UVs. */ + const struct UVPackIsland_Params params = { + .rotate = true, + .only_selected_uvs = false, + .only_selected_faces = false, + .correct_aspect = false, + .use_seams = true, + .margin_method = ED_UVPACK_MARGIN_SCALED, + .margin = 0.001f, + }; + ED_uvedit_pack_islands_multi(scene, &ob, 1, &bm, NULL, ¶ms); + + /* Write back from BMesh to Mesh. */ BM_mesh_bm_to_me(bmain, bm, me, (&(struct BMeshToMeshParams){0})); BM_mesh_free(bm); diff --git a/source/blender/freestyle/intern/blender_interface/BlenderFileLoader.cpp b/source/blender/freestyle/intern/blender_interface/BlenderFileLoader.cpp index 19589491bc4..0d41b5a773d 100644 --- a/source/blender/freestyle/intern/blender_interface/BlenderFileLoader.cpp +++ b/source/blender/freestyle/intern/blender_interface/BlenderFileLoader.cpp @@ -73,7 +73,8 @@ NodeGroup *BlenderFileLoader::Load() break; } - if (ob->base_flag & (BASE_HOLDOUT | BASE_INDIRECT_ONLY)) { + if ((ob->base_flag & (BASE_HOLDOUT | BASE_INDIRECT_ONLY)) || + (ob->visibility_flag & OB_HOLDOUT)) { continue; } diff --git a/source/blender/freestyle/intern/python/BPy_Freestyle.cpp b/source/blender/freestyle/intern/python/BPy_Freestyle.cpp index 96887f7a83b..237f1802026 100644 --- a/source/blender/freestyle/intern/python/BPy_Freestyle.cpp +++ b/source/blender/freestyle/intern/python/BPy_Freestyle.cpp @@ -531,7 +531,7 @@ PyObject *Freestyle_Init(void) const char *const path = BKE_appdir_folder_id(BLENDER_SYSTEM_SCRIPTS, "freestyle"); if (path) { char modpath[FILE_MAX]; - BLI_join_dirfile(modpath, sizeof(modpath), path, "modules"); + BLI_path_join(modpath, sizeof(modpath), path, "modules"); PyObject *sys_path = PySys_GetObject("path"); /* borrow */ PyObject *py_modpath = PyUnicode_FromString(modpath); PyList_Append(sys_path, py_modpath); diff --git a/source/blender/geometry/intern/add_curves_on_mesh.cc b/source/blender/geometry/intern/add_curves_on_mesh.cc index 25935691bf4..a03c9b994a9 100644 --- a/source/blender/geometry/intern/add_curves_on_mesh.cc +++ b/source/blender/geometry/intern/add_curves_on_mesh.cc @@ -385,10 +385,11 @@ AddCurvesOnMeshOutputs add_curves_on_mesh(CurvesGeometry &curves, return true; } bke::GSpanAttributeWriter attribute = attributes.lookup_for_write_span(id); - const int new_elements_num = attribute.domain == ATTR_DOMAIN_POINT ? new_points_num : - new_curves_num; + /* The new elements are added at the end of the array. */ + const int old_elements_num = attribute.domain == ATTR_DOMAIN_POINT ? old_points_num : + old_curves_num; const CPPType &type = attribute.span.type(); - GMutableSpan new_data = attribute.span.take_back(new_elements_num); + GMutableSpan new_data = attribute.span.drop_front(old_elements_num); type.fill_assign_n(type.default_value(), new_data.data(), new_data.size()); attribute.finish(); return true; diff --git a/source/blender/geometry/intern/fillet_curves.cc b/source/blender/geometry/intern/fillet_curves.cc index 1bbbee6edef..2479458f88d 100644 --- a/source/blender/geometry/intern/fillet_curves.cc +++ b/source/blender/geometry/intern/fillet_curves.cc @@ -148,12 +148,14 @@ static float limit_radius(const float3 &position_prev, const float displacement_prev = radius_prev * std::tan(angle_prev / 2.0f); const float segment_length_prev = math::distance(position, position_prev); const float total_displacement_prev = displacement_prev + displacement; - const float factor_prev = std::clamp(segment_length_prev / total_displacement_prev, 0.0f, 1.0f); + const float factor_prev = std::clamp( + safe_divide(segment_length_prev, total_displacement_prev), 0.0f, 1.0f); const float displacement_next = radius_next * std::tan(angle_next / 2.0f); const float segment_length_next = math::distance(position, position_next); const float total_displacement_next = displacement_next + displacement; - const float factor_next = std::clamp(segment_length_next / total_displacement_next, 0.0f, 1.0f); + const float factor_next = std::clamp( + safe_divide(segment_length_next, total_displacement_next), 0.0f, 1.0f); return radius * std::min(factor_prev, factor_next); } diff --git a/source/blender/geometry/intern/realize_instances.cc b/source/blender/geometry/intern/realize_instances.cc index 7b3c307cf37..c649bde06ca 100644 --- a/source/blender/geometry/intern/realize_instances.cc +++ b/source/blender/geometry/intern/realize_instances.cc @@ -17,6 +17,7 @@ #include "BKE_curves.hh" #include "BKE_deform.h" #include "BKE_geometry_set_instances.hh" +#include "BKE_instances.hh" #include "BKE_material.h" #include "BKE_mesh.h" #include "BKE_pointcloud.h" @@ -30,6 +31,8 @@ using blender::bke::AttributeMetaData; using blender::bke::custom_data_type_to_cpp_type; using blender::bke::CustomDataAttributes; using blender::bke::GSpanAttributeWriter; +using blender::bke::InstanceReference; +using blender::bke::Instances; using blender::bke::object_get_evaluated_geometry_set; using blender::bke::SpanAttributeWriter; @@ -370,11 +373,11 @@ static void gather_realize_tasks_recursive(GatherTasksInfo &gather_info, */ static Vector<std::pair<int, GSpan>> prepare_attribute_fallbacks( GatherTasksInfo &gather_info, - const InstancesComponent &instances_component, + const Instances &instances, const OrderedAttributes &ordered_attributes) { Vector<std::pair<int, GSpan>> attributes_to_override; - const CustomDataAttributes &attributes = instances_component.instance_attributes(); + const CustomDataAttributes &attributes = instances.custom_data_attributes(); attributes.foreach_attribute( [&](const AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) { const int attribute_index = ordered_attributes.ids.index_of_try(attribute_id); @@ -394,7 +397,7 @@ static Vector<std::pair<int, GSpan>> prepare_attribute_fallbacks( } /* Convert the attribute on the instances component to the expected attribute type. */ std::unique_ptr<GArray<>> temporary_array = std::make_unique<GArray<>>( - to_type, instances_component.instances_num()); + to_type, instances.instances_num()); conversions.convert_to_initialized_n(span, temporary_array->as_mutable_span()); span = temporary_array->as_span(); gather_info.r_temporary_arrays.append(std::move(temporary_array)); @@ -450,17 +453,17 @@ static void foreach_geometry_in_reference( } static void gather_realize_tasks_for_instances(GatherTasksInfo &gather_info, - const InstancesComponent &instances_component, + const Instances &instances, const float4x4 &base_transform, const InstanceContext &base_instance_context) { - const Span<InstanceReference> references = instances_component.references(); - const Span<int> handles = instances_component.instance_reference_handles(); - const Span<float4x4> transforms = instances_component.instance_transforms(); + const Span<InstanceReference> references = instances.references(); + const Span<int> handles = instances.reference_handles(); + const Span<float4x4> transforms = instances.transforms(); Span<int> stored_instance_ids; if (gather_info.create_id_attribute_on_any_component) { - std::optional<GSpan> ids = instances_component.instance_attributes().get_for_read("id"); + std::optional<GSpan> ids = instances.custom_data_attributes().get_for_read("id"); if (ids.has_value()) { stored_instance_ids = ids->typed<int>(); } @@ -469,11 +472,11 @@ static void gather_realize_tasks_for_instances(GatherTasksInfo &gather_info, /* Prepare attribute fallbacks. */ InstanceContext instance_context = base_instance_context; Vector<std::pair<int, GSpan>> pointcloud_attributes_to_override = prepare_attribute_fallbacks( - gather_info, instances_component, gather_info.pointclouds.attributes); + gather_info, instances, gather_info.pointclouds.attributes); Vector<std::pair<int, GSpan>> mesh_attributes_to_override = prepare_attribute_fallbacks( - gather_info, instances_component, gather_info.meshes.attributes); + gather_info, instances, gather_info.meshes.attributes); Vector<std::pair<int, GSpan>> curve_attributes_to_override = prepare_attribute_fallbacks( - gather_info, instances_component, gather_info.curves.attributes); + gather_info, instances, gather_info.curves.attributes); for (const int i : transforms.index_range()) { const int handle = handles[i]; @@ -584,8 +587,11 @@ static void gather_realize_tasks_recursive(GatherTasksInfo &gather_info, case GEO_COMPONENT_TYPE_INSTANCES: { const InstancesComponent &instances_component = *static_cast<const InstancesComponent *>( component); - gather_realize_tasks_for_instances( - gather_info, instances_component, base_transform, base_instance_context); + const Instances *instances = instances_component.get_for_read(); + if (instances != nullptr && instances->instances_num() > 0) { + gather_realize_tasks_for_instances( + gather_info, *instances, base_transform, base_instance_context); + } break; } case GEO_COMPONENT_TYPE_VOLUME: { @@ -645,8 +651,7 @@ static void gather_pointclouds_to_realize(const GeometrySet &geometry_set, r_pointclouds.add(pointcloud); } } - if (const InstancesComponent *instances = - geometry_set.get_component_for_read<InstancesComponent>()) { + if (const Instances *instances = geometry_set.get_instances_for_read()) { instances->foreach_referenced_geometry([&](const GeometrySet &instance_geometry_set) { gather_pointclouds_to_realize(instance_geometry_set, r_pointclouds); }); @@ -827,8 +832,7 @@ static void gather_meshes_to_realize(const GeometrySet &geometry_set, r_meshes.add(mesh); } } - if (const InstancesComponent *instances = - geometry_set.get_component_for_read<InstancesComponent>()) { + if (const Instances *instances = geometry_set.get_instances_for_read()) { instances->foreach_referenced_geometry([&](const GeometrySet &instance_geometry_set) { gather_meshes_to_realize(instance_geometry_set, r_meshes); }); @@ -855,6 +859,7 @@ static AllMeshesInfo preprocess_meshes(const GeometrySet &geometry_set, } } } + info.create_material_index_attribute |= info.materials.size() > 1; info.realize_info.reinitialize(info.order.size()); for (const int mesh_index : info.realize_info.index_range()) { MeshRealizeInfo &mesh_info = info.realize_info[mesh_index]; @@ -1148,8 +1153,7 @@ static void gather_curves_to_realize(const GeometrySet &geometry_set, r_curves.add(curves); } } - if (const InstancesComponent *instances = - geometry_set.get_component_for_read<InstancesComponent>()) { + if (const Instances *instances = geometry_set.get_instances_for_read()) { instances->foreach_referenced_geometry([&](const GeometrySet &instance_geometry_set) { gather_curves_to_realize(instance_geometry_set, r_curves); }); @@ -1415,9 +1419,8 @@ static void execute_realize_curve_tasks(const RealizeInstancesOptions &options, static void remove_id_attribute_from_instances(GeometrySet &geometry_set) { geometry_set.modify_geometry_sets([&](GeometrySet &sub_geometry) { - if (sub_geometry.has<InstancesComponent>()) { - InstancesComponent &component = sub_geometry.get_component_for_write<InstancesComponent>(); - component.instance_attributes().remove("id"); + if (Instances *instances = sub_geometry.get_instances_for_write()) { + instances->custom_data_attributes().remove("id"); } }); } diff --git a/source/blender/geometry/intern/resample_curves.cc b/source/blender/geometry/intern/resample_curves.cc index a7f6ac16f8d..3be850ec097 100644 --- a/source/blender/geometry/intern/resample_curves.cc +++ b/source/blender/geometry/intern/resample_curves.cc @@ -139,6 +139,9 @@ static void gather_point_attributes_to_interpolate( if (meta_data.domain != ATTR_DOMAIN_POINT) { return true; } + if (meta_data.data_type == CD_PROP_STRING) { + return true; + } if (!interpolate_attribute_to_curves(id, dst_curves.curve_type_counts())) { return true; } diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpenciloutline.c b/source/blender/gpencil_modifiers/intern/MOD_gpenciloutline.c index 9a0ee4d9d92..387e3c2d5ce 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpenciloutline.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpenciloutline.c @@ -209,6 +209,7 @@ static void generateStrokes(GpencilModifierData *md, Depsgraph *depsgraph, Objec BKE_gpencil_layer_transform_matrix_get(depsgraph, ob, gpl, diff_mat); LISTBASE_FOREACH_MUTABLE (bGPDstroke *, gps, &gpf->strokes) { + gps->flag &= ~GP_STROKE_TAG; convert_stroke(md, ob, gpl, gpf, gps, viewmat, diff_mat); } } diff --git a/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c b/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c index a8cba959689..c1e71bde254 100644 --- a/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c +++ b/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c @@ -1953,8 +1953,6 @@ static LineartEdgeNeighbor *lineart_build_edge_neighbor(Mesh *me, int total_edge LineartEdgeNeighbor *edge_nabr = MEM_mallocN(sizeof(LineartEdgeNeighbor) * total_edges, "LineartEdgeNeighbor arr"); - MLoopTri *mlooptri = me->runtime.looptris.array; - TaskParallelSettings en_settings; BLI_parallel_range_settings_defaults(&en_settings); /* Set the minimum amount of edges a thread has to process. */ @@ -1963,7 +1961,7 @@ static LineartEdgeNeighbor *lineart_build_edge_neighbor(Mesh *me, int total_edge EdgeNeighborData en_data; en_data.adj_e = adj_e; en_data.edge_nabr = edge_nabr; - en_data.mlooptri = mlooptri; + en_data.mlooptri = BKE_mesh_runtime_looptri_ensure(me); en_data.mloop = BKE_mesh_loops(me); BLI_task_parallel_range(0, total_edges, &en_data, lineart_edge_neighbor_init_task, &en_settings); diff --git a/source/blender/gpu/CMakeLists.txt b/source/blender/gpu/CMakeLists.txt index e2285a3fd3e..58b1cd0a50b 100644 --- a/source/blender/gpu/CMakeLists.txt +++ b/source/blender/gpu/CMakeLists.txt @@ -186,9 +186,11 @@ set(OPENGL_SRC set(METAL_SRC metal/mtl_backend.mm + metal/mtl_batch.mm metal/mtl_command_buffer.mm metal/mtl_context.mm metal/mtl_debug.mm + metal/mtl_drawlist.mm metal/mtl_framebuffer.mm metal/mtl_immediate.mm metal/mtl_index_buffer.mm @@ -280,6 +282,8 @@ set(GLSL_SRC shaders/gpu_shader_2D_image_vert.glsl shaders/gpu_shader_2D_image_rect_vert.glsl shaders/gpu_shader_2D_image_multi_rect_vert.glsl + shaders/gpu_shader_icon_frag.glsl + shaders/gpu_shader_icon_vert.glsl shaders/gpu_shader_image_frag.glsl shaders/gpu_shader_image_desaturate_frag.glsl shaders/gpu_shader_image_overlays_merge_frag.glsl @@ -332,6 +336,7 @@ set(GLSL_SRC shaders/compositor/compositor_alpha_crop.glsl shaders/compositor/compositor_bilateral_blur.glsl shaders/compositor/compositor_blur.glsl + shaders/compositor/compositor_blur_variable_size.glsl shaders/compositor/compositor_bokeh_image.glsl shaders/compositor/compositor_box_mask.glsl shaders/compositor/compositor_convert.glsl @@ -346,6 +351,8 @@ set(GLSL_SRC shaders/compositor/compositor_morphological_distance_feather.glsl shaders/compositor/compositor_morphological_distance_threshold.glsl shaders/compositor/compositor_morphological_step.glsl + shaders/compositor/compositor_normalize.glsl + shaders/compositor/compositor_parallel_reduction.glsl shaders/compositor/compositor_projector_lens_distortion.glsl shaders/compositor/compositor_realize_on_domain.glsl shaders/compositor/compositor_screen_lens_distortion.glsl @@ -353,6 +360,8 @@ set(GLSL_SRC shaders/compositor/compositor_split_viewer.glsl shaders/compositor/compositor_symmetric_blur.glsl shaders/compositor/compositor_symmetric_separable_blur.glsl + shaders/compositor/compositor_tone_map_photoreceptor.glsl + shaders/compositor/compositor_tone_map_simple.glsl shaders/compositor/library/gpu_shader_compositor_alpha_over.glsl shaders/compositor/library/gpu_shader_compositor_blur_common.glsl @@ -602,6 +611,7 @@ set(SRC_SHADER_CREATE_INFOS shaders/infos/gpu_shader_3D_smooth_color_info.hh shaders/infos/gpu_shader_3D_uniform_color_info.hh shaders/infos/gpu_shader_gpencil_stroke_info.hh + shaders/infos/gpu_shader_icon_info.hh shaders/infos/gpu_shader_instance_varying_color_varying_size_info.hh shaders/infos/gpu_shader_keyframe_shape_info.hh shaders/infos/gpu_shader_line_dashed_uniform_color_info.hh @@ -612,6 +622,7 @@ set(SRC_SHADER_CREATE_INFOS shaders/compositor/infos/compositor_alpha_crop_info.hh shaders/compositor/infos/compositor_bilateral_blur_info.hh shaders/compositor/infos/compositor_blur_info.hh + shaders/compositor/infos/compositor_blur_variable_size_info.hh shaders/compositor/infos/compositor_bokeh_image_info.hh shaders/compositor/infos/compositor_box_mask_info.hh shaders/compositor/infos/compositor_convert_info.hh @@ -626,6 +637,8 @@ set(SRC_SHADER_CREATE_INFOS shaders/compositor/infos/compositor_morphological_distance_info.hh shaders/compositor/infos/compositor_morphological_distance_threshold_info.hh shaders/compositor/infos/compositor_morphological_step_info.hh + shaders/compositor/infos/compositor_normalize_info.hh + shaders/compositor/infos/compositor_parallel_reduction_info.hh shaders/compositor/infos/compositor_projector_lens_distortion_info.hh shaders/compositor/infos/compositor_realize_on_domain_info.hh shaders/compositor/infos/compositor_screen_lens_distortion_info.hh @@ -633,6 +646,8 @@ set(SRC_SHADER_CREATE_INFOS shaders/compositor/infos/compositor_split_viewer_info.hh shaders/compositor/infos/compositor_symmetric_blur_info.hh shaders/compositor/infos/compositor_symmetric_separable_blur_info.hh + shaders/compositor/infos/compositor_tone_map_photoreceptor_info.hh + shaders/compositor/infos/compositor_tone_map_simple_info.hh ) set(SRC_SHADER_CREATE_INFOS_MTL diff --git a/source/blender/gpu/GPU_context.h b/source/blender/gpu/GPU_context.h index b59ea9e55d2..ac82774039a 100644 --- a/source/blender/gpu/GPU_context.h +++ b/source/blender/gpu/GPU_context.h @@ -21,6 +21,8 @@ extern "C" { * automatically initializes the back-end, and #GPU_context_discard frees it when there * are no more contexts. */ bool GPU_backend_supported(void); +void GPU_backend_type_selection_set(const eGPUBackendType backend); +eGPUBackendType GPU_backend_type_selection_get(void); eGPUBackendType GPU_backend_get_type(void); /** Opaque type hiding blender::gpu::Context. */ diff --git a/source/blender/gpu/GPU_shader.h b/source/blender/gpu/GPU_shader.h index 3f35db42eb9..1148207fc57 100644 --- a/source/blender/gpu/GPU_shader.h +++ b/source/blender/gpu/GPU_shader.h @@ -209,6 +209,10 @@ typedef enum eGPUBuiltinShader { GPU_SHADER_KEYFRAME_SHAPE, GPU_SHADER_SIMPLE_LIGHTING, /** + * Draw an icon, leaving a semi-transparent rectangle on top of the icon. + */ + GPU_SHADER_ICON, + /** * Take a 2D position and color for each vertex with linear interpolation in window space. * * \param color: in vec4 diff --git a/source/blender/gpu/intern/gpu_context.cc b/source/blender/gpu/intern/gpu_context.cc index 48d7b2019c5..f6b88c4231c 100644 --- a/source/blender/gpu/intern/gpu_context.cc +++ b/source/blender/gpu/intern/gpu_context.cc @@ -223,9 +223,19 @@ void GPU_render_step() /* NOTE: To enable Metal API, we need to temporarily change this to `GPU_BACKEND_METAL`. * Until a global switch is added, Metal also needs to be enabled in GHOST_ContextCGL: * `m_useMetalForRendering = true`. */ -static const eGPUBackendType g_backend_type = GPU_BACKEND_OPENGL; +static eGPUBackendType g_backend_type = GPU_BACKEND_OPENGL; static GPUBackend *g_backend = nullptr; +void GPU_backend_type_selection_set(const eGPUBackendType backend) +{ + g_backend_type = backend; +} + +eGPUBackendType GPU_backend_type_selection_get() +{ + return g_backend_type; +} + bool GPU_backend_supported(void) { switch (g_backend_type) { diff --git a/source/blender/gpu/intern/gpu_framebuffer_private.hh b/source/blender/gpu/intern/gpu_framebuffer_private.hh index 76e816e7f65..5afcc102e44 100644 --- a/source/blender/gpu/intern/gpu_framebuffer_private.hh +++ b/source/blender/gpu/intern/gpu_framebuffer_private.hh @@ -95,11 +95,6 @@ class FrameBuffer { #endif public: - /* Reference of a pointer that needs to be cleaned when deallocating the frame-buffer. - * Points to #BPyGPUFrameBuffer::fb */ - void **ref = nullptr; - - public: FrameBuffer(const char *name); virtual ~FrameBuffer(); diff --git a/source/blender/gpu/intern/gpu_immediate.cc b/source/blender/gpu/intern/gpu_immediate.cc index 3b4accf9cc5..81c0a65bb7c 100644 --- a/source/blender/gpu/intern/gpu_immediate.cc +++ b/source/blender/gpu/intern/gpu_immediate.cc @@ -45,7 +45,7 @@ void immBindShader(GPUShader *shader) BLI_assert(imm->shader == nullptr); imm->shader = shader; - imm->builtin_shader_bound = GPU_SHADER_TEXT; /* Default value. */ + imm->builtin_shader_bound = std::nullopt; if (!imm->vertex_format.packed) { VertexFormat_pack(&imm->vertex_format); @@ -125,9 +125,12 @@ static void wide_line_workaround_start(GPUPrimType prim_type) /* No need to change the shader. */ return; } + if (!imm->builtin_shader_bound) { + return; + } eGPUBuiltinShader polyline_sh; - switch (imm->builtin_shader_bound) { + switch (*imm->builtin_shader_bound) { case GPU_SHADER_3D_CLIPPED_UNIFORM_COLOR: polyline_sh = GPU_SHADER_3D_POLYLINE_CLIPPED_UNIFORM_COLOR; break; @@ -180,8 +183,8 @@ static void wide_line_workaround_end() } immUnbindProgram(); - immBindBuiltinProgram(imm->prev_builtin_shader); - imm->prev_builtin_shader = GPU_SHADER_TEXT; + immBindBuiltinProgram(*imm->prev_builtin_shader); + imm->prev_builtin_shader = std::nullopt; } } diff --git a/source/blender/gpu/intern/gpu_immediate_private.hh b/source/blender/gpu/intern/gpu_immediate_private.hh index 74ebbdc7ae3..c4e11e7082b 100644 --- a/source/blender/gpu/intern/gpu_immediate_private.hh +++ b/source/blender/gpu/intern/gpu_immediate_private.hh @@ -9,6 +9,8 @@ #pragma once +#include <optional> + #include "GPU_batch.h" #include "GPU_primitive.h" #include "GPU_shader.h" @@ -42,9 +44,9 @@ class Immediate { /** Wide Line workaround. */ /** Previously bound shader to restore after drawing. */ - eGPUBuiltinShader prev_builtin_shader = GPU_SHADER_TEXT; - /** Builtin shader index. Used to test if the workaround can be done. */ - eGPUBuiltinShader builtin_shader_bound = GPU_SHADER_TEXT; + std::optional<eGPUBuiltinShader> prev_builtin_shader; + /** Builtin shader index. Used to test if the line width workaround can be done. */ + std::optional<eGPUBuiltinShader> builtin_shader_bound; /** Uniform color: Kept here to update the wide-line shader just before #immBegin. */ float uniform_color[4]; diff --git a/source/blender/gpu/intern/gpu_shader_builder.cc b/source/blender/gpu/intern/gpu_shader_builder.cc index 3aa2963ecd0..abb45ca074a 100644 --- a/source/blender/gpu/intern/gpu_shader_builder.cc +++ b/source/blender/gpu/intern/gpu_shader_builder.cc @@ -15,6 +15,8 @@ #include "GPU_init_exit.h" #include "gpu_shader_create_info_private.hh" +#include "BLI_vector.hh" + #include "CLG_log.h" namespace blender::gpu::shader_builder { @@ -41,6 +43,22 @@ void ShaderBuilder::init() CLG_init(); GHOST_GLSettings glSettings = {0}; + switch (GPU_backend_type_selection_get()) { + case GPU_BACKEND_OPENGL: + glSettings.context_type = GHOST_kDrawingContextTypeOpenGL; + break; + +#ifdef WITH_METAL_BACKEND + case GPU_BACKEND_METAL: + glSettings.context_type = GHOST_kDrawingContextTypeMetal; + break; +#endif + + default: + BLI_assert_unreachable(); + break; + } + ghost_system_ = GHOST_CreateSystem(); ghost_context_ = GHOST_CreateOpenGLContext(ghost_system_, glSettings); GHOST_ActivateOpenGLContext(ghost_context_); @@ -73,13 +91,32 @@ int main(int argc, const char *argv[]) int exit_code = 0; - blender::gpu::shader_builder::ShaderBuilder builder; - builder.init(); - if (!builder.bake_create_infos()) { - exit_code = 1; + struct NamedBackend { + std::string name; + eGPUBackendType backend; + }; + + blender::Vector<NamedBackend> backends_to_validate; + backends_to_validate.append({"OpenGL", GPU_BACKEND_OPENGL}); +#ifdef WITH_METAL_BACKEND + backends_to_validate.append({"Metal", GPU_BACKEND_METAL}); +#endif + for (NamedBackend &backend : backends_to_validate) { + GPU_backend_type_selection_set(backend.backend); + if (!GPU_backend_supported()) { + printf("%s isn't supported on this platform. Shader compilation is skipped\n", + backend.name.c_str()); + continue; + } + blender::gpu::shader_builder::ShaderBuilder builder; + builder.init(); + if (!builder.bake_create_infos()) { + printf("Shader compilation failed for %s backend\n", backend.name.c_str()); + exit_code = 1; + } + builder.exit(); } - builder.exit(); - exit(exit_code); + exit(exit_code); return exit_code; } diff --git a/source/blender/gpu/intern/gpu_shader_builder_stubs.cc b/source/blender/gpu/intern/gpu_shader_builder_stubs.cc index 7a06ede5c6d..65bda7ba858 100644 --- a/source/blender/gpu/intern/gpu_shader_builder_stubs.cc +++ b/source/blender/gpu/intern/gpu_shader_builder_stubs.cc @@ -46,6 +46,15 @@ void IMB_freeImBuf(ImBuf * /*ibuf*/) BLI_assert_unreachable(); } +struct ImBuf *IMB_allocImBuf(unsigned int /*x*/, + unsigned int /*y*/, + unsigned char /*planes*/, + unsigned int /*flags*/) +{ + BLI_assert_unreachable(); + return nullptr; +} + /** \} */ /* -------------------------------------------------------------------- */ diff --git a/source/blender/gpu/intern/gpu_shader_builtin.c b/source/blender/gpu/intern/gpu_shader_builtin.c index 8a6586e06f6..470643ba863 100644 --- a/source/blender/gpu/intern/gpu_shader_builtin.c +++ b/source/blender/gpu/intern/gpu_shader_builtin.c @@ -153,6 +153,11 @@ static const GPUShaderStages builtin_shader_stages[GPU_SHADER_BUILTIN_LEN] = { .create_info = "gpu_shader_2D_diag_stripes", }, + [GPU_SHADER_ICON] = + { + .name = "GPU_SHADER_ICON", + .create_info = "gpu_shader_icon", + }, [GPU_SHADER_2D_IMAGE_OVERLAYS_MERGE] = { .name = "GPU_SHADER_2D_IMAGE_OVERLAYS_MERGE", diff --git a/source/blender/gpu/intern/gpu_texture_private.hh b/source/blender/gpu/intern/gpu_texture_private.hh index b96a9b870e5..124b1751b96 100644 --- a/source/blender/gpu/intern/gpu_texture_private.hh +++ b/source/blender/gpu/intern/gpu_texture_private.hh @@ -431,15 +431,16 @@ inline bool validate_data_format(eGPUTextureFormat tex_format, eGPUDataFormat da case GPU_DEPTH_COMPONENT24: case GPU_DEPTH_COMPONENT16: case GPU_DEPTH_COMPONENT32F: - return data_format == GPU_DATA_FLOAT; + return ELEM(data_format, GPU_DATA_FLOAT, GPU_DATA_UINT); case GPU_DEPTH24_STENCIL8: case GPU_DEPTH32F_STENCIL8: - return data_format == GPU_DATA_UINT_24_8; + return ELEM(data_format, GPU_DATA_UINT_24_8, GPU_DATA_UINT); case GPU_R8UI: case GPU_R16UI: case GPU_RG16UI: case GPU_R32UI: return data_format == GPU_DATA_UINT; + case GPU_R32I: case GPU_RG16I: case GPU_R16I: return data_format == GPU_DATA_INT; @@ -453,6 +454,8 @@ inline bool validate_data_format(eGPUTextureFormat tex_format, eGPUDataFormat da return ELEM(data_format, GPU_DATA_2_10_10_10_REV, GPU_DATA_FLOAT); case GPU_R11F_G11F_B10F: return ELEM(data_format, GPU_DATA_10_11_11_REV, GPU_DATA_FLOAT); + case GPU_RGBA16F: + return ELEM(data_format, GPU_DATA_HALF_FLOAT, GPU_DATA_FLOAT); default: return data_format == GPU_DATA_FLOAT; } @@ -585,7 +588,7 @@ inline eGPUFrameBufferBits to_framebuffer_bits(eGPUTextureFormat tex_format) static inline eGPUTextureFormat to_texture_format(const GPUVertFormat *format) { - if (format->attr_len > 1 || format->attr_len == 0) { + if (format->attr_len == 0) { BLI_assert_msg(0, "Incorrect vertex format for buffer texture"); return GPU_DEPTH_COMPONENT24; } diff --git a/source/blender/gpu/intern/gpu_vertex_format.cc b/source/blender/gpu/intern/gpu_vertex_format.cc index b30e3c358c8..76d95ac1b55 100644 --- a/source/blender/gpu/intern/gpu_vertex_format.cc +++ b/source/blender/gpu/intern/gpu_vertex_format.cc @@ -361,8 +361,12 @@ void VertexFormat_texture_buffer_pack(GPUVertFormat *format) * minimum per-vertex stride, which mandates 4-byte alignment in Metal. * This additional alignment padding caused smaller data types, e.g. U16, * to mis-align. */ - BLI_assert_msg(format->attr_len == 1, - "Texture buffer mode should only use a single vertex attribute."); + for (int i = 0; i < format->attr_len; i++) { + /* The buffer texture setup uses the first attribute for type and size. + * Make sure all attributes use the same size. */ + BLI_assert_msg(format->attrs[i].size == format->attrs[0].size, + "Texture buffer mode should only use a attributes with the same size."); + } /* Pack vertex format without minimum stride, as this is not required by texture buffers. */ VertexFormat_pack_impl(format, 1); diff --git a/source/blender/gpu/intern/gpu_viewport.c b/source/blender/gpu/intern/gpu_viewport.c index 71bdf9e336b..e267d5a2f12 100644 --- a/source/blender/gpu/intern/gpu_viewport.c +++ b/source/blender/gpu/intern/gpu_viewport.c @@ -147,6 +147,10 @@ static void gpu_viewport_textures_create(GPUViewport *viewport) if (viewport->depth_tx == NULL) { viewport->depth_tx = GPU_texture_create_2d( "dtxl_depth", UNPACK2(size), 1, GPU_DEPTH24_STENCIL8, NULL); + if (GPU_clear_viewport_workaround()) { + static int depth_clear = 0; + GPU_texture_clear(viewport->depth_tx, GPU_DATA_UINT_24_8, &depth_clear); + } } if (!viewport->depth_tx || !viewport->color_render_tx[0] || !viewport->color_overlay_tx[0]) { diff --git a/source/blender/gpu/metal/mtl_backend.mm b/source/blender/gpu/metal/mtl_backend.mm index 2ca1fd3f3d0..240951c1ebd 100644 --- a/source/blender/gpu/metal/mtl_backend.mm +++ b/source/blender/gpu/metal/mtl_backend.mm @@ -47,13 +47,11 @@ Context *MTLBackend::context_alloc(void *ghost_window, void *ghost_context) Batch *MTLBackend::batch_alloc() { - /* TODO(Metal): Full MTLBatch implementation. */ return new MTLBatch(); }; DrawList *MTLBackend::drawlist_alloc(int list_length) { - /* TODO(Metal): Full MTLDrawList implementation. */ return new MTLDrawList(list_length); }; @@ -420,6 +418,7 @@ void MTLBackend::capabilities_init(MTLContext *ctx) GCaps.depth_blitting_workaround = false; GCaps.use_main_context_workaround = false; GCaps.broken_amd_driver = false; + GCaps.clear_viewport_workaround = true; /* Metal related workarounds. */ /* Minimum per-vertex stride is 4 bytes in Metal. diff --git a/source/blender/gpu/metal/mtl_batch.hh b/source/blender/gpu/metal/mtl_batch.hh index 236367bf5a4..9e179e662b5 100644 --- a/source/blender/gpu/metal/mtl_batch.hh +++ b/source/blender/gpu/metal/mtl_batch.hh @@ -10,31 +10,126 @@ #pragma once #include "MEM_guardedalloc.h" - #include "gpu_batch_private.hh" +#include "mtl_index_buffer.hh" +#include "mtl_primitive.hh" +#include "mtl_shader.hh" +#include "mtl_vertex_buffer.hh" + +namespace blender::gpu { + +class MTLContext; +class MTLShaderInterface; + +#define GPU_VAO_STATIC_LEN 64 -namespace blender { -namespace gpu { +struct VertexBufferID { + uint32_t id : 16; + uint32_t is_instance : 15; + uint32_t used : 1; +}; -/* Pass-through MTLBatch. TODO(Metal): Implement. */ class MTLBatch : public Batch { + + /* Vertex Bind-state Caching for a given shader interface used with the Batch. */ + struct VertexDescriptorShaderInterfacePair { + MTLVertexDescriptor vertex_descriptor{}; + const ShaderInterface *interface = nullptr; + uint16_t attr_mask{}; + int num_buffers{}; + VertexBufferID bufferIds[GPU_BATCH_VBO_MAX_LEN] = {}; + /* Cache life index compares a cache entry with the active MTLBatch state. + * This is initially set to the cache life index of MTLBatch. If the batch has been modified, + * this index is incremented to cheaply invalidate existing cache entries. */ + uint32_t cache_life_index = 0; + }; + + class MTLVertexDescriptorCache { + + private: + MTLBatch *batch_; + + VertexDescriptorShaderInterfacePair cache_[GPU_VAO_STATIC_LEN] = {}; + MTLContext *cache_context_ = nullptr; + uint32_t cache_life_index_ = 0; + + public: + MTLVertexDescriptorCache(MTLBatch *batch) : batch_(batch){}; + VertexDescriptorShaderInterfacePair *find(const ShaderInterface *interface); + bool insert(VertexDescriptorShaderInterfacePair &data); + + private: + void vertex_descriptor_cache_init(MTLContext *ctx); + void vertex_descriptor_cache_clear(); + void vertex_descriptor_cache_ensure(); + }; + + private: + MTLShader *active_shader_ = nullptr; + bool shader_in_use_ = false; + MTLVertexDescriptorCache vao_cache = {this}; + + /* Topology emulation. */ + gpu::MTLBuffer *emulated_topology_buffer_ = nullptr; + GPUPrimType emulated_topology_type_; + uint32_t topology_buffer_input_v_count_ = 0; + uint32_t topology_buffer_output_v_count_ = 0; + public: - void draw(int v_first, int v_count, int i_first, int i_count) override - { - } + MTLBatch(){}; + ~MTLBatch(){}; + void draw(int v_first, int v_count, int i_first, int i_count) override; void draw_indirect(GPUStorageBuf *indirect_buf, intptr_t offset) override { + /* TODO(Metal): Support indirect draw commands. */ } - void multi_draw_indirect(GPUStorageBuf *indirect_buf, int count, intptr_t offset, intptr_t stride) override { + /* TODO(Metal): Support indirect draw commands. */ + } + + /* Returns an initialized RenderComandEncoder for drawing if all is good. + * Otherwise, nil. */ + id<MTLRenderCommandEncoder> bind(uint v_first, uint v_count, uint i_first, uint i_count); + void unbind(); + + /* Convenience getters. */ + MTLIndexBuf *elem_() const + { + return static_cast<MTLIndexBuf *>(unwrap(elem)); + } + MTLVertBuf *verts_(const int index) const + { + return static_cast<MTLVertBuf *>(unwrap(verts[index])); } + MTLVertBuf *inst_(const int index) const + { + return static_cast<MTLVertBuf *>(unwrap(inst[index])); + } + MTLShader *active_shader_get() const + { + return active_shader_; + } + + private: + void shader_bind(); + void draw_advanced(int v_first, int v_count, int i_first, int i_count); + int prepare_vertex_binding(MTLVertBuf *verts, + MTLRenderPipelineStateDescriptor &desc, + const MTLShaderInterface *interface, + uint16_t &attr_mask, + bool instanced); + + id<MTLBuffer> get_emulated_toplogy_buffer(GPUPrimType &in_out_prim_type, uint32_t &v_count); + + void prepare_vertex_descriptor_and_bindings( + MTLVertBuf **buffers, int &num_buffers, int v_first, int v_count, int i_first, int i_count); + MEM_CXX_CLASS_ALLOC_FUNCS("MTLBatch"); }; -} // namespace gpu -} // namespace blender +} // namespace blender::gpu diff --git a/source/blender/gpu/metal/mtl_batch.mm b/source/blender/gpu/metal/mtl_batch.mm new file mode 100644 index 00000000000..988fb9b793b --- /dev/null +++ b/source/blender/gpu/metal/mtl_batch.mm @@ -0,0 +1,998 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup gpu + * + * Metal implementation of GPUBatch. + */ + +#include "BLI_assert.h" +#include "BLI_span.hh" + +#include "BKE_global.h" + +#include "GPU_common.h" +#include "gpu_batch_private.hh" +#include "gpu_shader_private.hh" + +#include "mtl_batch.hh" +#include "mtl_context.hh" +#include "mtl_debug.hh" +#include "mtl_index_buffer.hh" +#include "mtl_shader.hh" +#include "mtl_vertex_buffer.hh" + +#include <string> + +namespace blender::gpu { + +/* -------------------------------------------------------------------- */ +/** \name Creation & Deletion + * \{ */ +void MTLBatch::draw(int v_first, int v_count, int i_first, int i_count) +{ + if (this->flag & GPU_BATCH_INVALID) { + this->shader_in_use_ = false; + } + this->draw_advanced(v_first, v_count, i_first, i_count); +} + +void MTLBatch::shader_bind() +{ + if (active_shader_ && active_shader_->is_valid()) { + active_shader_->bind(); + shader_in_use_ = true; + } +} + +void MTLBatch::MTLVertexDescriptorCache::vertex_descriptor_cache_init(MTLContext *ctx) +{ + BLI_assert(ctx != nullptr); + this->vertex_descriptor_cache_clear(); + cache_context_ = ctx; +} + +void MTLBatch::MTLVertexDescriptorCache::vertex_descriptor_cache_clear() +{ + cache_life_index_++; + cache_context_ = nullptr; +} + +void MTLBatch::MTLVertexDescriptorCache::vertex_descriptor_cache_ensure() +{ + if (this->cache_context_ != nullptr) { + + /* Invalidate vertex descriptor bindings cache if batch has changed. */ + if (batch_->flag & GPU_BATCH_DIRTY) { + batch_->flag &= ~GPU_BATCH_DIRTY; + this->vertex_descriptor_cache_clear(); + } + } + + /* Initialize cache if not ready. */ + if (cache_context_ == nullptr) { + this->vertex_descriptor_cache_init(MTLContext::get()); + } +} + +MTLBatch::VertexDescriptorShaderInterfacePair *MTLBatch::MTLVertexDescriptorCache::find( + const ShaderInterface *interface) +{ + this->vertex_descriptor_cache_ensure(); + for (int i = 0; i < GPU_VAO_STATIC_LEN; ++i) { + if (cache_[i].interface == interface && cache_[i].cache_life_index == cache_life_index_) { + return &cache_[i]; + } + } + return nullptr; +} + +bool MTLBatch::MTLVertexDescriptorCache::insert( + MTLBatch::VertexDescriptorShaderInterfacePair &data) +{ + vertex_descriptor_cache_ensure(); + for (int i = 0; i < GPU_VAO_STATIC_LEN; ++i) { + if (cache_[i].interface == nullptr || cache_[i].cache_life_index != cache_life_index_) { + cache_[i] = data; + cache_[i].cache_life_index = cache_life_index_; + return true; + } + } + return false; +} + +int MTLBatch::prepare_vertex_binding(MTLVertBuf *verts, + MTLRenderPipelineStateDescriptor &desc, + const MTLShaderInterface *interface, + uint16_t &attr_mask, + bool instanced) +{ + + const GPUVertFormat *format = &verts->format; + /* Whether the current vertex buffer has been added to the buffer layout descriptor. */ + bool buffer_added = false; + /* Per-vertex stride of current vertex buffer. */ + int buffer_stride = format->stride; + /* Buffer binding index of the vertex buffer once added to the buffer layout descriptor. */ + int buffer_index = -1; + int attribute_offset = 0; + + if (!active_shader_->get_uses_ssbo_vertex_fetch()) { + BLI_assert( + buffer_stride >= 4 && + "In Metal, Vertex buffer stride should be 4. SSBO Vertex fetch is not affected by this"); + } + + /* Iterate over GPUVertBuf vertex format and find attributes matching those in the active + * shader's interface. */ + for (uint32_t a_idx = 0; a_idx < format->attr_len; a_idx++) { + const GPUVertAttr *a = &format->attrs[a_idx]; + + if (format->deinterleaved) { + attribute_offset += ((a_idx == 0) ? 0 : format->attrs[a_idx - 1].size) * verts->vertex_len; + buffer_stride = a->size; + } + else { + attribute_offset = a->offset; + } + + /* Find attribute with the matching name. Attributes may have multiple compatible + * name aliases. */ + for (uint32_t n_idx = 0; n_idx < a->name_len; n_idx++) { + const char *name = GPU_vertformat_attr_name_get(format, a, n_idx); + const ShaderInput *input = interface->attr_get(name); + + if (input == nullptr || input->location == -1) { + /* Vertex/instance buffers provided have attribute data for attributes which are not needed + * by this particular shader. This shader only needs binding information for the attributes + * has in the shader interface. */ + MTL_LOG_WARNING( + "MTLBatch: Could not find attribute with name '%s' (defined in active vertex format) " + "in the shader interface for shader '%s'\n", + name, + interface->get_name()); + continue; + } + + /* Fetch metal attribute information. */ + const MTLShaderInputAttribute &mtl_attr = interface->get_attribute(input->location); + BLI_assert(mtl_attr.location >= 0); + /* Verify that the attribute location from the shader interface + * matches the attribute location returned. */ + BLI_assert(mtl_attr.location == input->location); + + /* Check if attribute is already present in the given slot. */ + if ((~attr_mask) & (1 << mtl_attr.location)) { + MTL_LOG_INFO( + " -- [Batch] Skipping attribute with input location %d (As one is already bound)\n", + mtl_attr.location); + } + else { + + /* Update attribute used-slot mask. */ + attr_mask &= ~(1 << mtl_attr.location); + + /* Add buffer layout entry in descriptor if it has not yet been added + * for current vertex buffer. */ + if (!buffer_added) { + buffer_index = desc.vertex_descriptor.num_vert_buffers; + desc.vertex_descriptor.buffer_layouts[buffer_index].step_function = + (instanced) ? MTLVertexStepFunctionPerInstance : MTLVertexStepFunctionPerVertex; + desc.vertex_descriptor.buffer_layouts[buffer_index].step_rate = 1; + desc.vertex_descriptor.buffer_layouts[buffer_index].stride = buffer_stride; + desc.vertex_descriptor.num_vert_buffers++; + buffer_added = true; + + MTL_LOG_INFO(" -- [Batch] Adding source %s buffer (Index: %d, Stride: %d)\n", + (instanced) ? "instance" : "vertex", + buffer_index, + buffer_stride); + } + else { + /* Ensure stride is correct for de-interleaved attributes. */ + desc.vertex_descriptor.buffer_layouts[buffer_index].stride = buffer_stride; + } + + /* Handle Matrix/Array vertex attribute types. + * Metal does not natively support these as attribute types, so we handle these cases + * by stacking together compatible types (e.g. 4xVec4 for Mat4) and combining + * the data in the shader. + * The generated Metal shader will contain a generated input binding, which reads + * in individual attributes and merges them into the desired type after vertex + * assembly. e.g. a Mat4 (Float4x4) will generate 4 Float4 attributes. */ + if (a->comp_len == 16 || a->comp_len == 12 || a->comp_len == 8) { + BLI_assert_msg( + a->comp_len == 16, + "only mat4 attributes currently supported -- Not ready to handle other long " + "component length attributes yet"); + + /* SSBO Vertex Fetch Attribute safety checks. */ + if (active_shader_->get_uses_ssbo_vertex_fetch()) { + /* When using SSBO vertex fetch, we do not need to expose split attributes, + * A matrix can be read directly as a whole block of contiguous data. */ + MTLSSBOAttribute ssbo_attr(mtl_attr.index, + buffer_index, + attribute_offset, + buffer_stride, + GPU_SHADER_ATTR_TYPE_MAT4, + instanced); + active_shader_->ssbo_vertex_fetch_bind_attribute(ssbo_attr); + desc.vertex_descriptor.ssbo_attributes[desc.vertex_descriptor.num_ssbo_attributes] = + ssbo_attr; + desc.vertex_descriptor.num_ssbo_attributes++; + } + else { + + /* Handle Mat4 attributes. */ + if (a->comp_len == 16) { + /* Debug safety checks. */ + BLI_assert_msg(mtl_attr.matrix_element_count == 4, + "mat4 type expected but there are fewer components"); + BLI_assert_msg(mtl_attr.size == 16, "Expecting subtype 'vec4' with 16 bytes"); + BLI_assert_msg( + mtl_attr.format == MTLVertexFormatFloat4, + "Per-attribute vertex format MUST be float4 for an input type of 'mat4'"); + + /* We have found the 'ROOT' attribute. A mat4 contains 4 consecutive float4 attribute + * locations we must map to. */ + for (int i = 0; i < a->comp_len / 4; i++) { + desc.vertex_descriptor.attributes[mtl_attr.location + i].format = + MTLVertexFormatFloat4; + /* Data is consecutive in the buffer for the whole matrix, each float4 will shift + * the offset by 16 bytes. */ + desc.vertex_descriptor.attributes[mtl_attr.location + i].offset = + attribute_offset + i * 16; + /* All source data for a matrix is in the same singular buffer. */ + desc.vertex_descriptor.attributes[mtl_attr.location + i].buffer_index = + buffer_index; + + /* Update total attribute account. */ + desc.vertex_descriptor.num_attributes = max_ii( + mtl_attr.location + i + 1, desc.vertex_descriptor.num_attributes); + MTL_LOG_INFO("-- Sub-Attrib Location: %d, offset: %d, buffer index: %d\n", + mtl_attr.location + i, + attribute_offset + i * 16, + buffer_index); + } + MTL_LOG_INFO( + "Float4x4 attribute type added for '%s' at attribute locations: %d to %d\n", + name, + mtl_attr.location, + mtl_attr.location + 3); + } + + /* Ensure we are not exceeding the attribute limit. */ + BLI_assert(desc.vertex_descriptor.num_attributes <= MTL_MAX_VERTEX_INPUT_ATTRIBUTES); + } + } + else { + + /* Handle Any required format conversions. + * NOTE(Metal): If there is a mis-match between the format of an attribute + * in the shader interface, and the specified format in the VertexBuffer VertexFormat, + * we need to perform a format conversion. + * + * The Metal API can perform certain conversions internally during vertex assembly: + * - Type Normalization e.g short2 to float2 between 0.0 to 1.0. + * - Type Truncation e.g. Float4 to Float2. + * - Type expansion e,g, Float3 to Float4 (Following 0,0,0,1 for assignment to empty + * elements). + * + * Certain conversion cannot be performed however, and in these cases, we need to + * instruct the shader to generate a specialized version with a conversion routine upon + * attribute read. + * - This handles cases such as conversion between types e.g. Integer to float without + * normalization. + * + * For more information on the supported and unsupported conversions, see: + * https://developer.apple.com/documentation/metal/mtlvertexattributedescriptor/1516081-format?language=objc + */ + MTLVertexFormat converted_format; + bool can_use_internal_conversion = mtl_convert_vertex_format( + mtl_attr.format, + (GPUVertCompType)a->comp_type, + a->comp_len, + (GPUVertFetchMode)a->fetch_mode, + &converted_format); + bool is_floating_point_format = (a->comp_type == GPU_COMP_F32); + + if (can_use_internal_conversion) { + desc.vertex_descriptor.attributes[mtl_attr.location].format = converted_format; + desc.vertex_descriptor.attributes[mtl_attr.location].format_conversion_mode = + is_floating_point_format ? (GPUVertFetchMode)GPU_FETCH_FLOAT : + (GPUVertFetchMode)GPU_FETCH_INT; + BLI_assert(converted_format != MTLVertexFormatInvalid); + } + else { + /* The internal implicit conversion is not supported. + * In this case, we need to handle conversion inside the shader. + * This is handled using `format_conversion_mode`. + * `format_conversion_mode` is assigned the blender-specified fetch mode (GPU_FETCH_*). + * This then controls how a given attribute is interpreted. The data will be read + * as specified and then converted appropriately to the correct form. + * + * e.g. if `GPU_FETCH_INT_TO_FLOAT` is specified, the specialized read-routine + * in the shader will read the data as an int, and cast this to floating point + * representation. (Rather than reading the source data as float). + * + * NOTE: Even if full conversion is not supported, we may still partially perform an + * implicit conversion where possible, such as vector truncation or expansion. */ + MTLVertexFormat converted_format; + bool can_convert = mtl_vertex_format_resize( + mtl_attr.format, a->comp_len, &converted_format); + desc.vertex_descriptor.attributes[mtl_attr.location].format = can_convert ? + converted_format : + mtl_attr.format; + desc.vertex_descriptor.attributes[mtl_attr.location].format_conversion_mode = + (GPUVertFetchMode)a->fetch_mode; + BLI_assert(desc.vertex_descriptor.attributes[mtl_attr.location].format != + MTLVertexFormatInvalid); + } + desc.vertex_descriptor.attributes[mtl_attr.location].offset = attribute_offset; + desc.vertex_descriptor.attributes[mtl_attr.location].buffer_index = buffer_index; + desc.vertex_descriptor.num_attributes = ((mtl_attr.location + 1) > + desc.vertex_descriptor.num_attributes) ? + (mtl_attr.location + 1) : + desc.vertex_descriptor.num_attributes; + + /* SSBO Vertex Fetch attribute bind. */ + if (active_shader_->get_uses_ssbo_vertex_fetch()) { + BLI_assert_msg(desc.vertex_descriptor.attributes[mtl_attr.location].format == + mtl_attr.format, + "SSBO Vertex Fetch does not support attribute conversion."); + + MTLSSBOAttribute ssbo_attr( + mtl_attr.index, + buffer_index, + attribute_offset, + buffer_stride, + MTLShader::ssbo_vertex_type_to_attr_type( + desc.vertex_descriptor.attributes[mtl_attr.location].format), + instanced); + + active_shader_->ssbo_vertex_fetch_bind_attribute(ssbo_attr); + desc.vertex_descriptor.ssbo_attributes[desc.vertex_descriptor.num_ssbo_attributes] = + ssbo_attr; + desc.vertex_descriptor.num_ssbo_attributes++; + } + + /* NOTE: We are setting num_attributes to be up to the maximum found index, because of + * this, it is possible that we may skip over certain attributes if they were not in the + * source GPUVertFormat. */ + MTL_LOG_INFO( + " -- Batch Attribute(%d): ORIG Shader Format: %d, ORIG Vert format: %d, Vert " + "components: %d, Fetch Mode %d --> FINAL FORMAT: %d\n", + mtl_attr.location, + (int)mtl_attr.format, + (int)a->comp_type, + (int)a->comp_len, + (int)a->fetch_mode, + (int)desc.vertex_descriptor.attributes[mtl_attr.location].format); + + MTL_LOG_INFO( + " -- [Batch] matching %s attribute '%s' (Attribute Index: %d, Buffer index: %d, " + "offset: %d)\n", + (instanced) ? "instance" : "vertex", + name, + mtl_attr.location, + buffer_index, + attribute_offset); + } + } + } + } + if (buffer_added) { + return buffer_index; + } + return -1; +} + +id<MTLRenderCommandEncoder> MTLBatch::bind(uint v_first, uint v_count, uint i_first, uint i_count) +{ + /* Setup draw call and render pipeline state here. Called by every draw, but setup here so that + * MTLDrawList only needs to perform setup a single time. */ + BLI_assert(this); + + /* Fetch Metal device. */ + MTLContext *ctx = MTLContext::get(); + if (!ctx) { + BLI_assert_msg(false, "No context available for rendering."); + return nil; + } + + /* Verify Shader. */ + active_shader_ = (shader) ? static_cast<MTLShader *>(unwrap(shader)) : nullptr; + + if (active_shader_ == nullptr || !active_shader_->is_valid()) { + /* Skip drawing if there is no valid Metal shader. + * This will occur if the path through which the shader is prepared + * is invalid (e.g. Python without create-info), or, the source shader uses a geometry pass. */ + BLI_assert_msg(false, "No valid Metal shader!"); + return nil; + } + + /* Check if using SSBO Fetch Mode. + * This is an alternative drawing mode to geometry shaders, wherein vertex buffers + * are bound as readable (random-access) GPU buffers and certain descriptor properties + * are passed using Shader uniforms. */ + bool uses_ssbo_fetch = active_shader_->get_uses_ssbo_vertex_fetch(); + + /* Prepare Vertex Descriptor and extract VertexBuffers to bind. */ + MTLVertBuf *buffers[GPU_BATCH_VBO_MAX_LEN] = {nullptr}; + int num_buffers = 0; + + /* Ensure Index Buffer is ready. */ + MTLIndexBuf *mtl_elem = static_cast<MTLIndexBuf *>(reinterpret_cast<IndexBuf *>(this->elem)); + if (mtl_elem != NULL) { + mtl_elem->upload_data(); + } + + /* Populate vertex descriptor with attribute binding information. + * The vertex descriptor and buffer layout descriptors describe + * how vertex data from bound vertex buffers maps to the + * shader's input. + * A unique vertex descriptor will result in a new PipelineStateObject + * being generated for the currently bound shader. */ + prepare_vertex_descriptor_and_bindings(buffers, num_buffers, v_first, v_count, i_first, i_count); + + /* Prepare Vertex Buffers - Run before RenderCommandEncoder in case BlitCommandEncoder buffer + * data operations are required. */ + for (int i = 0; i < num_buffers; i++) { + MTLVertBuf *buf_at_index = buffers[i]; + if (buf_at_index == NULL) { + BLI_assert_msg( + false, + "Total buffer count does not match highest buffer index, could be gaps in bindings"); + continue; + } + + MTLVertBuf *mtlvbo = static_cast<MTLVertBuf *>(reinterpret_cast<VertBuf *>(buf_at_index)); + mtlvbo->bind(); + } + + /* Ensure render pass is active and fetch active RenderCommandEncoder. */ + id<MTLRenderCommandEncoder> rec = ctx->ensure_begin_render_pass(); + + /* Fetch RenderPassState to enable resource binding for active pass. */ + MTLRenderPassState &rps = ctx->main_command_buffer.get_render_pass_state(); + + /* Debug Check: Ensure Frame-buffer instance is not dirty. */ + BLI_assert(!ctx->main_command_buffer.get_active_framebuffer()->get_dirty()); + + /* Bind Shader. */ + this->shader_bind(); + + /* GPU debug markers. */ + if (G.debug & G_DEBUG_GPU) { + [rec pushDebugGroup:[NSString stringWithFormat:@"batch_bind%@(shader: %s)", + this->elem ? @"(indexed)" : @"", + active_shader_->get_interface()->get_name()]]; + [rec insertDebugSignpost:[NSString + stringWithFormat:@"batch_bind%@(shader: %s)", + this->elem ? @"(indexed)" : @"", + active_shader_->get_interface()->get_name()]]; + } + + /* Ensure Context Render Pipeline State is fully setup and ready to execute the draw. */ + MTLPrimitiveType mtl_prim_type = gpu_prim_type_to_metal(this->prim_type); + if (!ctx->ensure_render_pipeline_state(mtl_prim_type)) { + printf("FAILED TO ENSURE RENDER PIPELINE STATE"); + BLI_assert(false); + + if (G.debug & G_DEBUG_GPU) { + [rec popDebugGroup]; + } + return nil; + } + + /*** Bind Vertex Buffers and Index Buffers **/ + + /* SSBO Vertex Fetch Buffer bindings. */ + if (uses_ssbo_fetch) { + + /* SSBO Vertex Fetch - Bind Index Buffer to appropriate slot -- if used. */ + id<MTLBuffer> idx_buffer = nil; + GPUPrimType final_prim_type = this->prim_type; + + if (mtl_elem != nullptr) { + + /* Fetch index buffer. This function can situationally return an optimized + * index buffer of a different primitive type. If this is the case, `final_prim_type` + * and `v_count` will be updated with the new format. + * NOTE: For indexed rendering, v_count represents the number of indices. */ + idx_buffer = mtl_elem->get_index_buffer(final_prim_type, v_count); + BLI_assert(idx_buffer != nil); + + /* Update uniforms for SSBO-vertex-fetch-mode indexed rendering to flag usage. */ + int &uniform_ssbo_index_mode_u16 = active_shader_->uni_ssbo_uses_index_mode_u16; + BLI_assert(uniform_ssbo_index_mode_u16 != -1); + int uses_index_mode_u16 = (mtl_elem->index_type_ == GPU_INDEX_U16) ? 1 : 0; + active_shader_->uniform_int(uniform_ssbo_index_mode_u16, 1, 1, &uses_index_mode_u16); + } + else { + idx_buffer = ctx->get_null_buffer(); + } + rps.bind_vertex_buffer(idx_buffer, 0, MTL_SSBO_VERTEX_FETCH_IBO_INDEX); + + /* Ensure all attributes are set */ + active_shader_->ssbo_vertex_fetch_bind_attributes_end(rec); + + /* Bind NULL Buffers for unused vertex data slots. */ + id<MTLBuffer> null_buffer = ctx->get_null_buffer(); + BLI_assert(null_buffer != nil); + for (int i = num_buffers; i < MTL_SSBO_VERTEX_FETCH_MAX_VBOS; i++) { + if (rps.cached_vertex_buffer_bindings[i].metal_buffer == nil) { + rps.bind_vertex_buffer(null_buffer, 0, i); + } + } + + /* Flag whether Indexed rendering is used or not. */ + int &uniform_ssbo_use_indexed = active_shader_->uni_ssbo_uses_indexed_rendering; + BLI_assert(uniform_ssbo_use_indexed != -1); + int uses_indexed_rendering = (mtl_elem != NULL) ? 1 : 0; + active_shader_->uniform_int(uniform_ssbo_use_indexed, 1, 1, &uses_indexed_rendering); + + /* Set SSBO-fetch-mode status uniforms. */ + BLI_assert(active_shader_->uni_ssbo_input_prim_type_loc != -1); + BLI_assert(active_shader_->uni_ssbo_input_vert_count_loc != -1); + GPU_shader_uniform_vector_int(reinterpret_cast<GPUShader *>(wrap(active_shader_)), + active_shader_->uni_ssbo_input_prim_type_loc, + 1, + 1, + (const int *)(&final_prim_type)); + GPU_shader_uniform_vector_int(reinterpret_cast<GPUShader *>(wrap(active_shader_)), + active_shader_->uni_ssbo_input_vert_count_loc, + 1, + 1, + (const int *)(&v_count)); + } + + /* Bind Vertex Buffers. */ + for (int i = 0; i < num_buffers; i++) { + MTLVertBuf *buf_at_index = buffers[i]; + if (buf_at_index == NULL) { + BLI_assert_msg( + false, + "Total buffer count does not match highest buffer index, could be gaps in bindings"); + continue; + } + /* Buffer handle. */ + MTLVertBuf *mtlvbo = static_cast<MTLVertBuf *>(reinterpret_cast<VertBuf *>(buf_at_index)); + mtlvbo->flag_used(); + + /* Fetch buffer from MTLVertexBuffer and bind. */ + id<MTLBuffer> mtl_buffer = mtlvbo->get_metal_buffer(); + + BLI_assert(mtl_buffer != nil); + rps.bind_vertex_buffer(mtl_buffer, 0, i); + } + + if (G.debug & G_DEBUG_GPU) { + [rec popDebugGroup]; + } + + /* Return Render Command Encoder used with setup. */ + return rec; +} + +void MTLBatch::unbind() +{ +} + +void MTLBatch::prepare_vertex_descriptor_and_bindings( + MTLVertBuf **buffers, int &num_buffers, int v_first, int v_count, int i_first, int i_count) +{ + + /* Here we populate the MTLContext vertex descriptor and resolve which buffers need to be bound. + */ + MTLStateManager *state_manager = static_cast<MTLStateManager *>( + MTLContext::get()->state_manager); + MTLRenderPipelineStateDescriptor &desc = state_manager->get_pipeline_descriptor(); + const MTLShaderInterface *interface = active_shader_->get_interface(); + uint16_t attr_mask = interface->get_enabled_attribute_mask(); + + /* Reset vertex descriptor to default state. */ + desc.reset_vertex_descriptor(); + + /* Fetch Vertex and Instance Buffers. */ + Span<MTLVertBuf *> mtl_verts(reinterpret_cast<MTLVertBuf **>(this->verts), + GPU_BATCH_VBO_MAX_LEN); + Span<MTLVertBuf *> mtl_inst(reinterpret_cast<MTLVertBuf **>(this->inst), + GPU_BATCH_INST_VBO_MAX_LEN); + + /* SSBO Vertex fetch also passes vertex descriptor information into the shader. */ + if (active_shader_->get_uses_ssbo_vertex_fetch()) { + active_shader_->ssbo_vertex_fetch_bind_attributes_begin(); + } + + /* Resolve Metal vertex buffer bindings. */ + /* Vertex Descriptors + * ------------------ + * Vertex Descriptors are required to generate a pipeline state, based on the current Batch's + * buffer bindings. These bindings are a unique matching, depending on what input attributes a + * batch has in its buffers, and those which are supported by the shader interface. + + * We iterate through the buffers and resolve which attributes satisfy the requirements of the + * currently bound shader. We cache this data, for a given Batch<->ShderInterface pairing in a + * VAO cache to avoid the need to recalculate this data. */ + bool buffer_is_instanced[GPU_BATCH_VBO_MAX_LEN] = {false}; + + VertexDescriptorShaderInterfacePair *descriptor = this->vao_cache.find(interface); + if (descriptor) { + desc.vertex_descriptor = descriptor->vertex_descriptor; + attr_mask = descriptor->attr_mask; + num_buffers = descriptor->num_buffers; + + for (int bid = 0; bid < GPU_BATCH_VBO_MAX_LEN; ++bid) { + if (descriptor->bufferIds[bid].used) { + if (descriptor->bufferIds[bid].is_instance) { + buffers[bid] = mtl_inst[descriptor->bufferIds[bid].id]; + buffer_is_instanced[bid] = true; + } + else { + buffers[bid] = mtl_verts[descriptor->bufferIds[bid].id]; + buffer_is_instanced[bid] = false; + } + } + } + + /* Use cached ssbo attribute binding data. */ + if (active_shader_->get_uses_ssbo_vertex_fetch()) { + BLI_assert(desc.vertex_descriptor.uses_ssbo_vertex_fetch); + for (int attr_id = 0; attr_id < desc.vertex_descriptor.num_ssbo_attributes; attr_id++) { + active_shader_->ssbo_vertex_fetch_bind_attribute( + desc.vertex_descriptor.ssbo_attributes[attr_id]); + } + } + } + else { + VertexDescriptorShaderInterfacePair pair{}; + pair.interface = interface; + + for (int i = 0; i < GPU_BATCH_VBO_MAX_LEN; ++i) { + pair.bufferIds[i].id = -1; + pair.bufferIds[i].is_instance = 0; + pair.bufferIds[i].used = 0; + } + /* NOTE: Attribute extraction order from buffer is the reverse of the OpenGL as we flag once an + * attribute is found, rather than pre-setting the mask. */ + /* Extract Instance attributes (These take highest priority). */ + for (int v = 0; v < GPU_BATCH_INST_VBO_MAX_LEN; v++) { + if (mtl_inst[v]) { + MTL_LOG_INFO(" -- [Batch] Checking bindings for bound instance buffer %p\n", mtl_inst[v]); + int buffer_ind = this->prepare_vertex_binding( + mtl_inst[v], desc, interface, attr_mask, true); + if (buffer_ind >= 0) { + buffers[buffer_ind] = mtl_inst[v]; + buffer_is_instanced[buffer_ind] = true; + + pair.bufferIds[buffer_ind].id = v; + pair.bufferIds[buffer_ind].used = 1; + pair.bufferIds[buffer_ind].is_instance = 1; + num_buffers = ((buffer_ind + 1) > num_buffers) ? (buffer_ind + 1) : num_buffers; + } + } + } + + /* Extract Vertex attributes (First-bound vertex buffer takes priority). */ + for (int v = 0; v < GPU_BATCH_VBO_MAX_LEN; v++) { + if (mtl_verts[v] != NULL) { + MTL_LOG_INFO(" -- [Batch] Checking bindings for bound vertex buffer %p\n", mtl_verts[v]); + int buffer_ind = this->prepare_vertex_binding( + mtl_verts[v], desc, interface, attr_mask, false); + if (buffer_ind >= 0) { + buffers[buffer_ind] = mtl_verts[v]; + buffer_is_instanced[buffer_ind] = false; + + pair.bufferIds[buffer_ind].id = v; + pair.bufferIds[buffer_ind].used = 1; + pair.bufferIds[buffer_ind].is_instance = 0; + num_buffers = ((buffer_ind + 1) > num_buffers) ? (buffer_ind + 1) : num_buffers; + } + } + } + + /* Add to VertexDescriptor cache */ + desc.vertex_descriptor.uses_ssbo_vertex_fetch = active_shader_->get_uses_ssbo_vertex_fetch(); + pair.attr_mask = attr_mask; + pair.vertex_descriptor = desc.vertex_descriptor; + pair.num_buffers = num_buffers; + if (!this->vao_cache.insert(pair)) { + printf( + "[Performance Warning] cache is full (Size: %d), vertex descriptor will not be cached\n", + GPU_VAO_STATIC_LEN); + } + } + +/* DEBUG: verify if our attribute bindings have been fully provided as expected. */ +#if MTL_DEBUG_SHADER_ATTRIBUTES == 1 + if (attr_mask != 0) { + for (uint16_t mask = 1, a = 0; a < 16; a++, mask <<= 1) { + if (attr_mask & mask) { + /* Fallback for setting default attributes, for missed slots. Attributes flagged with + * 'MTLVertexFormatInvalid' in the vertex descriptor are bound to a NULL buffer during PSO + * creation. */ + MTL_LOG_WARNING("MTLBatch: Missing expected attribute '%s' at index '%d' for shader: %s\n", + this->active_shader->interface->attributes[a].name, + a, + interface->name); + /* Ensure any assigned attribute has not been given an invalid format. This should not + * occur and may be the result of an unsupported attribute type conversion. */ + BLI_assert(desc.attributes[a].format == MTLVertexFormatInvalid); + } + } + } +#endif +} + +void MTLBatch::draw_advanced(int v_first, int v_count, int i_first, int i_count) +{ + +#if TRUST_NO_ONE + BLI_assert(v_count > 0 && i_count > 0); +#endif + + /* Setup RenderPipelineState for batch. */ + MTLContext *ctx = reinterpret_cast<MTLContext *>(GPU_context_active_get()); + id<MTLRenderCommandEncoder> rec = this->bind(v_first, v_count, i_first, i_count); + if (rec == nil) { + return; + } + + /* Fetch IndexBuffer and resolve primitive type. */ + MTLIndexBuf *mtl_elem = static_cast<MTLIndexBuf *>(reinterpret_cast<IndexBuf *>(this->elem)); + MTLPrimitiveType mtl_prim_type = gpu_prim_type_to_metal(this->prim_type); + + /* Render using SSBO Vertex Fetch. */ + if (active_shader_->get_uses_ssbo_vertex_fetch()) { + + /* Submit draw call with modified vertex count, which reflects vertices per primitive defined + * in the USE_SSBO_VERTEX_FETCH pragma. */ + int num_input_primitives = gpu_get_prim_count_from_type(v_count, this->prim_type); + int output_num_verts = num_input_primitives * + active_shader_->get_ssbo_vertex_fetch_output_num_verts(); + BLI_assert_msg( + mtl_vertex_count_fits_primitive_type( + output_num_verts, active_shader_->get_ssbo_vertex_fetch_output_prim_type()), + "Output Vertex count is not compatible with the requested output vertex primitive type"); + [rec drawPrimitives:active_shader_->get_ssbo_vertex_fetch_output_prim_type() + vertexStart:0 + vertexCount:output_num_verts + instanceCount:i_count + baseInstance:i_first]; + ctx->main_command_buffer.register_draw_counters(output_num_verts * i_count); + } + /* Perform regular draw. */ + else if (mtl_elem == NULL) { + + /* Primitive Type toplogy emulation. */ + if (mtl_needs_topology_emulation(this->prim_type)) { + + /* Generate index buffer for primitive types requiring emulation. */ + GPUPrimType emulated_prim_type = this->prim_type; + uint32_t emulated_v_count = v_count; + id<MTLBuffer> generated_index_buffer = this->get_emulated_toplogy_buffer(emulated_prim_type, + emulated_v_count); + BLI_assert(generated_index_buffer != nil); + + MTLPrimitiveType emulated_mtl_prim_type = gpu_prim_type_to_metal(emulated_prim_type); + + /* Temp: Disable culling for emulated primitive types. + * TODO(Metal): Support face winding in topology buffer. */ + [rec setCullMode:MTLCullModeNone]; + + if (generated_index_buffer != nil) { + BLI_assert(emulated_mtl_prim_type == MTLPrimitiveTypeTriangle || + emulated_mtl_prim_type == MTLPrimitiveTypeLine); + if (emulated_mtl_prim_type == MTLPrimitiveTypeTriangle) { + BLI_assert(emulated_v_count % 3 == 0); + } + if (emulated_mtl_prim_type == MTLPrimitiveTypeLine) { + BLI_assert(emulated_v_count % 2 == 0); + } + + /* Set depth stencil state (requires knowledge of primitive type). */ + ctx->ensure_depth_stencil_state(emulated_mtl_prim_type); + + [rec drawIndexedPrimitives:emulated_mtl_prim_type + indexCount:emulated_v_count + indexType:MTLIndexTypeUInt32 + indexBuffer:generated_index_buffer + indexBufferOffset:0 + instanceCount:i_count + baseVertex:v_first + baseInstance:i_first]; + } + else { + printf("[Note] Cannot draw batch -- Emulated Topology mode: %u not yet supported\n", + this->prim_type); + } + } + else { + /* Set depth stencil state (requires knowledge of primitive type). */ + ctx->ensure_depth_stencil_state(mtl_prim_type); + + /* Issue draw call. */ + [rec drawPrimitives:mtl_prim_type + vertexStart:v_first + vertexCount:v_count + instanceCount:i_count + baseInstance:i_first]; + } + ctx->main_command_buffer.register_draw_counters(v_count * i_count); + } + /* Perform indexed draw. */ + else { + + MTLIndexType index_type = MTLIndexBuf::gpu_index_type_to_metal(mtl_elem->index_type_); + uint32_t base_index = mtl_elem->index_base_; + uint32_t index_size = (mtl_elem->index_type_ == GPU_INDEX_U16) ? 2 : 4; + uint32_t v_first_ofs = ((v_first + mtl_elem->index_start_) * index_size); + BLI_assert_msg((v_first_ofs % index_size) == 0, + "Index offset is not 2/4-byte aligned as per METAL spec"); + + /* Fetch index buffer. May return an index buffer of a differing format, + * if index buffer optimization is used. In these cases, final_prim_type and + * index_count get updated with the new properties. */ + GPUPrimType final_prim_type = this->prim_type; + uint index_count = v_count; + + id<MTLBuffer> index_buffer = mtl_elem->get_index_buffer(final_prim_type, index_count); + mtl_prim_type = gpu_prim_type_to_metal(final_prim_type); + BLI_assert(index_buffer != nil); + + if (index_buffer != nil) { + + /* Set depth stencil state (requires knowledge of primitive type). */ + ctx->ensure_depth_stencil_state(mtl_prim_type); + + /* Issue draw call. */ + [rec drawIndexedPrimitives:mtl_prim_type + indexCount:index_count + indexType:index_type + indexBuffer:index_buffer + indexBufferOffset:v_first_ofs + instanceCount:i_count + baseVertex:base_index + baseInstance:i_first]; + ctx->main_command_buffer.register_draw_counters(index_count * i_count); + } + else { + BLI_assert_msg(false, "Index buffer does not have backing Metal buffer"); + } + } + + /* End of draw. */ + this->unbind(); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Topology emulation and optimization + * \{ */ + +id<MTLBuffer> MTLBatch::get_emulated_toplogy_buffer(GPUPrimType &in_out_prim_type, + uint32_t &in_out_v_count) +{ + + BLI_assert(in_out_v_count > 0); + /* Determine emulated primitive types. */ + GPUPrimType input_prim_type = in_out_prim_type; + uint32_t v_count = in_out_v_count; + GPUPrimType output_prim_type; + switch (input_prim_type) { + case GPU_PRIM_POINTS: + case GPU_PRIM_LINES: + case GPU_PRIM_TRIS: + BLI_assert_msg(false, "Optimal primitive types should not reach here."); + return nil; + break; + case GPU_PRIM_LINES_ADJ: + case GPU_PRIM_TRIS_ADJ: + BLI_assert_msg(false, "Adjacency primitive types should not reach here."); + return nil; + break; + case GPU_PRIM_LINE_STRIP: + case GPU_PRIM_LINE_LOOP: + case GPU_PRIM_LINE_STRIP_ADJ: + output_prim_type = GPU_PRIM_LINES; + break; + case GPU_PRIM_TRI_STRIP: + case GPU_PRIM_TRI_FAN: + output_prim_type = GPU_PRIM_TRIS; + break; + default: + BLI_assert_msg(false, "Invalid primitive type."); + return nil; + } + + /* Check if topology buffer exists and is valid. */ + if (this->emulated_topology_buffer_ != nullptr && + (emulated_topology_type_ != input_prim_type || topology_buffer_input_v_count_ != v_count)) { + + /* Release existing topology buffer. */ + emulated_topology_buffer_->free(); + emulated_topology_buffer_ = nullptr; + } + + /* Generate new topology index buffer. */ + if (this->emulated_topology_buffer_ == nullptr) { + /* Calculate IB len. */ + uint32_t output_prim_count = 0; + switch (input_prim_type) { + case GPU_PRIM_LINE_STRIP: + case GPU_PRIM_LINE_STRIP_ADJ: + output_prim_count = v_count - 1; + break; + case GPU_PRIM_LINE_LOOP: + output_prim_count = v_count; + break; + case GPU_PRIM_TRI_STRIP: + case GPU_PRIM_TRI_FAN: + output_prim_count = v_count - 2; + break; + default: + BLI_assert_msg(false, "Cannot generate optimized topology buffer for other types."); + break; + } + uint32_t output_IB_elems = output_prim_count * ((output_prim_type == GPU_PRIM_TRIS) ? 3 : 2); + + /* Allocate buffer. */ + uint32_t buffer_bytes = output_IB_elems * 4; + BLI_assert(buffer_bytes > 0); + this->emulated_topology_buffer_ = MTLContext::get_global_memory_manager().allocate( + buffer_bytes, true); + + /* Populate. */ + uint32_t *data = (uint32_t *)this->emulated_topology_buffer_->get_host_ptr(); + BLI_assert(data != nullptr); + + /* TODO(Metal): Support inverse winding modes. */ + bool winding_clockwise = false; + UNUSED_VARS(winding_clockwise); + + switch (input_prim_type) { + /* Line Loop. */ + case GPU_PRIM_LINE_LOOP: { + int line = 0; + for (line = 0; line < output_prim_count - 1; line++) { + data[line * 3 + 0] = line + 0; + data[line * 3 + 1] = line + 1; + } + /* Closing line. */ + data[line * 2 + 0] = line + 0; + data[line * 2 + 1] = 0; + } break; + + /* Triangle Fan. */ + case GPU_PRIM_TRI_FAN: { + for (int triangle = 0; triangle < output_prim_count; triangle++) { + data[triangle * 3 + 0] = 0; /* Always 0 */ + data[triangle * 3 + 1] = triangle + 1; + data[triangle * 3 + 2] = triangle + 2; + } + } break; + + default: + BLI_assert_msg(false, "Other primitive types do not require emulation."); + return nil; + } + + /* Flush. */ + this->emulated_topology_buffer_->flush(); + /* Assign members relating to current cached IB. */ + topology_buffer_input_v_count_ = v_count; + topology_buffer_output_v_count_ = output_IB_elems; + emulated_topology_type_ = input_prim_type; + } + + /* Return. */ + in_out_v_count = topology_buffer_output_v_count_; + in_out_prim_type = output_prim_type; + return (emulated_topology_buffer_) ? emulated_topology_buffer_->get_metal_buffer() : nil; +} + +/** \} */ + +} // blender::gpu diff --git a/source/blender/gpu/metal/mtl_context.mm b/source/blender/gpu/metal/mtl_context.mm index ef66a1f2111..50576379f0d 100644 --- a/source/blender/gpu/metal/mtl_context.mm +++ b/source/blender/gpu/metal/mtl_context.mm @@ -995,19 +995,21 @@ bool MTLContext::ensure_uniform_buffer_bindings( if (ubo.buffer_index >= 0) { - const uint32_t buffer_index = ubo.buffer_index; + /* Uniform Buffer index offset by 1 as the first shader buffer binding slot is reserved for + * the uniform PushConstantBlock. */ + const uint32_t buffer_index = ubo.buffer_index + 1; int ubo_offset = 0; id<MTLBuffer> ubo_buffer = nil; int ubo_size = 0; bool bind_dummy_buffer = false; - if (this->pipeline_state.ubo_bindings[buffer_index].bound) { + if (this->pipeline_state.ubo_bindings[ubo_index].bound) { /* Fetch UBO global-binding properties from slot. */ ubo_offset = 0; - ubo_buffer = this->pipeline_state.ubo_bindings[buffer_index].ubo->get_metal_buffer( + ubo_buffer = this->pipeline_state.ubo_bindings[ubo_index].ubo->get_metal_buffer( &ubo_offset); - ubo_size = this->pipeline_state.ubo_bindings[buffer_index].ubo->get_size(); + ubo_size = this->pipeline_state.ubo_bindings[ubo_index].ubo->get_size(); /* Use dummy zero buffer if no buffer assigned -- this is an optimization to avoid * allocating zero buffers. */ diff --git a/source/blender/gpu/metal/mtl_drawlist.hh b/source/blender/gpu/metal/mtl_drawlist.hh index ed99c76faa7..47055f3d7f4 100644 --- a/source/blender/gpu/metal/mtl_drawlist.hh +++ b/source/blender/gpu/metal/mtl_drawlist.hh @@ -9,34 +9,50 @@ #pragma once -#pragma once - +#include "BLI_sys_types.h" +#include "GPU_batch.h" +#include "MEM_guardedalloc.h" #include "gpu_drawlist_private.hh" -namespace blender { -namespace gpu { +#include "mtl_batch.hh" +#include "mtl_context.hh" + +namespace blender::gpu { /** - * TODO(Metal): MTLDrawList Implementation. Included as temporary stub. - */ + * Implementation of Multi Draw Indirect using OpenGL. + **/ class MTLDrawList : public DrawList { + + private: + /** Batch for which we are recording commands for. */ + MTLBatch *batch_; + /** Mapped memory bounds. */ + void *data_; + /** Length of the mapped buffer (in byte). */ + size_t data_size_; + /** Current offset inside the mapped buffer (in byte). */ + size_t command_offset_; + /** Current number of command recorded inside the mapped buffer. */ + uint32_t command_len_; + /** Is UINT_MAX if not drawing indexed geom. Also Avoid dereferencing batch. */ + uint32_t base_index_; + /** Also Avoid dereferencing batch. */ + uint32_t v_first_, v_count_; + /** Length of whole the buffer (in byte). */ + uint32_t buffer_size_; + public: - MTLDrawList(int length) - { - } - ~MTLDrawList() - { - } - - void append(GPUBatch *batch, int i_first, int i_count) override - { - } - void submit() override - { - } + MTLDrawList(int length); + ~MTLDrawList(); + + void append(GPUBatch *batch, int i_first, int i_count) override; + void submit() override; + + private: + void init(); MEM_CXX_CLASS_ALLOC_FUNCS("MTLDrawList"); }; -} // namespace gpu -} // namespace blender +} // namespace blender::gpu diff --git a/source/blender/gpu/metal/mtl_drawlist.mm b/source/blender/gpu/metal/mtl_drawlist.mm new file mode 100644 index 00000000000..99194d2b72c --- /dev/null +++ b/source/blender/gpu/metal/mtl_drawlist.mm @@ -0,0 +1,284 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup gpu + * + * Implementation of Multi Draw Indirect using OpenGL. + * Fallback if the needed extensions are not supported. + */ + +#include "BLI_assert.h" + +#include "GPU_batch.h" +#include "mtl_common.hh" +#include "mtl_drawlist.hh" +#include "mtl_primitive.hh" + +using namespace blender::gpu; + +namespace blender::gpu { + +/* Indirect draw call structure for reference. */ +/* MTLDrawPrimitivesIndirectArguments -- + * https://developer.apple.com/documentation/metal/mtldrawprimitivesindirectarguments?language=objc + */ +/* struct MTLDrawPrimitivesIndirectArguments { + * uint32_t vertexCount; + * uint32_t instanceCount; + * uint32_t vertexStart; + * uint32_t baseInstance; +};*/ + +/* MTLDrawIndexedPrimitivesIndirectArguments -- + * https://developer.apple.com/documentation/metal/mtldrawindexedprimitivesindirectarguments?language=objc + */ +/* struct MTLDrawIndexedPrimitivesIndirectArguments { + * uint32_t indexCount; + * uint32_t instanceCount; + * uint32_t indexStart; + * uint32_t baseVertex; + * uint32_t baseInstance; +};*/ + +#define MDI_ENABLED (buffer_size_ != 0) +#define MDI_DISABLED (buffer_size_ == 0) +#define MDI_INDEXED (base_index_ != UINT_MAX) + +MTLDrawList::MTLDrawList(int length) +{ + BLI_assert(length > 0); + batch_ = nullptr; + command_len_ = 0; + base_index_ = 0; + command_offset_ = 0; + data_size_ = 0; + buffer_size_ = sizeof(MTLDrawIndexedPrimitivesIndirectArguments) * length; + data_ = (void *)MEM_mallocN(buffer_size_, __func__); +} + +MTLDrawList::~MTLDrawList() +{ + if (data_) { + MEM_freeN(data_); + data_ = nullptr; + } +} + +void MTLDrawList::init() +{ + MTLContext *ctx = reinterpret_cast<MTLContext *>(GPU_context_active_get()); + BLI_assert(ctx); + BLI_assert(MDI_ENABLED); + BLI_assert(data_ == nullptr); + UNUSED_VARS_NDEBUG(ctx); + + batch_ = nullptr; + command_len_ = 0; + BLI_assert(data_); + + command_offset_ = 0; +} + +void MTLDrawList::append(GPUBatch *gpu_batch, int i_first, int i_count) +{ + /* Fallback when MultiDrawIndirect is not supported/enabled. */ + MTLShader *shader = static_cast<MTLShader *>(unwrap(gpu_batch->shader)); + bool requires_ssbo = (shader->get_uses_ssbo_vertex_fetch()); + bool requires_emulation = mtl_needs_topology_emulation(gpu_batch->prim_type); + if (MDI_DISABLED || requires_ssbo || requires_emulation) { + GPU_batch_draw_advanced(gpu_batch, 0, 0, i_first, i_count); + return; + } + + if (data_ == nullptr) { + this->init(); + } + BLI_assert(data_); + + MTLBatch *mtl_batch = static_cast<MTLBatch *>(gpu_batch); + BLI_assert(mtl_batch); + if (mtl_batch != batch_) { + /* Submit existing calls. */ + this->submit(); + + /* Begin new batch. */ + batch_ = mtl_batch; + + /* Cached for faster access. */ + MTLIndexBuf *el = batch_->elem_(); + base_index_ = el ? el->index_base_ : UINT_MAX; + v_first_ = el ? el->index_start_ : 0; + v_count_ = el ? el->index_len_ : batch_->verts_(0)->vertex_len; + } + + if (v_count_ == 0) { + /* Nothing to draw. */ + return; + } + + if (MDI_INDEXED) { + MTLDrawIndexedPrimitivesIndirectArguments *cmd = + reinterpret_cast<MTLDrawIndexedPrimitivesIndirectArguments *>((char *)data_ + + command_offset_); + cmd->indexStart = v_first_; + cmd->indexCount = v_count_; + cmd->instanceCount = i_count; + cmd->baseVertex = base_index_; + cmd->baseInstance = i_first; + } + else { + MTLDrawPrimitivesIndirectArguments *cmd = + reinterpret_cast<MTLDrawPrimitivesIndirectArguments *>((char *)data_ + command_offset_); + cmd->vertexStart = v_first_; + cmd->vertexCount = v_count_; + cmd->instanceCount = i_count; + cmd->baseInstance = i_first; + } + + size_t command_size = MDI_INDEXED ? sizeof(MTLDrawIndexedPrimitivesIndirectArguments) : + sizeof(MTLDrawPrimitivesIndirectArguments); + + command_offset_ += command_size; + command_len_++; + + /* Check if we can fit at least one other command. */ + if (command_offset_ + command_size > buffer_size_) { + this->submit(); + } + + return; +} + +void MTLDrawList::submit() +{ + /* Metal does not support MDI from the host side, but we still benefit from only executing the + * batch bind a single time, rather than per-draw. + * NOTE(Metal): Consider using #MTLIndirectCommandBuffer to achieve similar behavior. */ + if (command_len_ == 0) { + return; + } + + /* Something's wrong if we get here without MDI support. */ + BLI_assert(MDI_ENABLED); + BLI_assert(data_); + + /* Host-side MDI Currently unsupported on Metal. */ + bool can_use_MDI = false; + + /* Verify context. */ + MTLContext *ctx = reinterpret_cast<MTLContext *>(GPU_context_active_get()); + BLI_assert(ctx); + + /* Execute indirect draw calls. */ + MTLShader *shader = static_cast<MTLShader *>(unwrap(batch_->shader)); + bool SSBO_MODE = (shader->get_uses_ssbo_vertex_fetch()); + if (SSBO_MODE) { + can_use_MDI = false; + BLI_assert(false); + return; + } + + /* Heuristic to determine whether using indirect drawing is more efficient. */ + size_t command_size = MDI_INDEXED ? sizeof(MTLDrawIndexedPrimitivesIndirectArguments) : + sizeof(MTLDrawPrimitivesIndirectArguments); + const bool is_finishing_a_buffer = (command_offset_ + command_size > buffer_size_); + can_use_MDI = can_use_MDI && (is_finishing_a_buffer || command_len_ > 2); + + /* Bind Batch to setup render pipeline state. */ + id<MTLRenderCommandEncoder> rec = batch_->bind(0, 0, 0, 0); + if (!rec) { + BLI_assert_msg(false, "A RenderCommandEncoder should always be available!\n"); + return; + } + + /* Common properties. */ + MTLPrimitiveType mtl_prim_type = gpu_prim_type_to_metal(batch_->prim_type); + + /* Execute multi-draw indirect. */ + if (can_use_MDI && false) { + /* Metal Doesn't support MDI -- Singular Indirect draw calls are supported, + * but Multi-draw is not. + * TODO(Metal): Consider using #IndirectCommandBuffers to provide similar + * behavior. */ + } + else { + + /* Execute draws manually. */ + if (MDI_INDEXED) { + MTLDrawIndexedPrimitivesIndirectArguments *cmd = + (MTLDrawIndexedPrimitivesIndirectArguments *)data_; + MTLIndexBuf *mtl_elem = static_cast<MTLIndexBuf *>( + reinterpret_cast<IndexBuf *>(batch_->elem)); + BLI_assert(mtl_elem); + MTLIndexType index_type = MTLIndexBuf::gpu_index_type_to_metal(mtl_elem->index_type_); + uint32_t index_size = (mtl_elem->index_type_ == GPU_INDEX_U16) ? 2 : 4; + uint32_t v_first_ofs = (mtl_elem->index_start_ * index_size); + uint32_t index_count = cmd->indexCount; + + /* Fetch index buffer. May return an index buffer of a differing format, + * if index buffer optimization is used. In these cases, mtl_prim_type and + * index_count get updated with the new properties. */ + GPUPrimType final_prim_type = batch_->prim_type; + id<MTLBuffer> index_buffer = mtl_elem->get_index_buffer(final_prim_type, index_count); + BLI_assert(index_buffer != nil); + + /* Final primitive type. */ + mtl_prim_type = gpu_prim_type_to_metal(final_prim_type); + + if (index_buffer != nil) { + + /* Set depth stencil state (requires knowledge of primitive type). */ + ctx->ensure_depth_stencil_state(mtl_prim_type); + + for (int i = 0; i < command_len_; i++, cmd++) { + [rec drawIndexedPrimitives:mtl_prim_type + indexCount:index_count + indexType:index_type + indexBuffer:index_buffer + indexBufferOffset:v_first_ofs + instanceCount:cmd->instanceCount + baseVertex:cmd->baseVertex + baseInstance:cmd->baseInstance]; + ctx->main_command_buffer.register_draw_counters(cmd->indexCount * cmd->instanceCount); + } + } + else { + BLI_assert_msg(false, "Index buffer does not have backing Metal buffer"); + } + } + else { + MTLDrawPrimitivesIndirectArguments *cmd = (MTLDrawPrimitivesIndirectArguments *)data_; + + /* Verify if topology emulation is required. */ + if (mtl_needs_topology_emulation(batch_->prim_type)) { + BLI_assert_msg(false, "topology emulation cases should use fallback."); + } + else { + + /* Set depth stencil state (requires knowledge of primitive type). */ + ctx->ensure_depth_stencil_state(mtl_prim_type); + + for (int i = 0; i < command_len_; i++, cmd++) { + [rec drawPrimitives:mtl_prim_type + vertexStart:cmd->vertexStart + vertexCount:cmd->vertexCount + instanceCount:cmd->instanceCount + baseInstance:cmd->baseInstance]; + ctx->main_command_buffer.register_draw_counters(cmd->vertexCount * cmd->instanceCount); + } + } + } + } + + /* Unbind batch. */ + batch_->unbind(); + + /* Reset command offsets. */ + command_len_ = 0; + command_offset_ = 0; + + /* Avoid keeping reference to the batch. */ + batch_ = nullptr; +} + +} // namespace blender::gpu diff --git a/source/blender/gpu/metal/mtl_immediate.mm b/source/blender/gpu/metal/mtl_immediate.mm index aaebe7e20f8..ee48bdd6ee1 100644 --- a/source/blender/gpu/metal/mtl_immediate.mm +++ b/source/blender/gpu/metal/mtl_immediate.mm @@ -99,6 +99,9 @@ void MTLImmediate::end() MTLRenderPipelineStateDescriptor &desc = state_manager->get_pipeline_descriptor(); const MTLShaderInterface *interface = active_mtl_shader->get_interface(); + /* Reset vertex descriptor to default state. */ + desc.reset_vertex_descriptor(); + desc.vertex_descriptor.num_attributes = interface->get_total_attributes(); desc.vertex_descriptor.num_vert_buffers = 1; @@ -125,7 +128,7 @@ void MTLImmediate::end() * TODO(Metal): Cache this vertex state based on Vertex format and shaders. */ for (int i = 0; i < interface->get_total_attributes(); i++) { - /* Note: Attribute in VERTEX FORMAT does not necessarily share the same array index as + /* NOTE: Attribute in VERTEX FORMAT does not necessarily share the same array index as * attributes in shader interface. */ GPUVertAttr *attr = nullptr; const MTLShaderInputAttribute &mtl_shader_attribute = interface->get_attribute(i); diff --git a/source/blender/gpu/metal/mtl_pso_descriptor_state.hh b/source/blender/gpu/metal/mtl_pso_descriptor_state.hh index 198d309874b..04ceb5bdf03 100644 --- a/source/blender/gpu/metal/mtl_pso_descriptor_state.hh +++ b/source/blender/gpu/metal/mtl_pso_descriptor_state.hh @@ -243,6 +243,19 @@ struct MTLRenderPipelineStateDescriptor { return hash; } + + /* Reset the Vertex Descriptor to default. */ + void reset_vertex_descriptor() + { + vertex_descriptor.num_attributes = 0; + vertex_descriptor.num_vert_buffers = 0; + for (int i = 0; i < GPU_VERT_ATTR_MAX_LEN; i++) { + vertex_descriptor.attributes[i].format = MTLVertexFormatInvalid; + vertex_descriptor.attributes[i].offset = 0; + } + vertex_descriptor.uses_ssbo_vertex_fetch = false; + vertex_descriptor.num_ssbo_attributes = 0; + } }; } // namespace blender::gpu diff --git a/source/blender/gpu/metal/mtl_shader_interface.mm b/source/blender/gpu/metal/mtl_shader_interface.mm index 3703d5b5684..97a82345761 100644 --- a/source/blender/gpu/metal/mtl_shader_interface.mm +++ b/source/blender/gpu/metal/mtl_shader_interface.mm @@ -117,9 +117,7 @@ uint32_t MTLShaderInterface::add_uniform_block(uint32_t name_offset, MTLShaderUniformBlock &uni_block = ubos_[total_uniform_blocks_]; uni_block.name_offset = name_offset; - /* We offset the buffer binding index by one, as the first slot is reserved for push constant - * data. */ - uni_block.buffer_index = buffer_index + 1; + uni_block.buffer_index = buffer_index; uni_block.size = size; uni_block.current_offset = 0; uni_block.stage_mask = ShaderStage::BOTH; @@ -297,8 +295,10 @@ void MTLShaderInterface::prepare_common_shader_inputs() current_input->name_hash = BLI_hash_string(this->get_name_at_offset(shd_ubo.name_offset)); /* Location refers to the index in the ubos_ array. */ current_input->location = ubo_index; - /* Final binding location refers to the buffer binding index within the shader (Relative to - * MTL_uniform_buffer_base_index). */ + /* Binding location refers to the UBO bind slot in + * #MTLContextGlobalShaderPipelineState::ubo_bindings. The buffer bind index [[buffer(N)]] + * within the shader will apply an offset for bound vertex buffers and the default uniform + * PushConstantBlock. */ current_input->binding = shd_ubo.buffer_index; current_input++; } diff --git a/source/blender/gpu/metal/mtl_texture.hh b/source/blender/gpu/metal/mtl_texture.hh index ebc9eb2e00e..28b55306707 100644 --- a/source/blender/gpu/metal/mtl_texture.hh +++ b/source/blender/gpu/metal/mtl_texture.hh @@ -51,9 +51,9 @@ struct TextureUpdateRoutineSpecialisation { uint64_t hash() const { blender::DefaultHash<std::string> string_hasher; - return uint64_t(string_hasher( + return (uint64_t)string_hasher( this->input_data_type + this->output_data_type + - std::to_string((this->component_count_input << 8) + this->component_count_output))); + std::to_string((this->component_count_input << 8) + this->component_count_output)); } }; diff --git a/source/blender/gpu/metal/mtl_texture.mm b/source/blender/gpu/metal/mtl_texture.mm index 32029db6fd9..29dcc8d32ee 100644 --- a/source/blender/gpu/metal/mtl_texture.mm +++ b/source/blender/gpu/metal/mtl_texture.mm @@ -337,20 +337,6 @@ void gpu::MTLTexture::blit(gpu::MTLTexture *dst, GPU_batch_draw(quad); - /* TMP draw with IMM TODO(Metal): Remove this once GPUBatch is supported. */ - GPUVertFormat *imm_format = immVertexFormat(); - uint pos = GPU_vertformat_attr_add(imm_format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); - - immBindShader(shader); - immBegin(GPU_PRIM_TRI_STRIP, 4); - immVertex2f(pos, 1, 0); - immVertex2f(pos, 0, 0); - immVertex2f(pos, 1, 1); - immVertex2f(pos, 0, 1); - immEnd(); - immUnbindProgram(); - /**********************/ - /* restoring old pipeline state. */ GPU_depth_mask(depth_write_prev); GPU_stencil_write_mask_set(stencil_mask_prev); @@ -1472,10 +1458,82 @@ bool gpu::MTLTexture::init_internal() bool gpu::MTLTexture::init_internal(GPUVertBuf *vbo) { - /* Not a valid vertex buffer format, though verifying texture is not set as such - * as this is not supported on Apple Silicon. */ - BLI_assert_msg(this->format_ != GPU_DEPTH24_STENCIL8, - "Apple silicon does not support GPU_DEPTH24_S8"); + if (this->format_ == GPU_DEPTH24_STENCIL8) { + /* Apple Silicon requires GPU_DEPTH32F_STENCIL8 instead of GPU_DEPTH24_STENCIL8. */ + this->format_ = GPU_DEPTH32F_STENCIL8; + } + + MTLPixelFormat mtl_format = gpu_texture_format_to_metal(this->format_); + mtl_max_mips_ = 1; + mipmaps_ = 0; + this->mip_range_set(0, 0); + + /* Create texture from GPUVertBuf's buffer. */ + MTLVertBuf *mtl_vbo = static_cast<MTLVertBuf *>(unwrap(vbo)); + mtl_vbo->bind(); + mtl_vbo->flag_used(); + + /* Get Metal Buffer. */ + id<MTLBuffer> source_buffer = mtl_vbo->get_metal_buffer(); + BLI_assert(source_buffer); + + /* Verify size. */ + if (w_ <= 0) { + MTL_LOG_WARNING("Allocating texture buffer of width 0!\n"); + w_ = 1; + } + + /* Verify Texture and vertex buffer alignment. */ + int bytes_per_pixel = get_mtl_format_bytesize(mtl_format); + int bytes_per_row = bytes_per_pixel * w_; + + MTLContext *mtl_ctx = MTLContext::get(); + uint32_t align_requirement = static_cast<uint32_t>( + [mtl_ctx->device minimumLinearTextureAlignmentForPixelFormat:mtl_format]); + + /* Verify per-vertex size aligns with texture size. */ + const GPUVertFormat *format = GPU_vertbuf_get_format(vbo); + BLI_assert(bytes_per_pixel == format->stride && + "Pixel format stride MUST match the texture format stride -- These being different " + "is likely caused by Metal's VBO padding to a minimum of 4-bytes per-vertex"); + UNUSED_VARS_NDEBUG(format); + + /* Create texture descriptor. */ + BLI_assert(type_ == GPU_TEXTURE_BUFFER); + texture_descriptor_ = [[MTLTextureDescriptor alloc] init]; + texture_descriptor_.pixelFormat = mtl_format; + texture_descriptor_.textureType = MTLTextureTypeTextureBuffer; + texture_descriptor_.width = w_; + texture_descriptor_.height = 1; + texture_descriptor_.depth = 1; + texture_descriptor_.arrayLength = 1; + texture_descriptor_.mipmapLevelCount = mtl_max_mips_; + texture_descriptor_.usage = + MTLTextureUsageShaderRead | MTLTextureUsageShaderWrite | + MTLTextureUsagePixelFormatView; /* TODO(Metal): Optimize usage flags. */ + texture_descriptor_.storageMode = [source_buffer storageMode]; + texture_descriptor_.sampleCount = 1; + texture_descriptor_.cpuCacheMode = [source_buffer cpuCacheMode]; + texture_descriptor_.hazardTrackingMode = [source_buffer hazardTrackingMode]; + + texture_ = [source_buffer + newTextureWithDescriptor:texture_descriptor_ + offset:0 + bytesPerRow:ceil_to_multiple_u(bytes_per_row, align_requirement)]; + aligned_w_ = bytes_per_row / bytes_per_pixel; + + BLI_assert(texture_); + texture_.label = [NSString stringWithUTF8String:this->get_name()]; + is_baked_ = true; + is_dirty_ = false; + resource_mode_ = MTL_TEXTURE_MODE_VBO; + + /* Track Status. */ + vert_buffer_ = mtl_vbo; + vert_buffer_mtl_ = source_buffer; + /* Cleanup. */ + [texture_descriptor_ release]; + texture_descriptor_ = nullptr; return true; } @@ -1522,7 +1580,6 @@ bool gpu::MTLTexture::texture_is_baked() /* Prepare texture parameters after initialization, but before baking. */ void gpu::MTLTexture::prepare_internal() { - /* Derive implicit usage flags for Depth/Stencil attachments. */ if (format_flag_ & GPU_FORMAT_DEPTH || format_flag_ & GPU_FORMAT_STENCIL) { gpu_image_usage_flags_ |= GPU_TEXTURE_USAGE_ATTACHMENT; @@ -1687,7 +1744,7 @@ void gpu::MTLTexture::ensure_baked() /* Determine Resource Mode. */ resource_mode_ = MTL_TEXTURE_MODE_DEFAULT; - /* Create texture. */ + /* Standard texture allocation. */ texture_ = [ctx->device newTextureWithDescriptor:texture_descriptor_]; [texture_descriptor_ release]; diff --git a/source/blender/gpu/opengl/gl_batch.cc b/source/blender/gpu/opengl/gl_batch.cc index ff8867fe3e6..28105e326ee 100644 --- a/source/blender/gpu/opengl/gl_batch.cc +++ b/source/blender/gpu/opengl/gl_batch.cc @@ -272,8 +272,8 @@ void GLBatch::bind(int i_first) #if GPU_TRACK_INDEX_RANGE /* Can be removed if GL 4.3 is required. */ - if (!GLContext::fixed_restart_index_support && (elem != nullptr)) { - glPrimitiveRestartIndex(this->elem_()->restart_index()); + if (!GLContext::fixed_restart_index_support) { + glPrimitiveRestartIndex((elem != nullptr) ? this->elem_()->restart_index() : 0xFFFFFFFFu); } #endif diff --git a/source/blender/gpu/opengl/gl_shader_interface.cc b/source/blender/gpu/opengl/gl_shader_interface.cc index c9432fca561..ef97d74bf81 100644 --- a/source/blender/gpu/opengl/gl_shader_interface.cc +++ b/source/blender/gpu/opengl/gl_shader_interface.cc @@ -200,6 +200,9 @@ static Type gpu_type_from_gl_type(int gl_type) GLShaderInterface::GLShaderInterface(GLuint program) { + GLuint last_program; + glGetIntegerv(GL_CURRENT_PROGRAM, (GLint *)&last_program); + /* Necessary to make #glUniform works. */ glUseProgram(program); @@ -385,6 +388,8 @@ GLShaderInterface::GLShaderInterface(GLuint program) // this->debug_print(); this->sort_inputs(); + + glUseProgram(last_program); } GLShaderInterface::GLShaderInterface(GLuint program, const shader::ShaderCreateInfo &info) @@ -442,6 +447,9 @@ GLShaderInterface::GLShaderInterface(GLuint program, const shader::ShaderCreateI uint32_t name_buffer_offset = 0; /* Necessary to make #glUniform works. TODO(fclem) Remove. */ + GLuint last_program; + glGetIntegerv(GL_CURRENT_PROGRAM, (GLint *)&last_program); + glUseProgram(program); /* Attributes */ @@ -552,6 +560,8 @@ GLShaderInterface::GLShaderInterface(GLuint program, const shader::ShaderCreateI this->sort_inputs(); // this->debug_print(); + + glUseProgram(last_program); } GLShaderInterface::~GLShaderInterface() diff --git a/source/blender/gpu/shaders/compositor/compositor_blur.glsl b/source/blender/gpu/shaders/compositor/compositor_blur.glsl index 4f981c84f59..c7ac620f99b 100644 --- a/source/blender/gpu/shaders/compositor/compositor_blur.glsl +++ b/source/blender/gpu/shaders/compositor/compositor_blur.glsl @@ -18,13 +18,24 @@ vec4 load_input(ivec2 texel) } /* Given the texel in the range [-radius, radius] in both axis, load the appropriate weight from - * the weights texture, where the texel (0, 0) is considered the center of weights texture. */ + * the weights texture, where the given texel (0, 0) corresponds the center of weights texture. + * Note that we load the weights texture inverted along both directions to maintain the shape of + * the weights if it was not symmetrical. To understand why inversion makes sense, consider a 1D + * weights texture whose right half is all ones and whose left half is all zeros. Further, consider + * that we are blurring a single white pixel on a black background. When computing the value of a + * pixel that is to the right of the white pixel, the white pixel will be in the left region of the + * search window, and consequently, without inversion, a zero will be sampled from the left side of + * the weights texture and result will be zero. However, what we expect is that pixels to the right + * of the white pixel will be white, that is, they should sample a weight of 1 from the right side + * of the weights texture, hence the need for inversion. */ vec4 load_weight(ivec2 texel) { - /* Add the radius to transform the texel into the range [0, radius * 2], then divide by the upper - * bound plus one to transform the texel into the normalized range [0, 1] needed to sample the - * weights sampler. Finally, also add 0.5 to sample at the center of the pixels. */ - return texture(weights_tx, (texel + vec2(radius + 0.5)) / (radius * 2 + 1)); + /* Add the radius to transform the texel into the range [0, radius * 2], with an additional 0.5 + * to sample at the center of the pixels, then divide by the upper bound plus one to transform + * the texel into the normalized range [0, 1] needed to sample the weights sampler. Finally, + * invert the textures coordinates by subtracting from 1 to maintain the shape of the weights as + * mentioned in the function description. */ + return texture(weights_tx, 1.0 - ((texel + vec2(radius + 0.5)) / (radius * 2 + 1))); } void main() diff --git a/source/blender/gpu/shaders/compositor/compositor_blur_variable_size.glsl b/source/blender/gpu/shaders/compositor/compositor_blur_variable_size.glsl new file mode 100644 index 00000000000..9383bbf9825 --- /dev/null +++ b/source/blender/gpu/shaders/compositor/compositor_blur_variable_size.glsl @@ -0,0 +1,71 @@ +#pragma BLENDER_REQUIRE(gpu_shader_common_math_utils.glsl) +#pragma BLENDER_REQUIRE(gpu_shader_compositor_texture_utilities.glsl) + +/* Given the texel in the range [-radius, radius] in both axis, load the appropriate weight from + * the weights texture, where the given texel (0, 0) corresponds the center of weights texture. + * Note that we load the weights texture inverted along both directions to maintain the shape of + * the weights if it was not symmetrical. To understand why inversion makes sense, consider a 1D + * weights texture whose right half is all ones and whose left half is all zeros. Further, consider + * that we are blurring a single white pixel on a black background. When computing the value of a + * pixel that is to the right of the white pixel, the white pixel will be in the left region of the + * search window, and consequently, without inversion, a zero will be sampled from the left side of + * the weights texture and result will be zero. However, what we expect is that pixels to the right + * of the white pixel will be white, that is, they should sample a weight of 1 from the right side + * of the weights texture, hence the need for inversion. */ +vec4 load_weight(ivec2 texel, float radius) +{ + /* The center zero texel is always assigned a unit weight regardless of the corresponding weight + * in the weights texture. That's to guarantee that at last the center pixel will be accumulated + * even if the weights texture is zero at its center. */ + if (texel == ivec2(0)) { + return vec4(1.0); + } + + /* Add the radius to transform the texel into the range [0, radius * 2], with an additional 0.5 + * to sample at the center of the pixels, then divide by the upper bound plus one to transform + * the texel into the normalized range [0, 1] needed to sample the weights sampler. Finally, + * invert the textures coordinates by subtracting from 1 to maintain the shape of the weights as + * mentioned in the function description. */ + return texture(weights_tx, 1.0 - ((texel + vec2(radius + 0.5)) / (radius * 2 + 1))); +} + +void main() +{ + ivec2 texel = ivec2(gl_GlobalInvocationID.xy); + + /* The mask input is treated as a boolean. If it is zero, then no blurring happens for this + * pixel. Otherwise, the pixel is blurred normally and the mask value is irrelevant. */ + float mask = texture_load(mask_tx, texel).x; + if (mask == 0.0) { + imageStore(output_img, texel, texture_load(input_tx, texel)); + return; + } + + float center_size = texture_load(size_tx, texel).x * base_size; + + /* Go over the window of the given search radius and accumulate the colors multiplied by their + * respective weights as well as the weights themselves, but only if both the size of the center + * pixel and the size of the candidate pixel are less than both the x and y distances of the + * candidate pixel. */ + vec4 accumulated_color = vec4(0.0); + vec4 accumulated_weight = vec4(0.0); + for (int y = -search_radius; y <= search_radius; y++) { + for (int x = -search_radius; x <= search_radius; x++) { + float candidate_size = texture_load(size_tx, texel + ivec2(x, y)).x * base_size; + + /* Skip accumulation if either the x or y distances of the candidate pixel are larger than + * either the center or candidate pixel size. Note that the max and min functions here denote + * "either" in the aforementioned description. */ + float size = min(center_size, candidate_size); + if (max(abs(x), abs(y)) > size) { + continue; + } + + vec4 weight = load_weight(ivec2(x, y), size); + accumulated_color += texture_load(input_tx, texel + ivec2(x, y)) * weight; + accumulated_weight += weight; + } + } + + imageStore(output_img, texel, safe_divide(accumulated_color, accumulated_weight)); +} diff --git a/source/blender/gpu/shaders/compositor/compositor_normalize.glsl b/source/blender/gpu/shaders/compositor/compositor_normalize.glsl new file mode 100644 index 00000000000..53dfeb01730 --- /dev/null +++ b/source/blender/gpu/shaders/compositor/compositor_normalize.glsl @@ -0,0 +1,10 @@ +#pragma BLENDER_REQUIRE(gpu_shader_compositor_texture_utilities.glsl) + +void main() +{ + ivec2 texel = ivec2(gl_GlobalInvocationID.xy); + float value = texture_load(input_tx, texel).x; + float normalized_value = (value - minimum) * scale; + float clamped_value = clamp(normalized_value, 0.0, 1.0); + imageStore(output_img, texel, vec4(clamped_value)); +} diff --git a/source/blender/gpu/shaders/compositor/compositor_parallel_reduction.glsl b/source/blender/gpu/shaders/compositor/compositor_parallel_reduction.glsl new file mode 100644 index 00000000000..f6f84aa24c1 --- /dev/null +++ b/source/blender/gpu/shaders/compositor/compositor_parallel_reduction.glsl @@ -0,0 +1,98 @@ +#pragma BLENDER_REQUIRE(gpu_shader_compositor_texture_utilities.glsl) + +/* This shader reduces the given texture into a smaller texture of a size equal to the number of + * work groups. In particular, each work group reduces its contents into a single value and writes + * that value to a single pixel in the output image. The shader can be dispatched multiple times to + * eventually reduce the image into a single pixel. + * + * The shader works by loading the whole data of each work group into a linear array, then it + * reduces the second half of the array onto the first half of the array, then it reduces the + * second quarter of the array onto the first quarter or the array, and so on until only one + * element remains. The following figure illustrates the process for sum reduction on 8 elements. + * + * .---. .---. .---. .---. .---. .---. .---. .---. + * | 0 | | 1 | | 2 | | 3 | | 4 | | 5 | | 6 | | 7 | Original data. + * '---' '---' '---' '---' '---' '---' '---' '---' + * |.____|_____|_____|_____| | | | + * || |.____|_____|___________| | | + * || || |.____|_________________| | + * || || || |.______________________| <--First reduction. Stride = 4. + * || || || || + * .---. .---. .---. .----. + * | 4 | | 6 | | 8 | | 10 | <--Data after first reduction. + * '---' '---' '---' '----' + * |.____|_____| | + * || |.__________| <--Second reduction. Stride = 2. + * || || + * .----. .----. + * | 12 | | 16 | <--Data after second reduction. + * '----' '----' + * |.____| + * || <--Third reduction. Stride = 1. + * .----. + * | 28 | + * '----' <--Data after third reduction. + * + * + * The shader is generic enough to implement many types of reductions. This is done by using macros + * that the developer should define to implement a certain reduction operation. Those include, + * TYPE, IDENTITY, INITIALIZE, LOAD, and REDUCE. See the implementation below for more information + * as well as the compositor_parallel_reduction_info.hh for example reductions operations. */ + +/* Doing the reduction in shared memory is faster, so create a shared array where the whole data + * of the work group will be loaded and reduced. The 2D structure of the work group is irrelevant + * for reduction, so we just load the data in a 1D array to simplify reduction. The developer is + * expected to define the TYPE macro to be a float or a vec4, depending on the type of data being + * reduced. */ +const uint reduction_size = gl_WorkGroupSize.x * gl_WorkGroupSize.y; +shared TYPE reduction_data[reduction_size]; + +void main() +{ + /* Load the data from the texture, while returning IDENTITY for out of bound coordinates. The + * developer is expected to define the IDENTITY macro to be a vec4 that does not affect the + * output of the reduction. For instance, sum reductions have an identity of vec4(0.0), while + * max value reductions have an identity of vec4(FLT_MIN). */ + vec4 value = texture_load(input_tx, ivec2(gl_GlobalInvocationID.xy), IDENTITY); + + /* Initialize the shared array given the previously loaded value. This step can be different + * depending on whether this is the initial reduction pass or a latter one. Indeed, the input + * texture for the initial reduction is the source texture itself, while the input texture to a + * latter reduction pass is an intermediate texture after one or more reductions have happened. + * This is significant because the data being reduced might be computed from the original data + * and different from it, for instance, when summing the luminance of an image, the original data + * is a vec4 color, while the reduced data is a float luminance value. So for the initial + * reduction pass, the luminance will be computed from the color, reduced, then stored into an + * intermediate float texture. On the other hand, for latter reduction passes, the luminance will + * be loaded directly and reduced without extra processing. So the developer is expected to + * define the INITIALIZE and LOAD macros to be expressions that derive the needed value from the + * loaded value for the initial reduction pass and latter ones respectively. */ + reduction_data[gl_LocalInvocationIndex] = is_initial_reduction ? INITIALIZE(value) : LOAD(value); + + /* Reduce the reduction data by half on every iteration until only one element remains. See the + * above figure for an intuitive understanding of the stride value. */ + for (uint stride = reduction_size / 2; stride > 0; stride /= 2) { + barrier(); + + /* Only the threads up to the current stride should be active as can be seen in the diagram + * above. */ + if (gl_LocalInvocationIndex >= stride) { + continue; + } + + /* Reduce each two elements that are stride apart, writing the result to the element with the + * lower index, as can be seen in the diagram above. The developer is expected to define the + * REDUCE macro to be a commutative and associative binary operator suitable for parallel + * reduction. */ + reduction_data[gl_LocalInvocationIndex] = REDUCE( + reduction_data[gl_LocalInvocationIndex], reduction_data[gl_LocalInvocationIndex + stride]); + } + + /* Finally, the result of the reduction is available as the first element in the reduction data, + * write it to the pixel corresponding to the work group, making sure only the one thread writes + * it. */ + barrier(); + if (gl_LocalInvocationIndex == 0) { + imageStore(output_img, ivec2(gl_WorkGroupID.xy), vec4(reduction_data[0])); + } +} diff --git a/source/blender/gpu/shaders/compositor/compositor_tone_map_photoreceptor.glsl b/source/blender/gpu/shaders/compositor/compositor_tone_map_photoreceptor.glsl new file mode 100644 index 00000000000..167006585ca --- /dev/null +++ b/source/blender/gpu/shaders/compositor/compositor_tone_map_photoreceptor.glsl @@ -0,0 +1,22 @@ +#pragma BLENDER_REQUIRE(gpu_shader_compositor_texture_utilities.glsl) + +/* Tone mapping based on equation (1) and the trilinear interpolation between equations (6) and (7) + * from Reinhard, Erik, and Kate Devlin. "Dynamic range reduction inspired by photoreceptor + * physiology." IEEE transactions on visualization and computer graphics 11.1 (2005): 13-24. */ +void main() +{ + ivec2 texel = ivec2(gl_GlobalInvocationID.xy); + + vec4 input_color = texture_load(input_tx, texel); + float input_luminance = dot(input_color.rgb, luminance_coefficients); + + /* Trilinear interpolation between equations (6) and (7) from Reinhard's 2005 paper. */ + vec4 local_adaptation_level = mix(vec4(input_luminance), input_color, chromatic_adaptation); + vec4 adaptation_level = mix(global_adaptation_level, local_adaptation_level, light_adaptation); + + /* Equation (1) from Reinhard's 2005 paper, assuming Vmax is 1. */ + vec4 semi_saturation = pow(intensity * adaptation_level, vec4(contrast)); + vec4 tone_mapped_color = input_color / (input_color + semi_saturation); + + imageStore(output_img, texel, vec4(tone_mapped_color.rgb, input_color.a)); +} diff --git a/source/blender/gpu/shaders/compositor/compositor_tone_map_simple.glsl b/source/blender/gpu/shaders/compositor/compositor_tone_map_simple.glsl new file mode 100644 index 00000000000..ce42d021dd1 --- /dev/null +++ b/source/blender/gpu/shaders/compositor/compositor_tone_map_simple.glsl @@ -0,0 +1,26 @@ +#pragma BLENDER_REQUIRE(gpu_shader_compositor_texture_utilities.glsl) +#pragma BLENDER_REQUIRE(gpu_shader_common_math_utils.glsl) + +/* Tone mapping based on equation (3) from Reinhard, Erik, et al. "Photographic tone reproduction + * for digital images." Proceedings of the 29th annual conference on Computer graphics and + * interactive techniques. 2002. */ +void main() +{ + ivec2 texel = ivec2(gl_GlobalInvocationID.xy); + + vec4 input_color = texture_load(input_tx, texel); + + /* Equation (2) from Reinhard's 2002 paper. */ + vec4 scaled_color = input_color * luminance_scale; + + /* Equation (3) from Reinhard's 2002 paper, but with the 1 replaced with the blend factor for + * more flexibility. See ToneMapOperation::compute_luminance_scale_blend_factor. */ + vec4 denominator = luminance_scale_blend_factor + scaled_color; + vec4 tone_mapped_color = safe_divide(scaled_color, denominator); + + if (inverse_gamma != 0.0) { + tone_mapped_color = pow(max(tone_mapped_color, vec4(0.0)), vec4(inverse_gamma)); + } + + imageStore(output_img, texel, vec4(tone_mapped_color.rgb, input_color.a)); +} diff --git a/source/blender/gpu/shaders/compositor/infos/compositor_blur_variable_size_info.hh b/source/blender/gpu/shaders/compositor/infos/compositor_blur_variable_size_info.hh new file mode 100644 index 00000000000..05b6385fd1e --- /dev/null +++ b/source/blender/gpu/shaders/compositor/infos/compositor_blur_variable_size_info.hh @@ -0,0 +1,15 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "gpu_shader_create_info.hh" + +GPU_SHADER_CREATE_INFO(compositor_blur_variable_size) + .local_group_size(16, 16) + .push_constant(Type::FLOAT, "base_size") + .push_constant(Type::INT, "search_radius") + .sampler(0, ImageType::FLOAT_2D, "input_tx") + .sampler(1, ImageType::FLOAT_2D, "weights_tx") + .sampler(2, ImageType::FLOAT_2D, "size_tx") + .sampler(3, ImageType::FLOAT_2D, "mask_tx") + .image(0, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img") + .compute_source("compositor_blur_variable_size.glsl") + .do_static_compilation(true); diff --git a/source/blender/gpu/shaders/compositor/infos/compositor_normalize_info.hh b/source/blender/gpu/shaders/compositor/infos/compositor_normalize_info.hh new file mode 100644 index 00000000000..02fdc424014 --- /dev/null +++ b/source/blender/gpu/shaders/compositor/infos/compositor_normalize_info.hh @@ -0,0 +1,12 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "gpu_shader_create_info.hh" + +GPU_SHADER_CREATE_INFO(compositor_normalize) + .local_group_size(16, 16) + .push_constant(Type::FLOAT, "minimum") + .push_constant(Type::FLOAT, "scale") + .sampler(0, ImageType::FLOAT_2D, "input_tx") + .image(0, GPU_R16F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img") + .compute_source("compositor_normalize.glsl") + .do_static_compilation(true); diff --git a/source/blender/gpu/shaders/compositor/infos/compositor_parallel_reduction_info.hh b/source/blender/gpu/shaders/compositor/infos/compositor_parallel_reduction_info.hh new file mode 100644 index 00000000000..e2252b14758 --- /dev/null +++ b/source/blender/gpu/shaders/compositor/infos/compositor_parallel_reduction_info.hh @@ -0,0 +1,149 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "gpu_shader_create_info.hh" + +GPU_SHADER_CREATE_INFO(compositor_parallel_reduction_shared) + .local_group_size(16, 16) + .push_constant(Type::BOOL, "is_initial_reduction") + .sampler(0, ImageType::FLOAT_2D, "input_tx") + .compute_source("compositor_parallel_reduction.glsl"); + +/* -------------------------------------------------------------------- + * Sum Reductions. + */ + +GPU_SHADER_CREATE_INFO(compositor_sum_shared) + .additional_info("compositor_parallel_reduction_shared") + .define("IDENTITY", "vec4(0.0)") + .define("REDUCE(lhs, rhs)", "lhs + rhs"); + +GPU_SHADER_CREATE_INFO(compositor_sum_float_shared) + .additional_info("compositor_sum_shared") + .image(0, GPU_R32F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img") + .define("TYPE", "float") + .define("LOAD(value)", "value.x"); + +GPU_SHADER_CREATE_INFO(compositor_sum_red) + .additional_info("compositor_sum_float_shared") + .define("INITIALIZE(value)", "value.r") + .do_static_compilation(true); + +GPU_SHADER_CREATE_INFO(compositor_sum_green) + .additional_info("compositor_sum_float_shared") + .define("INITIALIZE(value)", "value.g") + .do_static_compilation(true); + +GPU_SHADER_CREATE_INFO(compositor_sum_blue) + .additional_info("compositor_sum_float_shared") + .define("INITIALIZE(value)", "value.b") + .do_static_compilation(true); + +GPU_SHADER_CREATE_INFO(compositor_sum_luminance) + .additional_info("compositor_sum_float_shared") + .push_constant(Type::VEC3, "luminance_coefficients") + .define("INITIALIZE(value)", "dot(value.rgb, luminance_coefficients)") + .do_static_compilation(true); + +GPU_SHADER_CREATE_INFO(compositor_sum_log_luminance) + .additional_info("compositor_sum_float_shared") + .push_constant(Type::VEC3, "luminance_coefficients") + .define("INITIALIZE(value)", "log(max(dot(value.rgb, luminance_coefficients), 1e-5))") + .do_static_compilation(true); + +GPU_SHADER_CREATE_INFO(compositor_sum_color) + .additional_info("compositor_sum_shared") + .image(0, GPU_RGBA32F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img") + .define("TYPE", "vec4") + .define("INITIALIZE(value)", "value") + .define("LOAD(value)", "value") + .do_static_compilation(true); + +/* -------------------------------------------------------------------- + * Sum Of Squared Difference Reductions. + */ + +GPU_SHADER_CREATE_INFO(compositor_sum_squared_difference_float_shared) + .additional_info("compositor_parallel_reduction_shared") + .image(0, GPU_R32F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img") + .push_constant(Type::FLOAT, "subtrahend") + .define("TYPE", "float") + .define("IDENTITY", "vec4(subtrahend)") + .define("LOAD(value)", "value.x") + .define("REDUCE(lhs, rhs)", "lhs + rhs"); + +GPU_SHADER_CREATE_INFO(compositor_sum_red_squared_difference) + .additional_info("compositor_sum_squared_difference_float_shared") + .define("INITIALIZE(value)", "pow(value.r - subtrahend, 2.0)") + .do_static_compilation(true); + +GPU_SHADER_CREATE_INFO(compositor_sum_green_squared_difference) + .additional_info("compositor_sum_squared_difference_float_shared") + .define("INITIALIZE(value)", "pow(value.g - subtrahend, 2.0)") + .do_static_compilation(true); + +GPU_SHADER_CREATE_INFO(compositor_sum_blue_squared_difference) + .additional_info("compositor_sum_squared_difference_float_shared") + .define("INITIALIZE(value)", "pow(value.b - subtrahend, 2.0)") + .do_static_compilation(true); + +GPU_SHADER_CREATE_INFO(compositor_sum_luminance_squared_difference) + .additional_info("compositor_sum_squared_difference_float_shared") + .push_constant(Type::VEC3, "luminance_coefficients") + .define("INITIALIZE(value)", "pow(dot(value.rgb, luminance_coefficients) - subtrahend, 2.0)") + .do_static_compilation(true); + +/* -------------------------------------------------------------------- + * Maximum Reductions. + */ + +GPU_SHADER_CREATE_INFO(compositor_maximum_luminance) + .additional_info("compositor_parallel_reduction_shared") + .typedef_source("common_math_lib.glsl") + .image(0, GPU_R32F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img") + .push_constant(Type::VEC3, "luminance_coefficients") + .define("TYPE", "float") + .define("IDENTITY", "vec4(FLT_MIN)") + .define("INITIALIZE(value)", "dot(value.rgb, luminance_coefficients)") + .define("LOAD(value)", "value.x") + .define("REDUCE(lhs, rhs)", "max(lhs, rhs)") + .do_static_compilation(true); + +GPU_SHADER_CREATE_INFO(compositor_maximum_float_in_range) + .additional_info("compositor_parallel_reduction_shared") + .image(0, GPU_R32F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img") + .push_constant(Type::FLOAT, "lower_bound") + .push_constant(Type::FLOAT, "upper_bound") + .define("TYPE", "float") + .define("IDENTITY", "vec4(lower_bound)") + .define("INITIALIZE(v)", "((v.x <= upper_bound) && (v.x >= lower_bound)) ? v.x : lower_bound") + .define("LOAD(value)", "value.x") + .define("REDUCE(lhs, rhs)", "((rhs > lhs) && (rhs <= upper_bound)) ? rhs : lhs") + .do_static_compilation(true); + +/* -------------------------------------------------------------------- + * Minimum Reductions. + */ + +GPU_SHADER_CREATE_INFO(compositor_minimum_luminance) + .additional_info("compositor_parallel_reduction_shared") + .typedef_source("common_math_lib.glsl") + .image(0, GPU_R32F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img") + .push_constant(Type::VEC3, "luminance_coefficients") + .define("TYPE", "float") + .define("IDENTITY", "vec4(FLT_MAX)") + .define("INITIALIZE(value)", "dot(value.rgb, luminance_coefficients)") + .define("LOAD(value)", "value.x") + .define("REDUCE(lhs, rhs)", "min(lhs, rhs)") + .do_static_compilation(true); + +GPU_SHADER_CREATE_INFO(compositor_minimum_float_in_range) + .additional_info("compositor_parallel_reduction_shared") + .image(0, GPU_R32F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img") + .push_constant(Type::FLOAT, "lower_bound") + .push_constant(Type::FLOAT, "upper_bound") + .define("TYPE", "float") + .define("IDENTITY", "vec4(upper_bound)") + .define("INITIALIZE(v)", "((v.x <= upper_bound) && (v.x >= lower_bound)) ? v.x : upper_bound") + .define("LOAD(value)", "value.x") + .define("REDUCE(lhs, rhs)", "((rhs < lhs) && (rhs >= lower_bound)) ? rhs : lhs") + .do_static_compilation(true); diff --git a/source/blender/gpu/shaders/compositor/infos/compositor_tone_map_photoreceptor_info.hh b/source/blender/gpu/shaders/compositor/infos/compositor_tone_map_photoreceptor_info.hh new file mode 100644 index 00000000000..a460c9d58a6 --- /dev/null +++ b/source/blender/gpu/shaders/compositor/infos/compositor_tone_map_photoreceptor_info.hh @@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "gpu_shader_create_info.hh" + +GPU_SHADER_CREATE_INFO(compositor_tone_map_photoreceptor) + .local_group_size(16, 16) + .push_constant(Type::VEC4, "global_adaptation_level") + .push_constant(Type::FLOAT, "contrast") + .push_constant(Type::FLOAT, "intensity") + .push_constant(Type::FLOAT, "chromatic_adaptation") + .push_constant(Type::FLOAT, "light_adaptation") + .push_constant(Type::VEC3, "luminance_coefficients") + .sampler(0, ImageType::FLOAT_2D, "input_tx") + .image(0, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img") + .compute_source("compositor_tone_map_photoreceptor.glsl") + .do_static_compilation(true); diff --git a/source/blender/gpu/shaders/compositor/infos/compositor_tone_map_simple_info.hh b/source/blender/gpu/shaders/compositor/infos/compositor_tone_map_simple_info.hh new file mode 100644 index 00000000000..2b220af9460 --- /dev/null +++ b/source/blender/gpu/shaders/compositor/infos/compositor_tone_map_simple_info.hh @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "gpu_shader_create_info.hh" + +GPU_SHADER_CREATE_INFO(compositor_tone_map_simple) + .local_group_size(16, 16) + .push_constant(Type::FLOAT, "luminance_scale") + .push_constant(Type::FLOAT, "luminance_scale_blend_factor") + .push_constant(Type::FLOAT, "inverse_gamma") + .sampler(0, ImageType::FLOAT_2D, "input_tx") + .image(0, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img") + .compute_source("compositor_tone_map_simple.glsl") + .do_static_compilation(true); diff --git a/source/blender/gpu/shaders/gpu_shader_icon_frag.glsl b/source/blender/gpu/shaders/gpu_shader_icon_frag.glsl new file mode 100644 index 00000000000..4452349f23c --- /dev/null +++ b/source/blender/gpu/shaders/gpu_shader_icon_frag.glsl @@ -0,0 +1,42 @@ +/** + * Draw the icons, leaving a semi-transparent rectangle on top of the icon. + * + * The top-left corner of the rectangle is rounded and drawned with anti-alias. + * The anti-alias is done by transitioning from the outer to the inner radius of + * the rounded corner, and the rectangle sides. + */ + +void main() +{ + /* Top-left rounded corner parameters. */ + const float circle_radius_outer = 0.1; + const float circle_radius_inner = 0.075; + + /** + * Add a bit transparency to see a bit of the icon, without + * getting on the way of readability. */ + const float mask_transparency = 0.25; + + vec2 circle_center = vec2(circle_radius_outer - text_width, 0.5); + fragColor = texture(image, texCoord_interp) * color; + + /* radius in icon space (1 is the icon width). */ + float radius = length(mask_coord_interp - circle_center); + float mask = smoothstep(circle_radius_inner, circle_radius_outer, radius); + + bool lower_half = mask_coord_interp.y < circle_center.y; + bool right_half = mask_coord_interp.x > circle_center.x; + + if (right_half && mask_coord_interp.y < circle_center.y + circle_radius_outer) { + mask = smoothstep(circle_center.y + circle_radius_inner, + circle_center.y + circle_radius_outer, + mask_coord_interp.y); + } + if (lower_half && mask_coord_interp.x > circle_center.x - circle_radius_outer) { + mask = smoothstep(circle_center.x - circle_radius_inner, + circle_center.x - circle_radius_outer, + mask_coord_interp.x); + } + + fragColor = mix(vec4(0.0), fragColor, max(mask_transparency, mask)); +} diff --git a/source/blender/gpu/shaders/gpu_shader_icon_vert.glsl b/source/blender/gpu/shaders/gpu_shader_icon_vert.glsl new file mode 100644 index 00000000000..25f64bfe0b6 --- /dev/null +++ b/source/blender/gpu/shaders/gpu_shader_icon_vert.glsl @@ -0,0 +1,37 @@ +/** + * Simple shader that just draw one icon at the specified location + * does not need any vertex input (producing less call to immBegin/End) + */ + +void main() +{ + vec2 uv; + vec2 co; + + if (gl_VertexID == 0) { + co = rect_geom.xw; + uv = rect_icon.xw; + mask_coord_interp = vec2(0, 1); + } + else if (gl_VertexID == 1) { + co = rect_geom.xy; + uv = rect_icon.xy; + mask_coord_interp = vec2(0, 0); + } + else if (gl_VertexID == 2) { + co = rect_geom.zw; + uv = rect_icon.zw; + mask_coord_interp = vec2(1, 1); + } + else { + co = rect_geom.zy; + uv = rect_icon.zy; + mask_coord_interp = vec2(1, 0); + } + + /* Put origin in lower right corner. */ + mask_coord_interp.x -= 1; + + gl_Position = ModelViewProjectionMatrix * vec4(co, 0.0f, 1.0f); + texCoord_interp = uv; +} diff --git a/source/blender/gpu/shaders/infos/gpu_interface_info.hh b/source/blender/gpu/shaders/infos/gpu_interface_info.hh index d77c65e48a7..060def16f81 100644 --- a/source/blender/gpu/shaders/infos/gpu_interface_info.hh +++ b/source/blender/gpu/shaders/infos/gpu_interface_info.hh @@ -18,3 +18,6 @@ GPU_SHADER_INTERFACE_INFO(smooth_radii_outline_iface, "").smooth(Type::VEC4, "ra GPU_SHADER_INTERFACE_INFO(flat_color_smooth_tex_coord_interp_iface, "") .flat(Type::VEC4, "finalColor") .smooth(Type::VEC2, "texCoord_interp"); +GPU_SHADER_INTERFACE_INFO(smooth_icon_interp_iface, "") + .smooth(Type::VEC2, "texCoord_interp") + .smooth(Type::VEC2, "mask_coord_interp"); diff --git a/source/blender/gpu/shaders/infos/gpu_shader_icon_info.hh b/source/blender/gpu/shaders/infos/gpu_shader_icon_info.hh new file mode 100644 index 00000000000..3d4077bdb09 --- /dev/null +++ b/source/blender/gpu/shaders/infos/gpu_shader_icon_info.hh @@ -0,0 +1,22 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2022 Blender Foundation. All rights reserved. */ + +/** \file + * \ingroup gpu + */ + +#include "gpu_interface_info.hh" +#include "gpu_shader_create_info.hh" + +GPU_SHADER_CREATE_INFO(gpu_shader_icon) + .vertex_out(smooth_icon_interp_iface) + .fragment_out(0, Type::VEC4, "fragColor") + .push_constant(Type::MAT4, "ModelViewProjectionMatrix") + .push_constant(Type::VEC4, "color") + .push_constant(Type::VEC4, "rect_icon") + .push_constant(Type::VEC4, "rect_geom") + .push_constant(Type::FLOAT, "text_width") + .sampler(0, ImageType::FLOAT_2D, "image") + .vertex_source("gpu_shader_icon_vert.glsl") + .fragment_source("gpu_shader_icon_frag.glsl") + .do_static_compilation(true); diff --git a/source/blender/ikplugin/intern/itasc_plugin.cpp b/source/blender/ikplugin/intern/itasc_plugin.cpp index 78fb75ddb40..9317f14b7f1 100644 --- a/source/blender/ikplugin/intern/itasc_plugin.cpp +++ b/source/blender/ikplugin/intern/itasc_plugin.cpp @@ -1783,7 +1783,7 @@ static void execute_scene(struct Depsgraph *depsgraph, for (i = ikscene->targets.size(); i > 0; i--) { IK_Target *iktarget = ikscene->targets[i - 1]; if (!(iktarget->blenderConstraint->flag & CONSTRAINT_OFF) && iktarget->constraint) { - unsigned int nvalues; + uint nvalues; const iTaSC::ConstraintValues *values; values = iktarget->constraint->getControlParameters(&nvalues); iktarget->errorCallback(values, nvalues, iktarget); diff --git a/source/blender/imbuf/IMB_imbuf.h b/source/blender/imbuf/IMB_imbuf.h index 7e652e31506..5c76dfe52df 100644 --- a/source/blender/imbuf/IMB_imbuf.h +++ b/source/blender/imbuf/IMB_imbuf.h @@ -895,6 +895,13 @@ eGPUTextureFormat IMB_gpu_get_texture_format(const struct ImBuf *ibuf, bool use_grayscale); /** + * Ensures that values stored in the float rect can safely loaded into half float gpu textures. + * + * Does nothing when given image_buffer doesn't contain a float rect. + */ +void IMB_gpu_clamp_half_float(struct ImBuf *image_buffer); + +/** * The `ibuf` is only here to detect the storage type. The produced texture will have undefined * content. It will need to be populated by using #IMB_update_gpu_texture_sub(). */ diff --git a/source/blender/imbuf/intern/anim_movie.c b/source/blender/imbuf/intern/anim_movie.c index 4f8fe0d8cdc..71e0d0fe11e 100644 --- a/source/blender/imbuf/intern/anim_movie.c +++ b/source/blender/imbuf/intern/anim_movie.c @@ -1093,12 +1093,14 @@ static int ffmpeg_seek_by_byte(AVFormatContext *pFormatCtx) static int64_t ffmpeg_get_seek_pts(struct anim *anim, int64_t pts_to_search) { - /* Step back half a frame position to make sure that we get the requested - * frame and not the one after it. This is a workaround as ffmpeg will - * sometimes not seek to a frame after the requested pts even if - * AVSEEK_FLAG_BACKWARD is specified. + /* FFmpeg seeks internally using DTS values instead of PTS. In some files DTS and PTS values are + * offset and sometimes ffmpeg fails to take this into account when seeking. + * Therefore we need to seek backwards a certain offset to make sure the frame we want is in + * front of us. It is not possible to determine the exact needed offset, this value is determined + * experimentally. Note: Too big offset can impact performance. Current 3 frame offset has no + * measurable impact. */ - return pts_to_search - (ffmpeg_steps_per_frame_get(anim) / 2); + return pts_to_search - (ffmpeg_steps_per_frame_get(anim) * 3); } /* This gives us an estimate of which pts our requested frame will have. diff --git a/source/blender/imbuf/intern/colormanagement.c b/source/blender/imbuf/intern/colormanagement.c index ea5f4ec275d..5e132826a4c 100644 --- a/source/blender/imbuf/intern/colormanagement.c +++ b/source/blender/imbuf/intern/colormanagement.c @@ -668,7 +668,7 @@ void colormanagement_init(void) configdir = BKE_appdir_folder_id(BLENDER_DATAFILES, "colormanagement"); if (configdir) { - BLI_join_dirfile(configfile, sizeof(configfile), configdir, BCM_CONFIG_FILE); + BLI_path_join(configfile, sizeof(configfile), configdir, BCM_CONFIG_FILE); #ifdef WIN32 { diff --git a/source/blender/imbuf/intern/indexer.c b/source/blender/imbuf/intern/indexer.c index 735472b6bdf..eaa72441fb6 100644 --- a/source/blender/imbuf/intern/indexer.c +++ b/source/blender/imbuf/intern/indexer.c @@ -426,7 +426,7 @@ static bool get_proxy_filepath(struct anim *anim, return false; } - BLI_join_dirfile(filepath, FILE_MAXFILE + FILE_MAXDIR, index_dir, proxy_name); + BLI_path_join(filepath, FILE_MAXFILE + FILE_MAXDIR, index_dir, proxy_name); return true; } @@ -457,7 +457,7 @@ static void get_tc_filename(struct anim *anim, IMB_Timecode_Type tc, char *filep get_index_dir(anim, index_dir, sizeof(index_dir)); - BLI_join_dirfile(filepath, FILE_MAXFILE + FILE_MAXDIR, index_dir, index_name); + BLI_path_join(filepath, FILE_MAXFILE + FILE_MAXDIR, index_dir, index_name); } /* ---------------------------------------------------------------------- @@ -498,7 +498,9 @@ static struct proxy_output_ctx *alloc_proxy_output_ffmpeg( rv->anim = anim; get_proxy_filepath(rv->anim, rv->proxy_size, filepath, true); - BLI_make_existing_file(filepath); + if (!BLI_make_existing_file(filepath)) { + return NULL; + } rv->of = avformat_alloc_context(); rv->of->oformat = av_guess_format("avi", NULL, NULL); @@ -905,6 +907,14 @@ static IndexBuildContext *index_ffmpeg_create_context(struct anim *anim, } } + if (context->proxy_ctx[0] == NULL && context->proxy_ctx[1] == NULL && + context->proxy_ctx[2] == NULL && context->proxy_ctx[3] == NULL) { + avformat_close_input(&context->iFormatCtx); + avcodec_free_context(&context->iCodecCtx); + MEM_freeN(context); + return NULL; /* Nothing to transcode. */ + } + for (i = 0; i < num_indexers; i++) { if (tcs_in_use & tc_types[i]) { char filepath[FILE_MAX]; diff --git a/source/blender/imbuf/intern/transform.cc b/source/blender/imbuf/intern/transform.cc index edded204527..6d3452c64db 100644 --- a/source/blender/imbuf/intern/transform.cc +++ b/source/blender/imbuf/intern/transform.cc @@ -134,7 +134,7 @@ class NoDiscard : public BaseDiscard { * * Will never discard any pixels. */ - bool should_discard(const TransformUserData & /*user_data*/, const float UNUSED(uv[2])) override + bool should_discard(const TransformUserData & /*user_data*/, const float /*uv*/[2]) override { return false; } diff --git a/source/blender/imbuf/intern/util_gpu.c b/source/blender/imbuf/intern/util_gpu.c index 6f1275e1812..35cdefbaaeb 100644 --- a/source/blender/imbuf/intern/util_gpu.c +++ b/source/blender/imbuf/intern/util_gpu.c @@ -174,6 +174,7 @@ static void *imb_gpu_get_data(const ImBuf *ibuf, /* Other colorspace, store as float texture to avoid precision loss. */ data_rect = MEM_mallocN(sizeof(float[4]) * ibuf->x * ibuf->y, __func__); *r_freedata = freedata = true; + is_float_rect = true; if (data_rect == NULL) { return NULL; @@ -300,6 +301,16 @@ GPUTexture *IMB_create_gpu_texture(const char *name, int size[2] = {GPU_texture_size_with_limit(ibuf->x), GPU_texture_size_with_limit(ibuf->y)}; bool do_rescale = (ibuf->x != size[0]) || (ibuf->y != size[1]); + /* Correct the smaller size to maintain the original aspect ratio of the image. */ + if (do_rescale && ibuf->x != ibuf->y) { + if (size[0] > size[1]) { + size[1] = (int)(ibuf->y * ((float)size[0] / ibuf->x)); + } + else { + size[0] = (int)(ibuf->x * ((float)size[1] / ibuf->y)); + } + } + #ifdef WITH_DDS if (ibuf->ftype == IMB_FTYPE_DDS) { eGPUTextureFormat compressed_format; @@ -370,3 +381,19 @@ eGPUTextureFormat IMB_gpu_get_texture_format(const ImBuf *ibuf, return gpu_texture_format; } + +void IMB_gpu_clamp_half_float(ImBuf *image_buffer) +{ + const float half_min = -65504; + const float half_max = 65504; + if (!image_buffer->rect_float) { + return; + } + + int rect_float_len = image_buffer->x * image_buffer->y * + (image_buffer->channels == 0 ? 4 : image_buffer->channels); + + for (int i = 0; i < rect_float_len; i++) { + image_buffer->rect_float[i] = clamp_f(image_buffer->rect_float[i], half_min, half_max); + } +} diff --git a/source/blender/io/alembic/intern/abc_reader_mesh.cc b/source/blender/io/alembic/intern/abc_reader_mesh.cc index f08514dc45c..2531bd62609 100644 --- a/source/blender/io/alembic/intern/abc_reader_mesh.cc +++ b/source/blender/io/alembic/intern/abc_reader_mesh.cc @@ -763,7 +763,7 @@ Mesh *AbcMeshReader::read_mesh(Mesh *existing_mesh, std::map<std::string, int> mat_map; bke::MutableAttributeAccessor attributes = new_mesh->attributes_for_write(); bke::SpanAttributeWriter<int> material_indices = - attributes.lookup_or_add_for_write_only_span<int>("material_index", ATTR_DOMAIN_FACE); + attributes.lookup_or_add_for_write_span<int>("material_index", ATTR_DOMAIN_FACE); assign_facesets_to_material_indices(sample_sel, material_indices.span, mat_map); material_indices.finish(); } @@ -823,8 +823,8 @@ void AbcMeshReader::readFaceSetsSample(Main *bmain, Mesh *mesh, const ISampleSel { std::map<std::string, int> mat_map; bke::MutableAttributeAccessor attributes = mesh->attributes_for_write(); - bke::SpanAttributeWriter<int> material_indices = - attributes.lookup_or_add_for_write_only_span<int>("material_index", ATTR_DOMAIN_FACE); + bke::SpanAttributeWriter<int> material_indices = attributes.lookup_or_add_for_write_span<int>( + "material_index", ATTR_DOMAIN_FACE); assign_facesets_to_material_indices(sample_sel, material_indices.span, mat_map); material_indices.finish(); utils::assign_materials(bmain, m_object, mat_map); diff --git a/source/blender/io/alembic/intern/alembic_capi.cc b/source/blender/io/alembic/intern/alembic_capi.cc index 39595089109..b92ce5b4cfb 100644 --- a/source/blender/io/alembic/intern/alembic_capi.cc +++ b/source/blender/io/alembic/intern/alembic_capi.cc @@ -608,15 +608,11 @@ static void import_endjob(void *user_data) lc = BKE_layer_collection_get_active(view_layer); - /* Add all objects to the collection (don't do sync for each object). */ - BKE_layer_collection_resync_forbid(); for (AbcObjectReader *reader : data->readers) { Object *ob = reader->object(); BKE_collection_object_add(data->bmain, lc->collection, ob); } - /* Sync the collection, and do view layer operations. */ - BKE_layer_collection_resync_allow(); - BKE_main_collection_sync(data->bmain); + /* Sync and do the view layer operations. */ BKE_view_layer_synced_ensure(scene, view_layer); for (AbcObjectReader *reader : data->readers) { Object *ob = reader->object(); diff --git a/source/blender/io/collada/DocumentExporter.cpp b/source/blender/io/collada/DocumentExporter.cpp index 56adbca13bd..07392e9c4ce 100644 --- a/source/blender/io/collada/DocumentExporter.cpp +++ b/source/blender/io/collada/DocumentExporter.cpp @@ -145,7 +145,7 @@ static COLLADABU::NativeString make_temp_filepath(const char *name, const char * name = "untitled"; } - BLI_join_dirfile(tempfile, sizeof(tempfile), BKE_tempdir_session(), name); + BLI_path_join(tempfile, sizeof(tempfile), BKE_tempdir_session(), name); if (extension) { BLI_path_extension_ensure(tempfile, FILE_MAX, extension); diff --git a/source/blender/io/collada/DocumentImporter.cpp b/source/blender/io/collada/DocumentImporter.cpp index 660bbd7edb2..5e432682564 100644 --- a/source/blender/io/collada/DocumentImporter.cpp +++ b/source/blender/io/collada/DocumentImporter.cpp @@ -937,7 +937,7 @@ bool DocumentImporter::writeImage(const COLLADAFW::Image *image) const char *workpath; BLI_split_dir_part(this->import_settings->filepath, dir, sizeof(dir)); - BLI_join_dirfile(absolute_path, sizeof(absolute_path), dir, imagepath.c_str()); + BLI_path_join(absolute_path, sizeof(absolute_path), dir, imagepath.c_str()); if (BLI_exists(absolute_path)) { workpath = absolute_path; } diff --git a/source/blender/io/collada/ImageExporter.cpp b/source/blender/io/collada/ImageExporter.cpp index 1223abbaf95..070eb36de31 100644 --- a/source/blender/io/collada/ImageExporter.cpp +++ b/source/blender/io/collada/ImageExporter.cpp @@ -70,7 +70,7 @@ void ImagesExporter::export_UV_Image(Image *image, bool use_copies) BLI_strncpy(export_file, name.c_str(), sizeof(export_file)); BKE_image_path_ensure_ext_from_imformat(export_file, &imageFormat); - BLI_join_dirfile(export_path, sizeof(export_path), export_dir, export_file); + BLI_path_join(export_path, sizeof(export_path), export_dir, export_file); /* make dest directory if it doesn't exist */ BLI_make_existing_file(export_path); diff --git a/source/blender/io/common/intern/path_util.cc b/source/blender/io/common/intern/path_util.cc index 18632b410f8..63ff6cf29ee 100644 --- a/source/blender/io/common/intern/path_util.cc +++ b/source/blender/io/common/intern/path_util.cc @@ -28,8 +28,7 @@ std::string path_reference(StringRefNull filepath, } else if (mode == PATH_REFERENCE_COPY) { char filepath_cpy[PATH_MAX]; - BLI_path_join( - filepath_cpy, PATH_MAX, base_dst.c_str(), BLI_path_basename(filepath_abs), nullptr); + BLI_path_join(filepath_cpy, PATH_MAX, base_dst.c_str(), BLI_path_basename(filepath_abs)); copy_set->add(std::make_pair(filepath_abs, filepath_cpy)); BLI_strncpy(filepath_abs, filepath_cpy, PATH_MAX); mode = PATH_REFERENCE_RELATIVE; diff --git a/source/blender/io/stl/CMakeLists.txt b/source/blender/io/stl/CMakeLists.txt index 3a21da5c579..f7eb933d198 100644 --- a/source/blender/io/stl/CMakeLists.txt +++ b/source/blender/io/stl/CMakeLists.txt @@ -2,7 +2,7 @@ set(INC . - ./importer + importer ../common ../../blenkernel ../../blenlib diff --git a/source/blender/io/usd/intern/usd_capi_import.cc b/source/blender/io/usd/intern/usd_capi_import.cc index 5808c6bc77a..b8cc43beeb9 100644 --- a/source/blender/io/usd/intern/usd_capi_import.cc +++ b/source/blender/io/usd/intern/usd_capi_import.cc @@ -317,8 +317,7 @@ static void import_endjob(void *customdata) lc = BKE_layer_collection_get_active(view_layer); - /* Add all objects to the collection (don't do sync for each object). */ - BKE_layer_collection_resync_forbid(); + /* Add all objects to the collection. */ for (USDPrimReader *reader : data->archive->readers()) { if (!reader) { continue; @@ -330,9 +329,7 @@ static void import_endjob(void *customdata) BKE_collection_object_add(data->bmain, lc->collection, ob); } - /* Sync the collection, and do view layer operations. */ - BKE_layer_collection_resync_allow(); - BKE_main_collection_sync(data->bmain); + /* Sync and do the view layer operations. */ BKE_view_layer_synced_ensure(scene, view_layer); for (USDPrimReader *reader : data->archive->readers()) { if (!reader) { diff --git a/source/blender/io/usd/intern/usd_reader_mesh.cc b/source/blender/io/usd/intern/usd_reader_mesh.cc index 77c79852141..01db6baeb5c 100644 --- a/source/blender/io/usd/intern/usd_reader_mesh.cc +++ b/source/blender/io/usd/intern/usd_reader_mesh.cc @@ -804,8 +804,8 @@ void USDMeshReader::readFaceSetsSample(Main *bmain, Mesh *mesh, const double mot std::map<pxr::SdfPath, int> mat_map; bke::MutableAttributeAccessor attributes = mesh->attributes_for_write(); - bke::SpanAttributeWriter<int> material_indices = - attributes.lookup_or_add_for_write_only_span<int>("material_index", ATTR_DOMAIN_FACE); + bke::SpanAttributeWriter<int> material_indices = attributes.lookup_or_add_for_write_span<int>( + "material_index", ATTR_DOMAIN_FACE); this->assign_facesets_to_material_indices(motionSampleTime, material_indices.span, &mat_map); material_indices.finish(); /* Build material name map if it's not built yet. */ @@ -914,7 +914,7 @@ Mesh *USDMeshReader::read_mesh(Mesh *existing_mesh, std::map<pxr::SdfPath, int> mat_map; bke::MutableAttributeAccessor attributes = active_mesh->attributes_for_write(); bke::SpanAttributeWriter<int> material_indices = - attributes.lookup_or_add_for_write_only_span<int>("material_index", ATTR_DOMAIN_FACE); + attributes.lookup_or_add_for_write_span<int>("material_index", ATTR_DOMAIN_FACE); assign_facesets_to_material_indices(motionSampleTime, material_indices.span, &mat_map); material_indices.finish(); } diff --git a/source/blender/io/usd/intern/usd_writer_material.cc b/source/blender/io/usd/intern/usd_writer_material.cc index c195bf0e0bd..75abae79519 100644 --- a/source/blender/io/usd/intern/usd_writer_material.cc +++ b/source/blender/io/usd/intern/usd_writer_material.cc @@ -380,7 +380,7 @@ static void export_in_memory_texture(Image *ima, BKE_image_path_ensure_ext_from_imformat(file_name, &imageFormat); char export_path[FILE_MAX]; - BLI_path_join(export_path, FILE_MAX, export_dir.c_str(), file_name, nullptr); + BLI_path_join(export_path, FILE_MAX, export_dir.c_str(), file_name); if (!allow_overwrite && BLI_exists(export_path)) { return; @@ -576,7 +576,7 @@ static std::string get_tex_image_asset_path(bNode *node, BLI_split_file_part(path.c_str(), file_path, FILE_MAX); if (export_params.relative_paths) { - BLI_path_join(exp_path, FILE_MAX, ".", "textures", file_path, nullptr); + BLI_path_join(exp_path, FILE_MAX, ".", "textures", file_path); } else { /* Create absolute path in the textures directory. */ @@ -588,7 +588,7 @@ static std::string get_tex_image_asset_path(bNode *node, char dir_path[FILE_MAX]; BLI_split_dir_part(stage_path.c_str(), dir_path, FILE_MAX); - BLI_path_join(exp_path, FILE_MAX, dir_path, "textures", file_path, nullptr); + BLI_path_join(exp_path, FILE_MAX, dir_path, "textures", file_path); } BLI_str_replace_char(exp_path, '\\', '/'); return exp_path; @@ -645,7 +645,7 @@ static void copy_tiled_textures(Image *ima, BLI_split_file_part(src_tile_path, dest_filename, sizeof(dest_filename)); char dest_tile_path[FILE_MAX]; - BLI_path_join(dest_tile_path, FILE_MAX, dest_dir.c_str(), dest_filename, nullptr); + BLI_path_join(dest_tile_path, FILE_MAX, dest_dir.c_str(), dest_filename); if (!allow_overwrite && BLI_exists(dest_tile_path)) { continue; @@ -680,7 +680,7 @@ static void copy_single_file(Image *ima, const std::string &dest_dir, const bool BLI_split_file_part(source_path, file_name, FILE_MAX); char dest_path[FILE_MAX]; - BLI_path_join(dest_path, FILE_MAX, dest_dir.c_str(), file_name, nullptr); + BLI_path_join(dest_path, FILE_MAX, dest_dir.c_str(), file_name); if (!allow_overwrite && BLI_exists(dest_path)) { return; @@ -726,7 +726,7 @@ static void export_texture(bNode *node, BLI_split_dir_part(stage_path.c_str(), usd_dir_path, FILE_MAX); char tex_dir_path[FILE_MAX]; - BLI_path_join(tex_dir_path, FILE_MAX, usd_dir_path, "textures", SEP_STR, nullptr); + BLI_path_join(tex_dir_path, FILE_MAX, usd_dir_path, "textures", SEP_STR); BLI_dir_create_recursive(tex_dir_path); diff --git a/source/blender/io/usd/intern/usd_writer_volume.cc b/source/blender/io/usd/intern/usd_writer_volume.cc index 8cc3c65ee70..c6a27c5f663 100644 --- a/source/blender/io/usd/intern/usd_writer_volume.cc +++ b/source/blender/io/usd/intern/usd_writer_volume.cc @@ -152,7 +152,7 @@ std::optional<std::string> USDVolumeWriter::construct_vdb_file_path(const Volume strcat(vdb_file_name, ".vdb"); char vdb_file_path[FILE_MAX]; - BLI_path_join(vdb_file_path, sizeof(vdb_file_path), vdb_directory_path, vdb_file_name, nullptr); + BLI_path_join(vdb_file_path, sizeof(vdb_file_path), vdb_directory_path, vdb_file_name); return vdb_file_path; } diff --git a/source/blender/io/usd/tests/usd_tests_common.cc b/source/blender/io/usd/tests/usd_tests_common.cc index 9f18a289433..ea4e704006d 100644 --- a/source/blender/io/usd/tests/usd_tests_common.cc +++ b/source/blender/io/usd/tests/usd_tests_common.cc @@ -29,7 +29,7 @@ std::string register_usd_plugins_for_tests() } const size_t path_len = BLI_path_join( - usd_datafiles_dir, FILE_MAX, release_dir.c_str(), "datafiles", "usd", nullptr); + usd_datafiles_dir, FILE_MAX, release_dir.c_str(), "datafiles", "usd"); /* #BLI_path_join removes trailing slashes, but the USD library requires one in order to * recognize the path as directory. */ diff --git a/source/blender/io/wavefront_obj/CMakeLists.txt b/source/blender/io/wavefront_obj/CMakeLists.txt index f7958ef4ec6..bfbc715a45f 100644 --- a/source/blender/io/wavefront_obj/CMakeLists.txt +++ b/source/blender/io/wavefront_obj/CMakeLists.txt @@ -2,8 +2,8 @@ set(INC . - ./exporter - ./importer + exporter + importer ../common ../../blenkernel ../../blenlib diff --git a/source/blender/io/wavefront_obj/IO_wavefront_obj.h b/source/blender/io/wavefront_obj/IO_wavefront_obj.h index 0a92bbca477..cf6464eeb37 100644 --- a/source/blender/io/wavefront_obj/IO_wavefront_obj.h +++ b/source/blender/io/wavefront_obj/IO_wavefront_obj.h @@ -35,7 +35,7 @@ struct OBJExportParams { /* Geometry Transform options. */ eIOAxis forward_axis; eIOAxis up_axis; - float scaling_factor; + float global_scale; /* File Write Options. */ bool export_selected_objects; @@ -65,6 +65,7 @@ struct OBJImportParams { char filepath[FILE_MAX]; /** Value 0 disables clamping. */ float clamp_size; + float global_scale; eIOAxis forward_axis; eIOAxis up_axis; bool import_vertex_groups; diff --git a/source/blender/io/wavefront_obj/exporter/obj_export_file_writer.cc b/source/blender/io/wavefront_obj/exporter/obj_export_file_writer.cc index 95be927589e..5c81cf7abca 100644 --- a/source/blender/io/wavefront_obj/exporter/obj_export_file_writer.cc +++ b/source/blender/io/wavefront_obj/exporter/obj_export_file_writer.cc @@ -261,7 +261,7 @@ void OBJWriter::write_vertex_coords(FormatHandler &fh, BLI_assert(tot_count == attribute.size()); obj_parallel_chunked_output(fh, tot_count, [&](FormatHandler &buf, int i) { - float3 vertex = obj_mesh_data.calc_vertex_coords(i, export_params_.scaling_factor); + float3 vertex = obj_mesh_data.calc_vertex_coords(i, export_params_.global_scale); ColorGeometry4f linear = attribute.get(i); float srgb[3]; linearrgb_to_srgb_v3_v3(srgb, linear); @@ -270,7 +270,7 @@ void OBJWriter::write_vertex_coords(FormatHandler &fh, } else { obj_parallel_chunked_output(fh, tot_count, [&](FormatHandler &buf, int i) { - float3 vertex = obj_mesh_data.calc_vertex_coords(i, export_params_.scaling_factor); + float3 vertex = obj_mesh_data.calc_vertex_coords(i, export_params_.global_scale); buf.write_obj_vertex(vertex[0], vertex[1], vertex[2]); }); } @@ -435,7 +435,7 @@ void OBJWriter::write_nurbs_curve(FormatHandler &fh, const OBJCurve &obj_nurbs_d const int total_vertices = obj_nurbs_data.total_spline_vertices(spline_idx); for (int vertex_idx = 0; vertex_idx < total_vertices; vertex_idx++) { const float3 vertex_coords = obj_nurbs_data.vertex_coordinates( - spline_idx, vertex_idx, export_params_.scaling_factor); + spline_idx, vertex_idx, export_params_.global_scale); fh.write_obj_vertex(vertex_coords[0], vertex_coords[1], vertex_coords[2]); } diff --git a/source/blender/io/wavefront_obj/exporter/obj_export_mesh.cc b/source/blender/io/wavefront_obj/exporter/obj_export_mesh.cc index 9f19a6390d3..d00c09b9013 100644 --- a/source/blender/io/wavefront_obj/exporter/obj_export_mesh.cc +++ b/source/blender/io/wavefront_obj/exporter/obj_export_mesh.cc @@ -265,13 +265,13 @@ const char *OBJMesh::get_object_material_name(const int16_t mat_nr) const return mat->id.name + 2; } -float3 OBJMesh::calc_vertex_coords(const int vert_index, const float scaling_factor) const +float3 OBJMesh::calc_vertex_coords(const int vert_index, const float global_scale) const { float3 r_coords; const Span<MVert> verts = export_mesh_eval_->verts(); copy_v3_v3(r_coords, verts[vert_index].co); mul_m4_v3(world_and_axes_transform_, r_coords); - mul_v3_fl(r_coords, scaling_factor); + mul_v3_fl(r_coords, global_scale); return r_coords; } diff --git a/source/blender/io/wavefront_obj/exporter/obj_export_mesh.hh b/source/blender/io/wavefront_obj/exporter/obj_export_mesh.hh index db29f5651ed..ec98468e2de 100644 --- a/source/blender/io/wavefront_obj/exporter/obj_export_mesh.hh +++ b/source/blender/io/wavefront_obj/exporter/obj_export_mesh.hh @@ -161,7 +161,7 @@ class OBJMesh : NonCopyable { /** * Calculate coordinates of the vertex at the given index. */ - float3 calc_vertex_coords(int vert_index, float scaling_factor) const; + float3 calc_vertex_coords(int vert_index, float global_scale) const; /** * Calculate vertex indices of all vertices of the polygon at the given index. */ diff --git a/source/blender/io/wavefront_obj/exporter/obj_export_nurbs.cc b/source/blender/io/wavefront_obj/exporter/obj_export_nurbs.cc index 172a59e5341..812c3e7b5d4 100644 --- a/source/blender/io/wavefront_obj/exporter/obj_export_nurbs.cc +++ b/source/blender/io/wavefront_obj/exporter/obj_export_nurbs.cc @@ -55,14 +55,14 @@ int OBJCurve::total_spline_vertices(const int spline_index) const float3 OBJCurve::vertex_coordinates(const int spline_index, const int vertex_index, - const float scaling_factor) const + const float global_scale) const { const Nurb *const nurb = static_cast<Nurb *>(BLI_findlink(&export_curve_->nurb, spline_index)); float3 r_coord; const BPoint &bpoint = nurb->bp[vertex_index]; copy_v3_v3(r_coord, bpoint.vec); mul_m4_v3(world_axes_transform_, r_coord); - mul_v3_fl(r_coord, scaling_factor); + mul_v3_fl(r_coord, global_scale); return r_coord; } diff --git a/source/blender/io/wavefront_obj/exporter/obj_export_nurbs.hh b/source/blender/io/wavefront_obj/exporter/obj_export_nurbs.hh index 65389d44f59..3f93112200f 100644 --- a/source/blender/io/wavefront_obj/exporter/obj_export_nurbs.hh +++ b/source/blender/io/wavefront_obj/exporter/obj_export_nurbs.hh @@ -37,7 +37,7 @@ class OBJCurve : NonCopyable { /** * Get coordinates of the vertex at the given index on the given spline. */ - float3 vertex_coordinates(int spline_index, int vertex_index, float scaling_factor) const; + float3 vertex_coordinates(int spline_index, int vertex_index, float global_scale) const; /** * Get total control points of the NURBS spline at the given index. This is different than total * vertices of a spline. diff --git a/source/blender/io/wavefront_obj/importer/importer_mesh_utils.cc b/source/blender/io/wavefront_obj/importer/importer_mesh_utils.cc index f33753d720d..204237088ab 100644 --- a/source/blender/io/wavefront_obj/importer/importer_mesh_utils.cc +++ b/source/blender/io/wavefront_obj/importer/importer_mesh_utils.cc @@ -103,6 +103,9 @@ void transform_object(Object *object, const OBJImportParams &import_params) IO_AXIS_Y, IO_AXIS_Z, import_params.forward_axis, import_params.up_axis, axes_transform); copy_m4_m3(obmat, axes_transform); + float scale_vec[3] = { + import_params.global_scale, import_params.global_scale, import_params.global_scale}; + rescale_m4(obmat, scale_vec); BKE_object_apply_mat4(object, obmat, true, false); if (import_params.clamp_size != 0.0f) { diff --git a/source/blender/io/wavefront_obj/importer/obj_import_file_reader.cc b/source/blender/io/wavefront_obj/importer/obj_import_file_reader.cc index bd1c2d32a12..7d5f023af4b 100644 --- a/source/blender/io/wavefront_obj/importer/obj_import_file_reader.cc +++ b/source/blender/io/wavefront_obj/importer/obj_import_file_reader.cc @@ -508,6 +508,15 @@ void OBJParser::parse(Vector<std::unique_ptr<Geometry>> &r_all_geometries, } /* Faces. */ else if (parse_keyword(p, end, "f")) { + /* If we don't have a material index assigned yet, get one. + * It means "usemtl" state came from the previous object. */ + if (state_material_index == -1 && !state_material_name.empty() && + curr_geom->material_indices_.is_empty()) { + curr_geom->material_indices_.add_new(state_material_name, 0); + curr_geom->material_order_.append(state_material_name); + state_material_index = 0; + } + geom_add_polygon(curr_geom, p, end, @@ -524,7 +533,10 @@ void OBJParser::parse(Vector<std::unique_ptr<Geometry>> &r_all_geometries, else if (parse_keyword(p, end, "o")) { state_shaded_smooth = false; state_group_name = ""; - state_material_name = ""; + /* Reset object-local material index that's used in face infos. + * NOTE: do not reset the material name; that has to carry over + * into the next object if needed. */ + state_material_index = -1; curr_geom = create_geometry( curr_geom, GEOM_MESH, StringRef(p, end).trim(), r_all_geometries); } @@ -753,7 +765,7 @@ MTLParser::MTLParser(StringRefNull mtl_library, StringRefNull obj_filepath) { char obj_file_dir[FILE_MAXDIR]; BLI_split_dir_part(obj_filepath.data(), obj_file_dir, FILE_MAXDIR); - BLI_path_join(mtl_file_path_, FILE_MAX, obj_file_dir, mtl_library.data(), nullptr); + BLI_path_join(mtl_file_path_, FILE_MAX, obj_file_dir, mtl_library.data()); BLI_split_dir_part(mtl_file_path_, mtl_dir_path_, FILE_MAXDIR); } diff --git a/source/blender/io/wavefront_obj/importer/obj_importer.cc b/source/blender/io/wavefront_obj/importer/obj_importer.cc index ccbcce64d65..a42ec47151d 100644 --- a/source/blender/io/wavefront_obj/importer/obj_importer.cc +++ b/source/blender/io/wavefront_obj/importer/obj_importer.cc @@ -42,9 +42,6 @@ static void geometry_to_blender_objects(Main *bmain, { LayerCollection *lc = BKE_layer_collection_get_active(view_layer); - /* Don't do collection syncs for each object, will do once after the loop. */ - BKE_layer_collection_resync_forbid(); - /* Sort objects by name: creating many objects is much faster if the creation * order is sorted by name. */ blender::parallel_sort( @@ -73,12 +70,8 @@ static void geometry_to_blender_objects(Main *bmain, } } - /* Sync the collection after all objects are created. */ - BKE_layer_collection_resync_allow(); - BKE_main_collection_sync(bmain); + /* Do object selections in a separate loop (allows just one view layer sync). */ BKE_view_layer_synced_ensure(scene, view_layer); - - /* After collection sync, select objects in the view layer and do DEG updates. */ for (Object *obj : objects) { Base *base = BKE_view_layer_base_find(view_layer, obj); BKE_view_layer_base_select_and_set_active(view_layer, base); diff --git a/source/blender/io/wavefront_obj/tests/obj_exporter_tests.cc b/source/blender/io/wavefront_obj/tests/obj_exporter_tests.cc index dcba78ac99e..5de3cdcd851 100644 --- a/source/blender/io/wavefront_obj/tests/obj_exporter_tests.cc +++ b/source/blender/io/wavefront_obj/tests/obj_exporter_tests.cc @@ -306,7 +306,7 @@ TEST_F(obj_exporter_regression_test, all_tris) TEST_F(obj_exporter_regression_test, all_quads) { OBJExportParamsDefault _export; - _export.params.scaling_factor = 2.0f; + _export.params.global_scale = 2.0f; _export.params.export_materials = false; compare_obj_export_to_golden( "io_tests/blend_geometry/all_quads.blend", "io_tests/obj/all_quads.obj", "", _export.params); @@ -429,7 +429,7 @@ TEST_F(obj_exporter_regression_test, cubes_positioned) { OBJExportParamsDefault _export; _export.params.export_materials = false; - _export.params.scaling_factor = 2.0f; + _export.params.global_scale = 2.0f; compare_obj_export_to_golden("io_tests/blend_geometry/cubes_positioned.blend", "io_tests/obj/cubes_positioned.obj", "", diff --git a/source/blender/io/wavefront_obj/tests/obj_exporter_tests.hh b/source/blender/io/wavefront_obj/tests/obj_exporter_tests.hh index 006d86312b6..a4d452e1309 100644 --- a/source/blender/io/wavefront_obj/tests/obj_exporter_tests.hh +++ b/source/blender/io/wavefront_obj/tests/obj_exporter_tests.hh @@ -19,7 +19,7 @@ struct OBJExportParamsDefault { params.forward_axis = IO_AXIS_NEGATIVE_Z; params.up_axis = IO_AXIS_Y; - params.scaling_factor = 1.f; + params.global_scale = 1.f; params.apply_modifiers = true; params.export_eval_mode = DAG_EVAL_VIEWPORT; diff --git a/source/blender/io/wavefront_obj/tests/obj_importer_tests.cc b/source/blender/io/wavefront_obj/tests/obj_importer_tests.cc index 8d1171097b8..f459e1ab1bd 100644 --- a/source/blender/io/wavefront_obj/tests/obj_importer_tests.cc +++ b/source/blender/io/wavefront_obj/tests/obj_importer_tests.cc @@ -8,6 +8,7 @@ #include "BKE_curve.h" #include "BKE_customdata.h" #include "BKE_main.h" +#include "BKE_material.h" #include "BKE_mesh.h" #include "BKE_object.h" #include "BKE_scene.h" @@ -22,6 +23,7 @@ #include "DEG_depsgraph_query.h" #include "DNA_curve_types.h" +#include "DNA_material_types.h" #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" #include "DNA_scene_types.h" @@ -41,6 +43,7 @@ struct Expectation { float3 normal_first; float2 uv_first; float4 color_first = {-1, -1, -1, -1}; + std::string first_mat; }; class obj_importer_test : public BlendfileLoadingBaseTest { @@ -57,6 +60,7 @@ class obj_importer_test : public BlendfileLoadingBaseTest { } OBJImportParams params; + params.global_scale = 1.0f; params.clamp_size = 0; params.forward_axis = IO_AXIS_NEGATIVE_Z; params.up_axis = IO_AXIS_Y; @@ -132,6 +136,10 @@ class obj_importer_test : public BlendfileLoadingBaseTest { // int cyclic = (nurb->flagu & CU_NURB_CYCLIC) ? 1 : 0; // EXPECT_EQ(cyclic, exp.mesh_totloop_or_curve_cyclic); } + if (!exp.first_mat.empty()) { + Material *mat = BKE_object_material_get(object, 1); + ASSERT_STREQ(mat ? mat->id.name : "<null>", exp.first_mat.c_str()); + } ++object_index; } DEG_OBJECT_ITER_END; @@ -309,7 +317,42 @@ TEST_F(obj_importer_test, import_materials) { Expectation expect[] = { {"OBCube", OB_MESH, 8, 12, 6, 24, float3(1, 1, -1), float3(-1, 1, 1)}, - {"OBmaterials", OB_MESH, 8, 12, 6, 24, float3(-1, -1, 1), float3(1, -1, -1)}, + {"OBmaterials", + OB_MESH, + 8, + 12, + 6, + 24, + float3(-1, -1, 1), + float3(1, -1, -1), + float3(0), + float2(0), + float4(-1), + "MAno_textures_red"}, + {"OBObjMtlAfter", + OB_MESH, + 3, + 3, + 1, + 3, + float3(3, 0, 0), + float3(5, 0, 0), + float3(0), + float2(0), + float4(-1), + "MAno_textures_red"}, + {"OBObjMtlBefore", + OB_MESH, + 3, + 3, + 1, + 3, + float3(6, 0, 0), + float3(8, 0, 0), + float3(0), + float2(0), + float4(-1), + "MAClay"}, }; import_and_check("materials.obj", expect, std::size(expect), 4, 8); } @@ -327,7 +370,9 @@ TEST_F(obj_importer_test, import_cubes_with_textures_rel) float3(1, 1, -1), float3(-1, -1, 1), float3(0, 1, 0), - float2(0.9935f, 0.0020f)}, + float2(0.9935f, 0.0020f), + float4(-1), + "MAMat_BaseRoughEmissNormal10"}, {"OBCubeTexMul", OB_MESH, 8, @@ -337,7 +382,9 @@ TEST_F(obj_importer_test, import_cubes_with_textures_rel) float3(4, -2, -1), float3(2, -4, 1), float3(0, 1, 0), - float2(0.9935f, 0.0020f)}, + float2(0.9935f, 0.0020f), + float4(-1), + "MAMat_BaseMul"}, {"OBCubeTiledTex", OB_MESH, 8, @@ -347,7 +394,9 @@ TEST_F(obj_importer_test, import_cubes_with_textures_rel) float3(4, 1, -1), float3(2, -1, 1), float3(0, 1, 0), - float2(0.9935f, 0.0020f)}, + float2(0.9935f, 0.0020f), + float4(-1), + "MAMat_BaseTiled"}, {"OBCubeTiledTexFromAnotherFolder", OB_MESH, 8, @@ -357,7 +406,9 @@ TEST_F(obj_importer_test, import_cubes_with_textures_rel) float3(7, 1, -1), float3(5, -1, 1), float3(0, 1, 0), - float2(0.9935f, 0.0020f)}, + float2(0.9935f, 0.0020f), + float4(-1), + "MAMat_EmissTiledAnotherFolder"}, }; import_and_check("cubes_with_textures_rel.obj", expect, std::size(expect), 4, 4); } @@ -455,7 +506,10 @@ TEST_F(obj_importer_test, import_all_objects) 26, float3(28, 1, -1), float3(26, 1, 1), - float3(-1, 0, 0)}, + float3(-1, 0, 0), + float2(0), + float4(-1), + "MARed"}, {"OBNurbsCircle", OB_MESH, 96, @@ -491,7 +545,10 @@ TEST_F(obj_importer_test, import_all_objects) 26, float3(4, 1, -1), float3(2, 1, 1), - float3(0.5774f, 0.5773f, 0.5774f)}, + float3(0.5774f, 0.5773f, 0.5774f), + float2(0), + float4(-1), + "MAMaterial"}, {"OBSurface", OB_MESH, 256, diff --git a/source/blender/makesdna/DNA_brush_defaults.h b/source/blender/makesdna/DNA_brush_defaults.h index 348e8f4e098..6e88275672a 100644 --- a/source/blender/makesdna/DNA_brush_defaults.h +++ b/source/blender/makesdna/DNA_brush_defaults.h @@ -92,7 +92,7 @@ .hardness = 0.0f, \ .automasking_boundary_edges_propagation_steps = 1, \ .automasking_cavity_blur_steps = 0,\ - .automasking_cavity_factor = 0.5f,\ + .automasking_cavity_factor = 1.0f,\ \ /* A kernel radius of 1 has almost no effect (T63233). */ \ .blur_kernel_radius = 2, \ diff --git a/source/blender/makesdna/DNA_brush_enums.h b/source/blender/makesdna/DNA_brush_enums.h index 570b569a4dd..8b889e17762 100644 --- a/source/blender/makesdna/DNA_brush_enums.h +++ b/source/blender/makesdna/DNA_brush_enums.h @@ -330,9 +330,8 @@ typedef enum eAutomasking_flag { BRUSH_AUTOMASKING_BOUNDARY_FACE_SETS = (1 << 3), BRUSH_AUTOMASKING_CAVITY_NORMAL = (1 << 4), - /* Note: normal and inverted are mutually exclusive, - * inverted has priority if both bits are set. - */ + /* NOTE: normal and inverted are mutually exclusive, + * inverted has priority if both bits are set. */ BRUSH_AUTOMASKING_CAVITY_INVERTED = (1 << 5), BRUSH_AUTOMASKING_CAVITY_ALL = (1 << 4) | (1 << 5), BRUSH_AUTOMASKING_CAVITY_USE_CURVE = (1 << 6), diff --git a/source/blender/makesdna/DNA_gpencil_types.h b/source/blender/makesdna/DNA_gpencil_types.h index 26dbb544f52..8b3f4956cfe 100644 --- a/source/blender/makesdna/DNA_gpencil_types.h +++ b/source/blender/makesdna/DNA_gpencil_types.h @@ -243,12 +243,15 @@ typedef struct bGPDstroke_Runtime { /** Runtime falloff factor (only for transform). */ float multi_frame_falloff; - /** Vertex offset in the VBO where this stroke starts. */ + /** Triangle offset in the IBO where this stroke starts. */ int stroke_start; /** Triangle offset in the IBO where this fill starts. */ int fill_start; + /** Vertex offset in the VBO where this stroke starts. */ + int vertex_start; /** Curve Handles offset in the IBO where this handle starts. */ int curve_start; + int _pad0; /** Original stroke (used to dereference evaluated data) */ struct bGPDstroke *gps_orig; @@ -613,8 +616,9 @@ typedef struct bGPdata_Runtime { /** Stroke buffer. */ void *sbuffer; /** Temp batches cleared after drawing. */ - struct GPUBatch *sbuffer_stroke_batch; - struct GPUBatch *sbuffer_fill_batch; + struct GPUVertBuf *sbuffer_position_buf; + struct GPUVertBuf *sbuffer_color_buf; + struct GPUBatch *sbuffer_batch; /** Temp stroke used for drawing. */ struct bGPDstroke *sbuffer_gps; diff --git a/source/blender/makesdna/DNA_mesh_types.h b/source/blender/makesdna/DNA_mesh_types.h index 2a17dbab8b3..3f951583741 100644 --- a/source/blender/makesdna/DNA_mesh_types.h +++ b/source/blender/makesdna/DNA_mesh_types.h @@ -19,10 +19,14 @@ namespace blender { template<typename T> class Span; template<typename T> class MutableSpan; namespace bke { +struct MeshRuntime; class AttributeAccessor; class MutableAttributeAccessor; } // namespace bke } // namespace blender +using MeshRuntimeHandle = blender::bke::MeshRuntime; +#else +typedef struct MeshRuntimeHandle MeshRuntimeHandle; #endif #ifdef __cplusplus @@ -30,133 +34,14 @@ extern "C" { #endif struct AnimData; -struct BVHCache; struct Ipo; struct Key; struct MCol; struct MEdge; struct MFace; -struct MLoopCol; struct MLoopTri; struct MVert; struct Material; -struct Mesh; -struct SubdivCCG; -struct SubsurfRuntimeData; - -# -# -typedef struct EditMeshData { - /** when set, \a vertexNos, polyNos are lazy initialized */ - const float (*vertexCos)[3]; - - /** lazy initialize (when \a vertexCos is set) */ - float const (*vertexNos)[3]; - float const (*polyNos)[3]; - /** also lazy init but don't depend on \a vertexCos */ - const float (*polyCos)[3]; -} EditMeshData; - -/** - * \warning Typical access is done via - * #BKE_mesh_runtime_looptri_ensure, #BKE_mesh_runtime_looptri_len. - */ -struct MLoopTri_Store { - DNA_DEFINE_CXX_METHODS(MLoopTri_Store) - - /* WARNING! swapping between array (ready-to-be-used data) and array_wip - * (where data is actually computed) - * shall always be protected by same lock as one used for looptris computing. */ - struct MLoopTri *array, *array_wip; - int len; - int len_alloc; -}; - -/** Runtime data, not saved in files. */ -typedef struct Mesh_Runtime { - DNA_DEFINE_CXX_METHODS(Mesh_Runtime) - - /* Evaluated mesh for objects which do not have effective modifiers. - * This mesh is used as a result of modifier stack evaluation. - * Since modifier stack evaluation is threaded on object level we need some synchronization. */ - struct Mesh *mesh_eval; - void *eval_mutex; - - /* A separate mutex is needed for normal calculation, because sometimes - * the normals are needed while #eval_mutex is already locked. */ - void *normals_mutex; - - /** Needed to ensure some thread-safety during render data pre-processing. */ - void *render_mutex; - - /** Lazily initialized SoA data from the #edit_mesh field in #Mesh. */ - struct EditMeshData *edit_data; - - /** - * Data used to efficiently draw the mesh in the viewport, especially useful when - * the same mesh is used in many objects or instances. See `draw_cache_impl_mesh.cc`. - */ - void *batch_cache; - - /** Cache for derived triangulation of the mesh. */ - struct MLoopTri_Store looptris; - - /** Cache for BVH trees generated for the mesh. Defined in 'BKE_bvhutil.c' */ - struct BVHCache *bvh_cache; - - /** Cache of non-manifold boundary data for Shrinkwrap Target Project. */ - struct ShrinkwrapBoundaryData *shrinkwrap_data; - - /** Needed in case we need to lazily initialize the mesh. */ - CustomData_MeshMasks cd_mask_extra; - - struct SubdivCCG *subdiv_ccg; - int subdiv_ccg_tot_level; - - /** Set by modifier stack if only deformed from original. */ - char deformed_only; - /** - * Copied from edit-mesh (hint, draw with edit-mesh data when true). - * - * Modifiers that edit the mesh data in-place must set this to false - * (most #eModifierTypeType_NonGeometrical modifiers). Otherwise the edit-mesh - * data will be used for drawing, missing changes from modifiers. See T79517. - */ - char is_original_bmesh; - - /** #eMeshWrapperType and others. */ - char wrapper_type; - /** - * A type mask from wrapper_type, - * in case there are differences in finalizing logic between types. - */ - char wrapper_type_finalize; - - /** - * Settings for lazily evaluating the subdivision on the CPU if needed. These are - * set in the modifier when GPU subdivision can be performed, and owned by the by - * the modifier in the object. - */ - struct SubsurfRuntimeData *subsurf_runtime_data; - void *_pad1; - - /** - * Caches for lazily computed vertex and polygon normals. These are stored here rather than in - * #CustomData because they can be calculated on a const mesh, and adding custom data layers on a - * const mesh is not thread-safe. - */ - char _pad2[6]; - char vert_normals_dirty; - char poly_normals_dirty; - float (*vert_normals)[3]; - float (*poly_normals)[3]; - - /** - * A #BLI_bitmap containing tags for the center vertices of subdivided polygons, set by the - * subdivision surface modifier and used by drawing code instead of polygon center face dots. - */ - uint32_t *subsurf_face_dot_tags; -} Mesh_Runtime; typedef struct Mesh { DNA_DEFINE_CXX_METHODS(Mesh) @@ -316,9 +201,13 @@ typedef struct Mesh { char _pad1[4]; - void *_pad2; - - Mesh_Runtime runtime; + /** + * Data that isn't saved in files, including caches of derived data, temporary data to improve + * the editing experience, etc. Runtime data is created when reading files and can be accessed + * without null checks, with the exception of some temporary meshes which should allocate and + * free the data if they are passed to functions that expect run-time data. + */ + MeshRuntimeHandle *runtime; #ifdef __cplusplus /** * Array of vertex positions (and various other data). Edges and faces are defined by indices @@ -383,16 +272,6 @@ typedef struct TFace { /* **************** MESH ********************* */ -/** #Mesh_Runtime.wrapper_type */ -typedef enum eMeshWrapperType { - /** Use mesh data (#Mesh.mvert, #Mesh.medge, #Mesh.mloop, #Mesh.mpoly). */ - ME_WRAPPER_TYPE_MDATA = 0, - /** Use edit-mesh data (#Mesh.edit_mesh, #Mesh_Runtime.edit_data). */ - ME_WRAPPER_TYPE_BMESH = 1, - /** Use subdivision mesh data (#Mesh_Runtime.mesh_eval). */ - ME_WRAPPER_TYPE_SUBD = 2, -} eMeshWrapperType; - /** #Mesh.texflag */ enum { ME_AUTOSPACE = 1, diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h index 9cd056e5a0c..748033cb015 100644 --- a/source/blender/makesdna/DNA_node_types.h +++ b/source/blender/makesdna/DNA_node_types.h @@ -135,16 +135,16 @@ typedef struct bNodeSocket { /** Default input value used for unlinked sockets. */ void *default_value; - /* execution data */ - /** Local stack index. */ + /** Local stack index for "node_exec". */ short stack_index; - /* XXX deprecated, kept for forward compatibility */ - short stack_type DNA_DEPRECATED; char display_shape; /* #eAttrDomain used when the geometry nodes modifier creates an attribute for a group * output. */ char attribute_domain; + + char _pad[2]; + /* Runtime-only cache of the number of input links, for multi-input sockets. */ short total_inputs; @@ -170,9 +170,6 @@ typedef struct bNodeSocket { int own_index DNA_DEPRECATED; /* XXX deprecated, only used for restoring old group node links */ int to_index DNA_DEPRECATED; - /* XXX deprecated, still forward compatible since verification - * restores pointer from matching own_index. */ - struct bNodeSocket *groupsock DNA_DEPRECATED; /** A link pointer, set in #BKE_ntree_update_main. */ struct bNodeLink *link; @@ -2082,6 +2079,21 @@ typedef enum CMPNodeFilterMethod { CMP_NODE_FILTER_SHARP_DIAMOND = 7, } CMPNodeFilterMethod; +/* Levels Node. Stored in custom1. */ +typedef enum CMPNodeLevelsChannel { + CMP_NODE_LEVLES_LUMINANCE = 1, + CMP_NODE_LEVLES_RED = 2, + CMP_NODE_LEVLES_GREEN = 3, + CMP_NODE_LEVLES_BLUE = 4, + CMP_NODE_LEVLES_LUMINANCE_BT709 = 5, +} CMPNodeLevelsChannel; + +/* Tone Map Node. Stored in NodeTonemap.type. */ +typedef enum CMPNodeToneMapType { + CMP_NODE_TONE_MAP_SIMPLE = 0, + CMP_NODE_TONE_MAP_PHOTORECEPTOR = 1, +} CMPNodeToneMapType; + /* Plane track deform node. */ enum { diff --git a/source/blender/makesdna/DNA_scene_types.h b/source/blender/makesdna/DNA_scene_types.h index 5fa5d4c7787..28359038be5 100644 --- a/source/blender/makesdna/DNA_scene_types.h +++ b/source/blender/makesdna/DNA_scene_types.h @@ -472,6 +472,7 @@ typedef struct ImageFormatData { #define R_IMF_IMTYPE_THEORA 33 #define R_IMF_IMTYPE_PSD 34 #define R_IMF_IMTYPE_WEBP 35 +#define R_IMF_IMTYPE_AV1 36 #define R_IMF_IMTYPE_INVALID 255 diff --git a/source/blender/makesdna/DNA_space_types.h b/source/blender/makesdna/DNA_space_types.h index 7f0dd2f9be6..d2d20bcde78 100644 --- a/source/blender/makesdna/DNA_space_types.h +++ b/source/blender/makesdna/DNA_space_types.h @@ -1169,6 +1169,10 @@ enum { FILE_ENTRY_NAME_FREE = 1 << 1, /* The preview for this entry is being loaded on another thread. */ FILE_ENTRY_PREVIEW_LOADING = 1 << 2, + /** For #FILE_TYPE_BLENDERLIB only: Denotes that the ID is known to not have a preview (none was + * found in the .blend). Stored so we don't keep trying to find non-existent previews every time + * we reload previews. When dealing with heavy files this can have quite an impact. */ + FILE_ENTRY_BLENDERLIB_NO_PREVIEW = 1 << 3, }; /** \} */ @@ -1184,6 +1188,12 @@ typedef struct SpaceImageOverlay { char _pad[4]; } SpaceImageOverlay; +typedef enum eSpaceImage_GridShapeSource { + SI_GRID_SHAPE_DYNAMIC = 0, + SI_GRID_SHAPE_FIXED = 1, + SI_GRID_SHAPE_PIXEL = 2, +} eSpaceImage_GridShapeSource; + typedef struct SpaceImage { SpaceLink *next, *prev; /** Storage of regions for inactive spaces. */ @@ -1230,7 +1240,9 @@ typedef struct SpaceImage { char around; char gizmo_flag; - char _pad1[3]; + + char grid_shape_source; + char _pad1[2]; int flag; @@ -1239,7 +1251,7 @@ typedef struct SpaceImage { int tile_grid_shape[2]; /** * UV editor custom-grid. Value of `{M,N}` will produce `MxN` grid. - * Use when #SI_CUSTOM_GRID is set. + * Use when `custom_grid_shape == SI_GRID_SHAPE_FIXED`. */ int custom_grid_subdiv[2]; @@ -1266,7 +1278,7 @@ typedef enum eSpaceImage_PixelRoundMode { SI_PIXEL_ROUND_DISABLED = 0, SI_PIXEL_ROUND_CENTER = 1, SI_PIXEL_ROUND_CORNER = 2, -} eSpaceImage_Round_Mode; +} eSpaceImage_PixelRoundMode; /** #SpaceImage.mode */ typedef enum eSpaceImage_Mode { @@ -1300,7 +1312,7 @@ typedef enum eSpaceImage_Flag { SI_FULLWINDOW = (1 << 16), SI_FLAG_UNUSED_17 = (1 << 17), - SI_CUSTOM_GRID = (1 << 18), + SI_FLAG_UNUSED_18 = (1 << 18), /** * This means that the image is drawn until it reaches the view edge, diff --git a/source/blender/makesdna/DNA_userdef_enums.h b/source/blender/makesdna/DNA_userdef_enums.h index e90aa0e0f07..dc368819ab0 100644 --- a/source/blender/makesdna/DNA_userdef_enums.h +++ b/source/blender/makesdna/DNA_userdef_enums.h @@ -39,6 +39,7 @@ typedef enum eDupli_ID_Flags { USER_DUP_LATTICE = (1 << 17), USER_DUP_CAMERA = (1 << 18), USER_DUP_SPEAKER = (1 << 19), + USER_DUP_NTREE = (1 << 20), USER_DUP_OBDATA = (~0) & ((1 << 24) - 1), diff --git a/source/blender/makesdna/DNA_windowmanager_types.h b/source/blender/makesdna/DNA_windowmanager_types.h index d57cca0b055..9b235fac049 100644 --- a/source/blender/makesdna/DNA_windowmanager_types.h +++ b/source/blender/makesdna/DNA_windowmanager_types.h @@ -364,7 +364,7 @@ typedef struct wmOperatorTypeMacro { struct wmOperatorTypeMacro *next, *prev; /* operator id */ - char idname[64]; + char idname[64]; /* OP_MAX_TYPENAME */ /* rna pointer to access properties, like keymap */ /** Operator properties, assigned to ptr->data and can be written to a file. */ struct IDProperty *properties; @@ -551,7 +551,7 @@ typedef struct wmOperator { /* saved */ /** Used to retrieve type pointer. */ - char idname[64]; + char idname[64]; /* OP_MAX_TYPENAME */ /** Saved, user-settable properties. */ IDProperty *properties; diff --git a/source/blender/makesdna/intern/CMakeLists.txt b/source/blender/makesdna/intern/CMakeLists.txt index 0d04d7df067..50fd51e88a1 100644 --- a/source/blender/makesdna/intern/CMakeLists.txt +++ b/source/blender/makesdna/intern/CMakeLists.txt @@ -116,8 +116,8 @@ blender_add_lib(bf_dna "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") # ----------------------------------------------------------------------------- # Build bf_dna_blenlib library set(INC + .. ../../blenlib - ../../makesdna ../../../../intern/atomic ../../../../intern/guardedalloc ) diff --git a/source/blender/makesrna/intern/CMakeLists.txt b/source/blender/makesrna/intern/CMakeLists.txt index 2b06daf34e3..f028b199098 100644 --- a/source/blender/makesrna/intern/CMakeLists.txt +++ b/source/blender/makesrna/intern/CMakeLists.txt @@ -200,7 +200,7 @@ set(INC ../../imbuf ../../makesdna ../../modifiers - ../../nodes/ + ../../nodes ../../sequencer ../../simulation ../../windowmanager diff --git a/source/blender/makesrna/intern/rna_brush.c b/source/blender/makesrna/intern/rna_brush.c index 3c9590ddcbe..137020ce3f8 100644 --- a/source/blender/makesrna/intern/rna_brush.c +++ b/source/blender/makesrna/intern/rna_brush.c @@ -92,7 +92,7 @@ static const EnumPropertyItem rna_enum_brush_texture_slot_map_texture_mode_items #endif /* clang-format off */ -/* Note: we don't actually turn these into a single enum bitmask property, +/* Note: we don't actually turn these into a single enum bit-mask property, * instead we construct individual boolean properties. */ const EnumPropertyItem RNA_automasking_flags[] = { {BRUSH_AUTOMASKING_TOPOLOGY, "use_automasking_topology", 0,"Topology", "Affect only vertices connected to the active vertex under the brush"}, @@ -3240,18 +3240,20 @@ static void rna_def_brush(BlenderRNA *brna) RNA_def_property_update(prop, 0, "rna_Brush_update"); } while ((++entry)->identifier); - prop = RNA_def_property(srna, "automasking_cavity_factor", PROP_FLOAT, PROP_NONE); + prop = RNA_def_property(srna, "automasking_cavity_factor", PROP_FLOAT, PROP_FACTOR); RNA_def_property_float_sdna(prop, NULL, "automasking_cavity_factor"); RNA_def_property_ui_text(prop, "Cavity Factor", "The contrast of the cavity mask"); - RNA_def_property_ui_range(prop, 0.0f, 1.0f, 0.1, 3); RNA_def_property_range(prop, 0.0f, 5.0f); + RNA_def_property_ui_range(prop, 0.0f, 1.0f, 0.1, 3); RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_update(prop, 0, "rna_Brush_update"); prop = RNA_def_property(srna, "automasking_cavity_blur_steps", PROP_INT, PROP_NONE); RNA_def_property_int_sdna(prop, NULL, "automasking_cavity_blur_steps"); + RNA_def_property_int_default(prop, 0); RNA_def_property_ui_text(prop, "Blur Steps", "The number of times the cavity mask is blurred"); - RNA_def_property_range(prop, 0.0f, 25.0f); + RNA_def_property_range(prop, 0, 25); + RNA_def_property_ui_range(prop, 0, 10, 1, 1); RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_update(prop, 0, "rna_Brush_update"); @@ -3614,6 +3616,10 @@ static void rna_def_brush(BlenderRNA *brna) RNA_def_property_ui_text( prop, "Use Vertex", "Use this brush in grease pencil vertex color mode"); + prop = RNA_def_property(srna, "use_paint_sculpt_curves", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "ob_mode", OB_MODE_SCULPT_CURVES); + RNA_def_property_ui_text(prop, "Use Sculpt", "Use this brush in sculpt curves mode"); + /* texture */ prop = RNA_def_property(srna, "texture_slot", PROP_POINTER, PROP_NONE); RNA_def_property_struct_type(prop, "BrushTextureSlot"); diff --git a/source/blender/makesrna/intern/rna_fluid.c b/source/blender/makesrna/intern/rna_fluid.c index a12ba7d9287..bd601d0a736 100644 --- a/source/blender/makesrna/intern/rna_fluid.c +++ b/source/blender/makesrna/intern/rna_fluid.c @@ -785,29 +785,29 @@ static const EnumPropertyItem *rna_Fluid_cobafield_itemf(bContext *UNUSED(C), tmp.value = FLUID_DOMAIN_FIELD_PHI; tmp.identifier = "PHI"; tmp.icon = 0; - tmp.name = N_("Fluid Levelset"); - tmp.description = N_("Levelset representation of the fluid"); + tmp.name = N_("Fluid Level Set"); + tmp.description = N_("Level set representation of the fluid"); RNA_enum_item_add(&item, &totitem, &tmp); tmp.value = FLUID_DOMAIN_FIELD_PHI_IN; tmp.identifier = "PHI_IN"; tmp.icon = 0; - tmp.name = N_("Inflow Levelset"); - tmp.description = N_("Levelset representation of the inflow"); + tmp.name = N_("Inflow Level Set"); + tmp.description = N_("Level set representation of the inflow"); RNA_enum_item_add(&item, &totitem, &tmp); tmp.value = FLUID_DOMAIN_FIELD_PHI_OUT; tmp.identifier = "PHI_OUT"; tmp.icon = 0; - tmp.name = N_("Outflow Levelset"); - tmp.description = N_("Levelset representation of the outflow"); + tmp.name = N_("Outflow Level Set"); + tmp.description = N_("Level set representation of the outflow"); RNA_enum_item_add(&item, &totitem, &tmp); tmp.value = FLUID_DOMAIN_FIELD_PHI_OBSTACLE; tmp.identifier = "PHI_OBSTACLE"; tmp.icon = 0; - tmp.name = N_("Obstacle Levelset"); - tmp.description = N_("Levelset representation of the obstacles"); + tmp.name = N_("Obstacle Level Set"); + tmp.description = N_("Level set representation of the obstacles"); RNA_enum_item_add(&item, &totitem, &tmp); } diff --git a/source/blender/makesrna/intern/rna_gpencil_modifier.c b/source/blender/makesrna/intern/rna_gpencil_modifier.c index 4a36443ca42..320ef169ae5 100644 --- a/source/blender/makesrna/intern/rna_gpencil_modifier.c +++ b/source/blender/makesrna/intern/rna_gpencil_modifier.c @@ -9,11 +9,8 @@ #include <stdlib.h> #include "DNA_armature_types.h" -#include "DNA_brush_types.h" -#include "DNA_cachefile_types.h" #include "DNA_gpencil_modifier_types.h" #include "DNA_gpencil_types.h" -#include "DNA_mesh_types.h" #include "DNA_modifier_types.h" #include "DNA_object_force_types.h" #include "DNA_object_types.h" @@ -21,20 +18,13 @@ #include "MEM_guardedalloc.h" -#include "BLI_math.h" -#include "BLI_rand.h" +#include "BLI_math_base.h" +#include "BLI_math_rotation.h" #include "BLI_string_utils.h" #include "BLT_translation.h" #include "BKE_animsys.h" -#include "BKE_data_transfer.h" -#include "BKE_dynamicpaint.h" -#include "BKE_effect.h" -#include "BKE_fluid.h" /* For BKE_fluid_modifier_free & BKE_fluid_modifier_create_type_data */ -#include "BKE_mesh_mapping.h" -#include "BKE_mesh_remap.h" -#include "BKE_multires.h" #include "RNA_access.h" #include "RNA_define.h" diff --git a/source/blender/makesrna/intern/rna_internal.h b/source/blender/makesrna/intern/rna_internal.h index 8e652753ec0..ea829e5cd86 100644 --- a/source/blender/makesrna/intern/rna_internal.h +++ b/source/blender/makesrna/intern/rna_internal.h @@ -322,7 +322,9 @@ void rna_object_vcollayer_name_set(struct PointerRNA *ptr, PointerRNA rna_object_shapekey_index_get(struct ID *id, int value); int rna_object_shapekey_index_set(struct ID *id, PointerRNA value, int current); -void rna_def_object_type_visibility_flags_common(StructRNA *srna, int noteflag); +void rna_def_object_type_visibility_flags_common(StructRNA *srna, + int noteflag, + const char *update_func); int rna_object_type_visibility_icon_get_common(int object_type_exclude_viewport, const int *object_type_exclude_select); diff --git a/source/blender/makesrna/intern/rna_layer.c b/source/blender/makesrna/intern/rna_layer.c index 427a38094be..b08d4b60fcc 100644 --- a/source/blender/makesrna/intern/rna_layer.c +++ b/source/blender/makesrna/intern/rna_layer.c @@ -369,6 +369,16 @@ static bool rna_LayerCollection_has_selected_objects(LayerCollection *lc, return false; } +void rna_LayerCollection_children_begin(CollectionPropertyIterator *iter, PointerRNA *ptr) +{ + Scene *scene = (Scene *)ptr->owner_id; + LayerCollection *lc = (LayerCollection *)ptr->data; + ViewLayer *view_layer = BKE_view_layer_find_from_collection(scene, lc); + BKE_view_layer_synced_ensure(scene, view_layer); + + rna_iterator_listbase_begin(iter, &lc->layer_collections, NULL); +} + static bool rna_LayerCollection_children_lookupint(struct PointerRNA *ptr, int key, struct PointerRNA *r_ptr) @@ -435,7 +445,7 @@ static void rna_def_layer_collection(BlenderRNA *brna) RNA_def_property_struct_type(prop, "LayerCollection"); RNA_def_property_ui_text(prop, "Children", "Child layer collections"); RNA_def_property_collection_funcs(prop, - NULL, + "rna_LayerCollection_children_begin", NULL, NULL, NULL, diff --git a/source/blender/makesrna/intern/rna_mesh.c b/source/blender/makesrna/intern/rna_mesh.c index 9c6702142f7..bad099815a4 100644 --- a/source/blender/makesrna/intern/rna_mesh.c +++ b/source/blender/makesrna/intern/rna_mesh.c @@ -406,12 +406,39 @@ static int rna_MeshLoopTriangle_index_get(PointerRNA *ptr) { const Mesh *mesh = rna_mesh(ptr); const MLoopTri *ltri = (MLoopTri *)ptr->data; - const int index = (int)(ltri - mesh->runtime.looptris.array); + const int index = (int)(ltri - BKE_mesh_runtime_looptri_ensure(mesh)); BLI_assert(index >= 0); - BLI_assert(index < mesh->runtime.looptris.len); + BLI_assert(index < BKE_mesh_runtime_looptri_len(mesh)); return index; } +static void rna_Mesh_loop_triangles_begin(CollectionPropertyIterator *iter, PointerRNA *ptr) +{ + const Mesh *mesh = rna_mesh(ptr); + const MLoopTri *looptris = BKE_mesh_runtime_looptri_ensure(mesh); + rna_iterator_array_begin( + iter, (void *)looptris, sizeof(MLoopTri), BKE_mesh_runtime_looptri_len(mesh), false, NULL); +} + +static int rna_Mesh_loop_triangles_length(PointerRNA *ptr) +{ + const Mesh *mesh = rna_mesh(ptr); + return BKE_mesh_runtime_looptri_len(mesh); +} + +int rna_Mesh_loop_triangles_lookup_int(PointerRNA *ptr, int index, PointerRNA *r_ptr) +{ + const Mesh *mesh = rna_mesh(ptr); + if (index < 0 || index >= BKE_mesh_runtime_looptri_len(mesh)) { + return false; + } + /* Casting away const is okay because this RNA type doesn't allow changing the value. */ + r_ptr->owner_id = (ID *)&mesh->id; + r_ptr->type = &RNA_MeshLoopTriangle; + r_ptr->data = (void *)&BKE_mesh_runtime_looptri_ensure(mesh)[index]; + return true; +} + static void rna_MeshVertex_normal_get(PointerRNA *ptr, float *value) { Mesh *mesh = rna_mesh(ptr); @@ -1502,8 +1529,9 @@ static char *rna_MeshPolygon_path(const PointerRNA *ptr) static char *rna_MeshLoopTriangle_path(const PointerRNA *ptr) { - return BLI_sprintfN("loop_triangles[%d]", - (int)((MLoopTri *)ptr->data - rna_mesh(ptr)->runtime.looptris.array)); + return BLI_sprintfN( + "loop_triangles[%d]", + (int)((MLoopTri *)ptr->data - BKE_mesh_runtime_looptri_ensure(rna_mesh(ptr)))); } static char *rna_MeshEdge_path(const PointerRNA *ptr) @@ -3684,7 +3712,15 @@ static void rna_def_mesh(BlenderRNA *brna) NULL); prop = RNA_def_property(srna, "loop_triangles", PROP_COLLECTION, PROP_NONE); - RNA_def_property_collection_sdna(prop, NULL, "runtime.looptris.array", "runtime.looptris.len"); + RNA_def_property_collection_funcs(prop, + "rna_Mesh_loop_triangles_begin", + "rna_iterator_array_next", + "rna_iterator_array_end", + "rna_iterator_array_get", + "rna_Mesh_loop_triangles_length", + "rna_Mesh_loop_triangles_lookup_int", + NULL, + NULL); RNA_def_property_struct_type(prop, "MeshLoopTriangle"); RNA_def_property_override_flag(prop, PROPOVERRIDE_IGNORE); RNA_def_property_ui_text(prop, "Loop Triangles", "Tessellation of mesh polygons into triangles"); diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c index b694e9da85f..7db97387ad7 100644 --- a/source/blender/makesrna/intern/rna_nodetree.c +++ b/source/blender/makesrna/intern/rna_nodetree.c @@ -5509,6 +5509,7 @@ static void def_sh_tex_image(StructRNA *srna) RNA_def_property_enum_items(prop, prop_image_extension); RNA_def_property_ui_text( prop, "Extension", "How the image is extrapolated past its original bounds"); + RNA_def_property_translation_context(prop, BLT_I18NCONTEXT_ID_IMAGE); RNA_def_property_update(prop, 0, "rna_Node_update"); prop = RNA_def_property(srna, "image_user", PROP_POINTER, PROP_NONE); @@ -5573,6 +5574,7 @@ static void def_geo_image_texture(StructRNA *srna) RNA_def_property_enum_items(prop, prop_image_extension); RNA_def_property_ui_text( prop, "Extension", "How the image is extrapolated past its original bounds"); + RNA_def_property_translation_context(prop, BLT_I18NCONTEXT_ID_IMAGE); RNA_def_property_update(prop, 0, "rna_Node_update"); } @@ -6811,11 +6813,11 @@ static void def_cmp_levels(StructRNA *srna) PropertyRNA *prop; static const EnumPropertyItem channel_items[] = { - {1, "COMBINED_RGB", 0, "Combined", "Combined RGB"}, - {2, "RED", 0, "Red", "Red Channel"}, - {3, "GREEN", 0, "Green", "Green Channel"}, - {4, "BLUE", 0, "Blue", "Blue Channel"}, - {5, "LUMINANCE", 0, "Luminance", "Luminance Channel"}, + {CMP_NODE_LEVLES_LUMINANCE, "COMBINED_RGB", 0, "Combined", "Combined RGB"}, + {CMP_NODE_LEVLES_RED, "RED", 0, "Red", "Red Channel"}, + {CMP_NODE_LEVLES_GREEN, "GREEN", 0, "Green", "Green Channel"}, + {CMP_NODE_LEVLES_BLUE, "BLUE", 0, "Blue", "Blue Channel"}, + {CMP_NODE_LEVLES_LUMINANCE_BT709, "LUMINANCE", 0, "Luminance", "Luminance Channel"}, {0, NULL, 0, NULL, NULL}, }; diff --git a/source/blender/makesrna/intern/rna_object_api.c b/source/blender/makesrna/intern/rna_object_api.c index c99c5fb723d..366a3597ce6 100644 --- a/source/blender/makesrna/intern/rna_object_api.c +++ b/source/blender/makesrna/intern/rna_object_api.c @@ -199,7 +199,7 @@ static bool rna_Object_holdout_get(Object *ob, bContext *C, PointerRNA *view_lay return false; } - return ((base->flag & BASE_HOLDOUT) != 0); + return ((base->flag & BASE_HOLDOUT) != 0) || ((ob->visibility_flag & OB_HOLDOUT) != 0); } static bool rna_Object_indirect_only_get(Object *ob, bContext *C, PointerRNA *view_layer_ptr) diff --git a/source/blender/makesrna/intern/rna_scene.c b/source/blender/makesrna/intern/rna_scene.c index 70afc763c2d..58455ff7de5 100644 --- a/source/blender/makesrna/intern/rna_scene.c +++ b/source/blender/makesrna/intern/rna_scene.c @@ -5928,6 +5928,7 @@ static void rna_def_scene_ffmpeg_settings(BlenderRNA *brna) {FFMPEG_MKV, "MKV", 0, "Matroska", ""}, {FFMPEG_FLV, "FLASH", 0, "Flash", ""}, {FFMPEG_WEBM, "WEBM", 0, "WebM", ""}, + {AV_CODEC_ID_AV1, "AV1", 0, "AV1", ""}, {0, NULL, 0, NULL, NULL}, }; @@ -5945,7 +5946,7 @@ static void rna_def_scene_ffmpeg_settings(BlenderRNA *brna) {AV_CODEC_ID_PNG, "PNG", 0, "PNG", ""}, {AV_CODEC_ID_QTRLE, "QTRLE", 0, "QT rle / QT Animation", ""}, {AV_CODEC_ID_THEORA, "THEORA", 0, "Theora", ""}, - {AV_CODEC_ID_VP9, "WEBM", 0, "WEBM / VP9", ""}, + {AV_CODEC_ID_VP9, "WEBM", 0, "WebM / VP9", ""}, {0, NULL, 0, NULL, NULL}, }; @@ -7861,6 +7862,7 @@ void RNA_def_scene(BlenderRNA *brna) RNA_def_property_flag(prop, PROP_EDITABLE); RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_ui_text(prop, "World", "World used for rendering the scene"); + RNA_def_property_translation_context(prop, BLT_I18NCONTEXT_ID_WORLD); RNA_def_property_update(prop, NC_SCENE | ND_WORLD, "rna_Scene_world_update"); prop = RNA_def_property(srna, "objects", PROP_COLLECTION, PROP_NONE); diff --git a/source/blender/makesrna/intern/rna_sculpt_paint.c b/source/blender/makesrna/intern/rna_sculpt_paint.c index d8ad3d1f41e..440309849ab 100644 --- a/source/blender/makesrna/intern/rna_sculpt_paint.c +++ b/source/blender/makesrna/intern/rna_sculpt_paint.c @@ -927,17 +927,20 @@ static void rna_def_sculpt(BlenderRNA *brna) RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); } while ((++entry)->identifier); - prop = RNA_def_property(srna, "automasking_cavity_factor", PROP_FLOAT, PROP_NONE); + prop = RNA_def_property(srna, "automasking_cavity_factor", PROP_FLOAT, PROP_FACTOR); RNA_def_property_float_sdna(prop, NULL, "automasking_cavity_factor"); RNA_def_property_ui_text(prop, "Cavity Factor", "The contrast of the cavity mask"); - RNA_def_property_ui_range(prop, 0.0f, 1.0f, 0.1, 3); + RNA_def_property_float_default(prop, 1.0f); RNA_def_property_range(prop, 0.0f, 5.0f); + RNA_def_property_ui_range(prop, 0.0f, 1.0f, 0.1, 3); RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); prop = RNA_def_property(srna, "automasking_cavity_blur_steps", PROP_INT, PROP_NONE); RNA_def_property_int_sdna(prop, NULL, "automasking_cavity_blur_steps"); RNA_def_property_ui_text(prop, "Blur Steps", "The number of times the cavity mask is blurred"); - RNA_def_property_range(prop, 0.0f, 25.0f); + RNA_def_property_int_default(prop, 0); + RNA_def_property_range(prop, 0, 25); + RNA_def_property_ui_range(prop, 0, 10, 1, 1); RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); prop = RNA_def_property(srna, "automasking_cavity_curve", PROP_POINTER, PROP_NONE); diff --git a/source/blender/makesrna/intern/rna_sequencer.c b/source/blender/makesrna/intern/rna_sequencer.c index 634754d54cc..c952210eecf 100644 --- a/source/blender/makesrna/intern/rna_sequencer.c +++ b/source/blender/makesrna/intern/rna_sequencer.c @@ -778,7 +778,7 @@ static void rna_Sequence_filepath_get(PointerRNA *ptr, char *value) { Sequence *seq = (Sequence *)(ptr->data); - BLI_join_dirfile(value, FILE_MAX, seq->strip->dir, seq->strip->stripdata->name); + BLI_path_join(value, FILE_MAX, seq->strip->dir, seq->strip->stripdata->name); } static int rna_Sequence_filepath_length(PointerRNA *ptr) @@ -786,7 +786,7 @@ static int rna_Sequence_filepath_length(PointerRNA *ptr) Sequence *seq = (Sequence *)(ptr->data); char path[FILE_MAX]; - BLI_join_dirfile(path, sizeof(path), seq->strip->dir, seq->strip->stripdata->name); + BLI_path_join(path, sizeof(path), seq->strip->dir, seq->strip->stripdata->name); return strlen(path); } @@ -804,7 +804,7 @@ static void rna_Sequence_proxy_filepath_get(PointerRNA *ptr, char *value) { StripProxy *proxy = (StripProxy *)(ptr->data); - BLI_join_dirfile(value, FILE_MAX, proxy->dir, proxy->file); + BLI_path_join(value, FILE_MAX, proxy->dir, proxy->file); } static int rna_Sequence_proxy_filepath_length(PointerRNA *ptr) @@ -812,7 +812,7 @@ static int rna_Sequence_proxy_filepath_length(PointerRNA *ptr) StripProxy *proxy = (StripProxy *)(ptr->data); char path[FILE_MAX]; - BLI_join_dirfile(path, sizeof(path), proxy->dir, proxy->file); + BLI_path_join(path, sizeof(path), proxy->dir, proxy->file); return strlen(path); } diff --git a/source/blender/makesrna/intern/rna_space.c b/source/blender/makesrna/intern/rna_space.c index 738b41828e0..b2663b89333 100644 --- a/source/blender/makesrna/intern/rna_space.c +++ b/source/blender/makesrna/intern/rna_space.c @@ -426,7 +426,11 @@ static const EnumPropertyItem rna_enum_shading_color_type_items[] = { {V3D_SHADING_OBJECT_COLOR, "OBJECT", 0, "Object", "Show object color"}, {V3D_SHADING_RANDOM_COLOR, "RANDOM", 0, "Random", "Show random object color"}, {V3D_SHADING_VERTEX_COLOR, "VERTEX", 0, "Attribute", "Show active color attribute"}, - {V3D_SHADING_TEXTURE_COLOR, "TEXTURE", 0, "Texture", "Show texture"}, + {V3D_SHADING_TEXTURE_COLOR, + "TEXTURE", + 0, + "Texture", + "Show the texture from the active image texture node using the active UV map coordinates"}, {0, NULL, 0, NULL, NULL}, }; @@ -986,6 +990,13 @@ static PointerRNA rna_SpaceView3D_region_3d_get(PointerRNA *ptr) return rna_pointer_inherit_refine(ptr, &RNA_RegionView3D, regiondata); } +static void rna_SpaceView3D_object_type_visibility_update(Main *UNUSED(bmain), + Scene *scene, + PointerRNA *UNUSED(ptr)) +{ + DEG_id_tag_update(&scene->id, ID_RECALC_BASE_FLAGS); +} + static void rna_SpaceView3D_region_quadviews_begin(CollectionPropertyIterator *iter, PointerRNA *ptr) { @@ -3510,6 +3521,13 @@ static void rna_def_space_image_uv(BlenderRNA *brna) {0, NULL, 0, NULL, NULL}, }; + static const EnumPropertyItem grid_shape_source_items[] = { + {SI_GRID_SHAPE_DYNAMIC, "DYNAMIC", 0, "Dynamic", "Dynamic grid"}, + {SI_GRID_SHAPE_FIXED, "FIXED", 0, "Fixed", "Manually set grid divisions"}, + {SI_GRID_SHAPE_PIXEL, "PIXEL", 0, "Pixel", "Grid aligns with pixels from image"}, + {0, NULL, 0, NULL, NULL}, + }; + srna = RNA_def_struct(brna, "SpaceUVEditor", NULL); RNA_def_struct_sdna(srna, "SpaceImage"); RNA_def_struct_nested(brna, srna, "SpaceImageEditor"); @@ -3583,10 +3601,9 @@ static void rna_def_space_image_uv(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Grid Over Image", "Show the grid over the image"); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_IMAGE, NULL); - prop = RNA_def_property(srna, "use_custom_grid", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "flag", SI_CUSTOM_GRID); - RNA_def_property_boolean_default(prop, true); - RNA_def_property_ui_text(prop, "Custom Grid", "Use a grid with a user-defined number of steps"); + prop = RNA_def_property(srna, "grid_shape_source", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, grid_shape_source_items); + RNA_def_property_ui_text(prop, "Grid Shape Source", "Specify source for the grid shape"); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_IMAGE, NULL); prop = RNA_def_property(srna, "custom_grid_subdivisions", PROP_INT, PROP_XYZ); @@ -4087,6 +4104,7 @@ static void rna_def_space_view3d_shading(BlenderRNA *brna) prop = RNA_def_property(srna, "background_type", PROP_ENUM, PROP_NONE); RNA_def_property_enum_items(prop, background_type_items); RNA_def_property_ui_text(prop, "Background", "Way to display the background"); + RNA_def_property_translation_context(prop, BLT_I18NCONTEXT_EDITOR_VIEW3D); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D | NS_VIEW3D_SHADING, NULL); prop = RNA_def_property(srna, "background_color", PROP_FLOAT, PROP_COLOR); @@ -5075,7 +5093,8 @@ static void rna_def_space_view3d(BlenderRNA *brna) prop, NC_SPACE | ND_SPACE_VIEW3D, "rna_SpaceView3D_mirror_xr_session_update"); rna_def_object_type_visibility_flags_common(srna, - NC_SPACE | ND_SPACE_VIEW3D | NS_VIEW3D_SHADING); + NC_SPACE | ND_SPACE_VIEW3D | NS_VIEW3D_SHADING, + "rna_SpaceView3D_object_type_visibility_update"); /* Helper for drawing the icon. */ prop = RNA_def_property(srna, "icon_from_show_object_viewport", PROP_INT, PROP_NONE); @@ -5265,6 +5284,7 @@ static void rna_def_space_properties(BlenderRNA *brna) RNA_def_property_enum_funcs( prop, NULL, "rna_SpaceProperties_context_set", "rna_SpaceProperties_context_itemf"); RNA_def_property_ui_text(prop, "", ""); + RNA_def_property_translation_context(prop, BLT_I18NCONTEXT_ID_ID); RNA_def_property_update( prop, NC_SPACE | ND_SPACE_PROPERTIES, "rna_SpaceProperties_context_update"); @@ -6562,6 +6582,7 @@ static void rna_def_fileselect_entry(BlenderRNA *brna) prop, "Data-block Type", "The type of the data-block, if the file represents one ('NONE' otherwise)"); + RNA_def_property_translation_context(prop, BLT_I18NCONTEXT_ID_ID); prop = RNA_def_property(srna, "local_id", PROP_POINTER, PROP_NONE); RNA_def_property_struct_type(prop, "ID"); @@ -7314,12 +7335,14 @@ static void rna_def_space_node(BlenderRNA *brna) RNA_def_property_enum_sdna(prop, NULL, "texfrom"); RNA_def_property_enum_items(prop, texture_id_type_items); RNA_def_property_ui_text(prop, "Texture Type", "Type of data to take texture from"); + RNA_def_property_translation_context(prop, BLT_I18NCONTEXT_ID_ID); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_NODE, NULL); prop = RNA_def_property(srna, "shader_type", PROP_ENUM, PROP_NONE); RNA_def_property_enum_sdna(prop, NULL, "shaderfrom"); RNA_def_property_enum_items(prop, shader_type_items); RNA_def_property_ui_text(prop, "Shader Type", "Type of data to take shader from"); + RNA_def_property_translation_context(prop, BLT_I18NCONTEXT_ID_ID); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_NODE, NULL); prop = RNA_def_property(srna, "id", PROP_POINTER, PROP_NONE); @@ -7515,6 +7538,7 @@ static void rna_def_space_clip(BlenderRNA *brna) RNA_def_property_enum_sdna(prop, NULL, "mode"); RNA_def_property_enum_items(prop, rna_enum_clip_editor_mode_items); RNA_def_property_ui_text(prop, "Mode", "Editing context being displayed"); + RNA_def_property_translation_context(prop, BLT_I18NCONTEXT_ID_MOVIECLIP); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_CLIP, "rna_SpaceClipEditor_clip_mode_update"); /* view */ diff --git a/source/blender/makesrna/intern/rna_space_api.c b/source/blender/makesrna/intern/rna_space_api.c index b3896919275..9790a85e1c8 100644 --- a/source/blender/makesrna/intern/rna_space_api.c +++ b/source/blender/makesrna/intern/rna_space_api.c @@ -119,7 +119,9 @@ void RNA_api_space_text(StructRNA *srna) RNA_def_function_output(func, parm); } -void rna_def_object_type_visibility_flags_common(StructRNA *srna, int noteflag) +void rna_def_object_type_visibility_flags_common(StructRNA *srna, + int noteflag, + const char *update_func) { PropertyRNA *prop; @@ -173,7 +175,7 @@ void rna_def_object_type_visibility_flags_common(StructRNA *srna, int noteflag) RNA_def_property_boolean_negative_sdna( prop, NULL, view_mask_member[mask_index], info[type_index].type_mask); RNA_def_property_ui_text(prop, info[type_index].name, ""); - RNA_def_property_update(prop, noteflag, NULL); + RNA_def_property_update(prop, noteflag, update_func); } } } diff --git a/source/blender/makesrna/intern/rna_texture.c b/source/blender/makesrna/intern/rna_texture.c index 26849bfa622..2254db4edaf 100644 --- a/source/blender/makesrna/intern/rna_texture.c +++ b/source/blender/makesrna/intern/rna_texture.c @@ -24,6 +24,8 @@ #include "BKE_node_tree_update.h" #include "BKE_paint.h" +#include "BLT_translation.h" + #include "RNA_define.h" #include "RNA_enum_types.h" @@ -1191,6 +1193,7 @@ static void rna_def_texture_image(BlenderRNA *brna) RNA_def_property_enum_items(prop, prop_image_extension); RNA_def_property_ui_text( prop, "Extension", "How the image is extrapolated past its original bounds"); + RNA_def_property_translation_context(prop, BLT_I18NCONTEXT_ID_IMAGE); RNA_def_property_update(prop, 0, "rna_Texture_update"); prop = RNA_def_property(srna, "repeat_x", PROP_INT, PROP_NONE); diff --git a/source/blender/makesrna/intern/rna_userdef.c b/source/blender/makesrna/intern/rna_userdef.c index c9e3822c996..e22ae205b0a 100644 --- a/source/blender/makesrna/intern/rna_userdef.c +++ b/source/blender/makesrna/intern/rna_userdef.c @@ -4057,6 +4057,7 @@ static void rna_def_userdef_studiolights(BlenderRNA *brna) STUDIOLIGHT_TYPE_WORLD, "Type", "The type for the new studio light"); + RNA_def_property_translation_context(parm, BLT_I18NCONTEXT_ID_LIGHT); RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); parm = RNA_def_pointer(func, "studio_light", "StudioLight", "", "Newly created StudioLight"); RNA_def_function_return(func, parm); @@ -4117,6 +4118,7 @@ static void rna_def_userdef_studiolight(BlenderRNA *brna) RNA_def_property_enum_funcs(prop, "rna_UserDef_studiolight_type_get", NULL, NULL); RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_property_ui_text(prop, "Type", ""); + RNA_def_property_translation_context(prop, BLT_I18NCONTEXT_ID_LIGHT); prop = RNA_def_property(srna, "name", PROP_STRING, PROP_NONE); RNA_def_property_string_funcs( @@ -5230,6 +5232,12 @@ static void rna_def_userdef_edit(BlenderRNA *brna) RNA_def_property_ui_text( prop, "Duplicate Volume", "Causes volume data to be duplicated with the object"); + prop = RNA_def_property(srna, "use_duplicate_node_tree", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "dupflag", USER_DUP_NTREE); + RNA_def_property_ui_text(prop, + "Duplicate Node Tree", + "Make copies of node groups when duplicating nodes in the node editor"); + /* Currently only used for insert offset (aka auto-offset), * maybe also be useful for later stuff though. */ prop = RNA_def_property(srna, "node_margin", PROP_INT, PROP_PIXEL); @@ -5742,8 +5750,8 @@ static void rna_def_userdef_input(BlenderRNA *brna) RNA_def_property_boolean_negative_sdna(prop, NULL, "uiflag", USER_NO_MULTITOUCH_GESTURES); RNA_def_property_ui_text( prop, - "Multitouch Gestures", - "Use multitouch gestures for navigation with touchpad, instead of scroll wheel emulation"); + "Multi-touch Gestures", + "Use multi-touch gestures for navigation with touchpad, instead of scroll wheel emulation"); RNA_def_property_update(prop, 0, "rna_userdef_input_devices"); prop = RNA_def_property(srna, "invert_mouse_zoom", PROP_BOOLEAN, PROP_NONE); diff --git a/source/blender/makesrna/intern/rna_wm.c b/source/blender/makesrna/intern/rna_wm.c index 7fd590005fd..eebe595820e 100644 --- a/source/blender/makesrna/intern/rna_wm.c +++ b/source/blender/makesrna/intern/rna_wm.c @@ -99,13 +99,6 @@ static const EnumPropertyItem event_ndof_type_items[] = { {NDOF_BUTTON_DOMINANT, "NDOF_BUTTON_DOMINANT", 0, "Dominant", ""}, {NDOF_BUTTON_PLUS, "NDOF_BUTTON_PLUS", 0, "Plus", ""}, {NDOF_BUTTON_MINUS, "NDOF_BUTTON_MINUS", 0, "Minus", ""}, -# if 0 /* Never used (converted to keyboard events by GHOST). */ - /* keyboard emulation */ - {NDOF_BUTTON_ESC, "NDOF_BUTTON_ESC", 0, "Esc"}, - {NDOF_BUTTON_ALT, "NDOF_BUTTON_ALT", 0, "Alt"}, - {NDOF_BUTTON_SHIFT, "NDOF_BUTTON_SHIFT", 0, "Shift"}, - {NDOF_BUTTON_CTRL, "NDOF_BUTTON_CTRL", 0, "Ctrl"}, -# endif /* general-purpose buttons */ {NDOF_BUTTON_1, "NDOF_BUTTON_1", 0, "Button 1", ""}, {NDOF_BUTTON_2, "NDOF_BUTTON_2", 0, "Button 2", ""}, @@ -120,6 +113,21 @@ static const EnumPropertyItem event_ndof_type_items[] = { {NDOF_BUTTON_A, "NDOF_BUTTON_A", 0, "Button A", ""}, {NDOF_BUTTON_B, "NDOF_BUTTON_B", 0, "Button B", ""}, {NDOF_BUTTON_C, "NDOF_BUTTON_C", 0, "Button C", ""}, + /* View buttons. */ + {NDOF_BUTTON_V1, "NDOF_BUTTON_V1", 0, "View 1", ""}, + {NDOF_BUTTON_V2, "NDOF_BUTTON_V2", 0, "View 2", ""}, + {NDOF_BUTTON_V3, "NDOF_BUTTON_V3", 0, "View 3", ""}, +# if 0 /* Never used (converted to keyboard events by GHOST). */ + /* keyboard emulation */ + {NDOF_BUTTON_ESC, "NDOF_BUTTON_ESC", 0, "Esc"}, + {NDOF_BUTTON_ENTER, "NDOF_BUTTON_ENTER", 0, "Enter"}, + {NDOF_BUTTON_DELETE, "NDOF_BUTTON_DELETE", 0, "Delete"}, + {NDOF_BUTTON_TAB, "NDOF_BUTTON_TAB", 0, "Tab"}, + {NDOF_BUTTON_SPACE, "NDOF_BUTTON_SPACE", 0, "Space"}, + {NDOF_BUTTON_ALT, "NDOF_BUTTON_ALT", 0, "Alt"}, + {NDOF_BUTTON_SHIFT, "NDOF_BUTTON_SHIFT", 0, "Shift"}, + {NDOF_BUTTON_CTRL, "NDOF_BUTTON_CTRL", 0, "Ctrl"}, +# endif {0, NULL, 0, NULL, NULL}, }; #endif /* RNA_RUNTIME */ @@ -1644,12 +1652,7 @@ static StructRNA *rna_MacroOperator_register(Main *bmain, return NULL; } - if (strlen(identifier) >= sizeof(dummyop.idname)) { - BKE_reportf(reports, - RPT_ERROR, - "Registering operator class: '%s' is too long, maximum length is %d", - identifier, - (int)sizeof(dummyop.idname)); + if (!WM_operator_py_idname_ok_or_report(reports, identifier, dummyot.idname)) { return NULL; } @@ -1661,10 +1664,6 @@ static StructRNA *rna_MacroOperator_register(Main *bmain, } } - if (!WM_operator_py_idname_ok_or_report(reports, identifier, dummyot.idname)) { - return NULL; - } - char idname_conv[sizeof(dummyop.idname)]; WM_operator_bl_idname(idname_conv, dummyot.idname); /* convert the idname from python */ @@ -1867,8 +1866,9 @@ static void rna_def_operator_common(StructRNA *srna) /* Registration */ prop = RNA_def_property(srna, "bl_idname", PROP_STRING, PROP_NONE); RNA_def_property_string_sdna(prop, NULL, "type->idname"); - /* Without setting the length the pointer size would be used. -3 because `.` -> `_OT_`. */ - RNA_def_property_string_maxlength(prop, OP_MAX_TYPENAME - 3); + /* String stored here is the 'BL' identifier (`OPMODULE_OT_my_op`), + * not the 'python' identifier (`opmodule.my_op`). */ + RNA_def_property_string_maxlength(prop, OP_MAX_TYPENAME); RNA_def_property_string_funcs(prop, NULL, NULL, "rna_Operator_bl_idname_set"); // RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_property_flag(prop, PROP_REGISTER); diff --git a/source/blender/makesrna/intern/rna_xr.c b/source/blender/makesrna/intern/rna_xr.c index dcfa1bbca51..c803e5dc9a8 100644 --- a/source/blender/makesrna/intern/rna_xr.c +++ b/source/blender/makesrna/intern/rna_xr.c @@ -2052,7 +2052,7 @@ static void rna_def_xr_session_settings(BlenderRNA *brna) "Allow the VR tracking origin to be defined independently of the headset location"); RNA_def_property_update(prop, NC_WM | ND_XR_DATA_CHANGED, NULL); - rna_def_object_type_visibility_flags_common(srna, NC_WM | ND_XR_DATA_CHANGED); + rna_def_object_type_visibility_flags_common(srna, NC_WM | ND_XR_DATA_CHANGED, NULL); /* Helper for drawing the icon. */ prop = RNA_def_property(srna, "icon_from_show_object_viewport", PROP_INT, PROP_NONE); diff --git a/source/blender/modifiers/intern/MOD_cast.c b/source/blender/modifiers/intern/MOD_cast.c index 1a4942fcaf1..30be1d33653 100644 --- a/source/blender/modifiers/intern/MOD_cast.c +++ b/source/blender/modifiers/intern/MOD_cast.c @@ -23,6 +23,7 @@ #include "BKE_lib_id.h" #include "BKE_lib_query.h" #include "BKE_mesh.h" +#include "BKE_mesh_runtime.h" #include "BKE_mesh_wrapper.h" #include "BKE_modifier.h" #include "BKE_screen.h" @@ -494,7 +495,7 @@ static void deformVertsEM(ModifierData *md, mesh_src = MOD_deform_mesh_eval_get(ctx->object, editData, mesh, NULL, verts_num, false); } - if (mesh && mesh->runtime.wrapper_type == ME_WRAPPER_TYPE_MDATA) { + if (mesh && BKE_mesh_wrapper_type(mesh) == ME_WRAPPER_TYPE_MDATA) { BLI_assert(mesh->totvert == verts_num); } diff --git a/source/blender/modifiers/intern/MOD_datatransfer.cc b/source/blender/modifiers/intern/MOD_datatransfer.cc index 4b6170598dd..25e8eb8fa20 100644 --- a/source/blender/modifiers/intern/MOD_datatransfer.cc +++ b/source/blender/modifiers/intern/MOD_datatransfer.cc @@ -213,7 +213,7 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh * dtmd->defgrp_name, invert_vgroup, &reports)) { - result->runtime.is_original_bmesh = false; + result->runtime->is_original_bmesh = false; } if (BKE_reports_contain(&reports, RPT_ERROR)) { diff --git a/source/blender/modifiers/intern/MOD_multires.cc b/source/blender/modifiers/intern/MOD_multires.cc index e4c90eb237b..2bc3763c46b 100644 --- a/source/blender/modifiers/intern/MOD_multires.cc +++ b/source/blender/modifiers/intern/MOD_multires.cc @@ -230,7 +230,7 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh * if ((ctx->object->mode & OB_MODE_SCULPT) && !for_orco && !for_render && !sculpt_base_mesh) { /* NOTE: CCG takes ownership over Subdiv. */ result = multires_as_ccg(mmd, ctx, mesh, subdiv); - result->runtime.subdiv_ccg_tot_level = mmd->totlvl; + result->runtime->subdiv_ccg_tot_level = mmd->totlvl; /* TODO(sergey): Usually it is sculpt stroke's update variants which * takes care of this, but is possible that we need this before the * stroke: i.e. when exiting blender right after stroke is done. @@ -238,7 +238,7 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh * * surely there is a better way of solving this. */ if (ctx->object->sculpt != nullptr) { SculptSession *sculpt_session = ctx->object->sculpt; - sculpt_session->subdiv_ccg = result->runtime.subdiv_ccg; + sculpt_session->subdiv_ccg = result->runtime->subdiv_ccg; sculpt_session->multires.active = true; sculpt_session->multires.modifier = mmd; sculpt_session->multires.level = mmd->sculptlvl; @@ -343,7 +343,7 @@ static void panel_draw(const bContext *C, Panel *panel) modifier_panel_end(layout, ptr); } -static void subdivisions_panel_draw(const bContext *UNUSED(C), Panel *panel) +static void subdivisions_panel_draw(const bContext * /*C*/, Panel *panel) { uiLayout *row; uiLayout *layout = panel->layout; @@ -406,7 +406,7 @@ static void subdivisions_panel_draw(const bContext *UNUSED(C), Panel *panel) uiItemO(layout, IFACE_("Delete Higher"), ICON_NONE, "OBJECT_OT_multires_higher_levels_delete"); } -static void shape_panel_draw(const bContext *UNUSED(C), Panel *panel) +static void shape_panel_draw(const bContext * /*C*/, Panel *panel) { uiLayout *row; uiLayout *layout = panel->layout; @@ -421,7 +421,7 @@ static void shape_panel_draw(const bContext *UNUSED(C), Panel *panel) uiItemO(row, IFACE_("Apply Base"), ICON_NONE, "OBJECT_OT_multires_base_apply"); } -static void generate_panel_draw(const bContext *UNUSED(C), Panel *panel) +static void generate_panel_draw(const bContext * /*C*/, Panel *panel) { uiLayout *col, *row; uiLayout *layout = panel->layout; @@ -449,7 +449,7 @@ static void generate_panel_draw(const bContext *UNUSED(C), Panel *panel) } } -static void advanced_panel_draw(const bContext *UNUSED(C), Panel *panel) +static void advanced_panel_draw(const bContext * /*C*/, Panel *panel) { uiLayout *col; uiLayout *layout = panel->layout; diff --git a/source/blender/modifiers/intern/MOD_normal_edit.cc b/source/blender/modifiers/intern/MOD_normal_edit.cc index 6f86bf1d8cb..43ded18fcc4 100644 --- a/source/blender/modifiers/intern/MOD_normal_edit.cc +++ b/source/blender/modifiers/intern/MOD_normal_edit.cc @@ -623,7 +623,7 @@ static Mesh *normalEditModifier_do(NormalEditModifierData *enmd, MEM_SAFE_FREE(loopnors); - result->runtime.is_original_bmesh = false; + result->runtime->is_original_bmesh = false; return result; } diff --git a/source/blender/modifiers/intern/MOD_particlesystem.cc b/source/blender/modifiers/intern/MOD_particlesystem.cc index b957bd26ca6..66291520176 100644 --- a/source/blender/modifiers/intern/MOD_particlesystem.cc +++ b/source/blender/modifiers/intern/MOD_particlesystem.cc @@ -159,7 +159,7 @@ static void deformVerts(ModifierData *md, BKE_mesh_tessface_ensure(psmd->mesh_final); - if (!psmd->mesh_final->runtime.deformed_only) { + if (!psmd->mesh_final->runtime->deformed_only) { /* Get the original mesh from the object, this is what the particles * are attached to so in case of non-deform modifiers we need to remap * them to the final mesh (typically subdivision surfaces). */ diff --git a/source/blender/modifiers/intern/MOD_remesh.c b/source/blender/modifiers/intern/MOD_remesh.c index 4241ca5a591..d6241fcb290 100644 --- a/source/blender/modifiers/intern/MOD_remesh.c +++ b/source/blender/modifiers/intern/MOD_remesh.c @@ -67,10 +67,9 @@ static void init_dualcon_mesh(DualConInput *input, Mesh *mesh) input->mloop = (void *)BKE_mesh_loops(mesh); input->loop_stride = sizeof(MLoop); - BKE_mesh_runtime_looptri_ensure(mesh); - input->looptri = (void *)mesh->runtime.looptris.array; + input->looptri = (void *)BKE_mesh_runtime_looptri_ensure(mesh); input->tri_stride = sizeof(MLoopTri); - input->tottri = mesh->runtime.looptris.len; + input->tottri = BKE_mesh_runtime_looptri_len(mesh); INIT_MINMAX(input->min, input->max); BKE_mesh_minmax(mesh, input->min, input->max); diff --git a/source/blender/modifiers/intern/MOD_subsurf.cc b/source/blender/modifiers/intern/MOD_subsurf.cc index 991bd0d876c..5e77f0ffa9e 100644 --- a/source/blender/modifiers/intern/MOD_subsurf.cc +++ b/source/blender/modifiers/intern/MOD_subsurf.cc @@ -208,7 +208,7 @@ static void subdiv_cache_mesh_wrapper_settings(const ModifierEvalContext *ctx, runtime_data->calc_loop_normals = false; /* Set at the end of modifier stack evaluation. */ runtime_data->use_loop_normals = (smd->flags & eSubsurfModifierFlag_UseCustomNormals); - mesh->runtime.subsurf_runtime_data = runtime_data; + mesh->runtime->subsurf_runtime_data = runtime_data; } /* Modifier itself. */ diff --git a/source/blender/modifiers/intern/MOD_util.cc b/source/blender/modifiers/intern/MOD_util.cc index 7d66f75b20f..589a3d28ad9 100644 --- a/source/blender/modifiers/intern/MOD_util.cc +++ b/source/blender/modifiers/intern/MOD_util.cc @@ -57,7 +57,7 @@ void MOD_init_texture(MappingInfoModifierData *dmd, const ModifierEvalContext *c /* TODO: to be renamed to get_texture_coords once we are done with moving modifiers to Mesh. */ void MOD_get_texture_coords(MappingInfoModifierData *dmd, - const ModifierEvalContext *UNUSED(ctx), + const ModifierEvalContext * /*ctx*/, Object *ob, Mesh *mesh, float (*cos)[3], @@ -188,7 +188,7 @@ Mesh *MOD_deform_mesh_eval_get(Object *ob, &mesh_prior_modifiers->id, nullptr, (LIB_ID_COPY_LOCALIZE | LIB_ID_COPY_CD_REFERENCE)); - mesh->runtime.deformed_only = 1; + mesh->runtime->deformed_only = 1; } if (em != nullptr) { @@ -218,7 +218,7 @@ Mesh *MOD_deform_mesh_eval_get(Object *ob, } } - if (mesh && mesh->runtime.wrapper_type == ME_WRAPPER_TYPE_MDATA) { + if (mesh && mesh->runtime->wrapper_type == ME_WRAPPER_TYPE_MDATA) { BLI_assert(mesh->totvert == verts_num); } diff --git a/source/blender/modifiers/intern/MOD_uvproject.cc b/source/blender/modifiers/intern/MOD_uvproject.cc index 248b7b48a7c..c07b2059b5b 100644 --- a/source/blender/modifiers/intern/MOD_uvproject.cc +++ b/source/blender/modifiers/intern/MOD_uvproject.cc @@ -52,7 +52,7 @@ static void initData(ModifierData *md) MEMCPY_STRUCT_AFTER(umd, DNA_struct_default_get(UVProjectModifierData), modifier); } -static void requiredDataMask(ModifierData *UNUSED(md), CustomData_MeshMasks *r_cddata_masks) +static void requiredDataMask(ModifierData * /*md*/, CustomData_MeshMasks *r_cddata_masks) { /* ask for UV coordinates */ r_cddata_masks->lmask |= CD_MASK_MLOOPUV; @@ -90,7 +90,7 @@ struct Projector { }; static Mesh *uvprojectModifier_do(UVProjectModifierData *umd, - const ModifierEvalContext *UNUSED(ctx), + const ModifierEvalContext * /*ctx*/, Object *ob, Mesh *mesh) { @@ -283,7 +283,7 @@ static Mesh *uvprojectModifier_do(UVProjectModifierData *umd, } } - mesh->runtime.is_original_bmesh = false; + mesh->runtime->is_original_bmesh = false; return mesh; } @@ -298,7 +298,7 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh * return result; } -static void panel_draw(const bContext *UNUSED(C), Panel *panel) +static void panel_draw(const bContext * /*C*/, Panel *panel) { uiLayout *sub; uiLayout *layout = panel->layout; diff --git a/source/blender/modifiers/intern/MOD_uvwarp.cc b/source/blender/modifiers/intern/MOD_uvwarp.cc index 85fd8946d0b..4ec273bcbc6 100644 --- a/source/blender/modifiers/intern/MOD_uvwarp.cc +++ b/source/blender/modifiers/intern/MOD_uvwarp.cc @@ -93,7 +93,7 @@ struct UVWarpData { static void uv_warp_compute(void *__restrict userdata, const int i, - const TaskParallelTLS *__restrict UNUSED(tls)) + const TaskParallelTLS *__restrict /*tls*/) { const UVWarpData *data = static_cast<const UVWarpData *>(userdata); @@ -216,7 +216,7 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh * settings.use_threading = (polys_num > 1000); BLI_task_parallel_range(0, polys_num, &data, uv_warp_compute, &settings); - mesh->runtime.is_original_bmesh = false; + mesh->runtime->is_original_bmesh = false; return mesh; } @@ -241,7 +241,7 @@ static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphConte DEG_add_depends_on_transform_relation(ctx->node, "UVWarp Modifier"); } -static void panel_draw(const bContext *UNUSED(C), Panel *panel) +static void panel_draw(const bContext * /*C*/, Panel *panel) { uiLayout *col; uiLayout *layout = panel->layout; @@ -283,7 +283,7 @@ static void panel_draw(const bContext *UNUSED(C), Panel *panel) modifier_panel_end(layout, ptr); } -static void transform_panel_draw(const bContext *UNUSED(C), Panel *panel) +static void transform_panel_draw(const bContext * /*C*/, Panel *panel) { uiLayout *layout = panel->layout; diff --git a/source/blender/modifiers/intern/MOD_wave.cc b/source/blender/modifiers/intern/MOD_wave.cc index 962bb60c8ad..647e0324707 100644 --- a/source/blender/modifiers/intern/MOD_wave.cc +++ b/source/blender/modifiers/intern/MOD_wave.cc @@ -21,6 +21,7 @@ #include "BKE_context.h" #include "BKE_deform.h" #include "BKE_editmesh.h" +#include "BKE_editmesh_cache.h" #include "BKE_lib_id.h" #include "BKE_lib_query.h" #include "BKE_mesh.h" @@ -337,7 +338,7 @@ static void deformVertsEM(ModifierData *md, if (!ELEM(mesh_src, nullptr, mesh)) { /* Important not to free `vertexCos` owned by the caller. */ - EditMeshData *edit_data = mesh_src->runtime.edit_data; + EditMeshData *edit_data = mesh_src->runtime->edit_data; if (edit_data->vertexCos == vertexCos) { edit_data->vertexCos = nullptr; } diff --git a/source/blender/modifiers/intern/MOD_weighted_normal.cc b/source/blender/modifiers/intern/MOD_weighted_normal.cc index e7b1cb45234..1ebd5423d39 100644 --- a/source/blender/modifiers/intern/MOD_weighted_normal.cc +++ b/source/blender/modifiers/intern/MOD_weighted_normal.cc @@ -676,7 +676,7 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh * MEM_SAFE_FREE(wn_data.mode_pair); MEM_SAFE_FREE(wn_data.items_data); - result->runtime.is_original_bmesh = false; + result->runtime->is_original_bmesh = false; return result; } @@ -705,12 +705,12 @@ static void requiredDataMask(ModifierData *md, CustomData_MeshMasks *r_cddata_ma } } -static bool dependsOnNormals(ModifierData *UNUSED(md)) +static bool dependsOnNormals(ModifierData * /*md*/) { return true; } -static void panel_draw(const bContext *UNUSED(C), Panel *panel) +static void panel_draw(const bContext * /*C*/, Panel *panel) { uiLayout *col; uiLayout *layout = panel->layout; diff --git a/source/blender/modifiers/intern/MOD_weightvgedit.cc b/source/blender/modifiers/intern/MOD_weightvgedit.cc index bcfd47491d5..e4fbca633f7 100644 --- a/source/blender/modifiers/intern/MOD_weightvgedit.cc +++ b/source/blender/modifiers/intern/MOD_weightvgedit.cc @@ -96,7 +96,7 @@ static void requiredDataMask(ModifierData *md, CustomData_MeshMasks *r_cddata_ma /* No need to ask for CD_PREVIEW_MLOOPCOL... */ } -static bool dependsOnTime(Scene *UNUSED(scene), ModifierData *md) +static bool dependsOnTime(Scene * /*scene*/, ModifierData *md) { WeightVGEditModifierData *wmd = (WeightVGEditModifierData *)md; @@ -142,7 +142,7 @@ static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphConte } } -static bool isDisabled(const Scene *UNUSED(scene), ModifierData *md, bool UNUSED(useRenderParams)) +static bool isDisabled(const Scene * /*scene*/, ModifierData *md, bool /*useRenderParams*/) { WeightVGEditModifierData *wmd = (WeightVGEditModifierData *)md; /* If no vertex group, bypass. */ @@ -278,13 +278,13 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh * MEM_freeN(new_w); MEM_freeN(dw); - mesh->runtime.is_original_bmesh = false; + mesh->runtime->is_original_bmesh = false; /* Return the vgroup-modified mesh. */ return mesh; } -static void panel_draw(const bContext *UNUSED(C), Panel *panel) +static void panel_draw(const bContext * /*C*/, Panel *panel) { uiLayout *sub, *col, *row; uiLayout *layout = panel->layout; @@ -326,7 +326,7 @@ static void panel_draw(const bContext *UNUSED(C), Panel *panel) modifier_panel_end(layout, ptr); } -static void falloff_panel_draw(const bContext *UNUSED(C), Panel *panel) +static void falloff_panel_draw(const bContext * /*C*/, Panel *panel) { uiLayout *row, *sub; uiLayout *layout = panel->layout; @@ -366,7 +366,7 @@ static void panelRegister(ARegionType *region_type) region_type, "influence", "Influence", nullptr, influence_panel_draw, panel_type); } -static void blendWrite(BlendWriter *writer, const ID *UNUSED(id_owner), const ModifierData *md) +static void blendWrite(BlendWriter *writer, const ID * /*id_owner*/, const ModifierData *md) { const WeightVGEditModifierData *wmd = (const WeightVGEditModifierData *)md; diff --git a/source/blender/modifiers/intern/MOD_weightvgmix.cc b/source/blender/modifiers/intern/MOD_weightvgmix.cc index 720f9fac948..d860e93d535 100644 --- a/source/blender/modifiers/intern/MOD_weightvgmix.cc +++ b/source/blender/modifiers/intern/MOD_weightvgmix.cc @@ -144,7 +144,7 @@ static void requiredDataMask(ModifierData *md, CustomData_MeshMasks *r_cddata_ma /* No need to ask for CD_PREVIEW_MLOOPCOL... */ } -static bool dependsOnTime(Scene *UNUSED(scene), ModifierData *md) +static bool dependsOnTime(Scene * /*scene*/, ModifierData *md) { WeightVGMixModifierData *wmd = (WeightVGMixModifierData *)md; @@ -190,7 +190,7 @@ static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphConte } } -static bool isDisabled(const Scene *UNUSED(scene), ModifierData *md, bool UNUSED(useRenderParams)) +static bool isDisabled(const Scene * /*scene*/, ModifierData *md, bool /*useRenderParams*/) { WeightVGMixModifierData *wmd = (WeightVGMixModifierData *)md; /* If no vertex group, bypass. */ @@ -438,13 +438,13 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh * MEM_freeN(dw2); MEM_SAFE_FREE(indices); - mesh->runtime.is_original_bmesh = false; + mesh->runtime->is_original_bmesh = false; /* Return the vgroup-modified mesh. */ return mesh; } -static void panel_draw(const bContext *UNUSED(C), Panel *panel) +static void panel_draw(const bContext * /*C*/, Panel *panel) { uiLayout *layout = panel->layout; diff --git a/source/blender/modifiers/intern/MOD_weightvgproximity.cc b/source/blender/modifiers/intern/MOD_weightvgproximity.cc index faee2b9e6d5..93052f4215d 100644 --- a/source/blender/modifiers/intern/MOD_weightvgproximity.cc +++ b/source/blender/modifiers/intern/MOD_weightvgproximity.cc @@ -345,7 +345,7 @@ static void requiredDataMask(ModifierData *md, CustomData_MeshMasks *r_cddata_ma /* No need to ask for CD_PREVIEW_MLOOPCOL... */ } -static bool dependsOnTime(Scene *UNUSED(scene), ModifierData *md) +static bool dependsOnTime(Scene * /*scene*/, ModifierData *md) { WeightVGProximityModifierData *wmd = (WeightVGProximityModifierData *)md; @@ -403,7 +403,7 @@ static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphConte } } -static bool isDisabled(const Scene *UNUSED(scene), ModifierData *md, bool UNUSED(useRenderParams)) +static bool isDisabled(const Scene * /*scene*/, ModifierData *md, bool /*useRenderParams*/) { WeightVGProximityModifierData *wmd = (WeightVGProximityModifierData *)md; /* If no vertex group, bypass. */ @@ -639,13 +639,13 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh * TIMEIT_END(perf); #endif - mesh->runtime.is_original_bmesh = false; + mesh->runtime->is_original_bmesh = false; /* Return the vgroup-modified mesh. */ return mesh; } -static void panel_draw(const bContext *UNUSED(C), Panel *panel) +static void panel_draw(const bContext * /*C*/, Panel *panel) { uiLayout *col; uiLayout *layout = panel->layout; @@ -673,7 +673,7 @@ static void panel_draw(const bContext *UNUSED(C), Panel *panel) uiItemR(layout, ptr, "normalize", 0, nullptr, ICON_NONE); } -static void falloff_panel_draw(const bContext *UNUSED(C), Panel *panel) +static void falloff_panel_draw(const bContext * /*C*/, Panel *panel) { uiLayout *row, *sub; uiLayout *layout = panel->layout; @@ -714,7 +714,7 @@ static void panelRegister(ARegionType *region_type) region_type, "influence", "Influence", nullptr, influence_panel_draw, panel_type); } -static void blendWrite(BlendWriter *writer, const ID *UNUSED(id_owner), const ModifierData *md) +static void blendWrite(BlendWriter *writer, const ID * /*id_owner*/, const ModifierData *md) { const WeightVGProximityModifierData *wmd = (const WeightVGProximityModifierData *)md; diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt index 422ee747649..2398aefc67a 100644 --- a/source/blender/nodes/CMakeLists.txt +++ b/source/blender/nodes/CMakeLists.txt @@ -50,7 +50,7 @@ set(SRC intern/node_multi_function.cc intern/node_socket.cc intern/node_socket_declarations.cc - intern/node_util.c + intern/node_util.cc intern/socket_search_link.cc NOD_common.h diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h index fd3f94d264a..3cd25bdde3c 100644 --- a/source/blender/nodes/NOD_static_types.h +++ b/source/blender/nodes/NOD_static_types.h @@ -370,7 +370,7 @@ DefNode(GeometryNode, GEO_NODE_MESH_TO_POINTS, def_geo_mesh_to_points, "MESH_TO_ DefNode(GeometryNode, GEO_NODE_MESH_TO_VOLUME, def_geo_mesh_to_volume, "MESH_TO_VOLUME", MeshToVolume, "Mesh to Volume", "Create a fog volume with the shape of the input mesh's surface") DefNode(GeometryNode, GEO_NODE_MESH_TOPOLOGY_CORNERS_OF_FACE, 0, "CORNERS_OF_FACE", CornersOfFace, "Corners of Face", "Retrieve corners that make up a face") DefNode(GeometryNode, GEO_NODE_MESH_TOPOLOGY_CORNERS_OF_VERTEX, 0, "CORNERS_OF_VERTEX", CornersOfVertex, "Corners of Vertex", "Retrieve face corners connected to vertices") -DefNode(GeometryNode, GEO_NODE_MESH_TOPOLOGY_EDGES_OF_CORNER, 0, "EDGES_OF_CORNER", EdgesOfCorner, "Edges of Corner", "Retrieve the edges on boths sides of a face corner") +DefNode(GeometryNode, GEO_NODE_MESH_TOPOLOGY_EDGES_OF_CORNER, 0, "EDGES_OF_CORNER", EdgesOfCorner, "Edges of Corner", "Retrieve the edges on both sides of a face corner") DefNode(GeometryNode, GEO_NODE_MESH_TOPOLOGY_EDGES_OF_VERTEX, 0, "EDGES_OF_VERTEX", EdgesOfVertex, "Edges of Vertex", "Retrieve the edges connected to each vertex") DefNode(GeometryNode, GEO_NODE_MESH_TOPOLOGY_FACE_OF_CORNER, 0, "FACE_OF_CORNER", FaceOfCorner, "Face of Corner", "Retrieve the face each face corner is part of") DefNode(GeometryNode, GEO_NODE_MESH_TOPOLOGY_OFFSET_CORNER_IN_FACE, 0, "OFFSET_CORNER_IN_FACE", OffsetCornerInFace, "Offset Corner in Face", "Retrieve corners in the same face as another") diff --git a/source/blender/nodes/composite/CMakeLists.txt b/source/blender/nodes/composite/CMakeLists.txt index 2537e8e93cc..4255a2dde21 100644 --- a/source/blender/nodes/composite/CMakeLists.txt +++ b/source/blender/nodes/composite/CMakeLists.txt @@ -18,6 +18,7 @@ set(INC ../../render ../../windowmanager ../../compositor/realtime_compositor + ../../compositor/realtime_compositor/algorithms ../../../../intern/guardedalloc # dna_type_offsets.h diff --git a/source/blender/nodes/composite/nodes/node_composite_bokehblur.cc b/source/blender/nodes/composite/nodes/node_composite_bokehblur.cc index 731c559dfdc..a581d87a463 100644 --- a/source/blender/nodes/composite/nodes/node_composite_bokehblur.cc +++ b/source/blender/nodes/composite/nodes/node_composite_bokehblur.cc @@ -70,10 +70,20 @@ class BokehBlurOperation : public NodeOperation { return; } + if (get_input("Size").is_single_value() || !get_variable_size()) { + execute_constant_size(); + } + else { + execute_variable_size(); + } + } + + void execute_constant_size() + { GPUShader *shader = shader_manager().get("compositor_blur"); GPU_shader_bind(shader); - GPU_shader_uniform_1i(shader, "radius", compute_blur_radius()); + GPU_shader_uniform_1i(shader, "radius", int(compute_blur_radius())); GPU_shader_uniform_1b(shader, "extend_bounds", get_extend_bounds()); const Result &input_image = get_input("Image"); @@ -88,7 +98,7 @@ class BokehBlurOperation : public NodeOperation { Domain domain = compute_domain(); if (get_extend_bounds()) { /* Add a radius amount of pixels in both sides of the image, hence the multiply by 2. */ - domain.size += int2(compute_blur_radius() * 2); + domain.size += int2(int(compute_blur_radius()) * 2); } Result &output_image = get_result("Image"); @@ -104,7 +114,42 @@ class BokehBlurOperation : public NodeOperation { input_mask.unbind_as_texture(); } - int compute_blur_radius() + void execute_variable_size() + { + GPUShader *shader = shader_manager().get("compositor_blur_variable_size"); + GPU_shader_bind(shader); + + GPU_shader_uniform_1f(shader, "base_size", compute_blur_radius()); + GPU_shader_uniform_1i(shader, "search_radius", get_max_size()); + + const Result &input_image = get_input("Image"); + input_image.bind_as_texture(shader, "input_tx"); + + const Result &input_weights = get_input("Bokeh"); + input_weights.bind_as_texture(shader, "weights_tx"); + + const Result &input_size = get_input("Size"); + input_size.bind_as_texture(shader, "size_tx"); + + const Result &input_mask = get_input("Bounding box"); + input_mask.bind_as_texture(shader, "mask_tx"); + + const Domain domain = compute_domain(); + Result &output_image = get_result("Image"); + output_image.allocate_texture(domain); + output_image.bind_as_image(shader, "output_img"); + + compute_dispatch_threads_at_least(shader, domain.size); + + GPU_shader_unbind(); + output_image.unbind_as_image(); + input_image.unbind_as_texture(); + input_weights.unbind_as_texture(); + input_size.unbind_as_texture(); + input_mask.unbind_as_texture(); + } + + float compute_blur_radius() { const int2 image_size = get_input("Image").domain().size; const int max_size = math::max(image_size.x, image_size.y); @@ -124,7 +169,7 @@ class BokehBlurOperation : public NodeOperation { return true; } - if (compute_blur_radius() == 0) { + if (compute_blur_radius() == 0.0f) { return true; } @@ -142,6 +187,16 @@ class BokehBlurOperation : public NodeOperation { { return bnode().custom1 & CMP_NODEFLAG_BLUR_EXTEND_BOUNDS; } + + bool get_variable_size() + { + return bnode().custom1 & CMP_NODEFLAG_BLUR_VARIABLE_SIZE; + } + + int get_max_size() + { + return static_cast<int>(bnode().custom4); + } }; static NodeOperation *get_compositor_operation(Context &context, DNode node) diff --git a/source/blender/nodes/composite/nodes/node_composite_cryptomatte.cc b/source/blender/nodes/composite/nodes/node_composite_cryptomatte.cc index 23a9385c4fc..fa3b14ae015 100644 --- a/source/blender/nodes/composite/nodes/node_composite_cryptomatte.cc +++ b/source/blender/nodes/composite/nodes/node_composite_cryptomatte.cc @@ -415,7 +415,8 @@ void register_node_type_cmp_cryptomatte_legacy() static bNodeType ntype; - cmp_node_type_base(&ntype, CMP_NODE_CRYPTOMATTE_LEGACY, "Cryptomatte", NODE_CLASS_MATTE); + cmp_node_type_base( + &ntype, CMP_NODE_CRYPTOMATTE_LEGACY, "Cryptomatte (Legacy)", NODE_CLASS_MATTE); node_type_socket_templates(&ntype, nullptr, file_ns::cmp_node_cryptomatte_out); node_type_init(&ntype, legacy_file_ns::node_init_cryptomatte_legacy); node_type_storage( diff --git a/source/blender/nodes/composite/nodes/node_composite_levels.cc b/source/blender/nodes/composite/nodes/node_composite_levels.cc index a4fe1f33813..4c901372b9f 100644 --- a/source/blender/nodes/composite/nodes/node_composite_levels.cc +++ b/source/blender/nodes/composite/nodes/node_composite_levels.cc @@ -5,9 +5,18 @@ * \ingroup cmpnodes */ +#include <cmath> + +#include "BLI_assert.h" +#include "BLI_math_vec_types.hh" +#include "BLI_math_vector.hh" + #include "UI_interface.h" #include "UI_resources.h" +#include "IMB_colormanagement.h" + +#include "COM_algorithm_parallel_reduction.hh" #include "COM_node_operation.hh" #include "node_composite_util.hh" @@ -18,7 +27,9 @@ namespace blender::nodes::node_composite_levels_cc { static void cmp_node_levels_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Color>(N_("Image")).default_value({0.0f, 0.0f, 0.0f, 1.0f}); + b.add_input<decl::Color>(N_("Image")) + .default_value({0.0f, 0.0f, 0.0f, 1.0f}) + .compositor_domain_priority(0); b.add_output<decl::Float>(N_("Mean")); b.add_output<decl::Float>(N_("Std Dev")); } @@ -36,13 +47,140 @@ static void node_composit_buts_view_levels(uiLayout *layout, bContext * /*C*/, P using namespace blender::realtime_compositor; class LevelsOperation : public NodeOperation { + private: + constexpr static float luminance_coefficients_bt709_[3] = {0.2126f, 0.7152f, 0.0722f}; + public: using NodeOperation::NodeOperation; void execute() override { - get_result("Mean").allocate_invalid(); - get_result("Std Dev").allocate_invalid(); + if (get_input("Image").is_single_value()) { + execute_single_value(); + return; + } + + const float mean = compute_mean(); + + Result &mean_result = get_result("Mean"); + if (mean_result.should_compute()) { + mean_result.allocate_single_value(); + mean_result.set_float_value(mean); + } + + Result &standard_deviation_result = get_result("Std Dev"); + if (standard_deviation_result.should_compute()) { + const float standard_deviation = compute_standard_deviation(mean); + standard_deviation_result.allocate_single_value(); + standard_deviation_result.set_float_value(standard_deviation); + } + } + + void execute_single_value() + { + Result &standard_deviation_result = get_result("Std Dev"); + if (standard_deviation_result.should_compute()) { + standard_deviation_result.allocate_single_value(); + standard_deviation_result.set_float_value(0.0f); + } + + Result &mean_result = get_result("Mean"); + if (!mean_result.should_compute()) { + return; + } + + mean_result.allocate_single_value(); + const float3 input = float3(get_input("Image").get_color_value()); + + switch (get_channel()) { + case CMP_NODE_LEVLES_RED: + mean_result.set_float_value(input.x); + break; + case CMP_NODE_LEVLES_GREEN: + mean_result.set_float_value(input.y); + break; + case CMP_NODE_LEVLES_BLUE: + mean_result.set_float_value(input.z); + break; + case CMP_NODE_LEVLES_LUMINANCE_BT709: + mean_result.set_float_value(math::dot(input, float3(luminance_coefficients_bt709_))); + break; + case CMP_NODE_LEVLES_LUMINANCE: { + float luminance_coefficients[3]; + IMB_colormanagement_get_luminance_coefficients(luminance_coefficients); + mean_result.set_float_value(math::dot(input, float3(luminance_coefficients))); + break; + } + default: + BLI_assert_unreachable(); + break; + } + } + + float compute_mean() + { + const Result &input = get_input("Image"); + return compute_sum() / (input.domain().size.x * input.domain().size.y); + } + + float compute_sum() + { + const Result &input = get_input("Image"); + switch (get_channel()) { + case CMP_NODE_LEVLES_RED: + return sum_red(context(), input.texture()); + case CMP_NODE_LEVLES_GREEN: + return sum_green(context(), input.texture()); + case CMP_NODE_LEVLES_BLUE: + return sum_blue(context(), input.texture()); + case CMP_NODE_LEVLES_LUMINANCE_BT709: + return sum_luminance(context(), input.texture(), float3(luminance_coefficients_bt709_)); + case CMP_NODE_LEVLES_LUMINANCE: { + float luminance_coefficients[3]; + IMB_colormanagement_get_luminance_coefficients(luminance_coefficients); + return sum_luminance(context(), input.texture(), float3(luminance_coefficients)); + } + default: + BLI_assert_unreachable(); + return 0.0f; + } + } + + float compute_standard_deviation(float mean) + { + const Result &input = get_input("Image"); + const float sum = compute_sum_squared_difference(mean); + return std::sqrt(sum / (input.domain().size.x * input.domain().size.y)); + } + + float compute_sum_squared_difference(float subtrahend) + { + const Result &input = get_input("Image"); + switch (get_channel()) { + case CMP_NODE_LEVLES_RED: + return sum_red_squared_difference(context(), input.texture(), subtrahend); + case CMP_NODE_LEVLES_GREEN: + return sum_green_squared_difference(context(), input.texture(), subtrahend); + case CMP_NODE_LEVLES_BLUE: + return sum_blue_squared_difference(context(), input.texture(), subtrahend); + case CMP_NODE_LEVLES_LUMINANCE_BT709: + return sum_luminance_squared_difference( + context(), input.texture(), float3(luminance_coefficients_bt709_), subtrahend); + case CMP_NODE_LEVLES_LUMINANCE: { + float luminance_coefficients[3]; + IMB_colormanagement_get_luminance_coefficients(luminance_coefficients); + return sum_luminance_squared_difference( + context(), input.texture(), float3(luminance_coefficients), subtrahend); + } + default: + BLI_assert_unreachable(); + return 0.0f; + } + } + + CMPNodeLevelsChannel get_channel() + { + return static_cast<CMPNodeLevelsChannel>(bnode().custom1); } }; diff --git a/source/blender/nodes/composite/nodes/node_composite_normalize.cc b/source/blender/nodes/composite/nodes/node_composite_normalize.cc index 21765825468..34fd63e5805 100644 --- a/source/blender/nodes/composite/nodes/node_composite_normalize.cc +++ b/source/blender/nodes/composite/nodes/node_composite_normalize.cc @@ -5,7 +5,9 @@ * \ingroup cmpnodes */ +#include "COM_algorithm_parallel_reduction.hh" #include "COM_node_operation.hh" +#include "COM_utilities.hh" #include "node_composite_util.hh" @@ -15,19 +17,60 @@ namespace blender::nodes::node_composite_normalize_cc { static void cmp_node_normalize_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Float>(N_("Value")).default_value(1.0f).min(0.0f).max(1.0f); + b.add_input<decl::Float>(N_("Value")) + .default_value(1.0f) + .min(0.0f) + .max(1.0f) + .compositor_domain_priority(0); b.add_output<decl::Float>(N_("Value")); } using namespace blender::realtime_compositor; class NormalizeOperation : public NodeOperation { + private: + /* The normalize operation is specifically designed to normalize Z Depth information. But since Z + * Depth can contain near infinite values, normalization is limited to [-range_, range], meaning + * that values outside of that range will be ignored when computing the maximum and minimum for + * normalization and will eventually be 0 or 1 if they are less than or larger than the range + * respectively. */ + constexpr static float range_ = 10000.0f; + public: using NodeOperation::NodeOperation; void execute() override { - get_input("Value").pass_through(get_result("Value")); + Result &input_image = get_input("Value"); + Result &output_image = get_result("Value"); + if (input_image.is_single_value()) { + input_image.pass_through(output_image); + return; + } + + const float maximum = maximum_float_in_range( + context(), input_image.texture(), -range_, range_); + const float minimum = minimum_float_in_range( + context(), input_image.texture(), -range_, range_); + const float scale = (maximum != minimum) ? (1.0f / (maximum - minimum)) : 0.0f; + + GPUShader *shader = shader_manager().get("compositor_normalize"); + GPU_shader_bind(shader); + + GPU_shader_uniform_1f(shader, "minimum", minimum); + GPU_shader_uniform_1f(shader, "scale", scale); + + input_image.bind_as_texture(shader, "input_tx"); + + const Domain domain = compute_domain(); + output_image.allocate_texture(domain); + output_image.bind_as_image(shader, "output_img"); + + compute_dispatch_threads_at_least(shader, domain.size); + + GPU_shader_unbind(); + output_image.unbind_as_image(); + input_image.unbind_as_texture(); } }; diff --git a/source/blender/nodes/composite/nodes/node_composite_pixelate.cc b/source/blender/nodes/composite/nodes/node_composite_pixelate.cc index c4e42f8247d..c65bb7bb747 100644 --- a/source/blender/nodes/composite/nodes/node_composite_pixelate.cc +++ b/source/blender/nodes/composite/nodes/node_composite_pixelate.cc @@ -36,7 +36,10 @@ class PixelateOperation : public NodeOperation { * matches its apparent size, that is, its size after the domain transformation. The pixelate * node has no effect if the input is scaled-up. See the compute_domain method for more * information. */ - get_input("Color").pass_through(get_result("Color")); + Result &result = get_result("Color"); + get_input("Color").pass_through(result); + + result.get_realization_options().interpolation = Interpolation::Nearest; } /* Compute a smaller-sized domain that matches the apparent size of the input while having a unit diff --git a/source/blender/nodes/composite/nodes/node_composite_scale.cc b/source/blender/nodes/composite/nodes/node_composite_scale.cc index b9f1f2da6a8..c524d7b8da9 100644 --- a/source/blender/nodes/composite/nodes/node_composite_scale.cc +++ b/source/blender/nodes/composite/nodes/node_composite_scale.cc @@ -88,7 +88,6 @@ class ScaleOperation : public NodeOperation { get_translation(), 0.0f, get_scale()); result.transform(transformation); - result.get_realization_options().interpolation = Interpolation::Bilinear; } float2 get_scale() diff --git a/source/blender/nodes/composite/nodes/node_composite_sepcomb_hsva.cc b/source/blender/nodes/composite/nodes/node_composite_sepcomb_hsva.cc index b655c0db106..3483285793a 100644 --- a/source/blender/nodes/composite/nodes/node_composite_sepcomb_hsva.cc +++ b/source/blender/nodes/composite/nodes/node_composite_sepcomb_hsva.cc @@ -54,7 +54,8 @@ void register_node_type_cmp_sephsva() static bNodeType ntype; - cmp_node_type_base(&ntype, CMP_NODE_SEPHSVA_LEGACY, "Separate HSVA", NODE_CLASS_CONVERTER); + cmp_node_type_base( + &ntype, CMP_NODE_SEPHSVA_LEGACY, "Separate HSVA (Legacy)", NODE_CLASS_CONVERTER); ntype.declare = file_ns::cmp_node_sephsva_declare; ntype.gather_link_search_ops = nullptr; ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node; @@ -107,7 +108,8 @@ void register_node_type_cmp_combhsva() static bNodeType ntype; - cmp_node_type_base(&ntype, CMP_NODE_COMBHSVA_LEGACY, "Combine HSVA", NODE_CLASS_CONVERTER); + cmp_node_type_base( + &ntype, CMP_NODE_COMBHSVA_LEGACY, "Combine HSVA (Legacy)", NODE_CLASS_CONVERTER); ntype.declare = file_ns::cmp_node_combhsva_declare; ntype.gather_link_search_ops = nullptr; ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node; diff --git a/source/blender/nodes/composite/nodes/node_composite_sepcomb_rgba.cc b/source/blender/nodes/composite/nodes/node_composite_sepcomb_rgba.cc index 1f4c9fd153f..9308052454d 100644 --- a/source/blender/nodes/composite/nodes/node_composite_sepcomb_rgba.cc +++ b/source/blender/nodes/composite/nodes/node_composite_sepcomb_rgba.cc @@ -54,7 +54,8 @@ void register_node_type_cmp_seprgba() static bNodeType ntype; - cmp_node_type_base(&ntype, CMP_NODE_SEPRGBA_LEGACY, "Separate RGBA", NODE_CLASS_CONVERTER); + cmp_node_type_base( + &ntype, CMP_NODE_SEPRGBA_LEGACY, "Separate RGBA (Legacy)", NODE_CLASS_CONVERTER); ntype.declare = file_ns::cmp_node_seprgba_declare; ntype.gather_link_search_ops = nullptr; ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node; @@ -107,7 +108,8 @@ void register_node_type_cmp_combrgba() static bNodeType ntype; - cmp_node_type_base(&ntype, CMP_NODE_COMBRGBA_LEGACY, "Combine RGBA", NODE_CLASS_CONVERTER); + cmp_node_type_base( + &ntype, CMP_NODE_COMBRGBA_LEGACY, "Combine RGBA (Legacy)", NODE_CLASS_CONVERTER); ntype.declare = file_ns::cmp_node_combrgba_declare; ntype.gather_link_search_ops = nullptr; ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node; diff --git a/source/blender/nodes/composite/nodes/node_composite_sepcomb_ycca.cc b/source/blender/nodes/composite/nodes/node_composite_sepcomb_ycca.cc index 78be1efbd8e..82fc37a18f6 100644 --- a/source/blender/nodes/composite/nodes/node_composite_sepcomb_ycca.cc +++ b/source/blender/nodes/composite/nodes/node_composite_sepcomb_ycca.cc @@ -81,7 +81,8 @@ void register_node_type_cmp_sepycca() static bNodeType ntype; - cmp_node_type_base(&ntype, CMP_NODE_SEPYCCA_LEGACY, "Separate YCbCrA", NODE_CLASS_CONVERTER); + cmp_node_type_base( + &ntype, CMP_NODE_SEPYCCA_LEGACY, "Separate YCbCrA (Legacy)", NODE_CLASS_CONVERTER); ntype.declare = file_ns::cmp_node_sepycca_declare; node_type_init(&ntype, file_ns::node_composit_init_mode_sepycca); ntype.gather_link_search_ops = nullptr; @@ -168,7 +169,8 @@ void register_node_type_cmp_combycca() static bNodeType ntype; - cmp_node_type_base(&ntype, CMP_NODE_COMBYCCA_LEGACY, "Combine YCbCrA", NODE_CLASS_CONVERTER); + cmp_node_type_base( + &ntype, CMP_NODE_COMBYCCA_LEGACY, "Combine YCbCrA (Legacy)", NODE_CLASS_CONVERTER); ntype.declare = file_ns::cmp_node_combycca_declare; node_type_init(&ntype, file_ns::node_composit_init_mode_combycca); ntype.gather_link_search_ops = nullptr; diff --git a/source/blender/nodes/composite/nodes/node_composite_sepcomb_yuva.cc b/source/blender/nodes/composite/nodes/node_composite_sepcomb_yuva.cc index 1f0eb04cfc3..b12e70ad9b8 100644 --- a/source/blender/nodes/composite/nodes/node_composite_sepcomb_yuva.cc +++ b/source/blender/nodes/composite/nodes/node_composite_sepcomb_yuva.cc @@ -54,7 +54,8 @@ void register_node_type_cmp_sepyuva() static bNodeType ntype; - cmp_node_type_base(&ntype, CMP_NODE_SEPYUVA_LEGACY, "Separate YUVA", NODE_CLASS_CONVERTER); + cmp_node_type_base( + &ntype, CMP_NODE_SEPYUVA_LEGACY, "Separate YUVA (Legacy)", NODE_CLASS_CONVERTER); ntype.declare = file_ns::cmp_node_sepyuva_declare; ntype.gather_link_search_ops = nullptr; ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node; @@ -107,7 +108,8 @@ void register_node_type_cmp_combyuva() static bNodeType ntype; - cmp_node_type_base(&ntype, CMP_NODE_COMBYUVA_LEGACY, "Combine YUVA", NODE_CLASS_CONVERTER); + cmp_node_type_base( + &ntype, CMP_NODE_COMBYUVA_LEGACY, "Combine YUVA (Legacy)", NODE_CLASS_CONVERTER); ntype.declare = file_ns::cmp_node_combyuva_declare; ntype.gather_link_search_ops = nullptr; ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node; diff --git a/source/blender/nodes/composite/nodes/node_composite_tonemap.cc b/source/blender/nodes/composite/nodes/node_composite_tonemap.cc index 2e06ad99df1..d26a01bb3c9 100644 --- a/source/blender/nodes/composite/nodes/node_composite_tonemap.cc +++ b/source/blender/nodes/composite/nodes/node_composite_tonemap.cc @@ -5,20 +5,35 @@ * \ingroup cmpnodes */ +#include <cmath> + +#include "BLI_assert.h" +#include "BLI_math_base.hh" +#include "BLI_math_vec_types.hh" +#include "BLI_math_vector.hh" + #include "RNA_access.h" #include "UI_interface.h" #include "UI_resources.h" +#include "IMB_colormanagement.h" + +#include "COM_algorithm_parallel_reduction.hh" #include "COM_node_operation.hh" +#include "COM_utilities.hh" #include "node_composite_util.hh" namespace blender::nodes::node_composite_tonemap_cc { +NODE_STORAGE_FUNCS(NodeTonemap) + static void cmp_node_tonemap_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); + b.add_input<decl::Color>(N_("Image")) + .default_value({1.0f, 1.0f, 1.0f, 1.0f}) + .compositor_domain_priority(0); b.add_output<decl::Color>(N_("Image")); } @@ -68,7 +83,236 @@ class ToneMapOperation : public NodeOperation { void execute() override { - get_input("Image").pass_through(get_result("Image")); + Result &input_image = get_input("Image"); + Result &output_image = get_result("Image"); + if (input_image.is_single_value()) { + input_image.pass_through(output_image); + return; + } + + switch (get_type()) { + case CMP_NODE_TONE_MAP_SIMPLE: + execute_simple(); + return; + case CMP_NODE_TONE_MAP_PHOTORECEPTOR: + execute_photoreceptor(); + return; + default: + BLI_assert_unreachable(); + return; + } + } + + /* Tone mapping based on equation (3) from Reinhard, Erik, et al. "Photographic tone reproduction + * for digital images." Proceedings of the 29th annual conference on Computer graphics and + * interactive techniques. 2002. */ + void execute_simple() + { + const float luminance_scale = compute_luminance_scale(); + const float luminance_scale_blend_factor = compute_luminance_scale_blend_factor(); + const float gamma = node_storage(bnode()).gamma; + const float inverse_gamma = gamma != 0.0f ? 1.0f / gamma : 0.0f; + + GPUShader *shader = shader_manager().get("compositor_tone_map_simple"); + GPU_shader_bind(shader); + + GPU_shader_uniform_1f(shader, "luminance_scale", luminance_scale); + GPU_shader_uniform_1f(shader, "luminance_scale_blend_factor", luminance_scale_blend_factor); + GPU_shader_uniform_1f(shader, "inverse_gamma", inverse_gamma); + + const Result &input_image = get_input("Image"); + input_image.bind_as_texture(shader, "input_tx"); + + const Domain domain = compute_domain(); + Result &output_image = get_result("Image"); + output_image.allocate_texture(domain); + output_image.bind_as_image(shader, "output_img"); + + compute_dispatch_threads_at_least(shader, domain.size); + + GPU_shader_unbind(); + output_image.unbind_as_image(); + input_image.unbind_as_texture(); + } + + /* Computes the scaling factor in equation (2) from Reinhard's 2002 paper. */ + float compute_luminance_scale() + { + const float geometric_mean = compute_geometric_mean_of_luminance(); + return geometric_mean != 0.0 ? node_storage(bnode()).key / geometric_mean : 0.0f; + } + + /* Computes equation (1) from Reinhard's 2002 paper. However, note that the equation in the paper + * is most likely wrong, and the intention is actually to compute the geometric mean through a + * logscale arithmetic mean, that is, the division should happen inside the exponential function, + * not outside of it. That's because the sum of the log luminance will be a very large negative + * number, whose exponential will almost always be zero, which is unexpected and useless. */ + float compute_geometric_mean_of_luminance() + { + return std::exp(compute_average_log_luminance()); + } + + /* Equation (3) from Reinhard's 2002 paper blends between high luminance scaling for high + * luminance values and low luminance scaling for low luminance values. This is done by adding 1 + * to the denominator, since for low luminance values, the denominator will be close to 1 and for + * high luminance values, the 1 in the denominator will be relatively insignificant. But the + * response of such function is not always ideal, so in this implementation, the 1 was exposed as + * a parameter to the user for more flexibility. */ + float compute_luminance_scale_blend_factor() + { + return node_storage(bnode()).offset; + } + + /* Tone mapping based on equation (1) and the trilinear interpolation between equations (6) and + * (7) from Reinhard, Erik, and Kate Devlin. "Dynamic range reduction inspired by photoreceptor + * physiology." IEEE transactions on visualization and computer graphics 11.1 (2005): 13-24. */ + void execute_photoreceptor() + { + const float4 global_adaptation_level = compute_global_adaptation_level(); + const float contrast = compute_contrast(); + const float intensity = compute_intensity(); + const float chromatic_adaptation = get_chromatic_adaptation(); + const float light_adaptation = get_light_adaptation(); + + GPUShader *shader = shader_manager().get("compositor_tone_map_photoreceptor"); + GPU_shader_bind(shader); + + GPU_shader_uniform_4fv(shader, "global_adaptation_level", global_adaptation_level); + GPU_shader_uniform_1f(shader, "contrast", contrast); + GPU_shader_uniform_1f(shader, "intensity", intensity); + GPU_shader_uniform_1f(shader, "chromatic_adaptation", chromatic_adaptation); + GPU_shader_uniform_1f(shader, "light_adaptation", light_adaptation); + + float luminance_coefficients[3]; + IMB_colormanagement_get_luminance_coefficients(luminance_coefficients); + GPU_shader_uniform_3fv(shader, "luminance_coefficients", luminance_coefficients); + + const Result &input_image = get_input("Image"); + input_image.bind_as_texture(shader, "input_tx"); + + const Domain domain = compute_domain(); + Result &output_image = get_result("Image"); + output_image.allocate_texture(domain); + output_image.bind_as_image(shader, "output_img"); + + compute_dispatch_threads_at_least(shader, domain.size); + + GPU_shader_unbind(); + output_image.unbind_as_image(); + input_image.unbind_as_texture(); + } + + /* Computes the global adaptation level from the trilinear interpolation equations constructed + * from equations (6) and (7) in Reinhard's 2005 paper. */ + float4 compute_global_adaptation_level() + { + const float4 average_color = compute_average_color(); + const float average_luminance = compute_average_luminance(); + const float chromatic_adaptation = get_chromatic_adaptation(); + return math::interpolate(float4(average_luminance), average_color, chromatic_adaptation); + } + + float4 compute_average_color() + { + /* The average color will reduce to zero if chromatic adaptation is zero, so just return zero + * in this case to avoid needlessly computing the average. See the trilinear interpolation + * equations constructed from equations (6) and (7) in Reinhard's 2005 paper. */ + if (get_chromatic_adaptation() == 0.0f) { + return float4(0.0f); + } + + const Result &input = get_input("Image"); + return sum_color(context(), input.texture()) / (input.domain().size.x * input.domain().size.y); + } + + float compute_average_luminance() + { + /* The average luminance will reduce to zero if chromatic adaptation is one, so just return + * zero in this case to avoid needlessly computing the average. See the trilinear interpolation + * equations constructed from equations (6) and (7) in Reinhard's 2005 paper. */ + if (get_chromatic_adaptation() == 1.0f) { + return 0.0f; + } + + float luminance_coefficients[3]; + IMB_colormanagement_get_luminance_coefficients(luminance_coefficients); + const Result &input = get_input("Image"); + float sum = sum_luminance(context(), input.texture(), luminance_coefficients); + return sum / (input.domain().size.x * input.domain().size.y); + } + + /* Computes equation (5) from Reinhard's 2005 paper. */ + float compute_intensity() + { + return std::exp(-node_storage(bnode()).f); + } + + /* If the contrast is not zero, return it, otherwise, a zero contrast denote automatic derivation + * of the contrast value based on equations (2) and (4) from Reinhard's 2005 paper. */ + float compute_contrast() + { + if (node_storage(bnode()).m != 0.0f) { + return node_storage(bnode()).m; + } + + const float log_maximum_luminance = compute_log_maximum_luminance(); + const float log_minimum_luminance = compute_log_minimum_luminance(); + + /* This is merely to guard against zero division later. */ + if (log_maximum_luminance == log_minimum_luminance) { + return 1.0f; + } + + const float average_log_luminance = compute_average_log_luminance(); + const float dynamic_range = log_maximum_luminance - log_minimum_luminance; + const float luminance_key = (log_maximum_luminance - average_log_luminance) / (dynamic_range); + + return 0.3f + 0.7f * std::pow(luminance_key, 1.4f); + } + + float compute_average_log_luminance() + { + const Result &input_image = get_input("Image"); + + float luminance_coefficients[3]; + IMB_colormanagement_get_luminance_coefficients(luminance_coefficients); + const float sum_of_log_luminance = sum_log_luminance( + context(), input_image.texture(), luminance_coefficients); + + return sum_of_log_luminance / (input_image.domain().size.x * input_image.domain().size.y); + } + + float compute_log_maximum_luminance() + { + float luminance_coefficients[3]; + IMB_colormanagement_get_luminance_coefficients(luminance_coefficients); + const float maximum = maximum_luminance( + context(), get_input("Image").texture(), luminance_coefficients); + return std::log(math::max(maximum, 1e-5f)); + } + + float compute_log_minimum_luminance() + { + float luminance_coefficients[3]; + IMB_colormanagement_get_luminance_coefficients(luminance_coefficients); + const float minimum = minimum_luminance( + context(), get_input("Image").texture(), luminance_coefficients); + return std::log(math::max(minimum, 1e-5f)); + } + + float get_chromatic_adaptation() + { + return node_storage(bnode()).c; + } + + float get_light_adaptation() + { + return node_storage(bnode()).a; + } + + CMPNodeToneMapType get_type() + { + return static_cast<CMPNodeToneMapType>(node_storage(bnode()).type); } }; 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 c4bee09e07b..df677e1c399 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_collection_info.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_collection_info.cc @@ -8,6 +8,7 @@ #include "UI_resources.h" #include "BKE_collection.h" +#include "BKE_instances.hh" #include "node_geometry_util.hh" @@ -69,8 +70,7 @@ static void node_geo_exec(GeoNodeExecParams params) const bool use_relative_transform = (storage.transform_space == GEO_NODE_TRANSFORM_SPACE_RELATIVE); - GeometrySet geometry_set_out; - InstancesComponent &instances = geometry_set_out.get_component_for_write<InstancesComponent>(); + std::unique_ptr<bke::Instances> instances = std::make_unique<bke::Instances>(); const bool separate_children = params.get_input<bool>("Separate Children"); if (separate_children) { @@ -84,7 +84,7 @@ static void node_geo_exec(GeoNodeExecParams params) children_objects.append(collection_object->ob); } - instances.reserve(children_collections.size() + children_objects.size()); + instances->reserve(children_collections.size() + children_objects.size()); Vector<InstanceListEntry> entries; entries.reserve(children_collections.size() + children_objects.size()); @@ -99,11 +99,11 @@ static void node_geo_exec(GeoNodeExecParams params) sub_v3_v3(transform.values[3], collection->instance_offset); } } - const int handle = instances.add_reference(*child_collection); + const int handle = instances->add_reference(*child_collection); entries.append({handle, &(child_collection->id.name[2]), transform}); } for (Object *child_object : children_objects) { - const int handle = instances.add_reference(*child_object); + const int handle = instances->add_reference(*child_object); float4x4 transform = float4x4::identity(); if (!reset_children) { if (use_relative_transform) { @@ -123,7 +123,7 @@ static void node_geo_exec(GeoNodeExecParams params) return BLI_strcasecmp_natural(a.name, b.name) < 0; }); for (const InstanceListEntry &entry : entries) { - instances.add_instance(entry.handle, entry.transform); + instances->add_instance(entry.handle, entry.transform); } } else { @@ -133,11 +133,11 @@ static void node_geo_exec(GeoNodeExecParams params) mul_m4_m4_pre(transform.values, self_object->imat); } - const int handle = instances.add_reference(*collection); - instances.add_instance(handle, transform); + const int handle = instances->add_reference(*collection); + instances->add_instance(handle, transform); } - params.set_output("Geometry", geometry_set_out); + params.set_output("Geometry", GeometrySet::create_with_instances(instances.release())); } } // namespace blender::nodes::node_geo_collection_info_cc diff --git a/source/blender/nodes/geometry/nodes/node_geo_deform_curves_on_surface.cc b/source/blender/nodes/geometry/nodes/node_geo_deform_curves_on_surface.cc index a12ae9bbb92..0932624bdc3 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_deform_curves_on_surface.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_deform_curves_on_surface.cc @@ -379,19 +379,22 @@ static void node_geo_exec(GeoNodeExecParams params) invalid_uv_count); /* Then also deform edit curve information for use in sculpt mode. */ const CurvesGeometry &curves_orig = CurvesGeometry::wrap(edit_hints->curves_id_orig.geometry); - deform_curves(curves_orig, - *surface_mesh_orig, - *surface_mesh_eval, - surface_uv_coords, - reverse_uv_sampler_orig, - reverse_uv_sampler_eval, - corner_normals_orig, - corner_normals_eval, - rest_positions, - transforms.surface_to_curves, - edit_hint_positions, - edit_hint_rotations, - invalid_uv_count); + const Span<float2> surface_uv_coords_orig = curves_orig.surface_uv_coords(); + if (!surface_uv_coords_orig.is_empty()) { + deform_curves(curves_orig, + *surface_mesh_orig, + *surface_mesh_eval, + surface_uv_coords_orig, + reverse_uv_sampler_orig, + reverse_uv_sampler_eval, + corner_normals_orig, + corner_normals_eval, + rest_positions, + transforms.surface_to_curves, + edit_hint_positions, + edit_hint_rotations, + invalid_uv_count); + } } curves.tag_positions_changed(); 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 86c8cd64489..3e48a9fd923 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc @@ -4,6 +4,7 @@ #include "UI_resources.h" #include "BLI_array.hh" +#include "BLI_array_utils.hh" #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" @@ -12,6 +13,7 @@ #include "BKE_attribute_math.hh" #include "BKE_curves.hh" #include "BKE_customdata.h" +#include "BKE_instances.hh" #include "BKE_mesh.h" #include "BKE_pointcloud.h" @@ -22,15 +24,9 @@ namespace blender::nodes::node_geo_delete_geometry_cc { using blender::bke::CustomDataAttributes; template<typename T> -static void copy_data_based_on_mask(Span<T> data, MutableSpan<T> r_data, IndexMask mask) -{ - for (const int i_out : mask.index_range()) { - r_data[i_out] = data[mask[i_out]]; - } -} - -template<typename T> -static void copy_data_based_on_map(Span<T> src, MutableSpan<T> dst, Span<int> index_map) +static void copy_data_based_on_map(const Span<T> src, + const Span<int> index_map, + MutableSpan<T> dst) { for (const int i_src : index_map.index_range()) { const int i_dst = index_map[i_src]; @@ -54,26 +50,17 @@ static void copy_attributes(const Map<AttributeIDRef, AttributeKind> &attributes if (!attribute) { continue; } - /* Only copy if it is on a domain we want. */ if (!domains.contains(attribute.domain)) { continue; } const eCustomDataType data_type = bke::cpp_type_to_custom_data_type(attribute.varray.type()); - GSpanAttributeWriter result_attribute = dst_attributes.lookup_or_add_for_write_only_span( attribute_id, attribute.domain, data_type); - if (!result_attribute) { continue; } - - attribute_math::convert_to_static_type(data_type, [&](auto dummy) { - using T = decltype(dummy); - VArraySpan<T> span{attribute.varray.typed<T>()}; - MutableSpan<T> out_span = result_attribute.span.typed<T>(); - out_span.copy_from(span); - }); + attribute.varray.materialize(result_attribute.span.data()); result_attribute.finish(); } } @@ -94,26 +81,19 @@ static void copy_attributes_based_on_mask(const Map<AttributeIDRef, AttributeKin if (!attribute) { continue; } - /* Only copy if it is on a domain we want. */ if (domain != attribute.domain) { continue; } const eCustomDataType data_type = bke::cpp_type_to_custom_data_type(attribute.varray.type()); - GSpanAttributeWriter result_attribute = dst_attributes.lookup_or_add_for_write_only_span( attribute_id, attribute.domain, data_type); - if (!result_attribute) { continue; } - attribute_math::convert_to_static_type(data_type, [&](auto dummy) { - using T = decltype(dummy); - VArraySpan<T> span{attribute.varray.typed<T>()}; - MutableSpan<T> out_span = result_attribute.span.typed<T>(); - copy_data_based_on_mask(span, out_span, mask); - }); + array_utils::gather(attribute.varray, mask, result_attribute.span); + result_attribute.finish(); } } @@ -130,16 +110,13 @@ static void copy_attributes_based_on_map(const Map<AttributeIDRef, AttributeKind if (!attribute) { continue; } - /* Only copy if it is on a domain we want. */ if (domain != attribute.domain) { continue; } const eCustomDataType data_type = bke::cpp_type_to_custom_data_type(attribute.varray.type()); - GSpanAttributeWriter result_attribute = dst_attributes.lookup_or_add_for_write_only_span( attribute_id, attribute.domain, data_type); - if (!result_attribute) { continue; } @@ -148,7 +125,7 @@ static void copy_attributes_based_on_map(const Map<AttributeIDRef, AttributeKind using T = decltype(dummy); VArraySpan<T> span{attribute.varray.typed<T>()}; MutableSpan<T> out_span = result_attribute.span.typed<T>(); - copy_data_based_on_map(span, out_span, index_map); + copy_data_based_on_map(span, index_map, out_span); }); result_attribute.finish(); } @@ -392,8 +369,8 @@ static void separate_point_cloud_selection(GeometrySet &geometry_set, static void delete_selected_instances(GeometrySet &geometry_set, const Field<bool> &selection_field) { - InstancesComponent &instances = geometry_set.get_component_for_write<InstancesComponent>(); - bke::GeometryFieldContext field_context{instances, ATTR_DOMAIN_INSTANCE}; + bke::Instances &instances = *geometry_set.get_instances_for_write(); + bke::InstancesFieldContext field_context{instances}; fn::FieldEvaluator evaluator{field_context, instances.instances_num()}; evaluator.set_selection(selection_field); @@ -404,7 +381,7 @@ static void delete_selected_instances(GeometrySet &geometry_set, return; } - instances.remove_instances(selection); + instances.remove(selection); } static void compute_selected_verts_from_vertex_selection(const Span<bool> vertex_selection, diff --git a/source/blender/nodes/geometry/nodes/node_geo_distribute_points_in_volume.cc b/source/blender/nodes/geometry/nodes/node_geo_distribute_points_in_volume.cc index 091337c28cf..95173bd23a5 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_distribute_points_in_volume.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_distribute_points_in_volume.cc @@ -15,6 +15,8 @@ #include "UI_interface.h" #include "UI_resources.h" +#include "DEG_depsgraph_query.h" + #include "node_geometry_util.hh" namespace blender::nodes { @@ -208,6 +210,7 @@ static void geo_node_distribute_points_in_volume_exec(GeoNodeExecParams params) } const VolumeComponent *component = geometry_set.get_component_for_read<VolumeComponent>(); const Volume *volume = component->get_for_read(); + BKE_volume_load(volume, DEG_get_bmain(params.depsgraph())); Vector<float3> positions; diff --git a/source/blender/nodes/geometry/nodes/node_geo_dual_mesh.cc b/source/blender/nodes/geometry/nodes/node_geo_dual_mesh.cc index 84e63845b84..9b1c13bf563 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_dual_mesh.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_dual_mesh.cc @@ -1,5 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ +#include "BLI_array_utils.hh" #include "BLI_task.hh" #include "DNA_mesh_types.h" @@ -105,18 +106,6 @@ static void copy_data_based_on_pairs(Span<T> data, } } -/* Copy using the map. */ -template<typename T> -static void copy_data_based_on_new_to_old_map(Span<T> data, - MutableSpan<T> r_data, - const Span<int> new_to_old_map) -{ - for (const int i : r_data.index_range()) { - const int old_i = new_to_old_map[i]; - r_data[i] = data[old_i]; - } -} - /** * Transfers the attributes from the original mesh to the new mesh using the following logic: * - If the attribute was on the face domain it is now on the point domain, and this is true @@ -168,7 +157,6 @@ static void transfer_attributes( src_attribute.varray.type()); GSpanAttributeWriter dst_attribute = dst_attributes.lookup_or_add_for_write_only_span( attribute_id, out_domain, data_type); - if (!dst_attribute) { continue; } @@ -177,20 +165,24 @@ static void transfer_attributes( using T = decltype(dummy); VArraySpan<T> span{src_attribute.varray.typed<T>()}; MutableSpan<T> dst_span = dst_attribute.span.typed<T>(); - if (src_attribute.domain == ATTR_DOMAIN_FACE) { - dst_span.take_front(span.size()).copy_from(span); - if (keep_boundaries) { - copy_data_based_on_pairs(span, dst_span, boundary_vertex_to_relevant_face_map); - } - } - else if (src_attribute.domain == ATTR_DOMAIN_POINT) { - copy_data_based_on_vertex_types(span, dst_span, vertex_types, keep_boundaries); - } - else if (src_attribute.domain == ATTR_DOMAIN_EDGE) { - copy_data_based_on_new_to_old_map(span, dst_span, new_to_old_edges_map); - } - else { - copy_data_based_on_new_to_old_map(span, dst_span, new_to_old_face_corners_map); + switch (src_attribute.domain) { + case ATTR_DOMAIN_POINT: + copy_data_based_on_vertex_types(span, dst_span, vertex_types, keep_boundaries); + break; + case ATTR_DOMAIN_EDGE: + array_utils::gather(span, new_to_old_edges_map, dst_span); + break; + case ATTR_DOMAIN_FACE: + dst_span.take_front(span.size()).copy_from(span); + if (keep_boundaries) { + copy_data_based_on_pairs(span, dst_span, boundary_vertex_to_relevant_face_map); + } + break; + case ATTR_DOMAIN_CORNER: + array_utils::gather(span, new_to_old_face_corners_map, dst_span); + break; + default: + BLI_assert_unreachable(); } }); dst_attribute.finish(); diff --git a/source/blender/nodes/geometry/nodes/node_geo_duplicate_elements.cc b/source/blender/nodes/geometry/nodes/node_geo_duplicate_elements.cc index 82d7e1d3652..486f900aca5 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_duplicate_elements.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_duplicate_elements.cc @@ -1,5 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ +#include "BLI_array_utils.hh" #include "BLI_map.hh" #include "BLI_noise.hh" #include "BLI_span.hh" @@ -11,6 +12,7 @@ #include "BKE_attribute_math.hh" #include "BKE_curves.hh" +#include "BKE_instances.hh" #include "BKE_mesh.h" #include "BKE_pointcloud.h" @@ -104,16 +106,6 @@ static void threaded_slice_fill(Span<int> offsets, }); } -template<typename T> -static void threaded_mapped_copy(const Span<int> mapping, const Span<T> src, MutableSpan<T> dst) -{ - threading::parallel_for(mapping.index_range(), 512, [&](IndexRange range) { - for (const int i : range) { - dst[i] = src[mapping[i]]; - } - }); -} - static void copy_hashed_ids(const Span<int> src, const int hash, MutableSpan<int> dst) { for (const int i : src.index_range()) { @@ -439,17 +431,17 @@ static void copy_face_attributes_without_id(GeometrySet &geometry_set, MutableSpan<T> dst = dst_attribute.span.typed<T>(); switch (out_domain) { - case ATTR_DOMAIN_FACE: - threaded_slice_fill<T>(offsets, selection, src, dst); + case ATTR_DOMAIN_POINT: + array_utils::gather(src, vert_mapping, dst); break; case ATTR_DOMAIN_EDGE: - threaded_mapped_copy<T>(edge_mapping, src, dst); + array_utils::gather(src, edge_mapping, dst); break; - case ATTR_DOMAIN_POINT: - threaded_mapped_copy<T>(vert_mapping, src, dst); + case ATTR_DOMAIN_FACE: + threaded_slice_fill<T>(offsets, selection, src, dst); break; case ATTR_DOMAIN_CORNER: - threaded_mapped_copy<T>(loop_mapping, src, dst); + array_utils::gather(src, loop_mapping, dst); break; default: break; @@ -652,7 +644,7 @@ static void copy_edge_attributes_without_id(GeometrySet &geometry_set, threaded_slice_fill<T>(offsets, selection, src, dst); break; case ATTR_DOMAIN_POINT: - threaded_mapped_copy<T>(point_mapping, src, dst); + array_utils::gather(src, point_mapping, dst); break; default: break; @@ -1031,10 +1023,9 @@ static void duplicate_instances(GeometrySet &geometry_set, return; } - const InstancesComponent &src_instances = - *geometry_set.get_component_for_read<InstancesComponent>(); + const bke::Instances &src_instances = *geometry_set.get_instances_for_read(); - bke::GeometryFieldContext field_context{src_instances, ATTR_DOMAIN_INSTANCE}; + bke::InstancesFieldContext field_context{src_instances}; FieldEvaluator evaluator{field_context, src_instances.instances_num()}; evaluator.add(count_field); evaluator.set_selection(selection_field); @@ -1048,20 +1039,20 @@ static void duplicate_instances(GeometrySet &geometry_set, return; } - GeometrySet dst_geometry; - InstancesComponent &dst_instances = dst_geometry.get_component_for_write<InstancesComponent>(); - dst_instances.resize(offsets.last()); + std::unique_ptr<bke::Instances> dst_instances = std::make_unique<bke::Instances>(); + + dst_instances->resize(offsets.last()); for (const int i_selection : selection.index_range()) { const IndexRange range = range_for_offsets_index(offsets, i_selection); if (range.size() == 0) { continue; } - const int old_handle = src_instances.instance_reference_handles()[i_selection]; - const InstanceReference reference = src_instances.references()[old_handle]; - const int new_handle = dst_instances.add_reference(reference); - const float4x4 transform = src_instances.instance_transforms()[i_selection]; - dst_instances.instance_transforms().slice(range).fill(transform); - dst_instances.instance_reference_handles().slice(range).fill(new_handle); + const int old_handle = src_instances.reference_handles()[i_selection]; + const bke::InstanceReference reference = src_instances.references()[old_handle]; + const int new_handle = dst_instances->add_reference(reference); + const float4x4 transform = src_instances.transforms()[i_selection]; + dst_instances->transforms().slice(range).fill(transform); + dst_instances->reference_handles().slice(range).fill(new_handle); } copy_attributes_without_id(geometry_set, @@ -1069,18 +1060,18 @@ static void duplicate_instances(GeometrySet &geometry_set, ATTR_DOMAIN_INSTANCE, offsets, selection, - *src_instances.attributes(), - *dst_instances.attributes_for_write()); + src_instances.attributes(), + dst_instances->attributes_for_write()); if (attribute_outputs.duplicate_index) { - create_duplicate_index_attribute(*dst_instances.attributes_for_write(), + create_duplicate_index_attribute(dst_instances->attributes_for_write(), ATTR_DOMAIN_INSTANCE, selection, attribute_outputs, offsets); } - geometry_set = std::move(dst_geometry); + geometry_set = GeometrySet::create_with_instances(dst_instances.release()); } /** \} */ diff --git a/source/blender/nodes/geometry/nodes/node_geo_extrude_mesh.cc b/source/blender/nodes/geometry/nodes/node_geo_extrude_mesh.cc index 1d8c9d6312c..151ba3e59cc 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_extrude_mesh.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_extrude_mesh.cc @@ -1,5 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ +#include "BLI_array_utils.hh" #include "BLI_disjoint_set.hh" #include "BLI_task.hh" #include "BLI_vector_set.hh" @@ -175,24 +176,6 @@ static MPoly new_poly(const int loopstart, const int totloop) return poly; } -template<typename T> void copy_with_indices(MutableSpan<T> dst, Span<T> src, Span<int> indices) -{ - BLI_assert(dst.size() == indices.size()); - for (const int i : dst.index_range()) { - dst[i] = src[indices[i]]; - } -} - -template<typename T> void copy_with_mask(MutableSpan<T> dst, Span<T> src, IndexMask mask) -{ - BLI_assert(dst.size() == mask.size()); - threading::parallel_for(mask.index_range(), 512, [&](const IndexRange range) { - for (const int i : range) { - dst[i] = src[mask[i]]; - } - }); -} - /** * \param get_mix_indices_fn: Returns a Span of indices of the source points to mix for every * result point. @@ -260,28 +243,29 @@ static void extrude_mesh_vertices(Mesh &mesh, if (!ELEM(meta_data.domain, ATTR_DOMAIN_POINT, ATTR_DOMAIN_EDGE)) { return true; } + if (meta_data.data_type == CD_PROP_STRING) { + return true; + } GSpanAttributeWriter attribute = attributes.lookup_or_add_for_write_span( id, meta_data.domain, meta_data.data_type); - attribute_math::convert_to_static_type(meta_data.data_type, [&](auto dummy) { - using T = decltype(dummy); - MutableSpan<T> data = attribute.span.typed<T>(); - switch (attribute.domain) { - case ATTR_DOMAIN_POINT: { - /* New vertices copy the attribute values from their source vertex. */ - copy_with_mask(data.slice(new_vert_range), data.as_span(), selection); - break; - } - case ATTR_DOMAIN_EDGE: { + switch (attribute.domain) { + case ATTR_DOMAIN_POINT: + /* New vertices copy the attribute values from their source vertex. */ + array_utils::gather(attribute.span, selection, attribute.span.slice(new_vert_range)); + break; + case ATTR_DOMAIN_EDGE: + attribute_math::convert_to_static_type(meta_data.data_type, [&](auto dummy) { + using T = decltype(dummy); + MutableSpan<T> data = attribute.span.typed<T>(); /* New edge values are mixed from of all the edges connected to the source vertex. */ copy_with_mixing(data.slice(new_edge_range), data.as_span(), [&](const int i) { return vert_to_edge_map[selection[i]].as_span(); }); - break; - } - default: - BLI_assert_unreachable(); - } - }); + }); + break; + default: + BLI_assert_unreachable(); + } attribute.finish(); return true; @@ -506,6 +490,9 @@ static void extrude_mesh_edges(Mesh &mesh, MutableAttributeAccessor attributes = mesh.attributes_for_write(); attributes.for_all([&](const AttributeIDRef &id, const AttributeMetaData meta_data) { + if (meta_data.data_type == CD_PROP_STRING) { + return true; + } GSpanAttributeWriter attribute = attributes.lookup_or_add_for_write_span( id, meta_data.domain, meta_data.data_type); if (!attribute) { @@ -518,13 +505,14 @@ static void extrude_mesh_edges(Mesh &mesh, switch (attribute.domain) { case ATTR_DOMAIN_POINT: { /* New vertices copy the attribute values from their source vertex. */ - copy_with_indices(data.slice(new_vert_range), data.as_span(), new_vert_indices); + array_utils::gather( + data.as_span(), new_vert_indices.as_span(), data.slice(new_vert_range)); break; } case ATTR_DOMAIN_EDGE: { /* Edges parallel to original edges copy the edge attributes from the original edges. */ MutableSpan<T> duplicate_data = data.slice(duplicate_edge_range); - copy_with_mask(duplicate_data, data.as_span(), edge_selection); + array_utils::gather(data.as_span(), edge_selection, duplicate_data); /* Edges connected to original vertices mix values of selected connected edges. */ MutableSpan<T> connect_data = data.slice(connect_edge_range); @@ -889,6 +877,9 @@ static void extrude_mesh_face_regions(Mesh &mesh, MutableAttributeAccessor attributes = mesh.attributes_for_write(); attributes.for_all([&](const AttributeIDRef &id, const AttributeMetaData meta_data) { + if (meta_data.data_type == CD_PROP_STRING) { + return true; + } GSpanAttributeWriter attribute = attributes.lookup_or_add_for_write_span( id, meta_data.domain, meta_data.data_type); if (!attribute) { @@ -901,17 +892,18 @@ static void extrude_mesh_face_regions(Mesh &mesh, switch (attribute.domain) { case ATTR_DOMAIN_POINT: { /* New vertices copy the attributes from their original vertices. */ - copy_with_indices(data.slice(new_vert_range), data.as_span(), new_vert_indices); + array_utils::gather( + data.as_span(), new_vert_indices.as_span(), data.slice(new_vert_range)); break; } case ATTR_DOMAIN_EDGE: { /* Edges parallel to original edges copy the edge attributes from the original edges. */ MutableSpan<T> boundary_data = data.slice(boundary_edge_range); - copy_with_indices(boundary_data, data.as_span(), boundary_edge_indices); + array_utils::gather(data.as_span(), boundary_edge_indices.as_span(), boundary_data); /* Edges inside of face regions also just duplicate their source data. */ MutableSpan<T> new_inner_data = data.slice(new_inner_edge_range); - copy_with_indices(new_inner_data, data.as_span(), new_inner_edge_indices); + array_utils::gather(data.as_span(), new_inner_edge_indices.as_span(), new_inner_data); /* Edges connected to original vertices mix values of selected connected edges. */ MutableSpan<T> connect_data = data.slice(connect_edge_range); @@ -923,8 +915,8 @@ static void extrude_mesh_face_regions(Mesh &mesh, case ATTR_DOMAIN_FACE: { /* New faces on the side of extrusions get the values from the corresponding selected * face. */ - copy_with_indices( - data.slice(side_poly_range), data.as_span(), edge_extruded_face_indices); + array_utils::gather( + data.as_span(), edge_extruded_face_indices.as_span(), data.slice(side_poly_range)); break; } case ATTR_DOMAIN_CORNER: { @@ -1143,6 +1135,9 @@ static void extrude_individual_mesh_faces(Mesh &mesh, MutableAttributeAccessor attributes = mesh.attributes_for_write(); attributes.for_all([&](const AttributeIDRef &id, const AttributeMetaData meta_data) { + if (meta_data.data_type == CD_PROP_STRING) { + return true; + } GSpanAttributeWriter attribute = attributes.lookup_or_add_for_write_span( id, meta_data.domain, meta_data.data_type); if (!attribute) { diff --git a/source/blender/nodes/geometry/nodes/node_geo_flip_faces.cc b/source/blender/nodes/geometry/nodes/node_geo_flip_faces.cc index 613425716d4..95a0013a9e1 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_flip_faces.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_flip_faces.cc @@ -47,6 +47,9 @@ static void mesh_flip_faces(Mesh &mesh, const Field<bool> &selection_field) MutableAttributeAccessor attributes = mesh.attributes_for_write(); attributes.for_all( [&](const bke::AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) { + if (meta_data.data_type == CD_PROP_STRING) { + return true; + } if (meta_data.domain == ATTR_DOMAIN_CORNER) { GSpanAttributeWriter attribute = attributes.lookup_or_add_for_write_span( attribute_id, ATTR_DOMAIN_CORNER, meta_data.data_type); diff --git a/source/blender/nodes/geometry/nodes/node_geo_geometry_to_instance.cc b/source/blender/nodes/geometry/nodes/node_geo_geometry_to_instance.cc index 8e64209a418..45808ff9996 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_geometry_to_instance.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_geometry_to_instance.cc @@ -1,5 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ +#include "BKE_instances.hh" + #include "node_geometry_util.hh" namespace blender::nodes::node_geo_geometry_to_instance_cc { @@ -13,15 +15,13 @@ static void node_declare(NodeDeclarationBuilder &b) static void node_geo_exec(GeoNodeExecParams params) { Vector<GeometrySet> geometries = params.extract_input<Vector<GeometrySet>>("Geometry"); - GeometrySet instances_geometry; - InstancesComponent &instances_component = - instances_geometry.get_component_for_write<InstancesComponent>(); + std::unique_ptr<bke::Instances> instances = std::make_unique<bke::Instances>(); for (GeometrySet &geometry : geometries) { geometry.ensure_owns_direct_data(); - const int handle = instances_component.add_reference(std::move(geometry)); - instances_component.add_instance(handle, float4x4::identity()); + const int handle = instances->add_reference(std::move(geometry)); + instances->add_instance(handle, float4x4::identity()); } - params.set_output("Instances", std::move(instances_geometry)); + params.set_output("Instances", GeometrySet::create_with_instances(instances.release())); } } // namespace blender::nodes::node_geo_geometry_to_instance_cc diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_instance_rotation.cc b/source/blender/nodes/geometry/nodes/node_geo_input_instance_rotation.cc index 75d43d2f771..f78815ebe74 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_input_instance_rotation.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_input_instance_rotation.cc @@ -2,6 +2,8 @@ #include "node_geometry_util.hh" +#include "BKE_instances.hh" + namespace blender::nodes::node_geo_input_instance_rotation_cc { static void node_declare(NodeDeclarationBuilder &b) @@ -15,12 +17,9 @@ class InstanceRotationFieldInput final : public bke::InstancesFieldInput { { } - GVArray get_varray_for_context(const InstancesComponent &instances, - const IndexMask /*mask*/) const final + GVArray get_varray_for_context(const bke::Instances &instances, IndexMask /*mask*/) const final { - auto rotation_fn = [&](const int i) -> float3 { - return instances.instance_transforms()[i].to_euler(); - }; + auto rotation_fn = [&](const int i) -> float3 { return instances.transforms()[i].to_euler(); }; return VArray<float3>::ForFunc(instances.instances_num(), rotation_fn); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_instance_scale.cc b/source/blender/nodes/geometry/nodes/node_geo_input_instance_scale.cc index dbb98d7e393..12ac48f8f11 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_input_instance_scale.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_input_instance_scale.cc @@ -2,6 +2,8 @@ #include "node_geometry_util.hh" +#include "BKE_instances.hh" + namespace blender::nodes::node_geo_input_instance_scale_cc { static void node_declare(NodeDeclarationBuilder &b) @@ -15,12 +17,9 @@ class InstanceScaleFieldInput final : public bke::InstancesFieldInput { { } - GVArray get_varray_for_context(const InstancesComponent &instances, - const IndexMask /*mask*/) const final + GVArray get_varray_for_context(const bke::Instances &instances, IndexMask /*mask*/) const final { - auto scale_fn = [&](const int i) -> float3 { - return instances.instance_transforms()[i].scale(); - }; + auto scale_fn = [&](const int i) -> float3 { return instances.transforms()[i].scale(); }; return VArray<float3>::ForFunc(instances.instances_num(), scale_fn); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_mesh_edge_vertices.cc b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_edge_vertices.cc index 149d44b2207..513ddd76055 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_input_mesh_edge_vertices.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_edge_vertices.cc @@ -113,7 +113,7 @@ class EdgePositionFieldInput final : public bke::MeshFieldInput { GVArray get_varray_for_context(const Mesh &mesh, const eAttrDomain domain, - IndexMask UNUSED(mask)) const final + IndexMask /*mask*/) const final { return construct_edge_positions_gvarray(mesh, vertex_, domain); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_instance_on_points.cc b/source/blender/nodes/geometry/nodes/node_geo_instance_on_points.cc index c6f214e72ac..64546684186 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_instance_on_points.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_instance_on_points.cc @@ -2,6 +2,7 @@ #include "DNA_collection_types.h" +#include "BLI_array_utils.hh" #include "BLI_hash.h" #include "BLI_task.hh" @@ -9,6 +10,7 @@ #include "UI_resources.h" #include "BKE_attribute_math.hh" +#include "BKE_instances.hh" #include "node_geometry_util.hh" @@ -43,7 +45,7 @@ static void node_declare(NodeDeclarationBuilder &b) } static void add_instances_from_component( - InstancesComponent &dst_component, + bke::Instances &dst_component, const GeometryComponent &src_component, const GeometrySet &instance, const GeoNodeExecParams ¶ms, @@ -80,25 +82,23 @@ static void add_instances_from_component( const int select_len = selection.index_range().size(); dst_component.resize(start_len + select_len); - MutableSpan<int> dst_handles = dst_component.instance_reference_handles().slice(start_len, - select_len); - MutableSpan<float4x4> dst_transforms = dst_component.instance_transforms().slice(start_len, - select_len); + MutableSpan<int> dst_handles = dst_component.reference_handles().slice(start_len, select_len); + MutableSpan<float4x4> dst_transforms = dst_component.transforms().slice(start_len, select_len); VArray<float3> positions = src_component.attributes()->lookup_or_default<float3>( "position", domain, {0, 0, 0}); - const InstancesComponent *src_instances = instance.get_component_for_read<InstancesComponent>(); + const bke::Instances *src_instances = instance.get_instances_for_read(); /* Maps handles from the source instances to handles on the new instance. */ Array<int> handle_mapping; /* Only fill #handle_mapping when it may be used below. */ if (src_instances != nullptr && (!pick_instance.is_single() || pick_instance.get_internal_single())) { - Span<InstanceReference> src_references = src_instances->references(); + Span<bke::InstanceReference> src_references = src_instances->references(); handle_mapping.reinitialize(src_references.size()); for (const int src_instance_handle : src_references.index_range()) { - const InstanceReference &reference = src_references[src_instance_handle]; + const bke::InstanceReference &reference = src_references[src_instance_handle]; const int dst_instance_handle = dst_component.add_reference(reference); handle_mapping[src_instance_handle] = dst_instance_handle; } @@ -106,7 +106,7 @@ static void add_instances_from_component( const int full_instance_handle = dst_component.add_reference(instance); /* Add this reference last, because it is the most likely one to be removed later on. */ - const int empty_reference_handle = dst_component.add_reference(InstanceReference()); + const int empty_reference_handle = dst_component.add_reference(bke::InstanceReference()); threading::parallel_for(selection.index_range(), 1024, [&](IndexRange selection_range) { for (const int range_i : selection_range) { @@ -129,12 +129,11 @@ static void add_instances_from_component( const int index = mod_i(original_index, std::max(src_instances_num, 1)); if (index < src_instances_num) { /* Get the reference to the source instance. */ - const int src_handle = src_instances->instance_reference_handles()[index]; + const int src_handle = src_instances->reference_handles()[index]; dst_handle = handle_mapping[src_handle]; /* Take transforms of the source instance into account. */ - mul_m4_m4_post(dst_transform.values, - src_instances->instance_transforms()[index].values); + mul_m4_m4_post(dst_transform.values, src_instances->transforms()[index].values); } } } @@ -157,7 +156,7 @@ static void add_instances_from_component( } } - bke::CustomDataAttributes &instance_attributes = dst_component.instance_attributes(); + bke::CustomDataAttributes &instance_attributes = dst_component.custom_data_attributes(); for (const auto item : attributes_to_propagate.items()) { const AttributeIDRef &attribute_id = item.key; const AttributeKind attribute_kind = item.value; @@ -174,18 +173,7 @@ static void add_instances_from_component( dst_attribute_opt = instance_attributes.get_for_write(attribute_id); } BLI_assert(dst_attribute_opt); - const GMutableSpan dst_attribute = dst_attribute_opt->slice(start_len, select_len); - threading::parallel_for(selection.index_range(), 1024, [&](IndexRange selection_range) { - attribute_math::convert_to_static_type(attribute_kind.data_type, [&](auto dummy) { - using T = decltype(dummy); - VArray<T> src = src_attribute.typed<T>(); - MutableSpan<T> dst = dst_attribute.typed<T>(); - for (const int range_i : selection_range) { - const int i = selection[range_i]; - dst[range_i] = src[i]; - } - }); - }); + array_utils::gather(src_attribute, selection, dst_attribute_opt->slice(start_len, select_len)); } } @@ -196,7 +184,15 @@ static void node_geo_exec(GeoNodeExecParams params) instance.ensure_owns_direct_data(); geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { - InstancesComponent &instances = geometry_set.get_component_for_write<InstancesComponent>(); + /* It's important not to invalidate the existing #InstancesComponent because it owns references + * to other geometry sets that are processed by this node. */ + InstancesComponent &instances_component = + geometry_set.get_component_for_write<InstancesComponent>(); + bke::Instances *dst_instances = instances_component.get_for_write(); + if (dst_instances == nullptr) { + dst_instances = new bke::Instances(); + instances_component.replace(dst_instances); + } const Array<GeometryComponentType> types{ GEO_COMPONENT_TYPE_MESH, GEO_COMPONENT_TYPE_POINT_CLOUD, GEO_COMPONENT_TYPE_CURVE}; @@ -208,14 +204,13 @@ static void node_geo_exec(GeoNodeExecParams params) for (const GeometryComponentType type : types) { if (geometry_set.has(type)) { - add_instances_from_component(instances, + add_instances_from_component(*dst_instances, *geometry_set.get_component_for_read(type), instance, params, attributes_to_propagate); } } - geometry_set.remove_geometry_during_modify(); }); @@ -223,8 +218,9 @@ static void node_geo_exec(GeoNodeExecParams params) * process them needlessly. * This should eventually be moved into the loop above, but currently this is quite tricky * because it might remove references that the loop still wants to iterate over. */ - InstancesComponent &instances = geometry_set.get_component_for_write<InstancesComponent>(); - instances.remove_unused_references(); + if (bke::Instances *instances = geometry_set.get_instances_for_write()) { + instances->remove_unused_references(); + } params.set_output("Instances", std::move(geometry_set)); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_instances_to_points.cc b/source/blender/nodes/geometry/nodes/node_geo_instances_to_points.cc index d4072a05e5f..acd00d119ab 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_instances_to_points.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_instances_to_points.cc @@ -3,6 +3,7 @@ #include "DNA_pointcloud_types.h" #include "BKE_attribute_math.hh" +#include "BKE_instances.hh" #include "BKE_pointcloud.h" #include "node_geometry_util.hh" @@ -27,7 +28,7 @@ static void convert_instances_to_points(GeometrySet &geometry_set, Field<float> radius_field, const Field<bool> selection_field) { - const InstancesComponent &instances = *geometry_set.get_component_for_read<InstancesComponent>(); + const bke::Instances &instances = *geometry_set.get_instances_for_read(); const bke::InstancesFieldContext context{instances}; fn::FieldEvaluator evaluator{context, instances.instances_num()}; @@ -70,7 +71,7 @@ static void convert_instances_to_points(GeometrySet &geometry_set, const AttributeIDRef &attribute_id = item.key; const AttributeKind attribute_kind = item.value; - const GVArray src = instances.attributes()->lookup_or_default( + const GVArray src = instances.attributes().lookup_or_default( attribute_id, ATTR_DOMAIN_INSTANCE, attribute_kind.data_type); BLI_assert(src); GSpanAttributeWriter dst = point_attributes.lookup_or_add_for_write_only_span( 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 74d1b5561bb..ea2646a9786 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc @@ -2,6 +2,8 @@ #include "GEO_realize_instances.hh" +#include "BKE_instances.hh" + #include "node_geometry_util.hh" namespace blender::nodes::node_geo_join_geometry_cc { @@ -29,6 +31,9 @@ static Map<AttributeIDRef, AttributeMetaData> get_final_attribute_info( if (attribute_id.is_named() && ignored_attributes.contains(attribute_id.name())) { return true; } + if (meta_data.data_type == CD_PROP_STRING) { + return true; + } info.add_or_modify( attribute_id, [&](AttributeMetaData *meta_data_final) { *meta_data_final = meta_data; }, @@ -97,31 +102,36 @@ static void join_attributes(Span<const GeometryComponent *> src_components, static void join_components(Span<const InstancesComponent *> src_components, GeometrySet &result) { - InstancesComponent &dst_component = result.get_component_for_write<InstancesComponent>(); + std::unique_ptr<bke::Instances> dst_instances = std::make_unique<bke::Instances>(); int tot_instances = 0; for (const InstancesComponent *src_component : src_components) { - tot_instances += src_component->instances_num(); + tot_instances += src_component->get_for_read()->instances_num(); } - dst_component.reserve(tot_instances); + dst_instances->reserve(tot_instances); for (const InstancesComponent *src_component : src_components) { - Span<InstanceReference> src_references = src_component->references(); + const bke::Instances &src_instances = *src_component->get_for_read(); + + Span<bke::InstanceReference> src_references = src_instances.references(); Array<int> handle_map(src_references.size()); for (const int src_handle : src_references.index_range()) { - handle_map[src_handle] = dst_component.add_reference(src_references[src_handle]); + handle_map[src_handle] = dst_instances->add_reference(src_references[src_handle]); } - Span<float4x4> src_transforms = src_component->instance_transforms(); - Span<int> src_reference_handles = src_component->instance_reference_handles(); + Span<float4x4> src_transforms = src_instances.transforms(); + Span<int> src_reference_handles = src_instances.reference_handles(); for (const int i : src_transforms.index_range()) { const int src_handle = src_reference_handles[i]; const int dst_handle = handle_map[src_handle]; const float4x4 &transform = src_transforms[i]; - dst_component.add_instance(dst_handle, transform); + dst_instances->add_instance(dst_handle, transform); } } + + result.replace_instances(dst_instances.release()); + InstancesComponent &dst_component = result.get_component_for_write<InstancesComponent>(); join_attributes(to_base_components(src_components), dst_component, {"position"}); } @@ -151,25 +161,23 @@ static void join_component_type(Span<GeometrySet> src_geometry_sets, GeometrySet return; } - GeometrySet instances_geometry_set; - InstancesComponent &instances = - instances_geometry_set.get_component_for_write<InstancesComponent>(); - if constexpr (is_same_any_v<Component, InstancesComponent, VolumeComponent>) { join_components(components, result); } else { + std::unique_ptr<bke::Instances> instances = std::make_unique<bke::Instances>(); for (const Component *component : components) { GeometrySet tmp_geo; tmp_geo.add(*component); - const int handle = instances.add_reference(InstanceReference{tmp_geo}); - instances.add_instance(handle, float4x4::identity()); + const int handle = instances->add_reference(bke::InstanceReference{tmp_geo}); + instances->add_instance(handle, float4x4::identity()); } geometry::RealizeInstancesOptions options; options.keep_original_ids = true; options.realize_instance_attributes = false; - GeometrySet joined_components = geometry::realize_instances(instances_geometry_set, options); + GeometrySet joined_components = geometry::realize_instances( + GeometrySet::create_with_instances(instances.release()), options); result.add(joined_components.get_component_for_write<Component>()); } } diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_topology_corners_of_vertex.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_topology_corners_of_vertex.cc index f45ff826a60..036af2d3b93 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_topology_corners_of_vertex.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_topology_corners_of_vertex.cc @@ -60,8 +60,8 @@ class CornersOfVertInput final : public bke::MeshFieldInput { { const IndexRange vert_range(mesh.totvert); const Span<MLoop> loops = mesh.loops(); - Array<Vector<int>> vert_to_loop_map = mesh_topology::build_vert_to_loop_map(loops, - mesh.totvert); + Array<Vector<int>> vert_to_loop_map = bke::mesh_topology::build_vert_to_loop_map(loops, + mesh.totvert); const bke::MeshFieldContext context{mesh, domain}; fn::FieldEvaluator evaluator{context, &mask}; @@ -93,6 +93,10 @@ class CornersOfVertInput final : public bke::MeshFieldInput { } const Span<int> corners = vert_to_loop_map[vert_i]; + if (corners.is_empty()) { + corner_of_vertex[selection_i] = 0; + continue; + } /* Retrieve the connected edge indices as 64 bit integers for #materialize_compressed. */ corner_indices.reinitialize(corners.size()); diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_topology_edges_of_corner.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_topology_edges_of_corner.cc index c46c64448bf..84b560cb48a 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_topology_edges_of_corner.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_topology_edges_of_corner.cc @@ -77,13 +77,13 @@ class CornerPreviousEdgeFieldInput final : public bke::MeshFieldInput { } const Span<MPoly> polys = mesh.polys(); const Span<MLoop> loops = mesh.loops(); - Array<int> loop_to_poly_map = mesh_topology::build_loop_to_poly_map(polys, mesh.totloop); + Array<int> loop_to_poly_map = bke::mesh_topology::build_loop_to_poly_map(polys, mesh.totloop); return VArray<int>::ForFunc( mesh.totloop, [polys, loops, loop_to_poly_map = std::move(loop_to_poly_map)](const int corner_i) { const int poly_i = loop_to_poly_map[corner_i]; const MPoly &poly = polys[poly_i]; - const int corner_i_prev = mesh_topology::previous_poly_loop(poly, corner_i); + const int corner_i_prev = bke::mesh_topology::previous_poly_loop(poly, corner_i); return loops[corner_i_prev].e; }); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_topology_edges_of_vertex.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_topology_edges_of_vertex.cc index d099cd7f8cc..f0cc191e217 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_topology_edges_of_vertex.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_topology_edges_of_vertex.cc @@ -60,8 +60,8 @@ class EdgesOfVertInput final : public bke::MeshFieldInput { { const IndexRange vert_range(mesh.totvert); const Span<MEdge> edges = mesh.edges(); - Array<Vector<int>> vert_to_edge_map = mesh_topology::build_vert_to_edge_map(edges, - mesh.totvert); + Array<Vector<int>> vert_to_edge_map = bke::mesh_topology::build_vert_to_edge_map(edges, + mesh.totvert); const bke::MeshFieldContext context{mesh, domain}; fn::FieldEvaluator evaluator{context, &mask}; @@ -93,6 +93,10 @@ class EdgesOfVertInput final : public bke::MeshFieldInput { } const Span<int> edges = vert_to_edge_map[vert_i]; + if (edges.is_empty()) { + edge_of_vertex[selection_i] = 0; + continue; + } /* Retrieve the connected edge indices as 64 bit integers for #materialize_compressed. */ edge_indices.reinitialize(edges.size()); diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_topology_face_of_corner.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_topology_face_of_corner.cc index 99def9e8bd1..d9f944ca11e 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_topology_face_of_corner.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_topology_face_of_corner.cc @@ -36,7 +36,7 @@ class CornerFaceIndexInput final : public bke::MeshFieldInput { return {}; } return VArray<int>::ForContainer( - mesh_topology::build_loop_to_poly_map(mesh.polys(), mesh.totloop)); + bke::mesh_topology::build_loop_to_poly_map(mesh.polys(), mesh.totloop)); } uint64_t hash() const final @@ -65,7 +65,7 @@ class CornerIndexInFaceInput final : public bke::MeshFieldInput { return {}; } const Span<MPoly> polys = mesh.polys(); - Array<int> loop_to_poly_map = mesh_topology::build_loop_to_poly_map(polys, mesh.totloop); + Array<int> loop_to_poly_map = bke::mesh_topology::build_loop_to_poly_map(polys, mesh.totloop); return VArray<int>::ForFunc( mesh.totloop, [polys, loop_to_poly_map = std::move(loop_to_poly_map)](const int corner_i) { const int poly_i = loop_to_poly_map[corner_i]; diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_topology_offset_corner_in_face.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_topology_offset_corner_in_face.cc index d7ea097be94..2cb9ae82fa1 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_topology_offset_corner_in_face.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_topology_offset_corner_in_face.cc @@ -52,7 +52,7 @@ class OffsetCornerInFaceFieldInput final : public bke::MeshFieldInput { const VArray<int> corner_indices = evaluator.get_evaluated<int>(0); const VArray<int> offsets = evaluator.get_evaluated<int>(1); - Array<int> loop_to_poly_map = mesh_topology::build_loop_to_poly_map(polys, mesh.totloop); + Array<int> loop_to_poly_map = bke::mesh_topology::build_loop_to_poly_map(polys, mesh.totloop); Array<int> offset_corners(mask.min_array_size()); threading::parallel_for(mask.index_range(), 2048, [&](const IndexRange range) { 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 3ce16fac464..bf064c6fcbe 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_object_info.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_object_info.cc @@ -3,6 +3,7 @@ #include "BLI_math_matrix.h" #include "BKE_geometry_set_instances.hh" +#include "BKE_instances.hh" #include "UI_interface.h" #include "UI_resources.h" @@ -68,14 +69,15 @@ static void node_geo_exec(GeoNodeExecParams params) GeometrySet geometry_set; if (params.get_input<bool>("As Instance")) { - InstancesComponent &instances = geometry_set.get_component_for_write<InstancesComponent>(); - const int handle = instances.add_reference(*object); + std::unique_ptr<bke::Instances> instances = std::make_unique<bke::Instances>(); + const int handle = instances->add_reference(*object); if (transform_space_relative) { - instances.add_instance(handle, transform); + instances->add_instance(handle, transform); } else { - instances.add_instance(handle, float4x4::identity()); + instances->add_instance(handle, float4x4::identity()); } + geometry_set = GeometrySet::create_with_instances(instances.release()); } else { geometry_set = bke::object_get_evaluated_geometry_set(*object); diff --git a/source/blender/nodes/geometry/nodes/node_geo_rotate_instances.cc b/source/blender/nodes/geometry/nodes/node_geo_rotate_instances.cc index 4ed94e67e74..fac92a7500c 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_rotate_instances.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_rotate_instances.cc @@ -2,6 +2,8 @@ #include "BLI_task.hh" +#include "BKE_instances.hh" + #include "node_geometry_util.hh" namespace blender::nodes::node_geo_rotate_instances_cc { @@ -16,10 +18,10 @@ static void node_declare(NodeDeclarationBuilder &b) b.add_output<decl::Geometry>(N_("Instances")); } -static void rotate_instances(GeoNodeExecParams ¶ms, InstancesComponent &instances_component) +static void rotate_instances(GeoNodeExecParams ¶ms, bke::Instances &instances) { - const bke::InstancesFieldContext context{instances_component}; - fn::FieldEvaluator evaluator{context, instances_component.instances_num()}; + const bke::InstancesFieldContext context{instances}; + fn::FieldEvaluator evaluator{context, instances.instances_num()}; evaluator.set_selection(params.extract_input<Field<bool>>("Selection")); evaluator.add(params.extract_input<Field<float3>>("Rotation")); evaluator.add(params.extract_input<Field<float3>>("Pivot Point")); @@ -31,14 +33,14 @@ static void rotate_instances(GeoNodeExecParams ¶ms, InstancesComponent &inst const VArray<float3> pivots = evaluator.get_evaluated<float3>(1); const VArray<bool> local_spaces = evaluator.get_evaluated<bool>(2); - MutableSpan<float4x4> instance_transforms = instances_component.instance_transforms(); + MutableSpan<float4x4> transforms = instances.transforms(); threading::parallel_for(selection.index_range(), 512, [&](IndexRange range) { for (const int i_selection : range) { const int i = selection[i_selection]; const float3 pivot = pivots[i]; const float3 euler = rotations[i]; - float4x4 &instance_transform = instance_transforms[i]; + float4x4 &instance_transform = transforms[i]; float4x4 rotation_matrix; float3 used_pivot; @@ -81,9 +83,8 @@ static void rotate_instances(GeoNodeExecParams ¶ms, InstancesComponent &inst static void node_geo_exec(GeoNodeExecParams params) { GeometrySet geometry_set = params.extract_input<GeometrySet>("Instances"); - if (geometry_set.has_instances()) { - InstancesComponent &instances = geometry_set.get_component_for_write<InstancesComponent>(); - rotate_instances(params, instances); + if (bke::Instances *instances = geometry_set.get_instances_for_write()) { + rotate_instances(params, *instances); } params.set_output("Instances", std::move(geometry_set)); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_sample_nearest_surface.cc b/source/blender/nodes/geometry/nodes/node_geo_sample_nearest_surface.cc index 44851a0ade5..95bf7199d63 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_sample_nearest_surface.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_sample_nearest_surface.cc @@ -240,7 +240,11 @@ static void node_geo_exec(GeoNodeExecParams params) params.set_default_remaining_outputs(); return; } - if (mesh->totpoly == 0 && mesh->totvert != 0) { + if (mesh->totvert == 0) { + params.set_default_remaining_outputs(); + return; + } + if (mesh->totpoly == 0) { params.error_message_add(NodeWarningType::Error, TIP_("The source mesh must have faces")); params.set_default_remaining_outputs(); return; diff --git a/source/blender/nodes/geometry/nodes/node_geo_scale_instances.cc b/source/blender/nodes/geometry/nodes/node_geo_scale_instances.cc index 21fe724e194..dacb130337f 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_scale_instances.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_scale_instances.cc @@ -2,6 +2,8 @@ #include "BLI_task.hh" +#include "BKE_instances.hh" + #include "node_geometry_util.hh" namespace blender::nodes::node_geo_scale_instances_cc { @@ -19,10 +21,10 @@ static void node_declare(NodeDeclarationBuilder &b) b.add_output<decl::Geometry>(N_("Instances")); } -static void scale_instances(GeoNodeExecParams ¶ms, InstancesComponent &instances_component) +static void scale_instances(GeoNodeExecParams ¶ms, bke::Instances &instances) { - const bke::InstancesFieldContext context{instances_component}; - fn::FieldEvaluator evaluator{context, instances_component.instances_num()}; + const bke::InstancesFieldContext context{instances}; + fn::FieldEvaluator evaluator{context, instances.instances_num()}; evaluator.set_selection(params.extract_input<Field<bool>>("Selection")); evaluator.add(params.extract_input<Field<float3>>("Scale")); evaluator.add(params.extract_input<Field<float3>>("Center")); @@ -34,13 +36,13 @@ static void scale_instances(GeoNodeExecParams ¶ms, InstancesComponent &insta const VArray<float3> pivots = evaluator.get_evaluated<float3>(1); const VArray<bool> local_spaces = evaluator.get_evaluated<bool>(2); - MutableSpan<float4x4> instance_transforms = instances_component.instance_transforms(); + MutableSpan<float4x4> transforms = instances.transforms(); threading::parallel_for(selection.index_range(), 512, [&](IndexRange range) { for (const int i_selection : range) { const int i = selection[i_selection]; const float3 pivot = pivots[i]; - float4x4 &instance_transform = instance_transforms[i]; + float4x4 &instance_transform = transforms[i]; if (local_spaces[i]) { instance_transform *= float4x4::from_location(pivot); @@ -61,9 +63,8 @@ static void scale_instances(GeoNodeExecParams ¶ms, InstancesComponent &insta static void node_geo_exec(GeoNodeExecParams params) { GeometrySet geometry_set = params.extract_input<GeometrySet>("Instances"); - if (geometry_set.has_instances()) { - InstancesComponent &instances = geometry_set.get_component_for_write<InstancesComponent>(); - scale_instances(params, instances); + if (bke::Instances *instances = geometry_set.get_instances_for_write()) { + scale_instances(params, *instances); } params.set_output("Instances", std::move(geometry_set)); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_string_to_curves.cc b/source/blender/nodes/geometry/nodes/node_geo_string_to_curves.cc index bbdabc09099..769a63f58cf 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_string_to_curves.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_string_to_curves.cc @@ -6,6 +6,7 @@ #include "BKE_curve.h" #include "BKE_curve_legacy_convert.hh" #include "BKE_curves.hh" +#include "BKE_instances.hh" #include "BKE_vfont.h" #include "BLI_hash.h" @@ -270,7 +271,7 @@ static std::optional<TextLayout> get_text_layout(GeoNodeExecParams ¶ms) /* Returns a mapping of UTF-32 character code to instance handle. */ static Map<int, int> create_curve_instances(GeoNodeExecParams ¶ms, TextLayout &layout, - InstancesComponent &instances) + bke::Instances &instances) { VFont *vfont = reinterpret_cast<VFont *>(params.node().id); Map<int, int> handles; @@ -315,13 +316,13 @@ static Map<int, int> create_curve_instances(GeoNodeExecParams ¶ms, return handles; } -static void add_instances_from_handles(InstancesComponent &instances, +static void add_instances_from_handles(bke::Instances &instances, const Map<int, int> &char_handles, const TextLayout &layout) { instances.resize(layout.positions.size()); - MutableSpan<int> handles = instances.instance_reference_handles(); - MutableSpan<float4x4> transforms = instances.instance_transforms(); + MutableSpan<int> handles = instances.reference_handles(); + MutableSpan<float4x4> transforms = instances.transforms(); threading::parallel_for(IndexRange(layout.positions.size()), 256, [&](IndexRange range) { for (const int i : range) { @@ -333,9 +334,9 @@ static void add_instances_from_handles(InstancesComponent &instances, static void create_attributes(GeoNodeExecParams ¶ms, const TextLayout &layout, - InstancesComponent &instances) + bke::Instances &instances) { - MutableAttributeAccessor attributes = *instances.attributes_for_write(); + MutableAttributeAccessor attributes = instances.attributes_for_write(); if (params.output_is_required("Line")) { StrongAnonymousAttributeID line_id = StrongAnonymousAttributeID("Line"); @@ -385,13 +386,12 @@ static void node_geo_exec(GeoNodeExecParams params) } /* Create and add instances. */ - GeometrySet geometry_set_out; - InstancesComponent &instances = geometry_set_out.get_component_for_write<InstancesComponent>(); - Map<int, int> char_handles = create_curve_instances(params, *layout, instances); - add_instances_from_handles(instances, char_handles, *layout); - create_attributes(params, *layout, instances); + std::unique_ptr<bke::Instances> instances = std::make_unique<bke::Instances>(); + Map<int, int> char_handles = create_curve_instances(params, *layout, *instances); + add_instances_from_handles(*instances, char_handles, *layout); + create_attributes(params, *layout, *instances); - params.set_output("Curve Instances", std::move(geometry_set_out)); + params.set_output("Curve Instances", GeometrySet::create_with_instances(instances.release())); } } // namespace blender::nodes::node_geo_string_to_curves_cc diff --git a/source/blender/nodes/geometry/nodes/node_geo_transform.cc b/source/blender/nodes/geometry/nodes/node_geo_transform.cc index 4130cad3bda..3c8a3f3ca76 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_transform.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_transform.cc @@ -11,6 +11,7 @@ #include "DNA_volume_types.h" #include "BKE_curves.hh" +#include "BKE_instances.hh" #include "BKE_mesh.h" #include "BKE_pointcloud.h" #include "BKE_volume.h" @@ -67,18 +68,18 @@ static void transform_pointcloud(PointCloud &pointcloud, const float4x4 &transfo position.finish(); } -static void translate_instances(InstancesComponent &instances, const float3 translation) +static void translate_instances(bke::Instances &instances, const float3 translation) { - MutableSpan<float4x4> transforms = instances.instance_transforms(); + MutableSpan<float4x4> transforms = instances.transforms(); for (float4x4 &transform : transforms) { add_v3_v3(transform.ptr()[3], translation); } } -static void transform_instances(InstancesComponent &instances, const float4x4 &transform) +static void transform_instances(bke::Instances &instances, const float4x4 &transform) { - MutableSpan<float4x4> instance_transforms = instances.instance_transforms(); - for (float4x4 &instance_transform : instance_transforms) { + MutableSpan<float4x4> transforms = instances.transforms(); + for (float4x4 &instance_transform : transforms) { instance_transform = transform * instance_transform; } } @@ -185,8 +186,8 @@ static void translate_geometry_set(GeoNodeExecParams ¶ms, if (Volume *volume = geometry.get_volume_for_write()) { translate_volume(params, *volume, translation, depsgraph); } - if (geometry.has_instances()) { - translate_instances(geometry.get_component_for_write<InstancesComponent>(), translation); + if (bke::Instances *instances = geometry.get_instances_for_write()) { + translate_instances(*instances, translation); } if (bke::CurvesEditHints *curve_edit_hints = geometry.get_curve_edit_hints_for_write()) { translate_curve_edit_hints(*curve_edit_hints, translation); @@ -210,8 +211,8 @@ void transform_geometry_set(GeoNodeExecParams ¶ms, if (Volume *volume = geometry.get_volume_for_write()) { transform_volume(params, *volume, transform, depsgraph); } - if (geometry.has_instances()) { - transform_instances(geometry.get_component_for_write<InstancesComponent>(), transform); + if (bke::Instances *instances = geometry.get_instances_for_write()) { + transform_instances(*instances, transform); } if (bke::CurvesEditHints *curve_edit_hints = geometry.get_curve_edit_hints_for_write()) { transform_curve_edit_hints(*curve_edit_hints, transform); diff --git a/source/blender/nodes/geometry/nodes/node_geo_translate_instances.cc b/source/blender/nodes/geometry/nodes/node_geo_translate_instances.cc index 3e9fe99adb0..23052abddc4 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_translate_instances.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_translate_instances.cc @@ -2,6 +2,8 @@ #include "BLI_task.hh" +#include "BKE_instances.hh" + #include "node_geometry_util.hh" namespace blender::nodes::node_geo_translate_instances_cc { @@ -15,10 +17,10 @@ static void node_declare(NodeDeclarationBuilder &b) b.add_output<decl::Geometry>(N_("Instances")); } -static void translate_instances(GeoNodeExecParams ¶ms, InstancesComponent &instances_component) +static void translate_instances(GeoNodeExecParams ¶ms, bke::Instances &instances) { - const bke::InstancesFieldContext context{instances_component}; - fn::FieldEvaluator evaluator{context, instances_component.instances_num()}; + const bke::InstancesFieldContext context{instances}; + fn::FieldEvaluator evaluator{context, instances.instances_num()}; evaluator.set_selection(params.extract_input<Field<bool>>("Selection")); evaluator.add(params.extract_input<Field<float3>>("Translation")); evaluator.add(params.extract_input<Field<bool>>("Local Space")); @@ -28,16 +30,16 @@ static void translate_instances(GeoNodeExecParams ¶ms, InstancesComponent &i const VArray<float3> translations = evaluator.get_evaluated<float3>(0); const VArray<bool> local_spaces = evaluator.get_evaluated<bool>(1); - MutableSpan<float4x4> instance_transforms = instances_component.instance_transforms(); + MutableSpan<float4x4> transforms = instances.transforms(); threading::parallel_for(selection.index_range(), 1024, [&](IndexRange range) { for (const int i_selection : range) { const int i = selection[i_selection]; if (local_spaces[i]) { - instance_transforms[i] *= float4x4::from_location(translations[i]); + transforms[i] *= float4x4::from_location(translations[i]); } else { - add_v3_v3(instance_transforms[i].values[3], translations[i]); + add_v3_v3(transforms[i].values[3], translations[i]); } } }); @@ -46,9 +48,8 @@ static void translate_instances(GeoNodeExecParams ¶ms, InstancesComponent &i static void node_geo_exec(GeoNodeExecParams params) { GeometrySet geometry_set = params.extract_input<GeometrySet>("Instances"); - if (geometry_set.has_instances()) { - InstancesComponent &instances = geometry_set.get_component_for_write<InstancesComponent>(); - translate_instances(params, instances); + if (bke::Instances *instances = geometry_set.get_instances_for_write()) { + translate_instances(params, *instances); } params.set_output("Instances", std::move(geometry_set)); } diff --git a/source/blender/nodes/intern/geometry_nodes_lazy_function.cc b/source/blender/nodes/intern/geometry_nodes_lazy_function.cc index 6475a16477a..197f0997160 100644 --- a/source/blender/nodes/intern/geometry_nodes_lazy_function.cc +++ b/source/blender/nodes/intern/geometry_nodes_lazy_function.cc @@ -158,8 +158,9 @@ class LazyFunctionForMultiInput : public LazyFunction { base_type_ = get_socket_cpp_type(socket); BLI_assert(base_type_ != nullptr); BLI_assert(socket.is_multi_input()); + const bNodeTree &btree = socket.owner_tree(); for (const bNodeLink *link : socket.directly_linked_links()) { - if (!link->is_muted()) { + if (!(link->is_muted() || nodeIsDanglingReroute(&btree, link->fromnode))) { inputs_.append({"Input", *base_type_}); } } @@ -603,6 +604,7 @@ class LazyFunctionForGroupNode : public LazyFunction { private: const bNode &group_node_; bool has_many_nodes_ = false; + bool use_fallback_outputs_ = false; std::optional<GeometryNodesLazyFunctionLogger> lf_logger_; std::optional<GeometryNodesLazyFunctionSideEffectProvider> lf_side_effect_provider_; std::optional<lf::GraphExecutor> graph_executor_; @@ -639,6 +641,9 @@ class LazyFunctionForGroupNode : public LazyFunction { } } } + else { + use_fallback_outputs_ = true; + } lf_logger_.emplace(lf_graph_info); lf_side_effect_provider_.emplace(); @@ -659,6 +664,11 @@ class LazyFunctionForGroupNode : public LazyFunction { * if every individual node is very small. */ lazy_threading::send_hint(); } + if (use_fallback_outputs_) { + /* The node group itself does not have an output node, so use default values as outputs. + * The group should still be executed in case it has side effects. */ + params.set_default_remaining_outputs(); + } /* The compute context changes when entering a node group. */ bke::NodeGroupComputeContext compute_context{user_data->compute_context, group_node_.name}; @@ -1073,9 +1083,7 @@ struct GeometryNodesLazyFunctionGraphBuilder { void insert_links_from_socket(const bNodeSocket &from_bsocket, lf::OutputSocket &from_lf_socket) { - const bNode &from_bnode = from_bsocket.owner_node(); - if (this->is_dangling_reroute_input(from_bnode)) { - /* Dangling reroutes should not be used as source of values. */ + if (nodeIsDanglingReroute(&btree_, &from_bsocket.owner_node())) { return; } @@ -1145,7 +1153,8 @@ struct GeometryNodesLazyFunctionGraphBuilder { if (multi_input_link == link) { break; } - if (!multi_input_link->is_muted()) { + if (!(multi_input_link->is_muted() || + nodeIsDanglingReroute(&btree_, multi_input_link->fromnode))) { link_index++; } } @@ -1174,33 +1183,6 @@ struct GeometryNodesLazyFunctionGraphBuilder { } } - bool is_dangling_reroute_input(const bNode &node) - { - if (!node.is_reroute()) { - return false; - } - const bNode *iter_node = &node; - /* It is guaranteed at a higher level that there are no link cycles. */ - while (true) { - const Span<const bNodeLink *> links = iter_node->input_socket(0).directly_linked_links(); - BLI_assert(links.size() <= 1); - if (links.is_empty()) { - return true; - } - const bNodeLink &link = *links[0]; - if (!link.is_available()) { - return false; - } - if (link.is_muted()) { - return false; - } - iter_node = link.fromnode; - if (!iter_node->is_reroute()) { - return false; - } - } - } - lf::OutputSocket *insert_type_conversion_if_necessary( lf::OutputSocket &from_socket, const CPPType &to_type, diff --git a/source/blender/nodes/intern/geometry_nodes_log.cc b/source/blender/nodes/intern/geometry_nodes_log.cc index 167bfef0f83..0f122307328 100644 --- a/source/blender/nodes/intern/geometry_nodes_log.cc +++ b/source/blender/nodes/intern/geometry_nodes_log.cc @@ -101,7 +101,7 @@ GeometryInfoLog::GeometryInfoLog(const GeometrySet &geometry_set) case GEO_COMPONENT_TYPE_INSTANCES: { const InstancesComponent &instances_component = *(const InstancesComponent *)component; InstancesInfo &info = this->instances_info.emplace(); - info.instances_num = instances_component.instances_num(); + info.instances_num = instances_component.attribute_domain_size(ATTR_DOMAIN_INSTANCE); break; } case GEO_COMPONENT_TYPE_EDIT: { diff --git a/source/blender/nodes/intern/node_util.c b/source/blender/nodes/intern/node_util.cc index ddab455509d..17be20b4e4b 100644 --- a/source/blender/nodes/intern/node_util.c +++ b/source/blender/nodes/intern/node_util.cc @@ -37,7 +37,7 @@ void node_free_curves(bNode *node) { - BKE_curvemapping_free(node->storage); + BKE_curvemapping_free(static_cast<CurveMapping *>(node->storage)); } void node_free_standard_storage(bNode *node) @@ -49,7 +49,7 @@ void node_free_standard_storage(bNode *node) void node_copy_curves(bNodeTree *UNUSED(dest_ntree), bNode *dest_node, const bNode *src_node) { - dest_node->storage = BKE_curvemapping_copy(src_node->storage); + dest_node->storage = BKE_curvemapping_copy(static_cast<CurveMapping *>(src_node->storage)); } void node_copy_standard_storage(bNodeTree *UNUSED(dest_ntree), @@ -63,7 +63,7 @@ void *node_initexec_curves(bNodeExecContext *UNUSED(context), bNode *node, bNodeInstanceKey UNUSED(key)) { - BKE_curvemapping_init(node->storage); + BKE_curvemapping_init(static_cast<CurveMapping *>(node->storage)); return NULL; /* unused return */ } @@ -87,9 +87,9 @@ void node_sock_label_clear(bNodeSocket *sock) void node_math_update(bNodeTree *ntree, bNode *node) { - bNodeSocket *sock1 = BLI_findlink(&node->inputs, 0); - bNodeSocket *sock2 = BLI_findlink(&node->inputs, 1); - bNodeSocket *sock3 = BLI_findlink(&node->inputs, 2); + bNodeSocket *sock1 = static_cast<bNodeSocket *>(BLI_findlink(&node->inputs, 0)); + bNodeSocket *sock2 = static_cast<bNodeSocket *>(BLI_findlink(&node->inputs, 1)); + bNodeSocket *sock3 = static_cast<bNodeSocket *>(BLI_findlink(&node->inputs, 2)); nodeSetSocketAvailability(ntree, sock2, !ELEM(node->custom1, @@ -305,7 +305,9 @@ static bNodeSocket *node_find_linkable_socket(bNodeTree *ntree, bNode *node, bNodeSocket *to_socket) { - bNodeSocket *first = to_socket->in_out == SOCK_IN ? node->inputs.first : node->outputs.first; + bNodeSocket *first = to_socket->in_out == SOCK_IN ? + static_cast<bNodeSocket *>(node->inputs.first) : + static_cast<bNodeSocket *>((node->outputs.first)); /* Wrap around the list end. */ bNodeSocket *socket_iter = to_socket->next ? to_socket->next : first; diff --git a/source/blender/nodes/intern/socket_search_link.cc b/source/blender/nodes/intern/socket_search_link.cc index 0bd838ff002..b440952b503 100644 --- a/source/blender/nodes/intern/socket_search_link.cc +++ b/source/blender/nodes/intern/socket_search_link.cc @@ -125,64 +125,23 @@ void search_link_ops_for_declarations(GatherLinkSearchOpParams ¶ms, } } -static void search_link_ops_for_socket_templates(GatherLinkSearchOpParams ¶ms, - const bNodeSocketTemplate *templates, - const eNodeSocketInOut in_out) -{ - const bNodeType &node_type = params.node_type(); - const bNodeTreeType &node_tree_type = *params.node_tree().typeinfo; - - Set<StringRef> socket_names; - for (const bNodeSocketTemplate *socket_template = templates; socket_template->type != -1; - socket_template++) { - eNodeSocketDatatype from = (eNodeSocketDatatype)socket_template->type; - eNodeSocketDatatype to = (eNodeSocketDatatype)params.other_socket().type; - if (in_out == SOCK_IN) { - std::swap(from, to); - } - if (node_tree_type.validate_link && !node_tree_type.validate_link(from, to)) { - continue; - } - if (!socket_names.add(socket_template->name)) { - /* See comment in #search_link_ops_for_declarations. */ - continue; - } - - params.add_item( - socket_template->name, [socket_template, node_type, in_out](LinkSearchOpParams ¶ms) { - bNode &node = params.add_node(node_type); - bNodeSocket *new_node_socket = bke::node_find_enabled_socket( - node, in_out, socket_template->name); - if (new_node_socket != nullptr) { - /* Rely on the way #nodeAddLink switches in/out if necessary. */ - nodeAddLink(¶ms.node_tree, ¶ms.node, ¶ms.socket, &node, new_node_socket); - } - }); - } -} - void search_link_ops_for_basic_node(GatherLinkSearchOpParams ¶ms) { const bNodeType &node_type = params.node_type(); + if (!node_type.declare) { + return; + } - if (node_type.declare) { - if (node_type.declaration_is_dynamic) { - /* Dynamic declarations (whatever they end up being) aren't supported - * by this function, but still avoid a crash in release builds. */ - BLI_assert_unreachable(); - return; - } + if (node_type.declaration_is_dynamic) { + /* Dynamic declarations (whatever they end up being) aren't supported + * by this function, but still avoid a crash in release builds. */ + BLI_assert_unreachable(); + return; + } - const NodeDeclaration &declaration = *node_type.fixed_declaration; + const NodeDeclaration &declaration = *node_type.fixed_declaration; - search_link_ops_for_declarations(params, declaration.sockets(params.in_out())); - } - else if (node_type.inputs && params.in_out() == SOCK_IN) { - search_link_ops_for_socket_templates(params, node_type.inputs, SOCK_IN); - } - else if (node_type.outputs && params.in_out() == SOCK_OUT) { - search_link_ops_for_socket_templates(params, node_type.outputs, SOCK_OUT); - } + search_link_ops_for_declarations(params, declaration.sockets(params.in_out())); } } // namespace blender::nodes diff --git a/source/blender/nodes/shader/nodes/node_shader_curves.cc b/source/blender/nodes/shader/nodes/node_shader_curves.cc index 439f2002ebc..c4dbc3ce6f1 100644 --- a/source/blender/nodes/shader/nodes/node_shader_curves.cc +++ b/source/blender/nodes/shader/nodes/node_shader_curves.cc @@ -12,7 +12,12 @@ namespace blender::nodes::node_shader_curves_cc { static void sh_node_curve_vec_declare(NodeDeclarationBuilder &b) { b.is_function_node(); - b.add_input<decl::Float>(N_("Fac")).min(0.0f).max(1.0f).default_value(1.0f).subtype(PROP_FACTOR); + b.add_input<decl::Float>(N_("Fac")) + .no_muted_links() + .min(0.0f) + .max(1.0f) + .default_value(1.0f) + .subtype(PROP_FACTOR); b.add_input<decl::Vector>(N_("Vector")).min(-1.0f).max(1.0f); b.add_output<decl::Vector>(N_("Vector")); } @@ -127,7 +132,12 @@ namespace blender::nodes::node_shader_curves_cc { static void sh_node_curve_rgb_declare(NodeDeclarationBuilder &b) { b.is_function_node(); - b.add_input<decl::Float>(N_("Fac")).min(0.0f).max(1.0f).default_value(1.0f).subtype(PROP_FACTOR); + b.add_input<decl::Float>(N_("Fac")) + .no_muted_links() + .min(0.0f) + .max(1.0f) + .default_value(1.0f) + .subtype(PROP_FACTOR); b.add_input<decl::Color>(N_("Color")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); b.add_output<decl::Color>(N_("Color")); } @@ -270,6 +280,7 @@ static void sh_node_curve_float_declare(NodeDeclarationBuilder &b) { b.is_function_node(); b.add_input<decl::Float>(N_("Factor")) + .no_muted_links() .min(0.0f) .max(1.0f) .default_value(1.0f) diff --git a/source/blender/nodes/shader/nodes/node_shader_mix.cc b/source/blender/nodes/shader/nodes/node_shader_mix.cc index 2efd57155b9..878648105d1 100644 --- a/source/blender/nodes/shader/nodes/node_shader_mix.cc +++ b/source/blender/nodes/shader/nodes/node_shader_mix.cc @@ -123,6 +123,19 @@ static void sh_node_mix_update(bNodeTree *ntree, bNode *node) nodeSetSocketAvailability(ntree, sock_factor_vec, use_vector_factor); } +class SocketSearchOp { + public: + std::string socket_name; + int type = MA_RAMP_BLEND; + void operator()(LinkSearchOpParams ¶ms) + { + bNode &node = params.add_node("ShaderNodeMix"); + node_storage(node).data_type = SOCK_RGBA; + node_storage(node).blend_type = type; + params.update_and_connect_available_socket(node, socket_name); + } +}; + static void node_mix_gather_link_searches(GatherLinkSearchOpParams ¶ms) { const eNodeSocketDatatype sock_type = static_cast<eNodeSocketDatatype>( @@ -132,6 +145,17 @@ static void node_mix_gather_link_searches(GatherLinkSearchOpParams ¶ms) const eNodeSocketDatatype type = ELEM(sock_type, SOCK_BOOLEAN, SOCK_INT) ? SOCK_FLOAT : sock_type; + const int weight = ELEM(params.other_socket().type, SOCK_RGBA) ? 0 : -1; + const std::string socket_name = params.in_out() == SOCK_IN ? "A" : "Result"; + for (const EnumPropertyItem *item = rna_enum_ramp_blend_items; item->identifier != nullptr; + item++) { + if (item->name != nullptr && item->identifier[0] != '\0') { + params.add_item(CTX_IFACE_(BLT_I18NCONTEXT_ID_NODETREE, item->name), + SocketSearchOp{socket_name, item->value}, + weight); + } + } + if (params.in_out() == SOCK_OUT) { params.add_item(IFACE_("Result"), [type](LinkSearchOpParams ¶ms) { bNode &node = params.add_node("ShaderNodeMix"); diff --git a/source/blender/nodes/shader/nodes/node_shader_mix_rgb.cc b/source/blender/nodes/shader/nodes/node_shader_mix_rgb.cc index ef0374e4539..d1578b48c79 100644 --- a/source/blender/nodes/shader/nodes/node_shader_mix_rgb.cc +++ b/source/blender/nodes/shader/nodes/node_shader_mix_rgb.cc @@ -150,7 +150,7 @@ void register_node_type_sh_mix_rgb() static bNodeType ntype; - sh_fn_node_type_base(&ntype, SH_NODE_MIX_RGB_LEGACY, "Mix", NODE_CLASS_OP_COLOR); + sh_fn_node_type_base(&ntype, SH_NODE_MIX_RGB_LEGACY, "Mix (Legacy)", NODE_CLASS_OP_COLOR); ntype.declare = file_ns::sh_node_mix_rgb_declare; ntype.labelfunc = node_blend_label; node_type_gpu(&ntype, file_ns::gpu_shader_mix_rgb); diff --git a/source/blender/nodes/shader/nodes/node_shader_sepcomb_hsv.cc b/source/blender/nodes/shader/nodes/node_shader_sepcomb_hsv.cc index 985342ac15d..b297ead1847 100644 --- a/source/blender/nodes/shader/nodes/node_shader_sepcomb_hsv.cc +++ b/source/blender/nodes/shader/nodes/node_shader_sepcomb_hsv.cc @@ -36,7 +36,7 @@ void register_node_type_sh_sephsv() static bNodeType ntype; - sh_node_type_base(&ntype, SH_NODE_SEPHSV_LEGACY, "Separate HSV", NODE_CLASS_CONVERTER); + sh_node_type_base(&ntype, SH_NODE_SEPHSV_LEGACY, "Separate HSV (Legacy)", NODE_CLASS_CONVERTER); ntype.declare = file_ns::node_declare_sephsv; node_type_gpu(&ntype, file_ns::gpu_shader_sephsv); ntype.gather_link_search_ops = nullptr; @@ -73,7 +73,7 @@ void register_node_type_sh_combhsv() static bNodeType ntype; - sh_node_type_base(&ntype, SH_NODE_COMBHSV_LEGACY, "Combine HSV", NODE_CLASS_CONVERTER); + sh_node_type_base(&ntype, SH_NODE_COMBHSV_LEGACY, "Combine HSV (Legacy)", NODE_CLASS_CONVERTER); ntype.declare = file_ns::node_declare_combhsv; node_type_gpu(&ntype, file_ns::gpu_shader_combhsv); ntype.gather_link_search_ops = nullptr; diff --git a/source/blender/nodes/shader/nodes/node_shader_sepcomb_rgb.cc b/source/blender/nodes/shader/nodes/node_shader_sepcomb_rgb.cc index 07586a54765..c298998cad5 100644 --- a/source/blender/nodes/shader/nodes/node_shader_sepcomb_rgb.cc +++ b/source/blender/nodes/shader/nodes/node_shader_sepcomb_rgb.cc @@ -76,7 +76,8 @@ void register_node_type_sh_seprgb() static bNodeType ntype; - sh_fn_node_type_base(&ntype, SH_NODE_SEPRGB_LEGACY, "Separate RGB", NODE_CLASS_CONVERTER); + sh_fn_node_type_base( + &ntype, SH_NODE_SEPRGB_LEGACY, "Separate RGB (Legacy)", NODE_CLASS_CONVERTER); ntype.declare = file_ns::sh_node_seprgb_declare; node_type_gpu(&ntype, file_ns::gpu_shader_seprgb); ntype.build_multi_function = file_ns::sh_node_seprgb_build_multi_function; @@ -120,7 +121,8 @@ void register_node_type_sh_combrgb() static bNodeType ntype; - sh_fn_node_type_base(&ntype, SH_NODE_COMBRGB_LEGACY, "Combine RGB", NODE_CLASS_CONVERTER); + sh_fn_node_type_base( + &ntype, SH_NODE_COMBRGB_LEGACY, "Combine RGB (Legacy)", NODE_CLASS_CONVERTER); ntype.declare = file_ns::sh_node_combrgb_declare; node_type_gpu(&ntype, file_ns::gpu_shader_combrgb); ntype.build_multi_function = file_ns::sh_node_combrgb_build_multi_function; diff --git a/source/blender/nodes/texture/CMakeLists.txt b/source/blender/nodes/texture/CMakeLists.txt index 77db71d4b1a..2d704ac2228 100644 --- a/source/blender/nodes/texture/CMakeLists.txt +++ b/source/blender/nodes/texture/CMakeLists.txt @@ -2,7 +2,7 @@ set(INC . - ../ + .. ../intern ../../editors/include ../../blenkernel diff --git a/source/blender/nodes/texture/nodes/node_texture_compose.c b/source/blender/nodes/texture/nodes/node_texture_compose.c index ef14062c72d..e36bc248ed1 100644 --- a/source/blender/nodes/texture/nodes/node_texture_compose.c +++ b/source/blender/nodes/texture/nodes/node_texture_compose.c @@ -42,7 +42,8 @@ void register_node_type_tex_compose(void) { static bNodeType ntype; - tex_node_type_base(&ntype, TEX_NODE_COMPOSE_LEGACY, "Combine RGBA", NODE_CLASS_OP_COLOR); + tex_node_type_base( + &ntype, TEX_NODE_COMPOSE_LEGACY, "Combine RGBA (Legacy)", NODE_CLASS_OP_COLOR); node_type_socket_templates(&ntype, inputs, outputs); node_type_exec(&ntype, NULL, NULL, exec); diff --git a/source/blender/python/gpu/gpu_py_platform.c b/source/blender/python/gpu/gpu_py_platform.c index b877e3ceb98..51366d199b0 100644 --- a/source/blender/python/gpu/gpu_py_platform.c +++ b/source/blender/python/gpu/gpu_py_platform.c @@ -11,6 +11,7 @@ #include "BLI_utildefines.h" +#include "GPU_context.h" #include "GPU_platform.h" #include "gpu_py_platform.h" /* Own include. */ @@ -83,6 +84,28 @@ static PyObject *pygpu_platform_device_type_get(PyObject *UNUSED(self)) return PyUnicode_FromString("UNKNOWN"); } +PyDoc_STRVAR(pygpu_platform_backend_type_get_doc, + ".. function:: backend_type_get()\n" + "\n" + " Get actuve GPU backend.\n" + "\n" + " :return: Backend type ('OPENGL', 'METAL', 'NONE', 'UNKNOWN').\n" + " :rtype: str\n"); +static PyObject *pygpu_platform_backend_type_get(PyObject *UNUSED(self)) +{ + switch (GPU_backend_get_type()) { + case GPU_BACKEND_METAL: + return PyUnicode_FromString("METAL"); + case GPU_BACKEND_NONE: + return PyUnicode_FromString("NONE"); + case GPU_BACKEND_OPENGL: + return PyUnicode_FromString("OPENGL"); + case GPU_BACKEND_ANY: + break; + } + return PyUnicode_FromString("UNKNOWN"); +} + /** \} */ /* -------------------------------------------------------------------- */ @@ -106,6 +129,10 @@ static struct PyMethodDef pygpu_platform__tp_methods[] = { (PyCFunction)pygpu_platform_device_type_get, METH_NOARGS, pygpu_platform_device_type_get_doc}, + {"backend_type_get", + (PyCFunction)pygpu_platform_backend_type_get, + METH_NOARGS, + pygpu_platform_backend_type_get_doc}, {NULL, NULL, 0, NULL}, }; diff --git a/source/blender/python/intern/bpy.c b/source/blender/python/intern/bpy.c index 77710d6df37..36d53d69eff 100644 --- a/source/blender/python/intern/bpy.c +++ b/source/blender/python/intern/bpy.c @@ -29,6 +29,8 @@ #include "GPU_state.h" +#include "WM_api.h" /* For #WM_ghost_backend */ + #include "bpy.h" #include "bpy_app.h" #include "bpy_capi_utils.h" @@ -536,6 +538,17 @@ static PyObject *bpy_rna_enum_items_static(PyObject *UNUSED(self)) return result; } +/* This is only exposed for (Unix/Linux), see: #GHOST_ISystem::getSystemBackend for details. */ +PyDoc_STRVAR(bpy_ghost_backend_doc, + ".. function:: _ghost_backend()\n" + "\n" + " :return: An identifier for the GHOST back-end.\n" + " :rtype: string\n"); +static PyObject *bpy_ghost_backend(PyObject *UNUSED(self)) +{ + return PyUnicode_FromString(WM_ghost_backend()); +} + static PyMethodDef bpy_methods[] = { {"script_paths", (PyCFunction)bpy_script_paths, METH_NOARGS, bpy_script_paths_doc}, {"blend_paths", @@ -552,10 +565,6 @@ static PyMethodDef bpy_methods[] = { (PyCFunction)bpy_resource_path, METH_VARARGS | METH_KEYWORDS, bpy_resource_path_doc}, - {"_driver_secure_code_test", - (PyCFunction)bpy_driver_secure_code_test, - METH_VARARGS | METH_KEYWORDS, - bpy_driver_secure_code_test_doc}, {"escape_identifier", (PyCFunction)bpy_escape_identifier, METH_O, bpy_escape_identifier_doc}, {"unescape_identifier", (PyCFunction)bpy_unescape_identifier, @@ -566,6 +575,14 @@ static PyMethodDef bpy_methods[] = { (PyCFunction)bpy_rna_enum_items_static, METH_NOARGS, bpy_rna_enum_items_static_doc}, + + /* Private functions (not part of the public API and may be removed at any time). */ + {"_driver_secure_code_test", + (PyCFunction)bpy_driver_secure_code_test, + METH_VARARGS | METH_KEYWORDS, + bpy_driver_secure_code_test_doc}, + {"_ghost_backend", (PyCFunction)bpy_ghost_backend, METH_NOARGS, bpy_ghost_backend_doc}, + {NULL, NULL, 0, NULL}, }; diff --git a/source/blender/render/intern/bake.c b/source/blender/render/intern/bake.c index 59719fb4590..8fd62f7ec34 100644 --- a/source/blender/render/intern/bake.c +++ b/source/blender/render/intern/bake.c @@ -590,7 +590,7 @@ bool RE_bake_pixels_populate_from_objects(struct Mesh *me_low, me_highpoly[i] = highpoly[i].me; BKE_mesh_runtime_looptri_ensure(me_highpoly[i]); - if (me_highpoly[i]->runtime.looptris.len != 0) { + if (BKE_mesh_runtime_looptri_len(me_highpoly[i]) != 0) { /* Create a BVH-tree for each `highpoly` object. */ BKE_bvhtree_from_mesh_get(&treeData[i], me_highpoly[i], BVHTREE_FROM_LOOPTRI, 2); diff --git a/source/blender/render/intern/engine.cc b/source/blender/render/intern/engine.cc index 5eb4db6faa4..b8757d33580 100644 --- a/source/blender/render/intern/engine.cc +++ b/source/blender/render/intern/engine.cc @@ -969,6 +969,40 @@ static void engine_render_view_layer(Render *re, engine_depsgraph_exit(engine); } +/* Callback function for engine_render_create_result to add all render passes to the result. */ +static void engine_render_add_result_pass_cb(void *user_data, + struct Scene *UNUSED(scene), + struct ViewLayer *view_layer, + const char *name, + int channels, + const char *chanid, + eNodeSocketDatatype UNUSED(type)) +{ + RenderResult *rr = (RenderResult *)user_data; + RE_create_render_pass(rr, name, channels, chanid, view_layer->name, RR_ALL_VIEWS, false); +} + +static RenderResult *engine_render_create_result(Render *re) +{ + RenderResult *rr = render_result_new(re, &re->disprect, RR_ALL_LAYERS, RR_ALL_VIEWS); + if (rr == nullptr) { + return nullptr; + } + + FOREACH_VIEW_LAYER_TO_RENDER_BEGIN (re, view_layer) { + RE_engine_update_render_passes( + re->engine, re->scene, view_layer, engine_render_add_result_pass_cb, rr); + } + FOREACH_VIEW_LAYER_TO_RENDER_END; + + /* Preview does not support deferred render result allocation. */ + if (re->r.scemode & R_BUTS_PREVIEW) { + render_result_passes_allocated_ensure(rr); + } + + return rr; +} + bool RE_engine_render(Render *re, bool do_all) { RenderEngineType *type = RE_engines_find(re->r.engine); @@ -1008,6 +1042,14 @@ bool RE_engine_render(Render *re, bool do_all) render_update_anim_renderdata(re, &re->scene->r, &re->scene->view_layers); } + /* Create engine. */ + RenderEngine *engine = re->engine; + + if (!engine) { + engine = RE_engine_create(type); + re->engine = engine; + } + /* create render result */ BLI_rw_mutex_lock(&re->resultmutex, THREAD_LOCK_WRITE); if (re->result == nullptr || !(re->r.scemode & R_BUTS_PREVIEW)) { @@ -1015,7 +1057,7 @@ bool RE_engine_render(Render *re, bool do_all) render_result_free(re->result); } - re->result = render_result_new(re, &re->disprect, RR_ALL_LAYERS, RR_ALL_VIEWS); + re->result = engine_render_create_result(re); } BLI_rw_mutex_unlock(&re->resultmutex); @@ -1024,6 +1066,9 @@ bool RE_engine_render(Render *re, bool do_all) if (re->draw_lock) { re->draw_lock(re->dlh, false); } + /* Free engine. */ + RE_engine_free(engine); + re->engine = nullptr; /* Too small image is handled earlier, here it could only happen if * there was no sufficient memory to allocate all passes. */ @@ -1036,14 +1081,6 @@ bool RE_engine_render(Render *re, bool do_all) re->i.cfra = re->scene->r.cfra; BLI_strncpy(re->i.scene_name, re->scene->id.name + 2, sizeof(re->i.scene_name)); - /* render */ - RenderEngine *engine = re->engine; - - if (!engine) { - engine = RE_engine_create(type); - re->engine = engine; - } - engine->flag |= RE_ENGINE_RENDERING; /* TODO: actually link to a parent which shouldn't happen */ diff --git a/source/blender/render/intern/render_result.cc b/source/blender/render/intern/render_result.cc index e8603f5e1b3..f9524fdbf05 100644 --- a/source/blender/render/intern/render_result.cc +++ b/source/blender/render/intern/render_result.cc @@ -290,90 +290,8 @@ RenderResult *render_result_new(Render *re, } } -#define RENDER_LAYER_ADD_PASS_SAFE(rr, rl, channels, name, viewname, chan_id) \ - do { \ - if (render_layer_add_pass(rr, rl, channels, name, viewname, chan_id, false) == nullptr) { \ - render_result_free(rr); \ - return nullptr; \ - } \ - } while (false) - /* A render-layer should always have a "Combined" pass. */ render_layer_add_pass(rr, rl, 4, "Combined", view, "RGBA", false); - - if (view_layer->passflag & SCE_PASS_Z) { - RENDER_LAYER_ADD_PASS_SAFE(rr, rl, 1, RE_PASSNAME_Z, view, "Z"); - } - if (view_layer->passflag & SCE_PASS_VECTOR) { - RENDER_LAYER_ADD_PASS_SAFE(rr, rl, 4, RE_PASSNAME_VECTOR, view, "XYZW"); - } - if (view_layer->passflag & SCE_PASS_NORMAL) { - RENDER_LAYER_ADD_PASS_SAFE(rr, rl, 3, RE_PASSNAME_NORMAL, view, "XYZ"); - } - if (view_layer->passflag & SCE_PASS_POSITION) { - RENDER_LAYER_ADD_PASS_SAFE(rr, rl, 3, RE_PASSNAME_POSITION, view, "XYZ"); - } - if (view_layer->passflag & SCE_PASS_UV) { - RENDER_LAYER_ADD_PASS_SAFE(rr, rl, 3, RE_PASSNAME_UV, view, "UVA"); - } - if (view_layer->passflag & SCE_PASS_EMIT) { - RENDER_LAYER_ADD_PASS_SAFE(rr, rl, 3, RE_PASSNAME_EMIT, view, "RGB"); - } - if (view_layer->passflag & SCE_PASS_AO) { - RENDER_LAYER_ADD_PASS_SAFE(rr, rl, 3, RE_PASSNAME_AO, view, "RGB"); - } - if (view_layer->passflag & SCE_PASS_ENVIRONMENT) { - RENDER_LAYER_ADD_PASS_SAFE(rr, rl, 3, RE_PASSNAME_ENVIRONMENT, view, "RGB"); - } - if (view_layer->passflag & SCE_PASS_SHADOW) { - RENDER_LAYER_ADD_PASS_SAFE(rr, rl, 3, RE_PASSNAME_SHADOW, view, "RGB"); - } - if (view_layer->passflag & SCE_PASS_INDEXOB) { - RENDER_LAYER_ADD_PASS_SAFE(rr, rl, 1, RE_PASSNAME_INDEXOB, view, "X"); - } - if (view_layer->passflag & SCE_PASS_INDEXMA) { - RENDER_LAYER_ADD_PASS_SAFE(rr, rl, 1, RE_PASSNAME_INDEXMA, view, "X"); - } - if (view_layer->passflag & SCE_PASS_MIST) { - RENDER_LAYER_ADD_PASS_SAFE(rr, rl, 1, RE_PASSNAME_MIST, view, "Z"); - } - if (view_layer->passflag & SCE_PASS_DIFFUSE_DIRECT) { - RENDER_LAYER_ADD_PASS_SAFE(rr, rl, 3, RE_PASSNAME_DIFFUSE_DIRECT, view, "RGB"); - } - if (view_layer->passflag & SCE_PASS_DIFFUSE_INDIRECT) { - RENDER_LAYER_ADD_PASS_SAFE(rr, rl, 3, RE_PASSNAME_DIFFUSE_INDIRECT, view, "RGB"); - } - if (view_layer->passflag & SCE_PASS_DIFFUSE_COLOR) { - RENDER_LAYER_ADD_PASS_SAFE(rr, rl, 3, RE_PASSNAME_DIFFUSE_COLOR, view, "RGB"); - } - if (view_layer->passflag & SCE_PASS_GLOSSY_DIRECT) { - RENDER_LAYER_ADD_PASS_SAFE(rr, rl, 3, RE_PASSNAME_GLOSSY_DIRECT, view, "RGB"); - } - if (view_layer->passflag & SCE_PASS_GLOSSY_INDIRECT) { - RENDER_LAYER_ADD_PASS_SAFE(rr, rl, 3, RE_PASSNAME_GLOSSY_INDIRECT, view, "RGB"); - } - if (view_layer->passflag & SCE_PASS_GLOSSY_COLOR) { - RENDER_LAYER_ADD_PASS_SAFE(rr, rl, 3, RE_PASSNAME_GLOSSY_COLOR, view, "RGB"); - } - if (view_layer->passflag & SCE_PASS_TRANSM_DIRECT) { - RENDER_LAYER_ADD_PASS_SAFE(rr, rl, 3, RE_PASSNAME_TRANSM_DIRECT, view, "RGB"); - } - if (view_layer->passflag & SCE_PASS_TRANSM_INDIRECT) { - RENDER_LAYER_ADD_PASS_SAFE(rr, rl, 3, RE_PASSNAME_TRANSM_INDIRECT, view, "RGB"); - } - if (view_layer->passflag & SCE_PASS_TRANSM_COLOR) { - RENDER_LAYER_ADD_PASS_SAFE(rr, rl, 3, RE_PASSNAME_TRANSM_COLOR, view, "RGB"); - } - if (view_layer->passflag & SCE_PASS_SUBSURFACE_DIRECT) { - RENDER_LAYER_ADD_PASS_SAFE(rr, rl, 3, RE_PASSNAME_SUBSURFACE_DIRECT, view, "RGB"); - } - if (view_layer->passflag & SCE_PASS_SUBSURFACE_INDIRECT) { - RENDER_LAYER_ADD_PASS_SAFE(rr, rl, 3, RE_PASSNAME_SUBSURFACE_INDIRECT, view, "RGB"); - } - if (view_layer->passflag & SCE_PASS_SUBSURFACE_COLOR) { - RENDER_LAYER_ADD_PASS_SAFE(rr, rl, 3, RE_PASSNAME_SUBSURFACE_COLOR, view, "RGB"); - } -#undef RENDER_LAYER_ADD_PASS_SAFE } } FOREACH_VIEW_LAYER_TO_RENDER_END; @@ -411,11 +329,6 @@ RenderResult *render_result_new(Render *re, rr->xof = re->disprect.xmin + BLI_rcti_cent_x(&re->disprect) - (re->winx / 2); rr->yof = re->disprect.ymin + BLI_rcti_cent_y(&re->disprect) - (re->winy / 2); - /* Preview does not support deferred render result allocation. */ - if (re->r.scemode & R_BUTS_PREVIEW) { - render_result_passes_allocated_ensure(rr); - } - return rr; } @@ -962,7 +875,7 @@ static void render_result_exr_file_cache_path(Scene *sce, sce->id.name + 2, path_hexdigest); - BLI_join_dirfile(r_path, FILE_CACHE_MAX, root, filename_full); + BLI_path_join(r_path, FILE_CACHE_MAX, root, filename_full); if (BLI_path_is_rel(r_path)) { BLI_path_abs(r_path, dirname); } diff --git a/source/blender/render/intern/texture_margin.cc b/source/blender/render/intern/texture_margin.cc index a42ddb6b830..3366111ed33 100644 --- a/source/blender/render/intern/texture_margin.cc +++ b/source/blender/render/intern/texture_margin.cc @@ -290,8 +290,8 @@ class TextureMarginMap { void build_tables() { - loop_to_poly_map_ = blender::mesh_topology::build_loop_to_poly_map({mpoly_, totpoly_}, - totloop_); + loop_to_poly_map_ = blender::bke::mesh_topology::build_loop_to_poly_map({mpoly_, totpoly_}, + totloop_); loop_adjacency_map_.resize(totloop_, -1); diff --git a/source/blender/sequencer/intern/proxy.c b/source/blender/sequencer/intern/proxy.c index 4220efab8bf..9f08db2aa45 100644 --- a/source/blender/sequencer/intern/proxy.c +++ b/source/blender/sequencer/intern/proxy.c @@ -106,7 +106,7 @@ bool seq_proxy_get_custom_file_fname(Sequence *seq, char *name, const int view_i return false; } - BLI_join_dirfile(fname, PROXY_MAXFILE, proxy->dir, proxy->file); + BLI_path_join(fname, PROXY_MAXFILE, proxy->dir, proxy->file); BLI_path_abs(fname, BKE_main_blendfile_path_from_global()); if (view_id > 0) { @@ -325,7 +325,7 @@ static bool seq_proxy_multiview_context_invalid(Sequence *seq, Scene *scene, con if (view_id == 0) { char path[FILE_MAX]; - BLI_join_dirfile(path, sizeof(path), seq->strip->dir, seq->strip->stripdata->name); + BLI_path_join(path, sizeof(path), seq->strip->dir, seq->strip->stripdata->name); BLI_path_abs(path, BKE_main_blendfile_path_from_global()); BKE_scene_multiview_view_prefix_get(scene, path, prefix, &ext); } diff --git a/source/blender/sequencer/intern/render.c b/source/blender/sequencer/intern/render.c index 91ecccbe0f8..dbbece73695 100644 --- a/source/blender/sequencer/intern/render.c +++ b/source/blender/sequencer/intern/render.c @@ -938,7 +938,7 @@ static ImBuf *seq_render_image_strip(const SeqRenderData *context, return NULL; } - BLI_join_dirfile(name, sizeof(name), seq->strip->dir, s_elem->name); + BLI_path_join(name, sizeof(name), seq->strip->dir, s_elem->name); BLI_path_abs(name, BKE_main_blendfile_path_from_global()); /* Try to get a proxy image. */ diff --git a/source/blender/sequencer/intern/strip_add.c b/source/blender/sequencer/intern/strip_add.c index 753a6ee39e0..7f4c91724fc 100644 --- a/source/blender/sequencer/intern/strip_add.c +++ b/source/blender/sequencer/intern/strip_add.c @@ -204,7 +204,7 @@ void SEQ_add_image_init_alpha_mode(Sequence *seq) char name[FILE_MAX]; ImBuf *ibuf; - BLI_join_dirfile(name, sizeof(name), seq->strip->dir, seq->strip->stripdata->name); + BLI_path_join(name, sizeof(name), seq->strip->dir, seq->strip->stripdata->name); BLI_path_abs(name, BKE_main_blendfile_path_from_global()); /* Initialize input color space. */ @@ -545,7 +545,7 @@ void SEQ_add_reload_new_file(Main *bmain, Scene *scene, Sequence *seq, const boo const bool is_multiview = (seq->flag & SEQ_USE_VIEWS) != 0 && (scene->r.scemode & R_MULTIVIEW) != 0; - BLI_join_dirfile(path, sizeof(path), seq->strip->dir, seq->strip->stripdata->name); + BLI_path_join(path, sizeof(path), seq->strip->dir, seq->strip->stripdata->name); BLI_path_abs(path, BKE_main_blendfile_path_from_global()); SEQ_relations_sequence_free_anim(seq); diff --git a/source/blender/sequencer/intern/utils.c b/source/blender/sequencer/intern/utils.c index 6d182b6bcca..5b70bc33e88 100644 --- a/source/blender/sequencer/intern/utils.c +++ b/source/blender/sequencer/intern/utils.c @@ -224,7 +224,7 @@ void seq_open_anim_file(Scene *scene, Sequence *seq, bool openfile) /* reset all the previously created anims */ SEQ_relations_sequence_free_anim(seq); - BLI_join_dirfile(name, sizeof(name), seq->strip->dir, seq->strip->stripdata->name); + BLI_path_join(name, sizeof(name), seq->strip->dir, seq->strip->stripdata->name); BLI_path_abs(name, BKE_main_blendfile_path_from_global()); proxy = seq->strip->proxy; diff --git a/source/blender/simulation/intern/hair_volume.cpp b/source/blender/simulation/intern/hair_volume.cpp index cb6c963b7d2..97042f433c2 100644 --- a/source/blender/simulation/intern/hair_volume.cpp +++ b/source/blender/simulation/intern/hair_volume.cpp @@ -618,17 +618,17 @@ BLI_INLINE void hair_volume_eval_grid_vertex_sample(HairGridVert *vert, } void SIM_hair_volume_add_segment(HairGrid *grid, - const float UNUSED(x1[3]), - const float UNUSED(v1[3]), + const float /*x1*/[3], + const float /*v1*/[3], const float x2[3], const float v2[3], const float x3[3], const float v3[3], - const float UNUSED(x4[3]), - const float UNUSED(v4[3]), - const float UNUSED(dir1[3]), - const float UNUSED(dir2[3]), - const float UNUSED(dir3[3])) + const float /*x4*/[3], + const float /*v4*/[3], + const float /*dir1*/[3], + const float /*dir2*/[3], + const float /*dir3*/[3]) { /* XXX simplified test implementation using a series of discrete sample along the segment, * instead of finding the closest point for all affected grid vertices. */ diff --git a/source/blender/windowmanager/WM_api.h b/source/blender/windowmanager/WM_api.h index 5b6f7939ab9..1f9de8040f6 100644 --- a/source/blender/windowmanager/WM_api.h +++ b/source/blender/windowmanager/WM_api.h @@ -119,6 +119,13 @@ void WM_init_splash(struct bContext *C); void WM_init_opengl(void); +/** + * Return an identifier for the underlying GHOST implementation. + * \warning Use of this function should be limited & never for compatibility checks. + * see: #GHOST_ISystem::getSystemBackend for details. + */ +const char *WM_ghost_backend(void); + void WM_check(struct bContext *C); void WM_reinit_gizmomap_all(struct Main *bmain); diff --git a/source/blender/windowmanager/intern/wm_dragdrop.cc b/source/blender/windowmanager/intern/wm_dragdrop.cc index 0896daec561..fb63abed9e9 100644 --- a/source/blender/windowmanager/intern/wm_dragdrop.cc +++ b/source/blender/windowmanager/intern/wm_dragdrop.cc @@ -853,7 +853,8 @@ static void wm_drag_draw_icon(bContext * /*C*/, wmWindow * /*win*/, wmDrag *drag y = xy[1] - 2 * UI_DPI_FAC; const uchar text_col[] = {255, 255, 255, 255}; - UI_icon_draw_ex(x, y, drag->icon, U.inv_dpi_fac, 0.8, 0.0f, text_col, false); + UI_icon_draw_ex( + x, y, drag->icon, U.inv_dpi_fac, 0.8, 0.0f, text_col, false, UI_NO_ICON_OVERLAY_TEXT); } } diff --git a/source/blender/windowmanager/intern/wm_event_system.cc b/source/blender/windowmanager/intern/wm_event_system.cc index affe0bcf45a..181ec89cabd 100644 --- a/source/blender/windowmanager/intern/wm_event_system.cc +++ b/source/blender/windowmanager/intern/wm_event_system.cc @@ -5620,7 +5620,7 @@ void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, void case GHOST_kEventNDOFButton: { GHOST_TEventNDOFButtonData *e = static_cast<GHOST_TEventNDOFButtonData *>(customdata); - event.type = NDOF_BUTTON_NONE + e->button; + event.type = NDOF_BUTTON_INDEX_AS_EVENT(e->button); switch (e->action) { case GHOST_kPress: diff --git a/source/blender/windowmanager/intern/wm_files.c b/source/blender/windowmanager/intern/wm_files.c index 7ac55b2c27a..b009a67efba 100644 --- a/source/blender/windowmanager/intern/wm_files.c +++ b/source/blender/windowmanager/intern/wm_files.c @@ -1166,12 +1166,10 @@ void wm_homefile_read_ex(bContext *C, const char *const cfgdir = BKE_appdir_folder_id(BLENDER_USER_CONFIG, NULL); if (!use_factory_settings) { if (cfgdir) { - BLI_path_join( - filepath_startup, sizeof(filepath_startup), cfgdir, BLENDER_STARTUP_FILE, NULL); + BLI_path_join(filepath_startup, sizeof(filepath_startup), cfgdir, BLENDER_STARTUP_FILE); filepath_startup_is_factory = false; if (use_userdef) { - BLI_path_join( - filepath_userdef, sizeof(filepath_startup), cfgdir, BLENDER_USERPREF_FILE, NULL); + BLI_path_join(filepath_userdef, sizeof(filepath_startup), cfgdir, BLENDER_USERPREF_FILE); } } else { @@ -1214,12 +1212,9 @@ void wm_homefile_read_ex(bContext *C, /* note that the path is being set even when 'use_factory_settings == true' * this is done so we can load a templates factory-settings */ if (!use_factory_settings) { - BLI_path_join(app_template_config, sizeof(app_template_config), cfgdir, app_template, NULL); - BLI_path_join(filepath_startup, - sizeof(filepath_startup), - app_template_config, - BLENDER_STARTUP_FILE, - NULL); + BLI_path_join(app_template_config, sizeof(app_template_config), cfgdir, app_template); + BLI_path_join( + filepath_startup, sizeof(filepath_startup), app_template_config, BLENDER_STARTUP_FILE); filepath_startup_is_factory = false; if (BLI_access(filepath_startup, R_OK) != 0) { filepath_startup[0] = '\0'; @@ -1230,11 +1225,8 @@ void wm_homefile_read_ex(bContext *C, } if (filepath_startup[0] == '\0') { - BLI_path_join(filepath_startup, - sizeof(filepath_startup), - app_template_system, - BLENDER_STARTUP_FILE, - NULL); + BLI_path_join( + filepath_startup, sizeof(filepath_startup), app_template_system, BLENDER_STARTUP_FILE); filepath_startup_is_factory = true; /* Update defaults only for system templates. */ @@ -1303,16 +1295,14 @@ void wm_homefile_read_ex(bContext *C, char temp_path[FILE_MAX]; temp_path[0] = '\0'; if (!use_factory_settings) { - BLI_path_join( - temp_path, sizeof(temp_path), app_template_config, BLENDER_USERPREF_FILE, NULL); + BLI_path_join(temp_path, sizeof(temp_path), app_template_config, BLENDER_USERPREF_FILE); if (BLI_access(temp_path, R_OK) != 0) { temp_path[0] = '\0'; } } if (temp_path[0] == '\0') { - BLI_path_join( - temp_path, sizeof(temp_path), app_template_system, BLENDER_USERPREF_FILE, NULL); + BLI_path_join(temp_path, sizeof(temp_path), app_template_system, BLENDER_USERPREF_FILE); } if (use_userdef) { @@ -1416,7 +1406,7 @@ void wm_history_file_read(void) LinkNode *l; int num; - BLI_join_dirfile(name, sizeof(name), cfgdir, BLENDER_HISTORY_FILE); + BLI_path_join(name, sizeof(name), cfgdir, BLENDER_HISTORY_FILE); LinkNode *lines = BLI_file_read_as_lines(name); @@ -1479,7 +1469,7 @@ static void wm_history_file_write(void) return; } - BLI_join_dirfile(name, sizeof(name), user_config_dir, BLENDER_HISTORY_FILE); + BLI_path_join(name, sizeof(name), user_config_dir, BLENDER_HISTORY_FILE); fp = BLI_fopen(name, "w"); if (fp) { @@ -1940,7 +1930,7 @@ static void wm_autosave_location(char filepath[FILE_MAX]) } #endif - BLI_join_dirfile(filepath, FILE_MAX, tempdir_base, path); + BLI_path_join(filepath, FILE_MAX, tempdir_base, path); } static void wm_autosave_write(Main *bmain, wmWindowManager *wm) @@ -2030,7 +2020,7 @@ void wm_autosave_delete(void) if (BLI_exists(filepath)) { char str[FILE_MAX]; - BLI_join_dirfile(str, sizeof(str), BKE_tempdir_base(), BLENDER_QUIT_FILE); + BLI_path_join(str, sizeof(str), BKE_tempdir_base(), BLENDER_QUIT_FILE); /* if global undo; remove tempsave, otherwise rename */ if (U.uiflag & USER_GLOBALUNDO) { @@ -2132,7 +2122,7 @@ static int wm_homefile_write_exec(bContext *C, wmOperator *op) /* update keymaps in user preferences */ WM_keyconfig_update(wm); - BLI_path_join(filepath, sizeof(filepath), cfgdir, BLENDER_STARTUP_FILE, NULL); + BLI_path_join(filepath, sizeof(filepath), cfgdir, BLENDER_STARTUP_FILE); printf("Writing homefile: '%s' ", filepath); @@ -2925,7 +2915,7 @@ void WM_OT_revert_mainfile(wmOperatorType *ot) bool WM_file_recover_last_session(bContext *C, ReportList *reports) { char filepath[FILE_MAX]; - BLI_join_dirfile(filepath, sizeof(filepath), BKE_tempdir_base(), BLENDER_QUIT_FILE); + BLI_path_join(filepath, sizeof(filepath), BKE_tempdir_base(), BLENDER_QUIT_FILE); G.fileflags |= G_FILE_RECOVER_READ; const bool success = wm_file_read_opwrap(C, filepath, reports); G.fileflags &= ~G_FILE_RECOVER_READ; diff --git a/source/blender/windowmanager/intern/wm_files_link.c b/source/blender/windowmanager/intern/wm_files_link.c index 0ea783af1af..bbe53bf7355 100644 --- a/source/blender/windowmanager/intern/wm_files_link.c +++ b/source/blender/windowmanager/intern/wm_files_link.c @@ -210,7 +210,7 @@ static int wm_link_append_exec(bContext *C, wmOperator *op) RNA_string_get(op->ptr, "filename", relname); RNA_string_get(op->ptr, "directory", root); - BLI_join_dirfile(path, sizeof(path), root, relname); + BLI_path_join(path, sizeof(path), root, relname); /* test if we have a valid data */ if (!BLO_library_path_explode(path, libname, &group, &name)) { @@ -284,7 +284,7 @@ static int wm_link_append_exec(bContext *C, wmOperator *op) RNA_BEGIN (op->ptr, itemptr, "files") { RNA_string_get(&itemptr, "name", relname); - BLI_join_dirfile(path, sizeof(path), root, relname); + BLI_path_join(path, sizeof(path), root, relname); if (BLO_library_path_explode(path, libname, &group, &name)) { if (!wm_link_append_item_poll(NULL, path, group, name, do_append)) { @@ -303,7 +303,7 @@ static int wm_link_append_exec(bContext *C, wmOperator *op) RNA_BEGIN (op->ptr, itemptr, "files") { RNA_string_get(&itemptr, "name", relname); - BLI_join_dirfile(path, sizeof(path), root, relname); + BLI_path_join(path, sizeof(path), root, relname); if (BLO_library_path_explode(path, libname, &group, &name)) { BlendfileLinkAppendContextItem *item; @@ -683,7 +683,7 @@ static int wm_lib_relocate_exec_do(bContext *C, wmOperator *op, bool do_reload) return OPERATOR_CANCELLED; } - BLI_join_dirfile(path, sizeof(path), root, libname); + BLI_path_join(path, sizeof(path), root, libname); if (!BLI_exists(path)) { BKE_reportf(op->reports, @@ -739,7 +739,7 @@ static int wm_lib_relocate_exec_do(bContext *C, wmOperator *op, bool do_reload) RNA_BEGIN (op->ptr, itemptr, "files") { RNA_string_get(&itemptr, "name", relname); - BLI_join_dirfile(path, sizeof(path), root, relname); + BLI_path_join(path, sizeof(path), root, relname); if (BLI_path_cmp(path, lib->filepath_abs) == 0 || !BLO_has_bfile_extension(relname)) { continue; diff --git a/source/blender/windowmanager/intern/wm_init_exit.c b/source/blender/windowmanager/intern/wm_init_exit.c index 32890b4014f..c5d7152246c 100644 --- a/source/blender/windowmanager/intern/wm_init_exit.c +++ b/source/blender/windowmanager/intern/wm_init_exit.c @@ -447,7 +447,7 @@ void WM_exit_ex(bContext *C, const bool do_python) bool has_edited; const int fileflags = G.fileflags & ~G_FILE_COMPRESS; - BLI_join_dirfile(filepath, sizeof(filepath), BKE_tempdir_base(), BLENDER_QUIT_FILE); + BLI_path_join(filepath, sizeof(filepath), BKE_tempdir_base(), BLENDER_QUIT_FILE); has_edited = ED_editors_flush_edits(bmain); diff --git a/source/blender/windowmanager/intern/wm_operators.c b/source/blender/windowmanager/intern/wm_operators.c index 58791dbd00a..fa89e7a4caa 100644 --- a/source/blender/windowmanager/intern/wm_operators.c +++ b/source/blender/windowmanager/intern/wm_operators.c @@ -107,19 +107,32 @@ /** \name Operator API * \{ */ +#define OP_BL_SEP_STRING "_OT_" +#define OP_BL_SEP_LEN 4 + +#define OP_PY_SEP_CHAR '.' +#define OP_PY_SEP_LEN 1 + +/* Difference between python 'identifier' and BL/C code one ("." separator replaced by "_OT_"), + * and final `\0` char. */ +#define OP_MAX_PY_IDNAME (OP_MAX_TYPENAME - OP_BL_SEP_LEN + OP_PY_SEP_LEN - 1) + size_t WM_operator_py_idname(char *dst, const char *src) { - const char *sep = strstr(src, "_OT_"); + const char *sep = strstr(src, OP_BL_SEP_STRING); if (sep) { - int ofs = (sep - src); + const size_t sep_offset = (size_t)(sep - src); /* NOTE: we use ascii `tolower` instead of system `tolower`, because the * latter depends on the locale, and can lead to `idname` mismatch. */ - memcpy(dst, src, sizeof(char) * ofs); - BLI_str_tolower_ascii(dst, ofs); + memcpy(dst, src, sep_offset); + BLI_str_tolower_ascii(dst, sep_offset); - dst[ofs] = '.'; - return BLI_strncpy_rlen(dst + (ofs + 1), sep + 4, OP_MAX_TYPENAME - (ofs + 1)) + (ofs + 1); + dst[sep_offset] = OP_PY_SEP_CHAR; + return BLI_strncpy_rlen(dst + (sep_offset + OP_PY_SEP_LEN), + sep + OP_BL_SEP_LEN, + OP_MAX_TYPENAME - sep_offset - OP_PY_SEP_LEN) + + (sep_offset + OP_PY_SEP_LEN); } /* Should not happen but support just in case. */ return BLI_strncpy_rlen(dst, src, OP_MAX_TYPENAME); @@ -127,15 +140,19 @@ size_t WM_operator_py_idname(char *dst, const char *src) size_t WM_operator_bl_idname(char *dst, const char *src) { - const char *sep = strchr(src, '.'); - int from_len; - if (sep && (from_len = strlen(src)) < OP_MAX_TYPENAME - 3) { - const int ofs = (sep - src); - memcpy(dst, src, sizeof(char) * ofs); - BLI_str_toupper_ascii(dst, ofs); - memcpy(dst + ofs, "_OT_", 4); - memcpy(dst + (ofs + 4), sep + 1, (from_len - ofs)); - return (from_len - ofs) - 1; + const size_t from_len = (size_t)strlen(src); + + const char *sep = strchr(src, OP_PY_SEP_CHAR); + if (sep && (from_len <= OP_MAX_PY_IDNAME)) { + const size_t sep_offset = (size_t)(sep - src); + memcpy(dst, src, sep_offset); + BLI_str_toupper_ascii(dst, sep_offset); + + memcpy(dst + sep_offset, OP_BL_SEP_STRING, OP_BL_SEP_LEN); + BLI_strncpy(dst + sep_offset + OP_BL_SEP_LEN, + sep + OP_PY_SEP_LEN, + from_len - sep_offset - OP_PY_SEP_LEN + 1); + return from_len + OP_BL_SEP_LEN - OP_PY_SEP_LEN; } /* Should not happen but support just in case. */ return BLI_strncpy_rlen(dst, src, OP_MAX_TYPENAME); @@ -166,14 +183,14 @@ bool WM_operator_py_idname_ok_or_report(ReportList *reports, } } - if (i > (MAX_NAME - 3)) { + if (i > OP_MAX_PY_IDNAME) { BKE_reportf(reports, RPT_ERROR, "Registering operator class: '%s', invalid bl_idname '%s', " "is too long, maximum length is %d", classname, idname, - MAX_NAME - 3); + OP_MAX_PY_IDNAME); return false; } diff --git a/source/blender/windowmanager/intern/wm_platform_support.c b/source/blender/windowmanager/intern/wm_platform_support.c index a0519506d29..ee93621545f 100644 --- a/source/blender/windowmanager/intern/wm_platform_support.c +++ b/source/blender/windowmanager/intern/wm_platform_support.c @@ -42,7 +42,7 @@ static bool wm_platform_support_check_approval(const char *platform_support_key, bool result = false; char filepath[FILE_MAX]; - BLI_join_dirfile(filepath, sizeof(filepath), cfgdir, BLENDER_PLATFORM_SUPPORT_FILE); + BLI_path_join(filepath, sizeof(filepath), cfgdir, BLENDER_PLATFORM_SUPPORT_FILE); LinkNode *lines = BLI_file_read_as_lines(filepath); for (LinkNode *line_node = lines; line_node; line_node = line_node->next) { char *line = line_node->link; diff --git a/source/blender/windowmanager/intern/wm_playanim.c b/source/blender/windowmanager/intern/wm_playanim.c index bf793ee41a0..3e5399a6f56 100644 --- a/source/blender/windowmanager/intern/wm_playanim.c +++ b/source/blender/windowmanager/intern/wm_playanim.c @@ -59,6 +59,8 @@ #include "DEG_depsgraph.h" +#include "wm_window_private.h" + #include "WM_api.h" /* only for WM_main_playanim */ #ifdef WITH_AUDASPACE @@ -1340,6 +1342,8 @@ static bool ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr ps_void) static void playanim_window_open(const char *title, int posx, int posy, int sizex, int sizey) { GHOST_GLSettings glsettings = {0}; + const eGPUBackendType gpu_backend = GPU_backend_type_selection_get(); + glsettings.context_type = wm_ghost_drawing_context_type(gpu_backend); uint32_t scr_w, scr_h; GHOST_GetMainDisplayDimensions(g_WS.ghost_system, &scr_w, &scr_h); @@ -1356,7 +1360,6 @@ static void playanim_window_open(const char *title, int posx, int posy, int size /* Could optionally start full-screen. */ GHOST_kWindowStateNormal, false, - GHOST_kDrawingContextTypeOpenGL, glsettings); } diff --git a/source/blender/windowmanager/intern/wm_splash_screen.c b/source/blender/windowmanager/intern/wm_splash_screen.c index 8fca3deef92..16e5f983bea 100644 --- a/source/blender/windowmanager/intern/wm_splash_screen.c +++ b/source/blender/windowmanager/intern/wm_splash_screen.c @@ -140,7 +140,7 @@ static ImBuf *wm_block_splash_image(int width, int *r_height) char template_directory[FILE_MAX]; if (BKE_appdir_app_template_id_search( U.app_template, template_directory, sizeof(template_directory))) { - BLI_join_dirfile(splash_filepath, sizeof(splash_filepath), template_directory, "splash.png"); + BLI_path_join(splash_filepath, sizeof(splash_filepath), template_directory, "splash.png"); ibuf = IMB_loadiffname(splash_filepath, IB_rect, NULL); } } @@ -218,7 +218,7 @@ static uiBlock *wm_block_create_splash(bContext *C, ARegion *region, void *UNUSE const char *const cfgdir = BKE_appdir_folder_id(BLENDER_USER_CONFIG, NULL); if (cfgdir) { - BLI_path_join(userpref, sizeof(userpref), cfgdir, BLENDER_USERPREF_FILE, NULL); + BLI_path_join(userpref, sizeof(userpref), cfgdir, BLENDER_USERPREF_FILE); } /* Draw setup screen if no preferences have been saved yet. */ diff --git a/source/blender/windowmanager/intern/wm_window.c b/source/blender/windowmanager/intern/wm_window.c index a4f92da2774..2ca7b5f470d 100644 --- a/source/blender/windowmanager/intern/wm_window.c +++ b/source/blender/windowmanager/intern/wm_window.c @@ -91,6 +91,9 @@ /* the global to talk to ghost */ static GHOST_SystemHandle g_system = NULL; +#if !(defined(WIN32) || defined(__APPLE__)) +static const char *g_system_backend_id = NULL; +#endif typedef enum eWinOverrideFlag { WIN_OVERRIDE_GEOM = (1 << 0), @@ -553,6 +556,9 @@ static void wm_window_ghostwindow_add(wmWindowManager *wm, glSettings.flags |= GHOST_glDebugContext; } + eGPUBackendType gpu_backend = GPU_backend_type_selection_get(); + glSettings.context_type = wm_ghost_drawing_context_type(gpu_backend); + int scr_w, scr_h; wm_get_desktopsize(&scr_w, &scr_h); int posy = (scr_h - win->posy - win->sizey); @@ -570,7 +576,6 @@ static void wm_window_ghostwindow_add(wmWindowManager *wm, win->sizey, (GHOST_TWindowState)win->windowstate, is_dialog, - GHOST_kDrawingContextTypeOpenGL, glSettings); if (ghostwin) { @@ -1552,6 +1557,9 @@ void wm_ghost_init(bContext *C) /* This will leak memory, it's preferable to crashing. */ exit(1); } +#if !(defined(WIN32) || defined(__APPLE__)) + g_system_backend_id = GHOST_SystemBackend(); +#endif GHOST_Debug debug = {0}; if (G.debug & G_DEBUG_GHOST) { @@ -1597,6 +1605,41 @@ void wm_ghost_exit(void) g_system = NULL; } +const char *WM_ghost_backend(void) +{ +#if !(defined(WIN32) || defined(__APPLE__)) + return g_system_backend_id ? g_system_backend_id : "NONE"; +#else + /* While this could be supported, at the moment it's only needed with GHOST X11/WAYLAND + * to check which was selected and the API call may be removed after that's no longer needed. + * Use dummy values to prevent this being used on other systems. */ + return g_system ? "DEFAULT" : "NONE"; +#endif +} + +GHOST_TDrawingContextType wm_ghost_drawing_context_type(const eGPUBackendType gpu_backend) +{ + switch (gpu_backend) { + case GPU_BACKEND_NONE: + return GHOST_kDrawingContextTypeNone; + case GPU_BACKEND_ANY: + case GPU_BACKEND_OPENGL: + return GHOST_kDrawingContextTypeOpenGL; + case GPU_BACKEND_METAL: +#ifdef WITH_METAL_BACKEND + return GHOST_kDrawingContextTypeMetal; +#else + BLI_assert_unreachable(); + return GHOST_kDrawingContextTypeNone; +#endif + } + + /* Avoid control reaches end of non-void function compilation warning, which could be promoted + * to error. */ + BLI_assert_unreachable(); + return GHOST_kDrawingContextTypeNone; +} + /** \} */ /* -------------------------------------------------------------------- */ diff --git a/source/blender/windowmanager/intern/wm_window_private.h b/source/blender/windowmanager/intern/wm_window_private.h index f68d4e3e693..dad3e749817 100644 --- a/source/blender/windowmanager/intern/wm_window_private.h +++ b/source/blender/windowmanager/intern/wm_window_private.h @@ -7,8 +7,11 @@ #pragma once #include "BLI_sys_types.h" + #include "GHOST_Types.h" +#include "GPU_context.h" + /* *************** Message box *************** */ /* `WM_ghost_show_message_box` is implemented in `wm_windows.c` it is * defined here as it was implemented to be used for showing @@ -21,3 +24,5 @@ void WM_ghost_show_message_box(const char *title, const char *continue_label, const char *link, GHOST_DialogOptions dialog_options); + +GHOST_TDrawingContextType wm_ghost_drawing_context_type(const eGPUBackendType gpu_backend); diff --git a/source/blender/windowmanager/wm_event_types.h b/source/blender/windowmanager/wm_event_types.h index 405b7225bd5..c36c57a12ae 100644 --- a/source/blender/windowmanager/wm_event_types.h +++ b/source/blender/windowmanager/wm_event_types.h @@ -252,8 +252,6 @@ enum { #define _NDOF_MIN NDOF_MOTION #define _NDOF_BUTTON_MIN NDOF_BUTTON_MENU - /* used internally, never sent */ - NDOF_BUTTON_NONE = NDOF_MOTION, /* these two are available from any 3Dconnexion device */ NDOF_BUTTON_MENU = 0x0191, /* 401 */ @@ -281,35 +279,42 @@ enum { NDOF_BUTTON_DOMINANT = 0x01a3, /* 419 */ NDOF_BUTTON_PLUS = 0x01a4, /* 420 */ NDOF_BUTTON_MINUS = 0x01a5, /* 421 */ + /* General-purpose buttons. */ + NDOF_BUTTON_1 = 0x01a6, /* 422 */ + NDOF_BUTTON_2 = 0x01a7, /* 423 */ + NDOF_BUTTON_3 = 0x01a8, /* 424 */ + NDOF_BUTTON_4 = 0x01a9, /* 425 */ + NDOF_BUTTON_5 = 0x01aa, /* 426 */ + NDOF_BUTTON_6 = 0x01ab, /* 427 */ + NDOF_BUTTON_7 = 0x01ac, /* 428 */ + NDOF_BUTTON_8 = 0x01ad, /* 429 */ + NDOF_BUTTON_9 = 0x01ae, /* 430 */ + NDOF_BUTTON_10 = 0x01af, /* 431 */ + /* more general-purpose buttons */ + NDOF_BUTTON_A = 0x01b0, /* 432 */ + NDOF_BUTTON_B = 0x01b1, /* 433 */ + NDOF_BUTTON_C = 0x01b2, /* 434 */ + /* Store/restore views. */ + NDOF_BUTTON_V1 = 0x01b3, /* 435 */ + NDOF_BUTTON_V2 = 0x01b4, /* 436 */ + NDOF_BUTTON_V3 = 0x01b5, /* 437 */ /* Disabled as GHOST converts these to keyboard events * which use regular keyboard event handling logic. */ #if 0 /* keyboard emulation */ - NDOF_BUTTON_ESC = 0x01a6, /* 422 */ - NDOF_BUTTON_ALT = 0x01a7, /* 423 */ - NDOF_BUTTON_SHIFT = 0x01a8, /* 424 */ - NDOF_BUTTON_CTRL = 0x01a9, /* 425 */ + NDOF_BUTTON_ESC = 0x01b6, /* 438 */ + NDOF_BUTTON_ENTER = 0x01b7, /* 439 */ + NDOF_BUTTON_DELETE = 0x01b8, /* 440 */ + NDOF_BUTTON_TAB = 0x01b9, /* 441 */ + NDOF_BUTTON_SPACE = 0x01ba, /* 442 */ + NDOF_BUTTON_ALT = 0x01bb, /* 443 */ + NDOF_BUTTON_SHIFT = 0x01bc, /* 444 */ + NDOF_BUTTON_CTRL = 0x01bd, /* 445 */ #endif - /* general-purpose buttons */ - NDOF_BUTTON_1 = 0x01aa, /* 426 */ - NDOF_BUTTON_2 = 0x01ab, /* 427 */ - NDOF_BUTTON_3 = 0x01ac, /* 428 */ - NDOF_BUTTON_4 = 0x01ad, /* 429 */ - NDOF_BUTTON_5 = 0x01ae, /* 430 */ - NDOF_BUTTON_6 = 0x01af, /* 431 */ - NDOF_BUTTON_7 = 0x01b0, /* 432 */ - NDOF_BUTTON_8 = 0x01b1, /* 433 */ - NDOF_BUTTON_9 = 0x01b2, /* 434 */ - NDOF_BUTTON_10 = 0x01b3, /* 435 */ - /* more general-purpose buttons */ - NDOF_BUTTON_A = 0x01b4, /* 436 */ - NDOF_BUTTON_B = 0x01b5, /* 437 */ - NDOF_BUTTON_C = 0x01b6, /* 438 */ - -#define _NDOF_MAX NDOF_BUTTON_C -#define _NDOF_BUTTON_MAX NDOF_BUTTON_C +#define _NDOF_MAX NDOF_BUTTON_V3 +#define _NDOF_BUTTON_MAX NDOF_BUTTON_V3 /* ********** End of Input devices. ********** */ @@ -449,6 +454,8 @@ enum eEventType_Mask { (EVT_TYPE_MASK_KEYBOARD | EVT_TYPE_MASK_MOUSE | EVT_TYPE_MASK_NDOF) #define EVT_TYPE_MASK_HOTKEY_EXCLUDE EVT_TYPE_MASK_KEYBOARD_MODIFIER +#define NDOF_BUTTON_INDEX_AS_EVENT(i) (_NDOF_BUTTON_MIN + (i)) + bool WM_event_type_mask_test(int event_type, enum eEventType_Mask mask); /** \} */ |