diff options
author | Hans Goudey <h.goudey@me.com> | 2022-10-17 21:22:35 +0300 |
---|---|---|
committer | Hans Goudey <h.goudey@me.com> | 2022-10-17 21:22:35 +0300 |
commit | b280dce0a489720fb86820666317506c5fba50cd (patch) | |
tree | 3662a1b556e49ddf2f5396f7a3548e4e0c3b8ee2 /source/blender/blenkernel | |
parent | 70ed39518e600f4478328fc44d9a683812532003 (diff) | |
parent | e8291f4504d320ea1eac0601a9b99263fbf305e8 (diff) |
Merge branch 'master' into node-add-asset-menu
Diffstat (limited to 'source/blender/blenkernel')
69 files changed, 1617 insertions, 1063 deletions
diff --git a/source/blender/blenkernel/BKE_blender_version.h b/source/blender/blenkernel/BKE_blender_version.h index 3c8f7d758b6..96df8f7a443 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 4 /* 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_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..6502fefec88 --- /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 fullfills 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_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_node.h b/source/blender/blenkernel/BKE_node.h index b1b4045370c..ecf7a556459 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 89743f1d2b4..b375d69b61c 100644 --- a/source/blender/blenkernel/BKE_pbvh.h +++ b/source/blender/blenkernel/BKE_pbvh.h @@ -267,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. */ @@ -504,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); 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 742f20fa65f..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; @@ -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 8a28e209c6a..62d03b2d79b 100644 --- a/source/blender/blenkernel/intern/asset_catalog.cc +++ b/source/blender/blenkernel/intern/asset_catalog.cc @@ -285,10 +285,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; } @@ -516,8 +516,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/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/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/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 7ee6b4cdfc0..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; diff --git a/source/blender/blenkernel/intern/customdata.cc b/source/blender/blenkernel/intern/customdata.cc index be99a8ee87b..6f2390fc28f 100644 --- a/source/blender/blenkernel/intern/customdata.cc +++ b/source/blender/blenkernel/intern/customdata.cc @@ -2382,16 +2382,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 +2407,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 +2456,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 +3679,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); } diff --git a/source/blender/blenkernel/intern/editmesh.cc b/source/blender/blenkernel/intern/editmesh.cc index 2ed5ec7469f..b586b4110f2 100644 --- a/source/blender/blenkernel/intern/editmesh.cc +++ b/source/blender/blenkernel/intern/editmesh.cc @@ -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..90568a8a080 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); }); @@ -611,7 +648,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 +675,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/image.cc b/source/blender/blenkernel/intern/image.cc index 409eb0067b5..51d8f02616f 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 { @@ -3331,7 +3331,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_save.cc b/source/blender/blenkernel/intern/image_save.cc index e227f9cba5e..4e47e71fb2b 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)); } diff --git a/source/blender/blenkernel/intern/instances.cc b/source/blender/blenkernel/intern/instances.cc new file mode 100644 index 00000000000..dd759453630 --- /dev/null +++ b/source/blender/blenkernel/intern/instances.cc @@ -0,0 +1,348 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#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_; +} + +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 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; + } + + Vector<int> new_handles(mask.size()); + copy_data_based_on_mask<int>(this->reference_handles(), new_handles, mask); + reference_handles_ = std::move(new_handles); + Vector<float4x4> new_transforms(mask.size()); + copy_data_based_on_mask<float4x4>(this->transforms(), new_transforms, mask); + 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(); +} + +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/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..027423f5774 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 { 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 cf2518d7c9b..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) { 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 76f2078e6c8..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); 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/node.cc b/source/blender/blenkernel/intern/node.cc index 654eb06a781..8028f990c42 100644 --- a/source/blender/blenkernel/intern/node.cc +++ b/source/blender/blenkernel/intern/node.cc @@ -2157,6 +2157,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..2d949fb5c65 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); @@ -3218,7 +3218,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 +3233,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..ceef6404bf0 100644 --- a/source/blender/blenkernel/intern/object_update.cc +++ b/source/blender/blenkernel/intern/object_update.cc @@ -373,10 +373,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 fbb7642c067..13243925b28 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" @@ -1743,7 +1744,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 +1786,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 +1987,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 +2049,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 +2244,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 +2266,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 +2285,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; @@ -2295,11 +2303,15 @@ PBVH *BKE_sculpt_object_pbvh_ensure(Depsgraph *depsgraph, Object *ob) 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 b23fe53e80c..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; } @@ -2872,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/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 fc47fb71bf3..bd26075f81f 100644 --- a/source/blender/blenkernel/intern/scene.cc +++ b/source/blender/blenkernel/intern/scene.cc @@ -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 8ccf2c9cec5..bf09be444b1 100644 --- a/source/blender/blenkernel/intern/subdiv_ccg.cc +++ b/source/blender/blenkernel/intern/subdiv_ccg.cc @@ -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; } 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; } |