Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'source/blender/blenkernel')
-rw-r--r--source/blender/blenkernel/BKE_animsys.h2
-rw-r--r--source/blender/blenkernel/BKE_armature.h8
-rw-r--r--source/blender/blenkernel/BKE_attribute.h1
-rw-r--r--source/blender/blenkernel/BKE_blender_version.h2
-rw-r--r--source/blender/blenkernel/BKE_blendfile.h53
-rw-r--r--source/blender/blenkernel/BKE_collection.h3
-rw-r--r--source/blender/blenkernel/BKE_context.h1
-rw-r--r--source/blender/blenkernel/BKE_cryptomatte.h11
-rw-r--r--source/blender/blenkernel/BKE_cryptomatte.hh16
-rw-r--r--source/blender/blenkernel/BKE_geometry_set.h10
-rw-r--r--source/blender/blenkernel/BKE_geometry_set.hh28
-rw-r--r--source/blender/blenkernel/BKE_global.h23
-rw-r--r--source/blender/blenkernel/BKE_gpencil.h5
-rw-r--r--source/blender/blenkernel/BKE_gpencil_modifier.h3
-rw-r--r--source/blender/blenkernel/BKE_lib_override.h15
-rw-r--r--source/blender/blenkernel/BKE_lib_query.h10
-rw-r--r--source/blender/blenkernel/BKE_lib_remap.h9
-rw-r--r--source/blender/blenkernel/BKE_main.h16
-rw-r--r--source/blender/blenkernel/BKE_mesh_boolean_convert.h2
-rw-r--r--source/blender/blenkernel/BKE_mesh_mirror.h2
-rw-r--r--source/blender/blenkernel/BKE_node.h50
-rw-r--r--source/blender/blenkernel/BKE_node_ui_storage.hh27
-rw-r--r--source/blender/blenkernel/BKE_volume_to_mesh.hh1
-rw-r--r--source/blender/blenkernel/CMakeLists.txt6
-rw-r--r--source/blender/blenkernel/intern/armature_pose.cc133
-rw-r--r--source/blender/blenkernel/intern/attribute_access.cc1546
-rw-r--r--source/blender/blenkernel/intern/attribute_access_intern.hh494
-rw-r--r--source/blender/blenkernel/intern/blender_copybuffer.c7
-rw-r--r--source/blender/blenkernel/intern/blender_undo.c21
-rw-r--r--source/blender/blenkernel/intern/blendfile.c144
-rw-r--r--source/blender/blenkernel/intern/bpath.c2
-rw-r--r--source/blender/blenkernel/intern/collection.c47
-rw-r--r--source/blender/blenkernel/intern/context.c9
-rw-r--r--source/blender/blenkernel/intern/cryptomatte.cc99
-rw-r--r--source/blender/blenkernel/intern/cryptomatte_test.cc52
-rw-r--r--source/blender/blenkernel/intern/displist.c12
-rw-r--r--source/blender/blenkernel/intern/geometry_component_instances.cc173
-rw-r--r--source/blender/blenkernel/intern/geometry_component_mesh.cc979
-rw-r--r--source/blender/blenkernel/intern/geometry_component_pointcloud.cc205
-rw-r--r--source/blender/blenkernel/intern/geometry_component_volume.cc100
-rw-r--r--source/blender/blenkernel/intern/geometry_set.cc439
-rw-r--r--source/blender/blenkernel/intern/geometry_set_instances.cc13
-rw-r--r--source/blender/blenkernel/intern/gpencil.c40
-rw-r--r--source/blender/blenkernel/intern/idtype.c2
-rw-r--r--source/blender/blenkernel/intern/lib_id.c11
-rw-r--r--source/blender/blenkernel/intern/lib_id_delete.c20
-rw-r--r--source/blender/blenkernel/intern/lib_override.c372
-rw-r--r--source/blender/blenkernel/intern/lib_query.c17
-rw-r--r--source/blender/blenkernel/intern/lib_remap.c41
-rw-r--r--source/blender/blenkernel/intern/main.c23
-rw-r--r--source/blender/blenkernel/intern/main_idmap.c10
-rw-r--r--source/blender/blenkernel/intern/mesh_boolean_convert.cc115
-rw-r--r--source/blender/blenkernel/intern/node.cc161
-rw-r--r--source/blender/blenkernel/intern/node_ui_storage.cc13
-rw-r--r--source/blender/blenkernel/intern/object_update.c10
-rw-r--r--source/blender/blenkernel/intern/outliner_treehash.c4
-rw-r--r--source/blender/blenkernel/intern/paint.c2
-rw-r--r--source/blender/blenkernel/intern/particle.c4
-rw-r--r--source/blender/blenkernel/intern/particle_distribute.c10
-rw-r--r--source/blender/blenkernel/intern/pbvh.c9
-rw-r--r--source/blender/blenkernel/intern/scene.c5
-rw-r--r--source/blender/blenkernel/intern/screen.c24
-rw-r--r--source/blender/blenkernel/intern/simulation.cc1
-rw-r--r--source/blender/blenkernel/intern/sound.c4
-rw-r--r--source/blender/blenkernel/intern/studiolight.c2
-rw-r--r--source/blender/blenkernel/intern/tracking_auto.c3
-rw-r--r--source/blender/blenkernel/intern/tracking_region_tracker.c9
-rw-r--r--source/blender/blenkernel/tracking_private.h1
68 files changed, 3525 insertions, 2167 deletions
diff --git a/source/blender/blenkernel/BKE_animsys.h b/source/blender/blenkernel/BKE_animsys.h
index 8666291cec8..d43332ae1ac 100644
--- a/source/blender/blenkernel/BKE_animsys.h
+++ b/source/blender/blenkernel/BKE_animsys.h
@@ -266,7 +266,7 @@ void BKE_animsys_evaluate_all_animation(struct Main *main,
void animsys_evaluate_action(struct PointerRNA *ptr,
struct bAction *act,
const struct AnimationEvalContext *anim_eval_context,
- const bool flush_to_original);
+ bool flush_to_original);
/* Evaluate Action Group */
void animsys_evaluate_action_group(struct PointerRNA *ptr,
diff --git a/source/blender/blenkernel/BKE_armature.h b/source/blender/blenkernel/BKE_armature.h
index db44a771095..f5face2120e 100644
--- a/source/blender/blenkernel/BKE_armature.h
+++ b/source/blender/blenkernel/BKE_armature.h
@@ -27,6 +27,8 @@
extern "C" {
#endif
+struct AnimationEvalContext;
+struct bAction;
struct BMEditMesh;
struct Bone;
struct Depsgraph;
@@ -193,6 +195,12 @@ void BKE_pose_where_is_bone(struct Depsgraph *depsgraph,
bool do_extra);
void BKE_pose_where_is_bone_tail(struct bPoseChannel *pchan);
+/* Evaluate the action and apply it to the pose. If any pose bones are selected, only FCurves that
+ * relate to those bones are evaluated. */
+void BKE_pose_apply_action(struct Object *ob,
+ struct bAction *action,
+ struct AnimationEvalContext *anim_eval_context);
+
/* get_objectspace_bone_matrix has to be removed still */
void get_objectspace_bone_matrix(struct Bone *bone,
float M_accumulatedMatrix[4][4],
diff --git a/source/blender/blenkernel/BKE_attribute.h b/source/blender/blenkernel/BKE_attribute.h
index 574d9904dc4..a98bfd1e3df 100644
--- a/source/blender/blenkernel/BKE_attribute.h
+++ b/source/blender/blenkernel/BKE_attribute.h
@@ -39,6 +39,7 @@ struct ReportList;
/* Attribute.domain */
typedef enum AttributeDomain {
+ ATTR_DOMAIN_AUTO = -1, /* Use for nodes to choose automatically based on other data. */
ATTR_DOMAIN_POINT = 0, /* Mesh, Hair or PointCloud Point */
ATTR_DOMAIN_EDGE = 1, /* Mesh Edge */
ATTR_DOMAIN_CORNER = 2, /* Mesh Corner */
diff --git a/source/blender/blenkernel/BKE_blender_version.h b/source/blender/blenkernel/BKE_blender_version.h
index 17eb6e19292..eb5f910f555 100644
--- a/source/blender/blenkernel/BKE_blender_version.h
+++ b/source/blender/blenkernel/BKE_blender_version.h
@@ -39,7 +39,7 @@ extern "C" {
/* Blender file format version. */
#define BLENDER_FILE_VERSION BLENDER_VERSION
-#define BLENDER_FILE_SUBVERSION 9
+#define BLENDER_FILE_SUBVERSION 13
/* 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_blendfile.h b/source/blender/blenkernel/BKE_blendfile.h
index f73c4a70809..429e294a337 100644
--- a/source/blender/blenkernel/BKE_blendfile.h
+++ b/source/blender/blenkernel/BKE_blendfile.h
@@ -23,6 +23,7 @@
extern "C" {
#endif
+struct BlendFileData;
struct BlendFileReadParams;
struct ID;
struct Main;
@@ -31,36 +32,32 @@ struct ReportList;
struct UserDef;
struct bContext;
-bool BKE_blendfile_read_ex(struct bContext *C,
- const char *filepath,
- const struct BlendFileReadParams *params,
- struct ReportList *reports,
- /* Extra args. */
- const bool startup_update_defaults,
- const char *startup_app_template);
-bool BKE_blendfile_read(struct bContext *C,
- const char *filepath,
- const struct BlendFileReadParams *params,
- struct ReportList *reports);
+void BKE_blendfile_read_setup_ex(struct bContext *C,
+ struct BlendFileData *bfd,
+ const struct BlendFileReadParams *params,
+ struct ReportList *reports,
+ /* Extra args. */
+ const bool startup_update_defaults,
+ const char *startup_app_template);
-bool BKE_blendfile_read_from_memory_ex(struct bContext *C,
- const void *filebuf,
- int filelength,
- const struct BlendFileReadParams *params,
- struct ReportList *reports,
- /* Extra args. */
- const bool startup_update_defaults,
- const char *startup_app_template);
-bool BKE_blendfile_read_from_memory(struct bContext *C,
- const void *filebuf,
- int filelength,
- const struct BlendFileReadParams *params,
- struct ReportList *reports);
+void BKE_blendfile_read_setup(struct bContext *C,
+ struct BlendFileData *bfd,
+ const struct BlendFileReadParams *params,
+ struct ReportList *reports);
-bool BKE_blendfile_read_from_memfile(struct bContext *C,
- struct MemFile *memfile,
- const struct BlendFileReadParams *params,
- struct ReportList *reports);
+struct BlendFileData *BKE_blendfile_read(const char *filepath,
+ const struct BlendFileReadParams *params,
+ struct ReportList *reports);
+
+struct BlendFileData *BKE_blendfile_read_from_memory(const void *filebuf,
+ int filelength,
+ const struct BlendFileReadParams *params,
+ struct ReportList *reports);
+
+struct BlendFileData *BKE_blendfile_read_from_memfile(struct Main *bmain,
+ struct MemFile *memfile,
+ const struct BlendFileReadParams *params,
+ struct ReportList *reports);
void BKE_blendfile_read_make_empty(struct bContext *C);
struct UserDef *BKE_blendfile_userdef_read(const char *filepath, struct ReportList *reports);
diff --git a/source/blender/blenkernel/BKE_collection.h b/source/blender/blenkernel/BKE_collection.h
index d15aebfe03d..d0fca5e3796 100644
--- a/source/blender/blenkernel/BKE_collection.h
+++ b/source/blender/blenkernel/BKE_collection.h
@@ -82,6 +82,8 @@ struct Collection *BKE_collection_master_add(void);
bool BKE_collection_has_object(struct Collection *collection, const struct Object *ob);
bool BKE_collection_has_object_recursive(struct Collection *collection, struct Object *ob);
+bool BKE_collection_has_object_recursive_instanced(struct Collection *collection,
+ struct Object *ob);
struct Collection *BKE_collection_object_find(struct Main *bmain,
struct Scene *scene,
struct Collection *collection,
@@ -123,6 +125,7 @@ bool BKE_collection_object_cyclic_check(struct Main *bmain,
/* Object list cache. */
struct ListBase BKE_collection_object_cache_get(struct Collection *collection);
+ListBase BKE_collection_object_cache_instanced_get(struct Collection *collection);
void BKE_collection_object_cache_free(struct Collection *collection);
struct Base *BKE_collection_or_layer_objects(const struct ViewLayer *view_layer,
diff --git a/source/blender/blenkernel/BKE_context.h b/source/blender/blenkernel/BKE_context.h
index 94392dd78da..3d30188e517 100644
--- a/source/blender/blenkernel/BKE_context.h
+++ b/source/blender/blenkernel/BKE_context.h
@@ -197,6 +197,7 @@ struct SpaceInfo *CTX_wm_space_info(const bContext *C);
struct SpaceUserPref *CTX_wm_space_userpref(const bContext *C);
struct SpaceClip *CTX_wm_space_clip(const bContext *C);
struct SpaceTopBar *CTX_wm_space_topbar(const bContext *C);
+struct SpaceSpreadsheet *CTX_wm_space_spreadsheet(const bContext *C);
void CTX_wm_manager_set(bContext *C, struct wmWindowManager *wm);
void CTX_wm_window_set(bContext *C, struct wmWindow *win);
diff --git a/source/blender/blenkernel/BKE_cryptomatte.h b/source/blender/blenkernel/BKE_cryptomatte.h
index 96e853e7ff8..576a2c1effd 100644
--- a/source/blender/blenkernel/BKE_cryptomatte.h
+++ b/source/blender/blenkernel/BKE_cryptomatte.h
@@ -30,16 +30,17 @@
extern "C" {
#endif
+/* Forward declarations. */
struct CryptomatteSession;
-struct ID;
-struct Main;
struct Material;
struct Object;
struct RenderResult;
+struct Scene;
struct CryptomatteSession *BKE_cryptomatte_init(void);
struct CryptomatteSession *BKE_cryptomatte_init_from_render_result(
const struct RenderResult *render_result);
+struct CryptomatteSession *BKE_cryptomatte_init_from_scene(const struct Scene *scene);
void BKE_cryptomatte_free(struct CryptomatteSession *session);
void BKE_cryptomatte_add_layer(struct CryptomatteSession *session, const char *layer_name);
@@ -54,6 +55,10 @@ uint32_t BKE_cryptomatte_asset_hash(struct CryptomatteSession *session,
const char *layer_name,
const struct Object *object);
float BKE_cryptomatte_hash_to_float(uint32_t cryptomatte_hash);
+bool BKE_cryptomatte_find_name(const struct CryptomatteSession *session,
+ const float encoded_hash,
+ char *r_name,
+ int name_len);
char *BKE_cryptomatte_entries_to_matte_id(struct NodeCryptomatte *node_storage);
void BKE_cryptomatte_matte_id_to_entries(struct NodeCryptomatte *node_storage,
@@ -65,4 +70,4 @@ void BKE_cryptomatte_store_metadata(const struct CryptomatteSession *session,
#ifdef __cplusplus
}
-#endif
+#endif \ No newline at end of file
diff --git a/source/blender/blenkernel/BKE_cryptomatte.hh b/source/blender/blenkernel/BKE_cryptomatte.hh
index f10b4c1f7c4..9e205d01765 100644
--- a/source/blender/blenkernel/BKE_cryptomatte.hh
+++ b/source/blender/blenkernel/BKE_cryptomatte.hh
@@ -26,9 +26,13 @@
#include <optional>
#include <string>
+#include "BKE_cryptomatte.h"
+
#include "BLI_map.hh"
#include "BLI_string_ref.hh"
+#include "BKE_cryptomatte.h"
+
struct ID;
namespace blender::bke::cryptomatte {
@@ -103,4 +107,16 @@ struct CryptomatteStampDataCallbackData {
static void extract_layer_manifest(void *_data, const char *propname, char *propvalue, int len);
};
+const blender::Vector<std::string> &BKE_cryptomatte_layer_names_get(
+ const CryptomatteSession &session);
+
+struct CryptomatteSessionDeleter {
+ void operator()(CryptomatteSession *session)
+ {
+ BKE_cryptomatte_free(session);
+ }
+};
+
+using CryptomatteSessionPtr = std::unique_ptr<CryptomatteSession, CryptomatteSessionDeleter>;
+
} // namespace blender::bke::cryptomatte
diff --git a/source/blender/blenkernel/BKE_geometry_set.h b/source/blender/blenkernel/BKE_geometry_set.h
index ac42674654f..08b4a25d946 100644
--- a/source/blender/blenkernel/BKE_geometry_set.h
+++ b/source/blender/blenkernel/BKE_geometry_set.h
@@ -28,6 +28,16 @@ struct Collection;
struct GeometrySet;
struct Object;
+/* Each geometry component has a specific type. The type determines what kind of data the component
+ * stores. Functions modifying a geometry will usually just modify a subset of the component types.
+ */
+typedef enum GeometryComponentType {
+ GEO_COMPONENT_TYPE_MESH = 0,
+ GEO_COMPONENT_TYPE_POINT_CLOUD = 1,
+ GEO_COMPONENT_TYPE_INSTANCES = 2,
+ GEO_COMPONENT_TYPE_VOLUME = 3,
+} GeometryComponentType;
+
void BKE_geometry_set_free(struct GeometrySet *geometry_set);
bool BKE_geometry_set_has_instances(const struct GeometrySet *geometry_set);
diff --git a/source/blender/blenkernel/BKE_geometry_set.hh b/source/blender/blenkernel/BKE_geometry_set.hh
index ad01814ce82..8cc37a3e711 100644
--- a/source/blender/blenkernel/BKE_geometry_set.hh
+++ b/source/blender/blenkernel/BKE_geometry_set.hh
@@ -40,16 +40,6 @@ struct Object;
struct PointCloud;
struct Volume;
-/* Each geometry component has a specific type. The type determines what kind of data the component
- * stores. Functions modifying a geometry will usually just modify a subset of the component types.
- */
-enum class GeometryComponentType {
- Mesh = 0,
- PointCloud = 1,
- Instances = 2,
- Volume = 3,
-};
-
enum class GeometryOwnershipType {
/* The geometry is owned. This implies that it can be changed. */
Owned = 0,
@@ -59,16 +49,6 @@ enum class GeometryOwnershipType {
ReadOnly = 2,
};
-/* Make it possible to use the component type as key in hash tables. */
-namespace blender {
-template<> struct DefaultHash<GeometryComponentType> {
- uint64_t operator()(const GeometryComponentType &value) const
- {
- return (uint64_t)value;
- }
-};
-} // namespace blender
-
namespace blender::bke {
class ComponentAttributeProviders;
}
@@ -392,7 +372,7 @@ class MeshComponent : public GeometryComponent {
bool is_empty() const final;
- static constexpr inline GeometryComponentType static_type = GeometryComponentType::Mesh;
+ static constexpr inline GeometryComponentType static_type = GEO_COMPONENT_TYPE_MESH;
private:
const blender::bke::ComponentAttributeProviders *get_attribute_providers() const final;
@@ -422,7 +402,7 @@ class PointCloudComponent : public GeometryComponent {
bool is_empty() const final;
- static constexpr inline GeometryComponentType static_type = GeometryComponentType::PointCloud;
+ static constexpr inline GeometryComponentType static_type = GEO_COMPONENT_TYPE_POINT_CLOUD;
private:
const blender::bke::ComponentAttributeProviders *get_attribute_providers() const final;
@@ -462,7 +442,7 @@ class InstancesComponent : public GeometryComponent {
bool is_empty() const final;
- static constexpr inline GeometryComponentType static_type = GeometryComponentType::Instances;
+ static constexpr inline GeometryComponentType static_type = GEO_COMPONENT_TYPE_INSTANCES;
};
/** A geometry component that stores volume grids. */
@@ -484,5 +464,5 @@ class VolumeComponent : public GeometryComponent {
const Volume *get_for_read() const;
Volume *get_for_write();
- static constexpr inline GeometryComponentType static_type = GeometryComponentType::Volume;
+ static constexpr inline GeometryComponentType static_type = GEO_COMPONENT_TYPE_VOLUME;
};
diff --git a/source/blender/blenkernel/BKE_global.h b/source/blender/blenkernel/BKE_global.h
index d6b6ffd425e..9e237679795 100644
--- a/source/blender/blenkernel/BKE_global.h
+++ b/source/blender/blenkernel/BKE_global.h
@@ -77,6 +77,7 @@ typedef struct Global {
* * 1112: Disable new Cloth internal springs handling (09/2014).
* * 1234: Disable new dyntopo code fixing skinny faces generation (04/2015).
* * 3001: Enable additional Fluid modifier (Mantaflow) options (02/2020).
+ * * 4000: Line Art state output and debugging logs (03/2021).
* * 16384 and above: Reserved for python (add-ons) usage.
*/
short debug_value;
@@ -170,9 +171,23 @@ enum {
/* Bits 11 to 22 (inclusive) are deprecated & need to be cleared */
- /** On read, use #FileGlobal.filename instead of the real location on-disk,
- * needed for recovering temp files so relative paths resolve */
- G_FILE_RECOVER = (1 << 23),
+ /**
+ * On read, use #FileGlobal.filename instead of the real location on-disk,
+ * needed for recovering temp files so relative paths resolve.
+ *
+ * \note In some ways it would be nicer to make this an argument passed to file loading.
+ * In practice this means recover needs to be passed around to too many low level functions,
+ * so keep this as a flag.
+ */
+ G_FILE_RECOVER_READ = (1 << 23),
+ /**
+ * On write, assign use #FileGlobal.filename, otherwise leave it blank,
+ * needed so files can be recovered at their original locations.
+ *
+ * \note only #BLENDER_QUIT_FILE and auto-save files include recovery information.
+ * As users/developers may not want their paths exposed in publicly distributed files.
+ */
+ G_FILE_RECOVER_WRITE = (1 << 24),
/** BMesh option to save as older mesh format */
/* #define G_FILE_MESH_COMPAT (1 << 26) */
/* #define G_FILE_GLSL_NO_ENV_LIGHTING (1 << 28) */ /* deprecated */
@@ -182,7 +197,7 @@ enum {
* Run-time only #G.fileflags which are never read or written to/from Blend files.
* This means we can change the values without worrying about do-versions.
*/
-#define G_FILE_FLAG_ALL_RUNTIME (G_FILE_NO_UI)
+#define G_FILE_FLAG_ALL_RUNTIME (G_FILE_NO_UI | G_FILE_RECOVER_READ | G_FILE_RECOVER_WRITE)
/** ENDIAN_ORDER: indicates what endianness the platform where the file was written had. */
#if !defined(__BIG_ENDIAN__) && !defined(__LITTLE_ENDIAN__)
diff --git a/source/blender/blenkernel/BKE_gpencil.h b/source/blender/blenkernel/BKE_gpencil.h
index 5cfdcf241d1..a0a3f30d6d8 100644
--- a/source/blender/blenkernel/BKE_gpencil.h
+++ b/source/blender/blenkernel/BKE_gpencil.h
@@ -212,6 +212,10 @@ void BKE_gpencil_layer_mask_sort(struct bGPdata *gpd, struct bGPDlayer *gpl);
void BKE_gpencil_layer_mask_sort_all(struct bGPdata *gpd);
void BKE_gpencil_layer_frames_sort(struct bGPDlayer *gpl, bool *r_has_duplicate_frames);
+struct bGPDlayer *BKE_gpencil_layer_get_by_name(struct bGPdata *gpd,
+ char *name,
+ int first_if_not_found);
+
/* Brush */
struct Material *BKE_gpencil_brush_material_get(struct Brush *brush);
void BKE_gpencil_brush_material_set(struct Brush *brush, struct Material *material);
@@ -231,6 +235,7 @@ struct Material *BKE_gpencil_object_material_new(struct Main *bmain,
int *r_index);
int BKE_gpencil_object_material_index_get(struct Object *ob, struct Material *ma);
+int BKE_gpencil_object_material_get_index_name(struct Object *ob, char *name);
struct Material *BKE_gpencil_object_material_from_brush_get(struct Object *ob,
struct Brush *brush);
diff --git a/source/blender/blenkernel/BKE_gpencil_modifier.h b/source/blender/blenkernel/BKE_gpencil_modifier.h
index c066c161f46..c6406c8478c 100644
--- a/source/blender/blenkernel/BKE_gpencil_modifier.h
+++ b/source/blender/blenkernel/BKE_gpencil_modifier.h
@@ -206,7 +206,8 @@ typedef struct GpencilModifierTypeInfo {
* This function is optional.
*/
void (*updateDepsgraph)(struct GpencilModifierData *md,
- const struct ModifierUpdateDepsgraphContext *ctx);
+ const struct ModifierUpdateDepsgraphContext *ctx,
+ const int mode);
/**
* Should return true if the modifier needs to be recalculated on time
diff --git a/source/blender/blenkernel/BKE_lib_override.h b/source/blender/blenkernel/BKE_lib_override.h
index 5fd451dc986..e0cb2d9abb0 100644
--- a/source/blender/blenkernel/BKE_lib_override.h
+++ b/source/blender/blenkernel/BKE_lib_override.h
@@ -50,6 +50,7 @@ struct Main;
struct Object;
struct PointerRNA;
struct PropertyRNA;
+struct ReportList;
struct Scene;
struct ViewLayer;
@@ -61,6 +62,8 @@ void BKE_lib_override_library_copy(struct ID *dst_id,
void BKE_lib_override_library_clear(struct IDOverrideLibrary *override, const bool do_id_user);
void BKE_lib_override_library_free(struct IDOverrideLibrary **override, const bool do_id_user);
+bool BKE_lib_override_library_is_user_edited(struct ID *id);
+
struct ID *BKE_lib_override_library_create_from_id(struct Main *bmain,
struct ID *reference_id,
const bool do_tagged_remap);
@@ -77,7 +80,12 @@ bool BKE_lib_override_library_proxy_convert(struct Main *bmain,
bool BKE_lib_override_library_resync(struct Main *bmain,
struct Scene *scene,
struct ViewLayer *view_layer,
- struct ID *id_root);
+ struct ID *id_root,
+ const bool do_hierarchy_enforce);
+void BKE_lib_override_library_main_resync(struct Main *bmain,
+ struct Scene *scene,
+ struct ViewLayer *view_layer);
+
void BKE_lib_override_library_delete(struct Main *bmain, struct ID *id_root);
struct IDOverrideLibraryProperty *BKE_lib_override_library_property_find(
@@ -122,6 +130,11 @@ bool BKE_lib_override_library_property_operation_operands_validate(
struct PropertyRNA *prop_src,
struct PropertyRNA *prop_storage);
+void BKE_lib_override_library_validate(struct Main *bmain,
+ struct ID *id,
+ struct ReportList *reports);
+void BKE_lib_override_library_main_validate(struct Main *bmain, struct ReportList *reports);
+
bool BKE_lib_override_library_status_check_local(struct Main *bmain, struct ID *local);
bool BKE_lib_override_library_status_check_reference(struct Main *bmain, struct ID *local);
diff --git a/source/blender/blenkernel/BKE_lib_query.h b/source/blender/blenkernel/BKE_lib_query.h
index f064d03261d..4e781aea9d3 100644
--- a/source/blender/blenkernel/BKE_lib_query.h
+++ b/source/blender/blenkernel/BKE_lib_query.h
@@ -71,6 +71,13 @@ enum {
IDWALK_CB_OVERRIDE_LIBRARY_REFERENCE = (1 << 5),
/**
+ * Indicates that this is an internal runtime ID pointer, like e.g. `ID.newid` or `ID.original`.
+ * \note Those should be ignored in most cases, and won't be processed/generated anyway unless
+ * `IDWALK_DO_INTERNAL_RUNTIME_POINTERS` option is enabled.
+ */
+ IDWALK_CB_INTERNAL = (1 << 6),
+
+ /**
* This ID usage is fully refcounted.
* Callback is responsible to deal accordingly with #ID.us if needed.
*/
@@ -126,6 +133,9 @@ enum {
IDWALK_IGNORE_EMBEDDED_ID = (1 << 3),
IDWALK_NO_INDIRECT_PROXY_DATA_USAGE = (1 << 8), /* Ugly special case :(((( */
+ /** Also process internal ID pointers like `ID.newid` or `ID.orig_id`.
+ * WARNING: Dangerous, use with caution. */
+ IDWALK_DO_INTERNAL_RUNTIME_POINTERS = (1 << 9),
};
typedef struct LibraryForeachIDData LibraryForeachIDData;
diff --git a/source/blender/blenkernel/BKE_lib_remap.h b/source/blender/blenkernel/BKE_lib_remap.h
index a8d75213d39..705d2b030e5 100644
--- a/source/blender/blenkernel/BKE_lib_remap.h
+++ b/source/blender/blenkernel/BKE_lib_remap.h
@@ -76,6 +76,15 @@ enum {
ID_REMAP_NO_INDIRECT_PROXY_DATA_USAGE = 1 << 4,
/** Do not remap library override pointers. */
ID_REMAP_SKIP_OVERRIDE_LIBRARY = 1 << 5,
+ /** Don't touch the user count (use for low level actions such as swapping pointers). */
+ ID_REMAP_SKIP_USER_CLEAR = 1 << 6,
+ /**
+ * Force internal ID runtime pointers (like `ID.newid`, `ID.orig_id` etc.) to also be processed.
+ * This should only be needed in some very specific cases, typically only BKE ID management code
+ * should need it (e.g. required from `id_delete` to ensure no runtime pointer remains using
+ * freed ones).
+ */
+ ID_REMAP_FORCE_INTERNAL_RUNTIME_POINTERS = 1 << 7,
};
/* Note: Requiring new_id to be non-null, this *may* not be the case ultimately,
diff --git a/source/blender/blenkernel/BKE_main.h b/source/blender/blenkernel/BKE_main.h
index b6116b32ca5..2c6e5ed3873 100644
--- a/source/blender/blenkernel/BKE_main.h
+++ b/source/blender/blenkernel/BKE_main.h
@@ -223,7 +223,7 @@ struct GSet *BKE_main_gset_create(struct Main *bmain, struct GSet *gset);
#define FOREACH_MAIN_LISTBASE_BEGIN(_bmain, _lb) \
{ \
- ListBase *_lbarray[MAX_LIBARRAY]; \
+ ListBase *_lbarray[INDEX_ID_MAX]; \
int _i = set_listbasepointers((_bmain), _lbarray); \
while (_i--) { \
(_lb) = _lbarray[_i];
@@ -234,9 +234,13 @@ struct GSet *BKE_main_gset_create(struct Main *bmain, struct GSet *gset);
((void)0)
/**
- * DO NOT use break statement with that macro,
- * use #FOREACH_MAIN_LISTBASE and #FOREACH_MAIN_LISTBASE_ID instead
- * if you need that kind of control flow. */
+ * Top level `foreach`-like macro allowing to loop over all IDs in a given #Main data-base.
+ *
+ * NOTE: Order tries to go from 'user IDs' to 'used IDs' (e.g. collections will be processed
+ * before objects, which will be processed before obdata types, etc.).
+ *
+ * WARNING: DO NOT use break statement with that macro, use #FOREACH_MAIN_LISTBASE and
+ * #FOREACH_MAIN_LISTBASE_ID instead if you need that kind of control flow. */
#define FOREACH_MAIN_ID_BEGIN(_bmain, _id) \
{ \
ListBase *_lb; \
@@ -259,8 +263,8 @@ const char *BKE_main_blendfile_path_from_global(void);
struct ListBase *which_libbase(struct Main *bmain, short type);
-#define MAX_LIBARRAY 41
-int set_listbasepointers(struct Main *main, struct ListBase *lb[MAX_LIBARRAY]);
+//#define INDEX_ID_MAX 41
+int set_listbasepointers(struct Main *main, struct ListBase *lb[]);
#define MAIN_VERSION_ATLEAST(main, ver, subver) \
((main)->versionfile > (ver) || \
diff --git a/source/blender/blenkernel/BKE_mesh_boolean_convert.h b/source/blender/blenkernel/BKE_mesh_boolean_convert.h
index be5cbb305fa..1bb1d9ea8dc 100644
--- a/source/blender/blenkernel/BKE_mesh_boolean_convert.h
+++ b/source/blender/blenkernel/BKE_mesh_boolean_convert.h
@@ -29,8 +29,10 @@ extern "C" {
Mesh *BKE_mesh_boolean(const Mesh **meshes,
const float (*obmats[])[4][4],
+ const short **material_remaps,
const int meshes_len,
const bool use_self,
+ const bool hole_tolerant,
const int boolean_mode);
#ifdef __cplusplus
diff --git a/source/blender/blenkernel/BKE_mesh_mirror.h b/source/blender/blenkernel/BKE_mesh_mirror.h
index a91f0787e68..7b230b04410 100644
--- a/source/blender/blenkernel/BKE_mesh_mirror.h
+++ b/source/blender/blenkernel/BKE_mesh_mirror.h
@@ -46,7 +46,7 @@ void BKE_mesh_mirror_apply_mirror_on_axis(struct Main *bmain,
struct Mesh *BKE_mesh_mirror_apply_mirror_on_axis_for_modifier(struct MirrorModifierData *mmd,
struct Object *ob,
const struct Mesh *mesh,
- int axis);
+ const int axis);
#ifdef __cplusplus
}
diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h
index d675df6d868..013c90197b2 100644
--- a/source/blender/blenkernel/BKE_node.h
+++ b/source/blender/blenkernel/BKE_node.h
@@ -47,6 +47,7 @@ struct BlendLibReader;
struct BlendWriter;
struct ColorManagedDisplaySettings;
struct ColorManagedViewSettings;
+struct CryptomatteSession;
struct FreestyleLineStyle;
struct GPUMaterial;
struct GPUNodeStack;
@@ -301,11 +302,11 @@ typedef struct bNodeType {
void (*free_self)(struct bNodeType *ntype);
/* **** execution callbacks **** */
- NodeInitExecFunction initexecfunc;
- NodeFreeExecFunction freeexecfunc;
- NodeExecFunction execfunc;
+ NodeInitExecFunction init_exec_fn;
+ NodeFreeExecFunction free_exec_fn;
+ NodeExecFunction exec_fn;
/* gpu */
- NodeGPUExecFunction gpufunc;
+ NodeGPUExecFunction gpu_fn;
/* Expands the bNode into nodes in a multi-function network, which will be evaluated later on. */
NodeExpandInMFNetworkFunction expand_in_mf_network;
@@ -626,6 +627,7 @@ struct bNodeLink *nodeAddLink(struct bNodeTree *ntree,
struct bNodeSocket *tosock);
void nodeRemLink(struct bNodeTree *ntree, struct bNodeLink *link);
void nodeRemSocketLinks(struct bNodeTree *ntree, struct bNodeSocket *sock);
+void nodeMuteLinkToggle(struct bNodeTree *ntree, struct bNodeLink *link);
bool nodeLinkIsHidden(const struct bNodeLink *link);
void nodeInternalRelink(struct bNodeTree *ntree, struct bNode *node);
@@ -829,10 +831,10 @@ void node_type_group_update(struct bNodeType *ntype,
struct bNode *node));
void node_type_exec(struct bNodeType *ntype,
- NodeInitExecFunction initexecfunc,
- NodeFreeExecFunction freeexecfunc,
- NodeExecFunction execfunc);
-void node_type_gpu(struct bNodeType *ntype, NodeGPUExecFunction gpufunc);
+ NodeInitExecFunction init_exec_fn,
+ NodeFreeExecFunction free_exec_fn,
+ NodeExecFunction exec_fn);
+void node_type_gpu(struct bNodeType *ntype, NodeGPUExecFunction gpu_fn);
void node_type_internal_links(struct bNodeType *ntype,
void (*update_internal_links)(struct bNodeTree *, struct bNode *));
@@ -1204,9 +1206,10 @@ void ntreeGPUMaterialNodes(struct bNodeTree *localtree,
#define CMP_NODE_PLANETRACKDEFORM 320
#define CMP_NODE_CORNERPIN 321
#define CMP_NODE_SWITCH_VIEW 322
-#define CMP_NODE_CRYPTOMATTE 323
+#define CMP_NODE_CRYPTOMATTE_LEGACY 323
#define CMP_NODE_DENOISE 324
#define CMP_NODE_EXPOSURE 325
+#define CMP_NODE_CRYPTOMATTE 326
/* channel toggles */
#define CMP_CHAN_RGB 1
@@ -1236,6 +1239,10 @@ void ntreeGPUMaterialNodes(struct bNodeTree *localtree,
#define CMP_TRACKPOS_RELATIVE_FRAME 2
#define CMP_TRACKPOS_ABSOLUTE_FRAME 3
+/* Cryptomatte source. */
+#define CMP_CRYPTOMATTE_SRC_RENDER 0
+#define CMP_CRYPTOMATTE_SRC_IMAGE 1
+
/* API */
void ntreeCompositExecTree(struct Scene *scene,
struct bNodeTree *ntree,
@@ -1278,10 +1285,15 @@ void ntreeCompositOutputFileUniqueLayer(struct ListBase *list,
void ntreeCompositColorBalanceSyncFromLGG(bNodeTree *ntree, bNode *node);
void ntreeCompositColorBalanceSyncFromCDL(bNodeTree *ntree, bNode *node);
-void ntreeCompositCryptomatteSyncFromAdd(bNodeTree *ntree, bNode *node);
-void ntreeCompositCryptomatteSyncFromRemove(bNodeTree *ntree, bNode *node);
-struct bNodeSocket *ntreeCompositCryptomatteAddSocket(struct bNodeTree *ntree, struct bNode *node);
-int ntreeCompositCryptomatteRemoveSocket(struct bNodeTree *ntree, struct bNode *node);
+void ntreeCompositCryptomatteSyncFromAdd(bNode *node);
+void ntreeCompositCryptomatteSyncFromRemove(bNode *node);
+bNodeSocket *ntreeCompositCryptomatteAddSocket(bNodeTree *ntree, bNode *node);
+int ntreeCompositCryptomatteRemoveSocket(bNodeTree *ntree, bNode *node);
+void ntreeCompositCryptomatteLayerPrefix(const bNode *node, char *r_prefix, size_t prefix_len);
+/* Update the runtime layer names with the cryptomatte layer names of the references
+ * render layer or image. */
+void ntreeCompositCryptomatteUpdateLayerNames(bNode *node);
+struct CryptomatteSession *ntreeCompositCryptomatteSession(bNode *node);
/** \} */
@@ -1371,7 +1383,17 @@ int ntreeTexExecTree(struct bNodeTree *ntree,
#define GEO_NODE_VOLUME_TO_MESH 1026
#define GEO_NODE_ATTRIBUTE_COMBINE_XYZ 1027
#define GEO_NODE_ATTRIBUTE_SEPARATE_XYZ 1028
-#define GEO_NODE_SUBDIVISION_SURFACE_SIMPLE 1029
+#define GEO_NODE_SUBDIVIDE 1029
+#define GEO_NODE_ATTRIBUTE_REMOVE 1030
+#define GEO_NODE_ATTRIBUTE_CONVERT 1031
+#define GEO_NODE_MESH_PRIMITIVE_CUBE 1032
+#define GEO_NODE_MESH_PRIMITIVE_CIRCLE 1033
+#define GEO_NODE_MESH_PRIMITIVE_UV_SPHERE 1034
+#define GEO_NODE_MESH_PRIMITIVE_CYLINDER 1035
+#define GEO_NODE_MESH_PRIMITIVE_ICO_SPHERE 1036
+#define GEO_NODE_MESH_PRIMITIVE_CONE 1037
+#define GEO_NODE_MESH_PRIMITIVE_LINE 1038
+#define GEO_NODE_MESH_PRIMITIVE_PLANE 1039
/** \} */
diff --git a/source/blender/blenkernel/BKE_node_ui_storage.hh b/source/blender/blenkernel/BKE_node_ui_storage.hh
index a49ff988272..be9510179c3 100644
--- a/source/blender/blenkernel/BKE_node_ui_storage.hh
+++ b/source/blender/blenkernel/BKE_node_ui_storage.hh
@@ -20,13 +20,17 @@
#include "BLI_hash.hh"
#include "BLI_map.hh"
+#include "BLI_multi_value_map.hh"
#include "BLI_session_uuid.h"
#include "BLI_set.hh"
#include "DNA_ID.h"
+#include "DNA_customdata_types.h"
#include "DNA_modifier_types.h"
#include "DNA_session_uuid_types.h"
+#include "BKE_attribute.h"
+
struct ModifierData;
struct Object;
struct bNode;
@@ -77,9 +81,26 @@ struct NodeWarning {
std::string message;
};
+struct AvailableAttributeInfo {
+ AttributeDomain domain;
+ CustomDataType data_type;
+
+ uint64_t hash() const
+ {
+ uint64_t domain_hash = (uint64_t)domain;
+ uint64_t data_type_hash = (uint64_t)data_type;
+ return (domain_hash * 33) ^ (data_type_hash * 89);
+ }
+
+ friend bool operator==(const AvailableAttributeInfo &a, const AvailableAttributeInfo &b)
+ {
+ return a.domain == b.domain && a.data_type == b.data_type;
+ }
+};
+
struct NodeUIStorage {
blender::Vector<NodeWarning> warnings;
- blender::Set<std::string> attribute_name_hints;
+ blender::MultiValueMap<std::string, AvailableAttributeInfo> attribute_hints;
};
struct NodeTreeUIStorage {
@@ -103,4 +124,6 @@ void BKE_nodetree_error_message_add(bNodeTree &ntree,
void BKE_nodetree_attribute_hint_add(bNodeTree &ntree,
const NodeTreeEvaluationContext &context,
const bNode &node,
- const blender::StringRef attribute_name);
+ const blender::StringRef attribute_name,
+ const AttributeDomain domain,
+ const CustomDataType data_type);
diff --git a/source/blender/blenkernel/BKE_volume_to_mesh.hh b/source/blender/blenkernel/BKE_volume_to_mesh.hh
index 1ec8a8e84cd..1f6e89636c4 100644
--- a/source/blender/blenkernel/BKE_volume_to_mesh.hh
+++ b/source/blender/blenkernel/BKE_volume_to_mesh.hh
@@ -21,7 +21,6 @@
#endif
struct Mesh;
-struct VolumeGrid;
namespace blender::bke {
diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt
index 1e7986eedd9..310ac6c4903 100644
--- a/source/blender/blenkernel/CMakeLists.txt
+++ b/source/blender/blenkernel/CMakeLists.txt
@@ -77,6 +77,7 @@ set(SRC
intern/appdir.c
intern/armature.c
intern/armature_deform.c
+ intern/armature_pose.cc
intern/armature_update.c
intern/asset.cc
intern/attribute.c
@@ -130,6 +131,10 @@ set(SRC
intern/fmodifier.c
intern/font.c
intern/freestyle.c
+ intern/geometry_component_instances.cc
+ intern/geometry_component_mesh.cc
+ intern/geometry_component_pointcloud.cc
+ intern/geometry_component_volume.cc
intern/geometry_set.cc
intern/geometry_set_instances.cc
intern/gpencil.c
@@ -429,6 +434,7 @@ set(SRC
nla_private.h
particle_private.h
tracking_private.h
+ intern/attribute_access_intern.hh
intern/CCGSubSurf.h
intern/CCGSubSurf_inline.h
intern/CCGSubSurf_intern.h
diff --git a/source/blender/blenkernel/intern/armature_pose.cc b/source/blender/blenkernel/intern/armature_pose.cc
new file mode 100644
index 00000000000..bb371b16c42
--- /dev/null
+++ b/source/blender/blenkernel/intern/armature_pose.cc
@@ -0,0 +1,133 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2015 Blender Foundation.
+ * All rights reserved.
+ *
+ * Defines and code for core node types
+ */
+
+/** \file
+ * \ingroup bke
+ */
+
+#include "BKE_animsys.h"
+#include "BKE_armature.h"
+
+#include "BLI_set.hh"
+
+#include "DNA_action_types.h"
+#include "DNA_anim_types.h"
+#include "DNA_armature_types.h"
+#include "DNA_object_types.h"
+
+#include "RNA_access.h"
+
+namespace {
+using BoneNameSet = blender::Set<std::string>;
+
+// Forward declarations.
+BoneNameSet pose_apply_find_selected_bones(const bPose *pose);
+void pose_apply_disable_fcurves_for_unselected_bones(bAction *action,
+ const BoneNameSet &selected_bone_names);
+void pose_apply_restore_fcurves(bAction *action);
+} // namespace
+
+void BKE_pose_apply_action(struct Object *ob,
+ struct bAction *action,
+ struct AnimationEvalContext *anim_eval_context)
+{
+ bPose *pose = ob->pose;
+ if (pose == nullptr) {
+ return;
+ }
+
+ const BoneNameSet selected_bone_names = pose_apply_find_selected_bones(pose);
+ const bool limit_to_selected_bones = !selected_bone_names.is_empty();
+
+ if (limit_to_selected_bones) {
+ /* Mute all FCurves that are not associated with selected bones. This separates the concept of
+ * bone selection from the FCurve evaluation code. */
+ pose_apply_disable_fcurves_for_unselected_bones(action, selected_bone_names);
+ }
+
+ /* Apply the Action. */
+ PointerRNA pose_owner_ptr;
+ RNA_id_pointer_create(&ob->id, &pose_owner_ptr);
+ animsys_evaluate_action(&pose_owner_ptr, action, anim_eval_context, false);
+
+ if (limit_to_selected_bones) {
+ pose_apply_restore_fcurves(action);
+ }
+}
+
+namespace {
+BoneNameSet pose_apply_find_selected_bones(const bPose *pose)
+{
+ BoneNameSet selected_bone_names;
+ bool all_bones_selected = true;
+ bool no_bones_selected = true;
+
+ LISTBASE_FOREACH (bPoseChannel *, pchan, &pose->chanbase) {
+ const bool is_selected = (pchan->bone->flag & BONE_SELECTED) != 0 &&
+ (pchan->bone->flag & BONE_HIDDEN_P) == 0;
+ all_bones_selected &= is_selected;
+ no_bones_selected &= !is_selected;
+
+ if (is_selected) {
+ /* Bone names are unique, so no need to check for duplicates. */
+ selected_bone_names.add_new(pchan->name);
+ }
+ }
+
+ /* If no bones are selected, act as if all are. */
+ if (all_bones_selected || no_bones_selected) {
+ return BoneNameSet(); /* An empty set means "ignore bone selection". */
+ }
+ return selected_bone_names;
+}
+
+void pose_apply_restore_fcurves(bAction *action)
+{
+ /* TODO(Sybren): Restore the FCurve flags, instead of just erasing the 'disabled' flag. */
+ LISTBASE_FOREACH (FCurve *, fcu, &action->curves) {
+ fcu->flag &= ~FCURVE_DISABLED;
+ }
+}
+
+void pose_apply_disable_fcurves_for_unselected_bones(bAction *action,
+ const BoneNameSet &selected_bone_names)
+{
+ LISTBASE_FOREACH (FCurve *, fcu, &action->curves) {
+ if (!fcu->rna_path || !strstr(fcu->rna_path, "pose.bones[")) {
+ continue;
+ }
+
+ /* Get bone name, and check if this bone is selected. */
+ char *bone_name = BLI_str_quoted_substrN(fcu->rna_path, "pose.bones[");
+ if (!bone_name) {
+ continue;
+ }
+ const bool is_selected = selected_bone_names.contains(bone_name);
+ MEM_freeN(bone_name);
+ if (is_selected) {
+ continue;
+ }
+
+ fcu->flag |= FCURVE_DISABLED;
+ }
+}
+
+} // namespace
diff --git a/source/blender/blenkernel/intern/attribute_access.cc b/source/blender/blenkernel/intern/attribute_access.cc
index aeb7fba47e8..91b5b01e3eb 100644
--- a/source/blender/blenkernel/intern/attribute_access.cc
+++ b/source/blender/blenkernel/intern/attribute_access.cc
@@ -36,6 +36,8 @@
#include "NOD_node_tree_multi_function.hh"
+#include "attribute_access_intern.hh"
+
static CLG_LogRef LOG = {"bke.attribute_access"};
using blender::float3;
@@ -46,9 +48,6 @@ using blender::bke::ReadAttributePtr;
using blender::bke::WriteAttributePtr;
using blender::fn::GMutableSpan;
-/* Can't include BKE_object_deform.h right now, due to an enum forward declaration. */
-extern "C" MDeformVert *BKE_object_defgroup_data_create(ID *id);
-
namespace blender::bke {
/* -------------------------------------------------------------------- */
@@ -158,101 +157,6 @@ void WriteAttribute::apply_span_if_necessary()
}
}
-class VertexWeightWriteAttribute final : public WriteAttribute {
- private:
- MDeformVert *dverts_;
- const int dvert_index_;
-
- public:
- VertexWeightWriteAttribute(MDeformVert *dverts, const int totvert, const int dvert_index)
- : WriteAttribute(ATTR_DOMAIN_POINT, CPPType::get<float>(), totvert),
- dverts_(dverts),
- dvert_index_(dvert_index)
- {
- }
-
- void get_internal(const int64_t index, void *r_value) const override
- {
- get_internal(dverts_, dvert_index_, index, r_value);
- }
-
- void set_internal(const int64_t index, const void *value) override
- {
- MDeformWeight *weight = BKE_defvert_ensure_index(&dverts_[index], dvert_index_);
- weight->weight = *reinterpret_cast<const float *>(value);
- }
-
- static void get_internal(const MDeformVert *dverts,
- const int dvert_index,
- const int64_t index,
- void *r_value)
- {
- if (dverts == nullptr) {
- *(float *)r_value = 0.0f;
- return;
- }
- const MDeformVert &dvert = dverts[index];
- for (const MDeformWeight &weight : Span(dvert.dw, dvert.totweight)) {
- if (weight.def_nr == dvert_index) {
- *(float *)r_value = weight.weight;
- return;
- }
- }
- *(float *)r_value = 0.0f;
- }
-};
-
-class VertexWeightReadAttribute final : public ReadAttribute {
- private:
- const MDeformVert *dverts_;
- const int dvert_index_;
-
- public:
- VertexWeightReadAttribute(const MDeformVert *dverts, const int totvert, const int dvert_index)
- : ReadAttribute(ATTR_DOMAIN_POINT, CPPType::get<float>(), totvert),
- dverts_(dverts),
- dvert_index_(dvert_index)
- {
- }
-
- void get_internal(const int64_t index, void *r_value) const override
- {
- VertexWeightWriteAttribute::get_internal(dverts_, dvert_index_, index, r_value);
- }
-};
-
-template<typename T> class ArrayWriteAttribute final : public WriteAttribute {
- private:
- MutableSpan<T> data_;
-
- public:
- ArrayWriteAttribute(AttributeDomain domain, MutableSpan<T> data)
- : WriteAttribute(domain, CPPType::get<T>(), data.size()), data_(data)
- {
- }
-
- void get_internal(const int64_t index, void *r_value) const override
- {
- new (r_value) T(data_[index]);
- }
-
- void set_internal(const int64_t index, const void *value) override
- {
- data_[index] = *reinterpret_cast<const T *>(value);
- }
-
- void initialize_span(const bool UNUSED(write_only)) override
- {
- array_buffer_ = data_.data();
- array_is_temporary_ = false;
- }
-
- void apply_span_if_necessary() override
- {
- /* Do nothing, because the span contains the attribute itself already. */
- }
-};
-
/* This is used by the #OutputAttributePtr class. */
class TemporaryWriteAttribute final : public WriteAttribute {
public:
@@ -301,135 +205,6 @@ class TemporaryWriteAttribute final : public WriteAttribute {
}
};
-template<typename T> class ArrayReadAttribute final : public ReadAttribute {
- private:
- Span<T> data_;
-
- public:
- ArrayReadAttribute(AttributeDomain domain, Span<T> data)
- : ReadAttribute(domain, CPPType::get<T>(), data.size()), data_(data)
- {
- }
-
- void get_internal(const int64_t index, void *r_value) const override
- {
- new (r_value) T(data_[index]);
- }
-
- void initialize_span() const override
- {
- /* The data will not be modified, so this const_cast is fine. */
- array_buffer_ = const_cast<T *>(data_.data());
- array_is_temporary_ = false;
- }
-};
-
-template<typename T> class OwnedArrayReadAttribute final : public ReadAttribute {
- private:
- Array<T> data_;
-
- public:
- OwnedArrayReadAttribute(AttributeDomain domain, Array<T> data)
- : ReadAttribute(domain, CPPType::get<T>(), data.size()), data_(std::move(data))
- {
- }
-
- void get_internal(const int64_t index, void *r_value) const override
- {
- new (r_value) T(data_[index]);
- }
-
- void initialize_span() const override
- {
- /* The data will not be modified, so this const_cast is fine. */
- array_buffer_ = const_cast<T *>(data_.data());
- array_is_temporary_ = false;
- }
-};
-
-template<typename StructT,
- typename ElemT,
- ElemT (*GetFunc)(const StructT &),
- void (*SetFunc)(StructT &, const ElemT &)>
-class DerivedArrayWriteAttribute final : public WriteAttribute {
- private:
- MutableSpan<StructT> data_;
-
- public:
- DerivedArrayWriteAttribute(AttributeDomain domain, MutableSpan<StructT> data)
- : WriteAttribute(domain, CPPType::get<ElemT>(), data.size()), data_(data)
- {
- }
-
- void get_internal(const int64_t index, void *r_value) const override
- {
- const StructT &struct_value = data_[index];
- const ElemT value = GetFunc(struct_value);
- new (r_value) ElemT(value);
- }
-
- void set_internal(const int64_t index, const void *value) override
- {
- StructT &struct_value = data_[index];
- const ElemT &typed_value = *reinterpret_cast<const ElemT *>(value);
- SetFunc(struct_value, typed_value);
- }
-};
-
-template<typename StructT, typename ElemT, ElemT (*GetFunc)(const StructT &)>
-class DerivedArrayReadAttribute final : public ReadAttribute {
- private:
- Span<StructT> data_;
-
- public:
- DerivedArrayReadAttribute(AttributeDomain domain, Span<StructT> data)
- : ReadAttribute(domain, CPPType::get<ElemT>(), data.size()), data_(data)
- {
- }
-
- void get_internal(const int64_t index, void *r_value) const override
- {
- const StructT &struct_value = data_[index];
- const ElemT value = GetFunc(struct_value);
- new (r_value) ElemT(value);
- }
-};
-
-class ConstantReadAttribute final : public ReadAttribute {
- private:
- void *value_;
-
- public:
- ConstantReadAttribute(AttributeDomain domain,
- const int64_t size,
- const CPPType &type,
- const void *value)
- : ReadAttribute(domain, type, size)
- {
- value_ = MEM_mallocN_aligned(type.size(), type.alignment(), __func__);
- type.copy_to_uninitialized(value, value_);
- }
-
- ~ConstantReadAttribute() override
- {
- this->cpp_type_.destruct(value_);
- MEM_freeN(value_);
- }
-
- void get_internal(const int64_t UNUSED(index), void *r_value) const override
- {
- this->cpp_type_.copy_to_uninitialized(value_, r_value);
- }
-
- void initialize_span() const override
- {
- const int element_size = cpp_type_.size();
- array_buffer_ = MEM_mallocN_aligned(size_ * element_size, cpp_type_.alignment(), __func__);
- array_is_temporary_ = true;
- cpp_type_.fill_uninitialized(value_, array_buffer_, size_);
- }
-};
-
class ConvertedReadAttribute final : public ReadAttribute {
private:
const CPPType &from_type_;
@@ -437,9 +212,6 @@ class ConvertedReadAttribute final : public ReadAttribute {
ReadAttributePtr base_attribute_;
const nodes::DataTypeConversions &conversions_;
- static constexpr int MaxValueSize = 64;
- static constexpr int MaxValueAlignment = 64;
-
public:
ConvertedReadAttribute(ReadAttributePtr base_attribute, const CPPType &to_type)
: ReadAttribute(base_attribute->domain(), to_type, base_attribute->size()),
@@ -448,17 +220,13 @@ class ConvertedReadAttribute final : public ReadAttribute {
base_attribute_(std::move(base_attribute)),
conversions_(nodes::get_implicit_type_conversions())
{
- if (from_type_.size() > MaxValueSize || from_type_.alignment() > MaxValueAlignment) {
- throw std::runtime_error(
- "type is larger than expected, the buffer size has to be increased");
- }
}
void get_internal(const int64_t index, void *r_value) const override
{
- AlignedBuffer<MaxValueSize, MaxValueAlignment> buffer;
- base_attribute_->get(index, buffer.ptr());
- conversions_.convert(from_type_, to_type_, buffer.ptr(), r_value);
+ BUFFER_FOR_CPP_TYPE_VALUE(from_type_, buffer);
+ base_attribute_->get(index, buffer);
+ conversions_.convert(from_type_, to_type_, buffer, r_value);
}
};
@@ -598,964 +366,318 @@ AttributeDomain attribute_domain_highest_priority(Span<AttributeDomain> domains)
return highest_priority_domain;
}
-/**
- * A #BuiltinAttributeProvider is responsible for exactly one attribute on a geometry component.
- * The attribute is identified by its name and has a fixed domain and type. Builtin attributes do
- * not follow the same loose rules as other attributes, because they are mapped to internal
- * "legacy" data structures. For example, some builtin attributes cannot be deleted. */
-class BuiltinAttributeProvider {
- public:
- /* Some utility enums to avoid hard to read booleans in function calls. */
- enum CreatableEnum {
- Creatable,
- NonCreatable,
- };
- enum WritableEnum {
- Writable,
- Readonly,
- };
- enum DeletableEnum {
- Deletable,
- NonDeletable,
- };
-
- protected:
- const std::string name_;
- const AttributeDomain domain_;
- const CustomDataType data_type_;
- const CreatableEnum createable_;
- const WritableEnum writable_;
- const DeletableEnum deletable_;
-
- public:
- BuiltinAttributeProvider(std::string name,
- const AttributeDomain domain,
- const CustomDataType data_type,
- const CreatableEnum createable,
- const WritableEnum writable,
- const DeletableEnum deletable)
- : name_(std::move(name)),
- domain_(domain),
- data_type_(data_type),
- createable_(createable),
- writable_(writable),
- deletable_(deletable)
- {
- }
-
- virtual ReadAttributePtr try_get_for_read(const GeometryComponent &component) const = 0;
- virtual WriteAttributePtr try_get_for_write(GeometryComponent &component) const = 0;
- virtual bool try_delete(GeometryComponent &component) const = 0;
- virtual bool try_create(GeometryComponent &UNUSED(component)) const = 0;
- virtual bool exists(const GeometryComponent &component) const = 0;
-
- StringRefNull name() const
- {
- return name_;
+ReadAttributePtr BuiltinCustomDataLayerProvider::try_get_for_read(
+ const GeometryComponent &component) const
+{
+ const CustomData *custom_data = custom_data_access_.get_const_custom_data(component);
+ if (custom_data == nullptr) {
+ return {};
}
- AttributeDomain domain() const
- {
- return domain_;
+ const int domain_size = component.attribute_domain_size(domain_);
+ const void *data = CustomData_get_layer(custom_data, stored_type_);
+ if (data == nullptr) {
+ return {};
}
+ return as_read_attribute_(data, domain_size);
+}
- CustomDataType data_type() const
- {
- return data_type_;
+WriteAttributePtr BuiltinCustomDataLayerProvider::try_get_for_write(
+ GeometryComponent &component) const
+{
+ if (writable_ != Writable) {
+ return {};
}
-};
-
-/**
- * A #DynamicAttributesProvider manages a set of named attributes on a geometry component. Each
- * attribute has a name, domain and type.
- */
-class DynamicAttributesProvider {
- public:
- virtual ReadAttributePtr try_get_for_read(const GeometryComponent &component,
- const StringRef attribute_name) const = 0;
- virtual WriteAttributePtr try_get_for_write(GeometryComponent &component,
- const StringRef attribute_name) const = 0;
- virtual bool try_delete(GeometryComponent &component, const StringRef attribute_name) const = 0;
- virtual bool try_create(GeometryComponent &UNUSED(component),
- const StringRef UNUSED(attribute_name),
- const AttributeDomain UNUSED(domain),
- const CustomDataType UNUSED(data_type)) const
- {
- /* Some providers should not create new attributes. */
- return false;
- };
-
- virtual bool foreach_attribute(const GeometryComponent &component,
- const AttributeForeachCallback callback) const = 0;
- virtual void supported_domains(Vector<AttributeDomain> &r_domains) const = 0;
-};
-
-/**
- * Utility to group together multiple functions that are used to access custom data on geometry
- * components in a generic way.
- */
-struct CustomDataAccessInfo {
- using CustomDataGetter = CustomData *(*)(GeometryComponent &component);
- using ConstCustomDataGetter = const CustomData *(*)(const GeometryComponent &component);
- using UpdateCustomDataPointers = void (*)(GeometryComponent &component);
-
- CustomDataGetter get_custom_data;
- ConstCustomDataGetter get_const_custom_data;
- UpdateCustomDataPointers update_custom_data_pointers;
-};
-
-/**
- * This provider is used to provide access to builtin attributes. It supports making internal types
- * available as different types. For example, the vertex position attribute is stored as part of
- * the #MVert struct, but is exposed as float3 attribute.
- */
-class BuiltinCustomDataLayerProvider final : public BuiltinAttributeProvider {
- using AsReadAttribute = ReadAttributePtr (*)(const void *data, const int domain_size);
- using AsWriteAttribute = WriteAttributePtr (*)(void *data, const int domain_size);
- using UpdateOnWrite = void (*)(GeometryComponent &component);
- const CustomDataType stored_type_;
- const CustomDataAccessInfo custom_data_access_;
- const AsReadAttribute as_read_attribute_;
- const AsWriteAttribute as_write_attribute_;
- const UpdateOnWrite update_on_write_;
-
- public:
- BuiltinCustomDataLayerProvider(std::string attribute_name,
- const AttributeDomain domain,
- const CustomDataType attribute_type,
- const CustomDataType stored_type,
- const CreatableEnum creatable,
- const WritableEnum writable,
- const DeletableEnum deletable,
- const CustomDataAccessInfo custom_data_access,
- const AsReadAttribute as_read_attribute,
- const AsWriteAttribute as_write_attribute,
- const UpdateOnWrite update_on_write)
- : BuiltinAttributeProvider(
- std::move(attribute_name), domain, attribute_type, creatable, writable, deletable),
- stored_type_(stored_type),
- custom_data_access_(custom_data_access),
- as_read_attribute_(as_read_attribute),
- as_write_attribute_(as_write_attribute),
- update_on_write_(update_on_write)
- {
+ CustomData *custom_data = custom_data_access_.get_custom_data(component);
+ if (custom_data == nullptr) {
+ return {};
}
-
- ReadAttributePtr try_get_for_read(const GeometryComponent &component) const final
- {
- const CustomData *custom_data = custom_data_access_.get_const_custom_data(component);
- if (custom_data == nullptr) {
- return {};
- }
- const int domain_size = component.attribute_domain_size(domain_);
- const void *data = CustomData_get_layer(custom_data, stored_type_);
- if (data == nullptr) {
- return {};
- }
- return as_read_attribute_(data, domain_size);
+ const int domain_size = component.attribute_domain_size(domain_);
+ void *data = CustomData_get_layer(custom_data, stored_type_);
+ if (data == nullptr) {
+ return {};
}
-
- WriteAttributePtr try_get_for_write(GeometryComponent &component) const final
- {
- if (writable_ != Writable) {
- return {};
- }
- CustomData *custom_data = custom_data_access_.get_custom_data(component);
- if (custom_data == nullptr) {
- return {};
- }
- const int domain_size = component.attribute_domain_size(domain_);
- void *data = CustomData_get_layer(custom_data, stored_type_);
- if (data == nullptr) {
- return {};
- }
- void *new_data = CustomData_duplicate_referenced_layer(custom_data, stored_type_, domain_size);
- if (data != new_data) {
- custom_data_access_.update_custom_data_pointers(component);
- data = new_data;
- }
- if (update_on_write_ != nullptr) {
- update_on_write_(component);
- }
- return as_write_attribute_(data, domain_size);
+ void *new_data = CustomData_duplicate_referenced_layer(custom_data, stored_type_, domain_size);
+ if (data != new_data) {
+ custom_data_access_.update_custom_data_pointers(component);
+ data = new_data;
}
-
- bool try_delete(GeometryComponent &component) const final
- {
- if (deletable_ != Deletable) {
- return false;
- }
- CustomData *custom_data = custom_data_access_.get_custom_data(component);
- if (custom_data == nullptr) {
- return {};
- }
-
- const int domain_size = component.attribute_domain_size(domain_);
- const int layer_index = CustomData_get_layer_index(custom_data, stored_type_);
- const bool delete_success = CustomData_free_layer(
- custom_data, stored_type_, domain_size, layer_index);
- if (delete_success) {
- custom_data_access_.update_custom_data_pointers(component);
- }
- return delete_success;
+ if (update_on_write_ != nullptr) {
+ update_on_write_(component);
}
+ return as_write_attribute_(data, domain_size);
+}
- bool try_create(GeometryComponent &component) const final
- {
- if (createable_ != Creatable) {
- return false;
- }
- CustomData *custom_data = custom_data_access_.get_custom_data(component);
- if (custom_data == nullptr) {
- return false;
- }
- if (CustomData_get_layer(custom_data, stored_type_) != nullptr) {
- /* Exists already. */
- return false;
- }
- const int domain_size = component.attribute_domain_size(domain_);
- const void *data = CustomData_add_layer(
- custom_data, stored_type_, CD_DEFAULT, nullptr, domain_size);
- const bool success = data != nullptr;
- if (success) {
- custom_data_access_.update_custom_data_pointers(component);
- }
- return success;
- }
-
- bool exists(const GeometryComponent &component) const final
- {
- const CustomData *custom_data = custom_data_access_.get_const_custom_data(component);
- if (custom_data == nullptr) {
- return false;
- }
- const void *data = CustomData_get_layer(custom_data, stored_type_);
- return data != nullptr;
- }
-};
-
-/**
- * This is the attribute provider for most user generated attributes.
- */
-class CustomDataAttributeProvider final : public DynamicAttributesProvider {
- private:
- static constexpr uint64_t supported_types_mask = CD_MASK_PROP_FLOAT | CD_MASK_PROP_FLOAT2 |
- CD_MASK_PROP_FLOAT3 | CD_MASK_PROP_INT32 |
- CD_MASK_PROP_COLOR | CD_MASK_PROP_BOOL;
- const AttributeDomain domain_;
- const CustomDataAccessInfo custom_data_access_;
-
- public:
- CustomDataAttributeProvider(const AttributeDomain domain,
- const CustomDataAccessInfo custom_data_access)
- : domain_(domain), custom_data_access_(custom_data_access)
- {
+bool BuiltinCustomDataLayerProvider::try_delete(GeometryComponent &component) const
+{
+ if (deletable_ != Deletable) {
+ return false;
}
-
- ReadAttributePtr try_get_for_read(const GeometryComponent &component,
- const StringRef attribute_name) const final
- {
- const CustomData *custom_data = custom_data_access_.get_const_custom_data(component);
- if (custom_data == nullptr) {
- return {};
- }
- const int domain_size = component.attribute_domain_size(domain_);
- for (const CustomDataLayer &layer : Span(custom_data->layers, custom_data->totlayer)) {
- if (layer.name != attribute_name) {
- continue;
- }
- const CustomDataType data_type = (CustomDataType)layer.type;
- switch (data_type) {
- case CD_PROP_FLOAT:
- return this->layer_to_read_attribute<float>(layer, domain_size);
- case CD_PROP_FLOAT2:
- return this->layer_to_read_attribute<float2>(layer, domain_size);
- case CD_PROP_FLOAT3:
- return this->layer_to_read_attribute<float3>(layer, domain_size);
- case CD_PROP_INT32:
- return this->layer_to_read_attribute<int>(layer, domain_size);
- case CD_PROP_COLOR:
- return this->layer_to_read_attribute<Color4f>(layer, domain_size);
- case CD_PROP_BOOL:
- return this->layer_to_read_attribute<bool>(layer, domain_size);
- default:
- break;
- }
- }
+ CustomData *custom_data = custom_data_access_.get_custom_data(component);
+ if (custom_data == nullptr) {
return {};
}
- WriteAttributePtr try_get_for_write(GeometryComponent &component,
- const StringRef attribute_name) const final
- {
- CustomData *custom_data = custom_data_access_.get_custom_data(component);
- if (custom_data == nullptr) {
- return {};
- }
- const int domain_size = component.attribute_domain_size(domain_);
- for (CustomDataLayer &layer : MutableSpan(custom_data->layers, custom_data->totlayer)) {
- if (layer.name != attribute_name) {
- continue;
- }
- CustomData_duplicate_referenced_layer_named(
- custom_data, layer.type, layer.name, domain_size);
- const CustomDataType data_type = (CustomDataType)layer.type;
- switch (data_type) {
- case CD_PROP_FLOAT:
- return this->layer_to_write_attribute<float>(layer, domain_size);
- case CD_PROP_FLOAT2:
- return this->layer_to_write_attribute<float2>(layer, domain_size);
- case CD_PROP_FLOAT3:
- return this->layer_to_write_attribute<float3>(layer, domain_size);
- case CD_PROP_INT32:
- return this->layer_to_write_attribute<int>(layer, domain_size);
- case CD_PROP_COLOR:
- return this->layer_to_write_attribute<Color4f>(layer, domain_size);
- case CD_PROP_BOOL:
- return this->layer_to_write_attribute<bool>(layer, domain_size);
- default:
- break;
- }
- }
- return {};
+ const int domain_size = component.attribute_domain_size(domain_);
+ const int layer_index = CustomData_get_layer_index(custom_data, stored_type_);
+ const bool delete_success = CustomData_free_layer(
+ custom_data, stored_type_, domain_size, layer_index);
+ if (delete_success) {
+ custom_data_access_.update_custom_data_pointers(component);
}
+ return delete_success;
+}
- bool try_delete(GeometryComponent &component, const StringRef attribute_name) const final
- {
- CustomData *custom_data = custom_data_access_.get_custom_data(component);
- if (custom_data == nullptr) {
- return false;
- }
- const int domain_size = component.attribute_domain_size(domain_);
- for (const int i : IndexRange(custom_data->totlayer)) {
- const CustomDataLayer &layer = custom_data->layers[i];
- if (this->type_is_supported((CustomDataType)layer.type) && layer.name == attribute_name) {
- CustomData_free_layer(custom_data, layer.type, domain_size, i);
- return true;
- }
- }
+bool BuiltinCustomDataLayerProvider::try_create(GeometryComponent &component) const
+{
+ if (createable_ != Creatable) {
return false;
}
-
- bool try_create(GeometryComponent &component,
- const StringRef attribute_name,
- const AttributeDomain domain,
- const CustomDataType data_type) const final
- {
- if (domain_ != domain) {
- return false;
- }
- if (!this->type_is_supported(data_type)) {
- return false;
- }
- CustomData *custom_data = custom_data_access_.get_custom_data(component);
- if (custom_data == nullptr) {
- return false;
- }
- for (const CustomDataLayer &layer : Span(custom_data->layers, custom_data->totlayer)) {
- if (layer.name == attribute_name) {
- return false;
- }
- }
- const int domain_size = component.attribute_domain_size(domain_);
- char attribute_name_c[MAX_NAME];
- attribute_name.copy(attribute_name_c);
- CustomData_add_layer_named(
- custom_data, data_type, CD_DEFAULT, nullptr, domain_size, attribute_name_c);
- return true;
+ CustomData *custom_data = custom_data_access_.get_custom_data(component);
+ if (custom_data == nullptr) {
+ return false;
}
-
- bool foreach_attribute(const GeometryComponent &component,
- const AttributeForeachCallback callback) const final
- {
- const CustomData *custom_data = custom_data_access_.get_const_custom_data(component);
- if (custom_data == nullptr) {
- return true;
- }
- for (const CustomDataLayer &layer : Span(custom_data->layers, custom_data->totlayer)) {
- const CustomDataType data_type = (CustomDataType)layer.type;
- if (this->type_is_supported(data_type)) {
- AttributeMetaData meta_data{domain_, data_type};
- if (!callback(layer.name, meta_data)) {
- return false;
- }
- }
- }
- return true;
+ if (CustomData_get_layer(custom_data, stored_type_) != nullptr) {
+ /* Exists already. */
+ return false;
}
-
- void supported_domains(Vector<AttributeDomain> &r_domains) const final
- {
- r_domains.append_non_duplicates(domain_);
+ const int domain_size = component.attribute_domain_size(domain_);
+ const void *data = CustomData_add_layer(
+ custom_data, stored_type_, CD_DEFAULT, nullptr, domain_size);
+ const bool success = data != nullptr;
+ if (success) {
+ custom_data_access_.update_custom_data_pointers(component);
}
+ return success;
+}
- private:
- template<typename T>
- ReadAttributePtr layer_to_read_attribute(const CustomDataLayer &layer,
- const int domain_size) const
- {
- return std::make_unique<ArrayReadAttribute<T>>(
- domain_, Span(static_cast<const T *>(layer.data), domain_size));
+bool BuiltinCustomDataLayerProvider::exists(const GeometryComponent &component) const
+{
+ const CustomData *custom_data = custom_data_access_.get_const_custom_data(component);
+ if (custom_data == nullptr) {
+ return false;
}
+ const void *data = CustomData_get_layer(custom_data, stored_type_);
+ return data != nullptr;
+}
- template<typename T>
- WriteAttributePtr layer_to_write_attribute(CustomDataLayer &layer, const int domain_size) const
- {
- return std::make_unique<ArrayWriteAttribute<T>>(
- domain_, MutableSpan(static_cast<T *>(layer.data), domain_size));
+ReadAttributePtr CustomDataAttributeProvider::try_get_for_read(
+ const GeometryComponent &component, const StringRef attribute_name) const
+{
+ const CustomData *custom_data = custom_data_access_.get_const_custom_data(component);
+ if (custom_data == nullptr) {
+ return {};
}
-
- bool type_is_supported(CustomDataType data_type) const
- {
- return ((1ULL << data_type) & supported_types_mask) != 0;
+ const int domain_size = component.attribute_domain_size(domain_);
+ for (const CustomDataLayer &layer : Span(custom_data->layers, custom_data->totlayer)) {
+ if (layer.name != attribute_name) {
+ continue;
+ }
+ const CustomDataType data_type = (CustomDataType)layer.type;
+ switch (data_type) {
+ case CD_PROP_FLOAT:
+ return this->layer_to_read_attribute<float>(layer, domain_size);
+ case CD_PROP_FLOAT2:
+ return this->layer_to_read_attribute<float2>(layer, domain_size);
+ case CD_PROP_FLOAT3:
+ return this->layer_to_read_attribute<float3>(layer, domain_size);
+ case CD_PROP_INT32:
+ return this->layer_to_read_attribute<int>(layer, domain_size);
+ case CD_PROP_COLOR:
+ return this->layer_to_read_attribute<Color4f>(layer, domain_size);
+ case CD_PROP_BOOL:
+ return this->layer_to_read_attribute<bool>(layer, domain_size);
+ default:
+ break;
+ }
}
-};
-
-static Mesh *get_mesh_from_component_for_write(GeometryComponent &component)
-{
- BLI_assert(component.type() == GeometryComponentType::Mesh);
- MeshComponent &mesh_component = static_cast<MeshComponent &>(component);
- return mesh_component.get_for_write();
+ return {};
}
-static const Mesh *get_mesh_from_component_for_read(const GeometryComponent &component)
+WriteAttributePtr CustomDataAttributeProvider::try_get_for_write(
+ GeometryComponent &component, const StringRef attribute_name) const
{
- BLI_assert(component.type() == GeometryComponentType::Mesh);
- const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component);
- return mesh_component.get_for_read();
-}
-
-/**
- * This attribute provider is used for uv maps and vertex colors.
- */
-class NamedLegacyCustomDataProvider final : public DynamicAttributesProvider {
- private:
- using AsReadAttribute = ReadAttributePtr (*)(const void *data, const int domain_size);
- using AsWriteAttribute = WriteAttributePtr (*)(void *data, const int domain_size);
- const AttributeDomain domain_;
- const CustomDataType attribute_type_;
- const CustomDataType stored_type_;
- const CustomDataAccessInfo custom_data_access_;
- const AsReadAttribute as_read_attribute_;
- const AsWriteAttribute as_write_attribute_;
-
- public:
- NamedLegacyCustomDataProvider(const AttributeDomain domain,
- const CustomDataType attribute_type,
- const CustomDataType stored_type,
- const CustomDataAccessInfo custom_data_access,
- const AsReadAttribute as_read_attribute,
- const AsWriteAttribute as_write_attribute)
- : domain_(domain),
- attribute_type_(attribute_type),
- stored_type_(stored_type),
- custom_data_access_(custom_data_access),
- as_read_attribute_(as_read_attribute),
- as_write_attribute_(as_write_attribute)
- {
- }
-
- ReadAttributePtr try_get_for_read(const GeometryComponent &component,
- const StringRef attribute_name) const final
- {
- const CustomData *custom_data = custom_data_access_.get_const_custom_data(component);
- if (custom_data == nullptr) {
- return {};
- }
- for (const CustomDataLayer &layer : Span(custom_data->layers, custom_data->totlayer)) {
- if (layer.type == stored_type_) {
- if (layer.name == attribute_name) {
- const int domain_size = component.attribute_domain_size(domain_);
- return as_read_attribute_(layer.data, domain_size);
- }
- }
- }
+ CustomData *custom_data = custom_data_access_.get_custom_data(component);
+ if (custom_data == nullptr) {
return {};
}
-
- WriteAttributePtr try_get_for_write(GeometryComponent &component,
- const StringRef attribute_name) const final
- {
- CustomData *custom_data = custom_data_access_.get_custom_data(component);
- if (custom_data == nullptr) {
- return {};
- }
- for (CustomDataLayer &layer : MutableSpan(custom_data->layers, custom_data->totlayer)) {
- if (layer.type == stored_type_) {
- if (layer.name == attribute_name) {
- const int domain_size = component.attribute_domain_size(domain_);
- void *data_old = layer.data;
- void *data_new = CustomData_duplicate_referenced_layer_named(
- custom_data, stored_type_, layer.name, domain_size);
- if (data_old != data_new) {
- custom_data_access_.update_custom_data_pointers(component);
- }
- return as_write_attribute_(layer.data, domain_size);
- }
- }
+ const int domain_size = component.attribute_domain_size(domain_);
+ for (CustomDataLayer &layer : MutableSpan(custom_data->layers, custom_data->totlayer)) {
+ if (layer.name != attribute_name) {
+ continue;
+ }
+ CustomData_duplicate_referenced_layer_named(custom_data, layer.type, layer.name, domain_size);
+ const CustomDataType data_type = (CustomDataType)layer.type;
+ switch (data_type) {
+ case CD_PROP_FLOAT:
+ return this->layer_to_write_attribute<float>(layer, domain_size);
+ case CD_PROP_FLOAT2:
+ return this->layer_to_write_attribute<float2>(layer, domain_size);
+ case CD_PROP_FLOAT3:
+ return this->layer_to_write_attribute<float3>(layer, domain_size);
+ case CD_PROP_INT32:
+ return this->layer_to_write_attribute<int>(layer, domain_size);
+ case CD_PROP_COLOR:
+ return this->layer_to_write_attribute<Color4f>(layer, domain_size);
+ case CD_PROP_BOOL:
+ return this->layer_to_write_attribute<bool>(layer, domain_size);
+ default:
+ break;
}
- return {};
}
+ return {};
+}
- bool try_delete(GeometryComponent &component, const StringRef attribute_name) const final
- {
- CustomData *custom_data = custom_data_access_.get_custom_data(component);
- if (custom_data == nullptr) {
- return false;
- }
- for (const int i : IndexRange(custom_data->totlayer)) {
- const CustomDataLayer &layer = custom_data->layers[i];
- if (layer.type == stored_type_) {
- if (layer.name == attribute_name) {
- const int domain_size = component.attribute_domain_size(domain_);
- CustomData_free_layer(custom_data, stored_type_, domain_size, i);
- custom_data_access_.update_custom_data_pointers(component);
- return true;
- }
- }
- }
+bool CustomDataAttributeProvider::try_delete(GeometryComponent &component,
+ const StringRef attribute_name) const
+{
+ CustomData *custom_data = custom_data_access_.get_custom_data(component);
+ if (custom_data == nullptr) {
return false;
}
-
- bool foreach_attribute(const GeometryComponent &component,
- const AttributeForeachCallback callback) const final
- {
- const CustomData *custom_data = custom_data_access_.get_const_custom_data(component);
- if (custom_data == nullptr) {
+ const int domain_size = component.attribute_domain_size(domain_);
+ for (const int i : IndexRange(custom_data->totlayer)) {
+ const CustomDataLayer &layer = custom_data->layers[i];
+ if (this->type_is_supported((CustomDataType)layer.type) && layer.name == attribute_name) {
+ CustomData_free_layer(custom_data, layer.type, domain_size, i);
return true;
}
- for (const CustomDataLayer &layer : Span(custom_data->layers, custom_data->totlayer)) {
- if (layer.type == stored_type_) {
- AttributeMetaData meta_data{domain_, attribute_type_};
- if (!callback(layer.name, meta_data)) {
- return false;
- }
- }
- }
- return true;
}
+ return false;
+}
- void supported_domains(Vector<AttributeDomain> &r_domains) const final
- {
- r_domains.append_non_duplicates(domain_);
+bool CustomDataAttributeProvider::try_create(GeometryComponent &component,
+ const StringRef attribute_name,
+ const AttributeDomain domain,
+ const CustomDataType data_type) const
+{
+ if (domain_ != domain) {
+ return false;
}
-};
-
-/**
- * This provider makes vertex groups available as float attributes.
- */
-class VertexGroupsAttributeProvider final : public DynamicAttributesProvider {
- public:
- ReadAttributePtr try_get_for_read(const GeometryComponent &component,
- const StringRef attribute_name) const final
- {
- BLI_assert(component.type() == GeometryComponentType::Mesh);
- const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component);
- const Mesh *mesh = mesh_component.get_for_read();
- const int vertex_group_index = mesh_component.vertex_group_names().lookup_default_as(
- attribute_name, -1);
- if (vertex_group_index < 0) {
- return {};
- }
- if (mesh == nullptr || mesh->dvert == nullptr) {
- static const float default_value = 0.0f;
- return std::make_unique<ConstantReadAttribute>(
- ATTR_DOMAIN_POINT, mesh->totvert, CPPType::get<float>(), &default_value);
- }
- return std::make_unique<VertexWeightReadAttribute>(
- mesh->dvert, mesh->totvert, vertex_group_index);
+ if (!this->type_is_supported(data_type)) {
+ return false;
}
-
- WriteAttributePtr try_get_for_write(GeometryComponent &component,
- const StringRef attribute_name) const final
- {
- BLI_assert(component.type() == GeometryComponentType::Mesh);
- MeshComponent &mesh_component = static_cast<MeshComponent &>(component);
- Mesh *mesh = mesh_component.get_for_write();
- if (mesh == nullptr) {
- return {};
- }
- const int vertex_group_index = mesh_component.vertex_group_names().lookup_default_as(
- attribute_name, -1);
- if (vertex_group_index < 0) {
- return {};
- }
- if (mesh->dvert == nullptr) {
- BKE_object_defgroup_data_create(&mesh->id);
- }
- else {
- /* Copy the data layer if it is shared with some other mesh. */
- mesh->dvert = (MDeformVert *)CustomData_duplicate_referenced_layer(
- &mesh->vdata, CD_MDEFORMVERT, mesh->totvert);
- }
- return std::make_unique<blender::bke::VertexWeightWriteAttribute>(
- mesh->dvert, mesh->totvert, vertex_group_index);
+ CustomData *custom_data = custom_data_access_.get_custom_data(component);
+ if (custom_data == nullptr) {
+ return false;
}
-
- bool try_delete(GeometryComponent &component, const StringRef attribute_name) const final
- {
- BLI_assert(component.type() == GeometryComponentType::Mesh);
- MeshComponent &mesh_component = static_cast<MeshComponent &>(component);
-
- const int vertex_group_index = mesh_component.vertex_group_names().pop_default_as(
- attribute_name, -1);
- if (vertex_group_index < 0) {
+ for (const CustomDataLayer &layer : Span(custom_data->layers, custom_data->totlayer)) {
+ if (layer.name == attribute_name) {
return false;
}
- Mesh *mesh = mesh_component.get_for_write();
- if (mesh == nullptr) {
- return true;
- }
- if (mesh->dvert == nullptr) {
- return true;
- }
- for (MDeformVert &dvert : MutableSpan(mesh->dvert, mesh->totvert)) {
- MDeformWeight *weight = BKE_defvert_find_index(&dvert, vertex_group_index);
- BKE_defvert_remove_group(&dvert, weight);
- }
- return true;
}
+ const int domain_size = component.attribute_domain_size(domain_);
+ char attribute_name_c[MAX_NAME];
+ attribute_name.copy(attribute_name_c);
+ CustomData_add_layer_named(
+ custom_data, data_type, CD_DEFAULT, nullptr, domain_size, attribute_name_c);
+ return true;
+}
- bool foreach_attribute(const GeometryComponent &component,
- const AttributeForeachCallback callback) const final
- {
- BLI_assert(component.type() == GeometryComponentType::Mesh);
- const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component);
- for (const auto item : mesh_component.vertex_group_names().items()) {
- const StringRefNull name = item.key;
- const int vertex_group_index = item.value;
- if (vertex_group_index >= 0) {
- AttributeMetaData meta_data{ATTR_DOMAIN_POINT, CD_PROP_FLOAT};
- if (!callback(name, meta_data)) {
- return false;
- }
- }
- }
+bool CustomDataAttributeProvider::foreach_attribute(const GeometryComponent &component,
+ const AttributeForeachCallback callback) const
+{
+ const CustomData *custom_data = custom_data_access_.get_const_custom_data(component);
+ if (custom_data == nullptr) {
return true;
}
-
- void supported_domains(Vector<AttributeDomain> &r_domains) const final
- {
- r_domains.append_non_duplicates(ATTR_DOMAIN_POINT);
- }
-};
-
-/**
- * This is a container for multiple attribute providers that are used by one geometry component
- * type (e.g. there is a set of attribute providers for mesh components).
- */
-class ComponentAttributeProviders {
- private:
- /**
- * Builtin attribute providers are identified by their name. Attribute names that are in this
- * map will only be accessed using builtin attribute providers. Therefore, these providers have
- * higher priority when an attribute name is looked up. Usually, that means that builtin
- * providers are checked before dynamic ones.
- */
- Map<std::string, const BuiltinAttributeProvider *> builtin_attribute_providers_;
- /**
- * An ordered list of dynamic attribute providers. The order is important because that is order
- * in which they are checked when an attribute is looked up.
- */
- Vector<const DynamicAttributesProvider *> dynamic_attribute_providers_;
- /**
- * All the domains that are supported by at least one of the providers above.
- */
- Vector<AttributeDomain> supported_domains_;
-
- public:
- ComponentAttributeProviders(Span<const BuiltinAttributeProvider *> builtin_attribute_providers,
- Span<const DynamicAttributesProvider *> dynamic_attribute_providers)
- : dynamic_attribute_providers_(dynamic_attribute_providers)
- {
- Set<AttributeDomain> domains;
- for (const BuiltinAttributeProvider *provider : builtin_attribute_providers) {
- /* Use #add_new to make sure that no two builtin attributes have the same name. */
- builtin_attribute_providers_.add_new(provider->name(), provider);
- supported_domains_.append_non_duplicates(provider->domain());
- }
- for (const DynamicAttributesProvider *provider : dynamic_attribute_providers) {
- provider->supported_domains(supported_domains_);
+ for (const CustomDataLayer &layer : Span(custom_data->layers, custom_data->totlayer)) {
+ const CustomDataType data_type = (CustomDataType)layer.type;
+ if (this->type_is_supported(data_type)) {
+ AttributeMetaData meta_data{domain_, data_type};
+ if (!callback(layer.name, meta_data)) {
+ return false;
+ }
}
}
-
- const Map<std::string, const BuiltinAttributeProvider *> &builtin_attribute_providers() const
- {
- return builtin_attribute_providers_;
- }
-
- Span<const DynamicAttributesProvider *> dynamic_attribute_providers() const
- {
- return dynamic_attribute_providers_;
- }
-
- Span<AttributeDomain> supported_domains() const
- {
- return supported_domains_;
- }
-};
-
-static float3 get_vertex_position(const MVert &vert)
-{
- return float3(vert.co);
-}
-
-static void set_vertex_position(MVert &vert, const float3 &position)
-{
- copy_v3_v3(vert.co, position);
-}
-
-static ReadAttributePtr make_vertex_position_read_attribute(const void *data,
- const int domain_size)
-{
- return std::make_unique<DerivedArrayReadAttribute<MVert, float3, get_vertex_position>>(
- ATTR_DOMAIN_POINT, Span<MVert>((const MVert *)data, domain_size));
+ return true;
}
-static WriteAttributePtr make_vertex_position_write_attribute(void *data, const int domain_size)
+ReadAttributePtr NamedLegacyCustomDataProvider::try_get_for_read(
+ const GeometryComponent &component, const StringRef attribute_name) const
{
- return std::make_unique<
- DerivedArrayWriteAttribute<MVert, float3, get_vertex_position, set_vertex_position>>(
- ATTR_DOMAIN_POINT, MutableSpan<MVert>((MVert *)data, domain_size));
-}
-
-static void tag_normals_dirty_when_writing_position(GeometryComponent &component)
-{
- Mesh *mesh = get_mesh_from_component_for_write(component);
- if (mesh != nullptr) {
- mesh->runtime.cd_dirty_vert |= CD_MASK_NORMAL;
+ const CustomData *custom_data = custom_data_access_.get_const_custom_data(component);
+ if (custom_data == nullptr) {
+ return {};
}
+ for (const CustomDataLayer &layer : Span(custom_data->layers, custom_data->totlayer)) {
+ if (layer.type == stored_type_) {
+ if (layer.name == attribute_name) {
+ const int domain_size = component.attribute_domain_size(domain_);
+ return as_read_attribute_(layer.data, domain_size);
+ }
+ }
+ }
+ return {};
}
-static int get_material_index(const MPoly &mpoly)
-{
- return static_cast<int>(mpoly.mat_nr);
-}
-
-static void set_material_index(MPoly &mpoly, const int &index)
-{
- mpoly.mat_nr = static_cast<short>(std::clamp(index, 0, SHRT_MAX));
-}
-
-static ReadAttributePtr make_material_index_read_attribute(const void *data, const int domain_size)
-{
- return std::make_unique<DerivedArrayReadAttribute<MPoly, int, get_material_index>>(
- ATTR_DOMAIN_POLYGON, Span<MPoly>((const MPoly *)data, domain_size));
-}
-
-static WriteAttributePtr make_material_index_write_attribute(void *data, const int domain_size)
-{
- return std::make_unique<
- DerivedArrayWriteAttribute<MPoly, int, get_material_index, set_material_index>>(
- ATTR_DOMAIN_POLYGON, MutableSpan<MPoly>((MPoly *)data, domain_size));
-}
-
-static float2 get_loop_uv(const MLoopUV &uv)
-{
- return float2(uv.uv);
-}
-
-static void set_loop_uv(MLoopUV &uv, const float2 &co)
-{
- copy_v2_v2(uv.uv, co);
-}
-
-static ReadAttributePtr make_uvs_read_attribute(const void *data, const int domain_size)
-{
- return std::make_unique<DerivedArrayReadAttribute<MLoopUV, float2, get_loop_uv>>(
- ATTR_DOMAIN_CORNER, Span((const MLoopUV *)data, domain_size));
-}
-
-static WriteAttributePtr make_uvs_write_attribute(void *data, const int domain_size)
-{
- return std::make_unique<DerivedArrayWriteAttribute<MLoopUV, float2, get_loop_uv, set_loop_uv>>(
- ATTR_DOMAIN_CORNER, MutableSpan((MLoopUV *)data, domain_size));
-}
-
-static Color4f get_loop_color(const MLoopCol &col)
-{
- Color4f value;
- rgba_uchar_to_float(value, &col.r);
- return value;
-}
-
-static void set_loop_color(MLoopCol &col, const Color4f &value)
-{
- rgba_float_to_uchar(&col.r, value);
-}
-
-static ReadAttributePtr make_vertex_color_read_attribute(const void *data, const int domain_size)
-{
- return std::make_unique<DerivedArrayReadAttribute<MLoopCol, Color4f, get_loop_color>>(
- ATTR_DOMAIN_CORNER, Span((const MLoopCol *)data, domain_size));
-}
-
-static WriteAttributePtr make_vertex_color_write_attribute(void *data, const int domain_size)
-{
- return std::make_unique<
- DerivedArrayWriteAttribute<MLoopCol, Color4f, get_loop_color, set_loop_color>>(
- ATTR_DOMAIN_CORNER, MutableSpan((MLoopCol *)data, domain_size));
-}
-
-template<typename T, AttributeDomain Domain>
-static ReadAttributePtr make_array_read_attribute(const void *data, const int domain_size)
+WriteAttributePtr NamedLegacyCustomDataProvider::try_get_for_write(
+ GeometryComponent &component, const StringRef attribute_name) const
{
- return std::make_unique<ArrayReadAttribute<T>>(Domain, Span<T>((const T *)data, domain_size));
+ CustomData *custom_data = custom_data_access_.get_custom_data(component);
+ if (custom_data == nullptr) {
+ return {};
+ }
+ for (CustomDataLayer &layer : MutableSpan(custom_data->layers, custom_data->totlayer)) {
+ if (layer.type == stored_type_) {
+ if (layer.name == attribute_name) {
+ const int domain_size = component.attribute_domain_size(domain_);
+ void *data_old = layer.data;
+ void *data_new = CustomData_duplicate_referenced_layer_named(
+ custom_data, stored_type_, layer.name, domain_size);
+ if (data_old != data_new) {
+ custom_data_access_.update_custom_data_pointers(component);
+ }
+ return as_write_attribute_(layer.data, domain_size);
+ }
+ }
+ }
+ return {};
}
-template<typename T, AttributeDomain Domain>
-static WriteAttributePtr make_array_write_attribute(void *data, const int domain_size)
+bool NamedLegacyCustomDataProvider::try_delete(GeometryComponent &component,
+ const StringRef attribute_name) const
{
- return std::make_unique<ArrayWriteAttribute<T>>(Domain, MutableSpan<T>((T *)data, domain_size));
+ CustomData *custom_data = custom_data_access_.get_custom_data(component);
+ if (custom_data == nullptr) {
+ return false;
+ }
+ for (const int i : IndexRange(custom_data->totlayer)) {
+ const CustomDataLayer &layer = custom_data->layers[i];
+ if (layer.type == stored_type_) {
+ if (layer.name == attribute_name) {
+ const int domain_size = component.attribute_domain_size(domain_);
+ CustomData_free_layer(custom_data, stored_type_, domain_size, i);
+ custom_data_access_.update_custom_data_pointers(component);
+ return true;
+ }
+ }
+ }
+ return false;
}
-/**
- * In this function all the attribute providers for a mesh component are created. Most data in this
- * function is statically allocated, because it does not change over time.
- */
-static ComponentAttributeProviders create_attribute_providers_for_mesh()
+bool NamedLegacyCustomDataProvider::foreach_attribute(
+ const GeometryComponent &component, const AttributeForeachCallback callback) const
{
- static auto update_custom_data_pointers = [](GeometryComponent &component) {
- Mesh *mesh = get_mesh_from_component_for_write(component);
- if (mesh != nullptr) {
- BKE_mesh_update_customdata_pointers(mesh, false);
+ const CustomData *custom_data = custom_data_access_.get_const_custom_data(component);
+ if (custom_data == nullptr) {
+ return true;
+ }
+ for (const CustomDataLayer &layer : Span(custom_data->layers, custom_data->totlayer)) {
+ if (layer.type == stored_type_) {
+ AttributeMetaData meta_data{domain_, attribute_type_};
+ if (!callback(layer.name, meta_data)) {
+ return false;
+ }
}
- };
-
-#define MAKE_MUTABLE_CUSTOM_DATA_GETTER(NAME) \
- [](GeometryComponent &component) -> CustomData * { \
- Mesh *mesh = get_mesh_from_component_for_write(component); \
- return mesh ? &mesh->NAME : nullptr; \
- }
-#define MAKE_CONST_CUSTOM_DATA_GETTER(NAME) \
- [](const GeometryComponent &component) -> const CustomData * { \
- const Mesh *mesh = get_mesh_from_component_for_read(component); \
- return mesh ? &mesh->NAME : nullptr; \
- }
-
- static CustomDataAccessInfo corner_access = {MAKE_MUTABLE_CUSTOM_DATA_GETTER(ldata),
- MAKE_CONST_CUSTOM_DATA_GETTER(ldata),
- update_custom_data_pointers};
- static CustomDataAccessInfo point_access = {MAKE_MUTABLE_CUSTOM_DATA_GETTER(vdata),
- MAKE_CONST_CUSTOM_DATA_GETTER(vdata),
- update_custom_data_pointers};
- static CustomDataAccessInfo edge_access = {MAKE_MUTABLE_CUSTOM_DATA_GETTER(edata),
- MAKE_CONST_CUSTOM_DATA_GETTER(edata),
- update_custom_data_pointers};
- static CustomDataAccessInfo polygon_access = {MAKE_MUTABLE_CUSTOM_DATA_GETTER(pdata),
- MAKE_CONST_CUSTOM_DATA_GETTER(pdata),
- update_custom_data_pointers};
-
-#undef MAKE_CONST_CUSTOM_DATA_GETTER
-#undef MAKE_MUTABLE_CUSTOM_DATA_GETTER
-
- static BuiltinCustomDataLayerProvider position("position",
- ATTR_DOMAIN_POINT,
- CD_PROP_FLOAT3,
- CD_MVERT,
- BuiltinAttributeProvider::NonCreatable,
- BuiltinAttributeProvider::Writable,
- BuiltinAttributeProvider::NonDeletable,
- point_access,
- make_vertex_position_read_attribute,
- make_vertex_position_write_attribute,
- tag_normals_dirty_when_writing_position);
-
- static BuiltinCustomDataLayerProvider material_index("material_index",
- ATTR_DOMAIN_POLYGON,
- CD_PROP_INT32,
- CD_MPOLY,
- BuiltinAttributeProvider::NonCreatable,
- BuiltinAttributeProvider::Writable,
- BuiltinAttributeProvider::NonDeletable,
- polygon_access,
- make_material_index_read_attribute,
- make_material_index_write_attribute,
- nullptr);
-
- static NamedLegacyCustomDataProvider uvs(ATTR_DOMAIN_CORNER,
- CD_PROP_FLOAT2,
- CD_MLOOPUV,
- corner_access,
- make_uvs_read_attribute,
- make_uvs_write_attribute);
-
- static NamedLegacyCustomDataProvider vertex_colors(ATTR_DOMAIN_CORNER,
- CD_PROP_COLOR,
- CD_MLOOPCOL,
- corner_access,
- make_vertex_color_read_attribute,
- make_vertex_color_write_attribute);
-
- static VertexGroupsAttributeProvider vertex_groups;
- static CustomDataAttributeProvider corner_custom_data(ATTR_DOMAIN_CORNER, corner_access);
- static CustomDataAttributeProvider point_custom_data(ATTR_DOMAIN_POINT, point_access);
- static CustomDataAttributeProvider edge_custom_data(ATTR_DOMAIN_EDGE, edge_access);
- static CustomDataAttributeProvider polygon_custom_data(ATTR_DOMAIN_POLYGON, polygon_access);
-
- return ComponentAttributeProviders({&position, &material_index},
- {&uvs,
- &vertex_colors,
- &corner_custom_data,
- &vertex_groups,
- &point_custom_data,
- &edge_custom_data,
- &polygon_custom_data});
+ }
+ return true;
}
-/**
- * In this function all the attribute providers for a point cloud component are created. Most data
- * in this function is statically allocated, because it does not change over time.
- */
-static ComponentAttributeProviders create_attribute_providers_for_point_cloud()
+void NamedLegacyCustomDataProvider::foreach_domain(
+ const FunctionRef<void(AttributeDomain)> callback) const
{
- static auto update_custom_data_pointers = [](GeometryComponent &component) {
- PointCloudComponent &pointcloud_component = static_cast<PointCloudComponent &>(component);
- PointCloud *pointcloud = pointcloud_component.get_for_write();
- if (pointcloud != nullptr) {
- BKE_pointcloud_update_customdata_pointers(pointcloud);
- }
- };
- static CustomDataAccessInfo point_access = {
- [](GeometryComponent &component) -> CustomData * {
- PointCloudComponent &pointcloud_component = static_cast<PointCloudComponent &>(component);
- PointCloud *pointcloud = pointcloud_component.get_for_write();
- return pointcloud ? &pointcloud->pdata : nullptr;
- },
- [](const GeometryComponent &component) -> const CustomData * {
- const PointCloudComponent &pointcloud_component = static_cast<const PointCloudComponent &>(
- component);
- const PointCloud *pointcloud = pointcloud_component.get_for_read();
- return pointcloud ? &pointcloud->pdata : nullptr;
- },
- update_custom_data_pointers};
-
- static BuiltinCustomDataLayerProvider position(
- "position",
- ATTR_DOMAIN_POINT,
- CD_PROP_FLOAT3,
- CD_PROP_FLOAT3,
- BuiltinAttributeProvider::NonCreatable,
- BuiltinAttributeProvider::Writable,
- BuiltinAttributeProvider::NonDeletable,
- point_access,
- make_array_read_attribute<float3, ATTR_DOMAIN_POINT>,
- make_array_write_attribute<float3, ATTR_DOMAIN_POINT>,
- nullptr);
- static BuiltinCustomDataLayerProvider radius(
- "radius",
- ATTR_DOMAIN_POINT,
- CD_PROP_FLOAT,
- CD_PROP_FLOAT,
- BuiltinAttributeProvider::Creatable,
- BuiltinAttributeProvider::Writable,
- BuiltinAttributeProvider::Deletable,
- point_access,
- make_array_read_attribute<float, ATTR_DOMAIN_POINT>,
- make_array_write_attribute<float, ATTR_DOMAIN_POINT>,
- nullptr);
- static CustomDataAttributeProvider point_custom_data(ATTR_DOMAIN_POINT, point_access);
- return ComponentAttributeProviders({&position, &radius}, {&point_custom_data});
+ callback(domain_);
}
} // namespace blender::bke
@@ -1999,173 +1121,3 @@ void OutputAttributePtr::apply_span_and_save()
}
/** \} */
-
-/* -------------------------------------------------------------------- */
-/** \name Point Cloud Component
- * \{ */
-
-const blender::bke::ComponentAttributeProviders *PointCloudComponent::get_attribute_providers()
- const
-{
- static blender::bke::ComponentAttributeProviders providers =
- blender::bke::create_attribute_providers_for_point_cloud();
- return &providers;
-}
-
-int PointCloudComponent::attribute_domain_size(const AttributeDomain domain) const
-{
- BLI_assert(domain == ATTR_DOMAIN_POINT);
- UNUSED_VARS_NDEBUG(domain);
- if (pointcloud_ == nullptr) {
- return 0;
- }
- return pointcloud_->totpoint;
-}
-
-/** \} */
-
-/* -------------------------------------------------------------------- */
-/** \name Mesh Component
- * \{ */
-
-const blender::bke::ComponentAttributeProviders *MeshComponent::get_attribute_providers() const
-{
- static blender::bke::ComponentAttributeProviders providers =
- blender::bke::create_attribute_providers_for_mesh();
- return &providers;
-}
-
-int MeshComponent::attribute_domain_size(const AttributeDomain domain) const
-{
- BLI_assert(this->attribute_domain_supported(domain));
- if (mesh_ == nullptr) {
- return 0;
- }
- switch (domain) {
- case ATTR_DOMAIN_CORNER:
- return mesh_->totloop;
- case ATTR_DOMAIN_POINT:
- return mesh_->totvert;
- case ATTR_DOMAIN_EDGE:
- return mesh_->totedge;
- case ATTR_DOMAIN_POLYGON:
- return mesh_->totpoly;
- default:
- BLI_assert(false);
- break;
- }
- return 0;
-}
-
-namespace blender::bke {
-
-template<typename T>
-static void adapt_mesh_domain_corner_to_point_impl(const Mesh &mesh,
- const TypedReadAttribute<T> &attribute,
- MutableSpan<T> r_values)
-{
- BLI_assert(r_values.size() == mesh.totvert);
- attribute_math::DefaultMixer<T> mixer(r_values);
-
- for (const int loop_index : IndexRange(mesh.totloop)) {
- const T value = attribute[loop_index];
- const MLoop &loop = mesh.mloop[loop_index];
- const int point_index = loop.v;
- mixer.mix_in(point_index, value);
- }
- mixer.finalize();
-}
-
-static ReadAttributePtr adapt_mesh_domain_corner_to_point(const Mesh &mesh,
- ReadAttributePtr attribute)
-{
- ReadAttributePtr new_attribute;
- const CustomDataType data_type = attribute->custom_data_type();
- attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
- using T = decltype(dummy);
- if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) {
- /* We compute all interpolated values at once, because for this interpolation, one has to
- * iterate over all loops anyway. */
- Array<T> values(mesh.totvert);
- adapt_mesh_domain_corner_to_point_impl<T>(mesh, *attribute, values);
- new_attribute = std::make_unique<OwnedArrayReadAttribute<T>>(ATTR_DOMAIN_POINT,
- std::move(values));
- }
- });
- return new_attribute;
-}
-
-template<typename T>
-static void adapt_mesh_domain_point_to_corner_impl(const Mesh &mesh,
- const TypedReadAttribute<T> &attribute,
- MutableSpan<T> r_values)
-{
- BLI_assert(r_values.size() == mesh.totloop);
-
- for (const int loop_index : IndexRange(mesh.totloop)) {
- const int vertex_index = mesh.mloop[loop_index].v;
- r_values[loop_index] = attribute[vertex_index];
- }
-}
-
-static ReadAttributePtr adapt_mesh_domain_point_to_corner(const Mesh &mesh,
- ReadAttributePtr attribute)
-{
- ReadAttributePtr new_attribute;
- const CustomDataType data_type = attribute->custom_data_type();
- attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
- using T = decltype(dummy);
- /* It is not strictly necessary to compute the value for all corners here. Instead one could
- * lazily lookup the mesh topology when a specific index accessed. This can be more efficient
- * when an algorithm only accesses very few of the corner values. However, for the algorithms
- * we currently have, precomputing the array is fine. Also, it is easier to implement. */
- Array<T> values(mesh.totloop);
- adapt_mesh_domain_point_to_corner_impl<T>(mesh, *attribute, values);
- new_attribute = std::make_unique<OwnedArrayReadAttribute<T>>(ATTR_DOMAIN_CORNER,
- std::move(values));
- });
- return new_attribute;
-}
-
-} // namespace blender::bke
-
-ReadAttributePtr MeshComponent::attribute_try_adapt_domain(ReadAttributePtr attribute,
- const AttributeDomain new_domain) const
-{
- if (!attribute) {
- return {};
- }
- if (attribute->size() == 0) {
- return {};
- }
- const AttributeDomain old_domain = attribute->domain();
- if (old_domain == new_domain) {
- return attribute;
- }
-
- switch (old_domain) {
- case ATTR_DOMAIN_CORNER: {
- switch (new_domain) {
- case ATTR_DOMAIN_POINT:
- return blender::bke::adapt_mesh_domain_corner_to_point(*mesh_, std::move(attribute));
- default:
- break;
- }
- break;
- }
- case ATTR_DOMAIN_POINT: {
- switch (new_domain) {
- case ATTR_DOMAIN_CORNER:
- return blender::bke::adapt_mesh_domain_point_to_corner(*mesh_, std::move(attribute));
- default:
- break;
- }
- }
- default:
- break;
- }
-
- return {};
-}
-
-/** \} */
diff --git a/source/blender/blenkernel/intern/attribute_access_intern.hh b/source/blender/blenkernel/intern/attribute_access_intern.hh
new file mode 100644
index 00000000000..806d10e9e89
--- /dev/null
+++ b/source/blender/blenkernel/intern/attribute_access_intern.hh
@@ -0,0 +1,494 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "BLI_map.hh"
+#include "BLI_span.hh"
+#include "BLI_string_ref.hh"
+#include "BLI_vector.hh"
+#include "BLI_vector_set.hh"
+
+#include "BKE_geometry_set.hh"
+
+namespace blender::bke {
+
+class ConstantReadAttribute final : public ReadAttribute {
+ private:
+ void *value_;
+
+ public:
+ ConstantReadAttribute(AttributeDomain domain,
+ const int64_t size,
+ const CPPType &type,
+ const void *value)
+ : ReadAttribute(domain, type, size)
+ {
+ value_ = MEM_mallocN_aligned(type.size(), type.alignment(), __func__);
+ type.copy_to_uninitialized(value, value_);
+ }
+
+ ~ConstantReadAttribute() override
+ {
+ this->cpp_type_.destruct(value_);
+ MEM_freeN(value_);
+ }
+
+ void get_internal(const int64_t UNUSED(index), void *r_value) const override
+ {
+ this->cpp_type_.copy_to_uninitialized(value_, r_value);
+ }
+
+ void initialize_span() const override
+ {
+ const int element_size = cpp_type_.size();
+ array_buffer_ = MEM_mallocN_aligned(size_ * element_size, cpp_type_.alignment(), __func__);
+ array_is_temporary_ = true;
+ cpp_type_.fill_uninitialized(value_, array_buffer_, size_);
+ }
+};
+
+template<typename T> class ArrayReadAttribute final : public ReadAttribute {
+ private:
+ Span<T> data_;
+
+ public:
+ ArrayReadAttribute(AttributeDomain domain, Span<T> data)
+ : ReadAttribute(domain, CPPType::get<T>(), data.size()), data_(data)
+ {
+ }
+
+ void get_internal(const int64_t index, void *r_value) const override
+ {
+ new (r_value) T(data_[index]);
+ }
+
+ void initialize_span() const override
+ {
+ /* The data will not be modified, so this const_cast is fine. */
+ array_buffer_ = const_cast<T *>(data_.data());
+ array_is_temporary_ = false;
+ }
+};
+
+template<typename T> class OwnedArrayReadAttribute final : public ReadAttribute {
+ private:
+ Array<T> data_;
+
+ public:
+ OwnedArrayReadAttribute(AttributeDomain domain, Array<T> data)
+ : ReadAttribute(domain, CPPType::get<T>(), data.size()), data_(std::move(data))
+ {
+ }
+
+ void get_internal(const int64_t index, void *r_value) const override
+ {
+ new (r_value) T(data_[index]);
+ }
+
+ void initialize_span() const override
+ {
+ /* The data will not be modified, so this const_cast is fine. */
+ array_buffer_ = const_cast<T *>(data_.data());
+ array_is_temporary_ = false;
+ }
+};
+
+template<typename StructT, typename ElemT, ElemT (*GetFunc)(const StructT &)>
+class DerivedArrayReadAttribute final : public ReadAttribute {
+ private:
+ Span<StructT> data_;
+
+ public:
+ DerivedArrayReadAttribute(AttributeDomain domain, Span<StructT> data)
+ : ReadAttribute(domain, CPPType::get<ElemT>(), data.size()), data_(data)
+ {
+ }
+
+ void get_internal(const int64_t index, void *r_value) const override
+ {
+ const StructT &struct_value = data_[index];
+ const ElemT value = GetFunc(struct_value);
+ new (r_value) ElemT(value);
+ }
+};
+
+template<typename T> class ArrayWriteAttribute final : public WriteAttribute {
+ private:
+ MutableSpan<T> data_;
+
+ public:
+ ArrayWriteAttribute(AttributeDomain domain, MutableSpan<T> data)
+ : WriteAttribute(domain, CPPType::get<T>(), data.size()), data_(data)
+ {
+ }
+
+ void get_internal(const int64_t index, void *r_value) const override
+ {
+ new (r_value) T(data_[index]);
+ }
+
+ void set_internal(const int64_t index, const void *value) override
+ {
+ data_[index] = *reinterpret_cast<const T *>(value);
+ }
+
+ void initialize_span(const bool UNUSED(write_only)) override
+ {
+ array_buffer_ = data_.data();
+ array_is_temporary_ = false;
+ }
+
+ void apply_span_if_necessary() override
+ {
+ /* Do nothing, because the span contains the attribute itself already. */
+ }
+};
+
+template<typename StructT,
+ typename ElemT,
+ ElemT (*GetFunc)(const StructT &),
+ void (*SetFunc)(StructT &, const ElemT &)>
+class DerivedArrayWriteAttribute final : public WriteAttribute {
+ private:
+ MutableSpan<StructT> data_;
+
+ public:
+ DerivedArrayWriteAttribute(AttributeDomain domain, MutableSpan<StructT> data)
+ : WriteAttribute(domain, CPPType::get<ElemT>(), data.size()), data_(data)
+ {
+ }
+
+ void get_internal(const int64_t index, void *r_value) const override
+ {
+ const StructT &struct_value = data_[index];
+ const ElemT value = GetFunc(struct_value);
+ new (r_value) ElemT(value);
+ }
+
+ void set_internal(const int64_t index, const void *value) override
+ {
+ StructT &struct_value = data_[index];
+ const ElemT &typed_value = *reinterpret_cast<const ElemT *>(value);
+ SetFunc(struct_value, typed_value);
+ }
+};
+
+/**
+ * Utility to group together multiple functions that are used to access custom data on geometry
+ * components in a generic way.
+ */
+struct CustomDataAccessInfo {
+ using CustomDataGetter = CustomData *(*)(GeometryComponent &component);
+ using ConstCustomDataGetter = const CustomData *(*)(const GeometryComponent &component);
+ using UpdateCustomDataPointers = void (*)(GeometryComponent &component);
+
+ CustomDataGetter get_custom_data;
+ ConstCustomDataGetter get_const_custom_data;
+ UpdateCustomDataPointers update_custom_data_pointers;
+};
+
+/**
+ * A #BuiltinAttributeProvider is responsible for exactly one attribute on a geometry component.
+ * The attribute is identified by its name and has a fixed domain and type. Builtin attributes do
+ * not follow the same loose rules as other attributes, because they are mapped to internal
+ * "legacy" data structures. For example, some builtin attributes cannot be deleted. */
+class BuiltinAttributeProvider {
+ public:
+ /* Some utility enums to avoid hard to read booleans in function calls. */
+ enum CreatableEnum {
+ Creatable,
+ NonCreatable,
+ };
+ enum WritableEnum {
+ Writable,
+ Readonly,
+ };
+ enum DeletableEnum {
+ Deletable,
+ NonDeletable,
+ };
+
+ protected:
+ const std::string name_;
+ const AttributeDomain domain_;
+ const CustomDataType data_type_;
+ const CreatableEnum createable_;
+ const WritableEnum writable_;
+ const DeletableEnum deletable_;
+
+ public:
+ BuiltinAttributeProvider(std::string name,
+ const AttributeDomain domain,
+ const CustomDataType data_type,
+ const CreatableEnum createable,
+ const WritableEnum writable,
+ const DeletableEnum deletable)
+ : name_(std::move(name)),
+ domain_(domain),
+ data_type_(data_type),
+ createable_(createable),
+ writable_(writable),
+ deletable_(deletable)
+ {
+ }
+
+ virtual ReadAttributePtr try_get_for_read(const GeometryComponent &component) const = 0;
+ virtual WriteAttributePtr try_get_for_write(GeometryComponent &component) const = 0;
+ virtual bool try_delete(GeometryComponent &component) const = 0;
+ virtual bool try_create(GeometryComponent &UNUSED(component)) const = 0;
+ virtual bool exists(const GeometryComponent &component) const = 0;
+
+ StringRefNull name() const
+ {
+ return name_;
+ }
+
+ AttributeDomain domain() const
+ {
+ return domain_;
+ }
+
+ CustomDataType data_type() const
+ {
+ return data_type_;
+ }
+};
+
+/**
+ * A #DynamicAttributesProvider manages a set of named attributes on a geometry component. Each
+ * attribute has a name, domain and type.
+ */
+class DynamicAttributesProvider {
+ public:
+ virtual ReadAttributePtr try_get_for_read(const GeometryComponent &component,
+ const StringRef attribute_name) const = 0;
+ virtual WriteAttributePtr try_get_for_write(GeometryComponent &component,
+ const StringRef attribute_name) const = 0;
+ virtual bool try_delete(GeometryComponent &component, const StringRef attribute_name) const = 0;
+ virtual bool try_create(GeometryComponent &UNUSED(component),
+ const StringRef UNUSED(attribute_name),
+ const AttributeDomain UNUSED(domain),
+ const CustomDataType UNUSED(data_type)) const
+ {
+ /* Some providers should not create new attributes. */
+ return false;
+ };
+
+ virtual bool foreach_attribute(const GeometryComponent &component,
+ const AttributeForeachCallback callback) const = 0;
+ virtual void foreach_domain(const FunctionRef<void(AttributeDomain)> callback) const = 0;
+};
+
+/**
+ * This is the attribute provider for most user generated attributes.
+ */
+class CustomDataAttributeProvider final : public DynamicAttributesProvider {
+ private:
+ static constexpr uint64_t supported_types_mask = CD_MASK_PROP_FLOAT | CD_MASK_PROP_FLOAT2 |
+ CD_MASK_PROP_FLOAT3 | CD_MASK_PROP_INT32 |
+ CD_MASK_PROP_COLOR | CD_MASK_PROP_BOOL;
+ const AttributeDomain domain_;
+ const CustomDataAccessInfo custom_data_access_;
+
+ public:
+ CustomDataAttributeProvider(const AttributeDomain domain,
+ const CustomDataAccessInfo custom_data_access)
+ : domain_(domain), custom_data_access_(custom_data_access)
+ {
+ }
+
+ ReadAttributePtr try_get_for_read(const GeometryComponent &component,
+ const StringRef attribute_name) const final;
+
+ WriteAttributePtr try_get_for_write(GeometryComponent &component,
+ const StringRef attribute_name) const final;
+
+ bool try_delete(GeometryComponent &component, const StringRef attribute_name) const final;
+
+ bool try_create(GeometryComponent &component,
+ const StringRef attribute_name,
+ const AttributeDomain domain,
+ const CustomDataType data_type) const final;
+
+ bool foreach_attribute(const GeometryComponent &component,
+ const AttributeForeachCallback callback) const final;
+
+ void foreach_domain(const FunctionRef<void(AttributeDomain)> callback) const final
+ {
+ callback(domain_);
+ }
+
+ private:
+ template<typename T>
+ ReadAttributePtr layer_to_read_attribute(const CustomDataLayer &layer,
+ const int domain_size) const
+ {
+ return std::make_unique<ArrayReadAttribute<T>>(
+ domain_, Span(static_cast<const T *>(layer.data), domain_size));
+ }
+
+ template<typename T>
+ WriteAttributePtr layer_to_write_attribute(CustomDataLayer &layer, const int domain_size) const
+ {
+ return std::make_unique<ArrayWriteAttribute<T>>(
+ domain_, MutableSpan(static_cast<T *>(layer.data), domain_size));
+ }
+
+ bool type_is_supported(CustomDataType data_type) const
+ {
+ return ((1ULL << data_type) & supported_types_mask) != 0;
+ }
+};
+
+/**
+ * This attribute provider is used for uv maps and vertex colors.
+ */
+class NamedLegacyCustomDataProvider final : public DynamicAttributesProvider {
+ private:
+ using AsReadAttribute = ReadAttributePtr (*)(const void *data, const int domain_size);
+ using AsWriteAttribute = WriteAttributePtr (*)(void *data, const int domain_size);
+ const AttributeDomain domain_;
+ const CustomDataType attribute_type_;
+ const CustomDataType stored_type_;
+ const CustomDataAccessInfo custom_data_access_;
+ const AsReadAttribute as_read_attribute_;
+ const AsWriteAttribute as_write_attribute_;
+
+ public:
+ NamedLegacyCustomDataProvider(const AttributeDomain domain,
+ const CustomDataType attribute_type,
+ const CustomDataType stored_type,
+ const CustomDataAccessInfo custom_data_access,
+ const AsReadAttribute as_read_attribute,
+ const AsWriteAttribute as_write_attribute)
+ : domain_(domain),
+ attribute_type_(attribute_type),
+ stored_type_(stored_type),
+ custom_data_access_(custom_data_access),
+ as_read_attribute_(as_read_attribute),
+ as_write_attribute_(as_write_attribute)
+ {
+ }
+
+ ReadAttributePtr try_get_for_read(const GeometryComponent &component,
+ const StringRef attribute_name) const final;
+ WriteAttributePtr try_get_for_write(GeometryComponent &component,
+ const StringRef attribute_name) const final;
+ bool try_delete(GeometryComponent &component, const StringRef attribute_name) const final;
+ bool foreach_attribute(const GeometryComponent &component,
+ const AttributeForeachCallback callback) const final;
+ void foreach_domain(const FunctionRef<void(AttributeDomain)> callback) const final;
+};
+
+/**
+ * This provider is used to provide access to builtin attributes. It supports making internal types
+ * available as different types. For example, the vertex position attribute is stored as part of
+ * the #MVert struct, but is exposed as float3 attribute.
+ */
+class BuiltinCustomDataLayerProvider final : public BuiltinAttributeProvider {
+ using AsReadAttribute = ReadAttributePtr (*)(const void *data, const int domain_size);
+ using AsWriteAttribute = WriteAttributePtr (*)(void *data, const int domain_size);
+ using UpdateOnRead = void (*)(const GeometryComponent &component);
+ using UpdateOnWrite = void (*)(GeometryComponent &component);
+ const CustomDataType stored_type_;
+ const CustomDataAccessInfo custom_data_access_;
+ const AsReadAttribute as_read_attribute_;
+ const AsWriteAttribute as_write_attribute_;
+ const UpdateOnWrite update_on_write_;
+
+ public:
+ BuiltinCustomDataLayerProvider(std::string attribute_name,
+ const AttributeDomain domain,
+ const CustomDataType attribute_type,
+ const CustomDataType stored_type,
+ const CreatableEnum creatable,
+ const WritableEnum writable,
+ const DeletableEnum deletable,
+ const CustomDataAccessInfo custom_data_access,
+ const AsReadAttribute as_read_attribute,
+ const AsWriteAttribute as_write_attribute,
+ const UpdateOnWrite update_on_write)
+ : BuiltinAttributeProvider(
+ std::move(attribute_name), domain, attribute_type, creatable, writable, deletable),
+ stored_type_(stored_type),
+ custom_data_access_(custom_data_access),
+ as_read_attribute_(as_read_attribute),
+ as_write_attribute_(as_write_attribute),
+ update_on_write_(update_on_write)
+ {
+ }
+
+ ReadAttributePtr try_get_for_read(const GeometryComponent &component) const final;
+ WriteAttributePtr try_get_for_write(GeometryComponent &component) const final;
+ bool try_delete(GeometryComponent &component) const final;
+ bool try_create(GeometryComponent &component) const final;
+ bool exists(const GeometryComponent &component) const final;
+};
+
+/**
+ * This is a container for multiple attribute providers that are used by one geometry component
+ * type (e.g. there is a set of attribute providers for mesh components).
+ */
+class ComponentAttributeProviders {
+ private:
+ /**
+ * Builtin attribute providers are identified by their name. Attribute names that are in this
+ * map will only be accessed using builtin attribute providers. Therefore, these providers have
+ * higher priority when an attribute name is looked up. Usually, that means that builtin
+ * providers are checked before dynamic ones.
+ */
+ Map<std::string, const BuiltinAttributeProvider *> builtin_attribute_providers_;
+ /**
+ * An ordered list of dynamic attribute providers. The order is important because that is order
+ * in which they are checked when an attribute is looked up.
+ */
+ Vector<const DynamicAttributesProvider *> dynamic_attribute_providers_;
+ /**
+ * All the domains that are supported by at least one of the providers above.
+ */
+ VectorSet<AttributeDomain> supported_domains_;
+
+ public:
+ ComponentAttributeProviders(Span<const BuiltinAttributeProvider *> builtin_attribute_providers,
+ Span<const DynamicAttributesProvider *> dynamic_attribute_providers)
+ : dynamic_attribute_providers_(dynamic_attribute_providers)
+ {
+ for (const BuiltinAttributeProvider *provider : builtin_attribute_providers) {
+ /* Use #add_new to make sure that no two builtin attributes have the same name. */
+ builtin_attribute_providers_.add_new(provider->name(), provider);
+ supported_domains_.add(provider->domain());
+ }
+ for (const DynamicAttributesProvider *provider : dynamic_attribute_providers) {
+ provider->foreach_domain([&](AttributeDomain domain) { supported_domains_.add(domain); });
+ }
+ }
+
+ const Map<std::string, const BuiltinAttributeProvider *> &builtin_attribute_providers() const
+ {
+ return builtin_attribute_providers_;
+ }
+
+ Span<const DynamicAttributesProvider *> dynamic_attribute_providers() const
+ {
+ return dynamic_attribute_providers_;
+ }
+
+ Span<AttributeDomain> supported_domains() const
+ {
+ return supported_domains_;
+ }
+};
+
+} // namespace blender::bke
diff --git a/source/blender/blenkernel/intern/blender_copybuffer.c b/source/blender/blenkernel/intern/blender_copybuffer.c
index 9f5e038fb82..ec8962d5f6d 100644
--- a/source/blender/blenkernel/intern/blender_copybuffer.c
+++ b/source/blender/blenkernel/intern/blender_copybuffer.c
@@ -94,8 +94,9 @@ bool BKE_copybuffer_read(Main *bmain_dst,
}
/* Here appending/linking starts. */
const int flag = 0;
+ const int id_tag_extra = 0;
struct LibraryLink_Params liblink_params;
- BLO_library_link_params_init(&liblink_params, bmain_dst, flag);
+ BLO_library_link_params_init(&liblink_params, bmain_dst, flag, id_tag_extra);
Main *mainl = BLO_library_link_begin(&bh, libname, &liblink_params);
BLO_library_link_copypaste(mainl, bh, id_types_mask);
BLO_library_link_end(mainl, &bh, &liblink_params);
@@ -130,6 +131,7 @@ int BKE_copybuffer_paste(bContext *C,
Main *mainl = NULL;
Library *lib;
BlendHandle *bh;
+ const int id_tag_extra = 0;
bh = BLO_blendhandle_from_file(libname, reports);
@@ -148,7 +150,8 @@ int BKE_copybuffer_paste(bContext *C,
/* here appending/linking starts */
struct LibraryLink_Params liblink_params;
- BLO_library_link_params_init_with_context(&liblink_params, bmain, flag, scene, view_layer, v3d);
+ BLO_library_link_params_init_with_context(
+ &liblink_params, bmain, flag, id_tag_extra, scene, view_layer, v3d);
mainl = BLO_library_link_begin(&bh, libname, &liblink_params);
const int num_pasted = BLO_library_link_copypaste(mainl, bh, id_types_mask);
diff --git a/source/blender/blenkernel/intern/blender_undo.c b/source/blender/blenkernel/intern/blender_undo.c
index d826aaf24e3..293b2c9c4c6 100644
--- a/source/blender/blenkernel/intern/blender_undo.c
+++ b/source/blender/blenkernel/intern/blender_undo.c
@@ -77,7 +77,12 @@ bool BKE_memfile_undo_decode(MemFileUndoData *mfu,
G.fileflags |= G_FILE_NO_UI;
if (UNDO_DISK) {
- success = BKE_blendfile_read(C, mfu->filename, &(const struct BlendFileReadParams){0}, NULL);
+ const struct BlendFileReadParams params = {0};
+ struct BlendFileData *bfd = BKE_blendfile_read(mfu->filename, &params, NULL);
+ if (bfd != NULL) {
+ BKE_blendfile_read_setup(C, bfd, &params, NULL);
+ success = true;
+ }
}
else {
struct BlendFileReadParams params = {0};
@@ -85,7 +90,12 @@ bool BKE_memfile_undo_decode(MemFileUndoData *mfu,
if (!use_old_bmain_data) {
params.skip_flags |= BLO_READ_SKIP_UNDO_OLD_MAIN;
}
- success = BKE_blendfile_read_from_memfile(C, &mfu->memfile, &params, NULL);
+ struct BlendFileData *bfd = BKE_blendfile_read_from_memfile(
+ bmain, &mfu->memfile, &params, NULL);
+ if (bfd != NULL) {
+ BKE_blendfile_read_setup(C, bfd, &params, NULL);
+ success = true;
+ }
}
/* Restore, bmain has been re-allocated. */
@@ -105,6 +115,9 @@ MemFileUndoData *BKE_memfile_undo_encode(Main *bmain, MemFileUndoData *mfu_prev)
{
MemFileUndoData *mfu = MEM_callocN(sizeof(MemFileUndoData), __func__);
+ /* Include recovery infomation since undo-data is written out as #BLENDER_QUIT_FILE. */
+ const int fileflags = G.fileflags | G_FILE_RECOVER_WRITE;
+
/* disk save version */
if (UNDO_DISK) {
static int counter = 0;
@@ -119,7 +132,7 @@ MemFileUndoData *BKE_memfile_undo_encode(Main *bmain, MemFileUndoData *mfu_prev)
BLI_join_dirfile(filename, sizeof(filename), BKE_tempdir_session(), numstr);
/* success = */ /* UNUSED */ BLO_write_file(
- bmain, filename, G.fileflags, &(const struct BlendFileWriteParams){0}, NULL);
+ bmain, filename, fileflags, &(const struct BlendFileWriteParams){0}, NULL);
BLI_strncpy(mfu->filename, filename, sizeof(mfu->filename));
}
@@ -128,7 +141,7 @@ MemFileUndoData *BKE_memfile_undo_encode(Main *bmain, MemFileUndoData *mfu_prev)
if (prevfile) {
BLO_memfile_clear_future(prevfile);
}
- /* success = */ /* UNUSED */ BLO_write_file_mem(bmain, prevfile, &mfu->memfile, G.fileflags);
+ /* success = */ /* UNUSED */ BLO_write_file_mem(bmain, prevfile, &mfu->memfile, fileflags);
mfu->undo_size = mfu->memfile.size;
}
diff --git a/source/blender/blenkernel/intern/blendfile.c b/source/blender/blenkernel/intern/blendfile.c
index 32710c4fa60..efbf19c7381 100644
--- a/source/blender/blenkernel/intern/blendfile.c
+++ b/source/blender/blenkernel/intern/blendfile.c
@@ -51,6 +51,7 @@
#include "BKE_keyconfig.h"
#include "BKE_layer.h"
#include "BKE_lib_id.h"
+#include "BKE_lib_override.h"
#include "BKE_main.h"
#include "BKE_preferences.h"
#include "BKE_report.h"
@@ -131,17 +132,15 @@ static void setup_app_userdef(BlendFileData *bfd)
* should be avoided or check (mode != LOAD_UNDO).
*
* \param bfd: Blend file data, freed by this function on exit.
- * \param filepath: File path or identifier.
*/
static void setup_app_data(bContext *C,
BlendFileData *bfd,
- const char *filepath,
const struct BlendFileReadParams *params,
ReportList *reports)
{
Main *bmain = G_MAIN;
Scene *curscene = NULL;
- const bool recover = (G.fileflags & G_FILE_RECOVER) != 0;
+ const bool recover = (G.fileflags & G_FILE_RECOVER_READ) != 0;
const bool is_startup = params->is_startup;
enum {
LOAD_UI = 1,
@@ -349,16 +348,10 @@ static void setup_app_data(bContext *C,
if (is_startup) {
bmain->name[0] = '\0';
}
- else if (recover && G.relbase_valid) {
- /* in case of autosave or quit.blend, use original filename instead
- * use relbase_valid to make sure the file is saved, else we get <memory2> in the filename */
- filepath = bfd->filename;
+ else if (recover) {
+ /* In case of autosave or quit.blend, use original filename instead. */
bmain->recovered = 1;
-
- /* these are the same at times, should never copy to the same location */
- if (bmain->name != filepath) {
- BLI_strncpy(bmain->name, filepath, FILE_MAX);
- }
+ BLI_strncpy(bmain->name, bfd->filename, FILE_MAX);
}
/* baseflags, groups, make depsgraph, etc */
@@ -401,11 +394,17 @@ static void setup_app_data(bContext *C,
* to recompute refcount for all local IDs too. */
BKE_main_id_refcount_recompute(bmain, false);
}
+
+ if (mode != LOAD_UNDO && !USER_EXPERIMENTAL_TEST(&U, no_override_auto_resync)) {
+ BKE_lib_override_library_main_resync(
+ bmain,
+ curscene,
+ bfd->cur_view_layer ? bfd->cur_view_layer : BKE_view_layer_default_view(curscene));
+ }
}
static void setup_app_blend_file_data(bContext *C,
BlendFileData *bfd,
- const char *filepath,
const struct BlendFileReadParams *params,
ReportList *reports)
{
@@ -413,7 +412,7 @@ static void setup_app_blend_file_data(bContext *C,
setup_app_userdef(bfd);
}
if ((params->skip_flags & BLO_READ_SKIP_DATA) == 0) {
- setup_app_data(C, bfd, filepath, params, reports);
+ setup_app_data(C, bfd, params, reports);
}
}
@@ -430,15 +429,46 @@ static void handle_subversion_warning(Main *main, ReportList *reports)
}
}
-bool BKE_blendfile_read_ex(bContext *C,
- const char *filepath,
- const struct BlendFileReadParams *params,
- ReportList *reports,
- /* Extra args. */
- const bool startup_update_defaults,
- const char *startup_app_template)
+/**
+ * Shared setup function that makes the data from `bfd` into the current blend file,
+ * replacing the contents of #G.main.
+ * This uses the bfd #BKE_blendfile_read and similarly named functions.
+ *
+ * This is done in a separate step so the caller may perform actions after it is known the file
+ * loaded correctly but before the file replaces the existing blend file contents.
+ */
+void BKE_blendfile_read_setup_ex(bContext *C,
+ BlendFileData *bfd,
+ const struct BlendFileReadParams *params,
+ ReportList *reports,
+ /* Extra args. */
+ const bool startup_update_defaults,
+ const char *startup_app_template)
{
+ if (startup_update_defaults) {
+ if ((params->skip_flags & BLO_READ_SKIP_DATA) == 0) {
+ BLO_update_defaults_startup_blend(bfd->main, startup_app_template);
+ }
+ }
+ setup_app_blend_file_data(C, bfd, params, reports);
+ BLO_blendfiledata_free(bfd);
+}
+
+void BKE_blendfile_read_setup(bContext *C,
+ BlendFileData *bfd,
+ const struct BlendFileReadParams *params,
+ ReportList *reports)
+{
+ BKE_blendfile_read_setup_ex(C, bfd, params, reports, false, NULL);
+}
+/**
+ * \return Blend file data, this must be passed to #BKE_blendfile_read_setup when non-NULL.
+ */
+struct BlendFileData *BKE_blendfile_read(const char *filepath,
+ const struct BlendFileReadParams *params,
+ ReportList *reports)
+{
/* Don't print startup file loading. */
if (params->is_startup == false) {
printf("Read blend: %s\n", filepath);
@@ -447,69 +477,40 @@ bool BKE_blendfile_read_ex(bContext *C,
BlendFileData *bfd = BLO_read_from_file(filepath, params->skip_flags, reports);
if (bfd) {
handle_subversion_warning(bfd->main, reports);
- if (startup_update_defaults) {
- if ((params->skip_flags & BLO_READ_SKIP_DATA) == 0) {
- BLO_update_defaults_startup_blend(bfd->main, startup_app_template);
- }
- }
- setup_app_blend_file_data(C, bfd, filepath, params, reports);
- BLO_blendfiledata_free(bfd);
}
else {
BKE_reports_prependf(reports, "Loading '%s' failed: ", filepath);
}
- return (bfd != NULL);
-}
-
-bool BKE_blendfile_read(bContext *C,
- const char *filepath,
- const struct BlendFileReadParams *params,
- ReportList *reports)
-{
- return BKE_blendfile_read_ex(C, filepath, params, reports, false, NULL);
+ return bfd;
}
-bool BKE_blendfile_read_from_memory_ex(bContext *C,
- const void *filebuf,
- int filelength,
- const struct BlendFileReadParams *params,
- ReportList *reports,
- /* Extra args. */
- const bool startup_update_defaults,
- const char *startup_app_template)
+/**
+ * \return Blend file data, this must be passed to #BKE_blendfile_read_setup when non-NULL.
+ */
+struct BlendFileData *BKE_blendfile_read_from_memory(const void *filebuf,
+ int filelength,
+ const struct BlendFileReadParams *params,
+ ReportList *reports)
{
BlendFileData *bfd = BLO_read_from_memory(filebuf, filelength, params->skip_flags, reports);
if (bfd) {
- if (startup_update_defaults) {
- if ((params->skip_flags & BLO_READ_SKIP_DATA) == 0) {
- BLO_update_defaults_startup_blend(bfd->main, startup_app_template);
- }
- }
- setup_app_blend_file_data(C, bfd, "<memory2>", params, reports);
- BLO_blendfiledata_free(bfd);
+ /* Pass. */
}
else {
BKE_reports_prepend(reports, "Loading failed: ");
}
- return (bfd != NULL);
-}
-
-bool BKE_blendfile_read_from_memory(bContext *C,
- const void *filebuf,
- int filelength,
- const struct BlendFileReadParams *params,
- ReportList *reports)
-{
- return BKE_blendfile_read_from_memory_ex(C, filebuf, filelength, params, reports, false, NULL);
+ return bfd;
}
-/* memfile is the undo buffer */
-bool BKE_blendfile_read_from_memfile(bContext *C,
- struct MemFile *memfile,
- const struct BlendFileReadParams *params,
- ReportList *reports)
+/**
+ * \return Blend file data, this must be passed to #BKE_blendfile_read_setup when non-NULL.
+ * \note `memfile` is the undo buffer.
+ */
+struct BlendFileData *BKE_blendfile_read_from_memfile(Main *bmain,
+ struct MemFile *memfile,
+ const struct BlendFileReadParams *params,
+ ReportList *reports)
{
- Main *bmain = CTX_data_main(C);
BlendFileData *bfd = BLO_read_from_memfile(
bmain, BKE_main_blendfile_path(bmain), memfile, params, reports);
if (bfd) {
@@ -520,14 +521,11 @@ bool BKE_blendfile_read_from_memfile(bContext *C,
BLI_assert(BLI_listbase_is_empty(&bfd->main->wm));
BLI_assert(BLI_listbase_is_empty(&bfd->main->workspaces));
BLI_assert(BLI_listbase_is_empty(&bfd->main->screens));
-
- setup_app_blend_file_data(C, bfd, "<memory1>", params, reports);
- BLO_blendfiledata_free(bfd);
}
else {
BKE_reports_prepend(reports, "Loading failed: ");
}
- return (bfd != NULL);
+ return bfd;
}
/**
@@ -865,7 +863,7 @@ bool BKE_blendfile_write_partial(Main *bmain_src,
ReportList *reports)
{
Main *bmain_dst = MEM_callocN(sizeof(Main), "copybuffer");
- ListBase *lbarray_dst[MAX_LIBARRAY], *lbarray_src[MAX_LIBARRAY];
+ ListBase *lbarray_dst[INDEX_ID_MAX], *lbarray_src[INDEX_ID_MAX];
int a, retval;
void *path_list_backup = NULL;
diff --git a/source/blender/blenkernel/intern/bpath.c b/source/blender/blenkernel/intern/bpath.c
index 2ad0ac950d0..47427beccba 100644
--- a/source/blender/blenkernel/intern/bpath.c
+++ b/source/blender/blenkernel/intern/bpath.c
@@ -783,7 +783,7 @@ void BKE_bpath_traverse_main(Main *bmain,
const int flag,
void *bpath_user_data)
{
- ListBase *lbarray[MAX_LIBARRAY];
+ ListBase *lbarray[INDEX_ID_MAX];
int a = set_listbasepointers(bmain, lbarray);
while (a--) {
BKE_bpath_traverse_id_list(bmain, lbarray[a], visit_cb, flag, bpath_user_data);
diff --git a/source/blender/blenkernel/intern/collection.c b/source/blender/blenkernel/intern/collection.c
index 28b91dcc8ce..4e4134c7c8f 100644
--- a/source/blender/blenkernel/intern/collection.c
+++ b/source/blender/blenkernel/intern/collection.c
@@ -122,7 +122,9 @@ static void collection_copy_data(Main *bmain, ID *id_dst, const ID *id_src, cons
}
collection_dst->flag &= ~COLLECTION_HAS_OBJECT_CACHE;
+ collection_dst->flag &= ~COLLECTION_HAS_OBJECT_CACHE_INSTANCED;
BLI_listbase_clear(&collection_dst->object_cache);
+ BLI_listbase_clear(&collection_dst->object_cache_instanced);
BLI_listbase_clear(&collection_dst->gobject);
BLI_listbase_clear(&collection_dst->children);
@@ -214,8 +216,10 @@ static void collection_blend_write(BlendWriter *writer, ID *id, const void *id_a
if (collection->id.us > 0 || BLO_write_is_undo(writer)) {
/* Clean up, important in undo case to reduce false detection of changed data-blocks. */
collection->flag &= ~COLLECTION_HAS_OBJECT_CACHE;
+ collection->flag &= ~COLLECTION_HAS_OBJECT_CACHE_INSTANCED;
collection->tag = 0;
BLI_listbase_clear(&collection->object_cache);
+ BLI_listbase_clear(&collection->object_cache_instanced);
BLI_listbase_clear(&collection->parents);
/* write LibData */
@@ -246,8 +250,10 @@ void BKE_collection_blend_read_data(BlendDataReader *reader, Collection *collect
BKE_previewimg_blend_read(reader, collection->preview);
collection->flag &= ~COLLECTION_HAS_OBJECT_CACHE;
+ collection->flag &= ~COLLECTION_HAS_OBJECT_CACHE_INSTANCED;
collection->tag = 0;
BLI_listbase_clear(&collection->object_cache);
+ BLI_listbase_clear(&collection->object_cache_instanced);
BLI_listbase_clear(&collection->parents);
#ifdef USE_COLLECTION_COMPAT_28
@@ -773,7 +779,10 @@ const char *BKE_collection_ui_name_get(struct Collection *collection)
/** \name Object List Cache
* \{ */
-static void collection_object_cache_fill(ListBase *lb, Collection *collection, int parent_restrict)
+static void collection_object_cache_fill(ListBase *lb,
+ Collection *collection,
+ int parent_restrict,
+ bool with_instances)
{
int child_restrict = collection->flag | parent_restrict;
@@ -784,6 +793,10 @@ static void collection_object_cache_fill(ListBase *lb, Collection *collection, i
base = MEM_callocN(sizeof(Base), "Object Base");
base->object = cob->ob;
BLI_addtail(lb, base);
+ if (with_instances && cob->ob->instance_collection) {
+ collection_object_cache_fill(
+ lb, cob->ob->instance_collection, child_restrict, with_instances);
+ }
}
/* Only collection flags are checked here currently, object restrict flag is checked
@@ -798,7 +811,7 @@ static void collection_object_cache_fill(ListBase *lb, Collection *collection, i
}
LISTBASE_FOREACH (CollectionChild *, child, &collection->children) {
- collection_object_cache_fill(lb, child->collection, child_restrict);
+ collection_object_cache_fill(lb, child->collection, child_restrict, with_instances);
}
}
@@ -809,7 +822,7 @@ ListBase BKE_collection_object_cache_get(Collection *collection)
BLI_mutex_lock(&cache_lock);
if (!(collection->flag & COLLECTION_HAS_OBJECT_CACHE)) {
- collection_object_cache_fill(&collection->object_cache, collection, 0);
+ collection_object_cache_fill(&collection->object_cache, collection, 0, false);
collection->flag |= COLLECTION_HAS_OBJECT_CACHE;
}
BLI_mutex_unlock(&cache_lock);
@@ -818,11 +831,29 @@ ListBase BKE_collection_object_cache_get(Collection *collection)
return collection->object_cache;
}
+ListBase BKE_collection_object_cache_instanced_get(Collection *collection)
+{
+ if (!(collection->flag & COLLECTION_HAS_OBJECT_CACHE_INSTANCED)) {
+ static ThreadMutex cache_lock = BLI_MUTEX_INITIALIZER;
+
+ BLI_mutex_lock(&cache_lock);
+ if (!(collection->flag & COLLECTION_HAS_OBJECT_CACHE_INSTANCED)) {
+ collection_object_cache_fill(&collection->object_cache_instanced, collection, 0, true);
+ collection->flag |= COLLECTION_HAS_OBJECT_CACHE_INSTANCED;
+ }
+ BLI_mutex_unlock(&cache_lock);
+ }
+
+ return collection->object_cache_instanced;
+}
+
static void collection_object_cache_free(Collection *collection)
{
/* Clear own cache an for all parents, since those are affected by changes as well. */
collection->flag &= ~COLLECTION_HAS_OBJECT_CACHE;
+ collection->flag &= ~COLLECTION_HAS_OBJECT_CACHE_INSTANCED;
BLI_freelistN(&collection->object_cache);
+ BLI_freelistN(&collection->object_cache_instanced);
LISTBASE_FOREACH (CollectionParent *, parent, &collection->parents) {
collection_object_cache_free(parent->collection);
@@ -928,6 +959,16 @@ bool BKE_collection_has_object_recursive(Collection *collection, Object *ob)
return (BLI_findptr(&objects, ob, offsetof(Base, object)));
}
+bool BKE_collection_has_object_recursive_instanced(Collection *collection, Object *ob)
+{
+ if (ELEM(NULL, collection, ob)) {
+ return false;
+ }
+
+ const ListBase objects = BKE_collection_object_cache_instanced_get(collection);
+ return (BLI_findptr(&objects, ob, offsetof(Base, object)));
+}
+
static Collection *collection_next_find(Main *bmain, Scene *scene, Collection *collection)
{
if (scene && collection == scene->master_collection) {
diff --git a/source/blender/blenkernel/intern/context.c b/source/blender/blenkernel/intern/context.c
index 6bc385ecd31..cbf7a4483c0 100644
--- a/source/blender/blenkernel/intern/context.c
+++ b/source/blender/blenkernel/intern/context.c
@@ -914,6 +914,15 @@ struct SpaceTopBar *CTX_wm_space_topbar(const bContext *C)
return NULL;
}
+struct SpaceSpreadsheet *CTX_wm_space_spreadsheet(const bContext *C)
+{
+ ScrArea *area = CTX_wm_area(C);
+ if (area && area->spacetype == SPACE_SPREADSHEET) {
+ return area->spacedata.first;
+ }
+ return NULL;
+}
+
void CTX_wm_manager_set(bContext *C, wmWindowManager *wm)
{
C->wm.manager = wm;
diff --git a/source/blender/blenkernel/intern/cryptomatte.cc b/source/blender/blenkernel/intern/cryptomatte.cc
index 8a6b89ce19e..bc89fa85cea 100644
--- a/source/blender/blenkernel/intern/cryptomatte.cc
+++ b/source/blender/blenkernel/intern/cryptomatte.cc
@@ -30,6 +30,7 @@
#include "DNA_material_types.h"
#include "DNA_node_types.h"
#include "DNA_object_types.h"
+#include "DNA_scene_types.h"
#include "BLI_compiler_attrs.h"
#include "BLI_dynstr.h"
@@ -50,10 +51,13 @@
struct CryptomatteSession {
blender::Map<std::string, blender::bke::cryptomatte::CryptomatteLayer> layers;
+ /* Layer names in order of creation. */
+ blender::Vector<std::string> layer_names;
CryptomatteSession();
CryptomatteSession(const Main *bmain);
- CryptomatteSession(StampData *metadata);
+ CryptomatteSession(StampData *stamp_data);
+ CryptomatteSession(const Scene *scene);
blender::bke::cryptomatte::CryptomatteLayer &add_layer(std::string layer_name);
std::optional<std::string> operator[](float encoded_hash) const;
@@ -99,8 +103,32 @@ CryptomatteSession::CryptomatteSession(StampData *stamp_data)
false);
}
+CryptomatteSession::CryptomatteSession(const Scene *scene)
+{
+ LISTBASE_FOREACH (ViewLayer *, view_layer, &scene->view_layers) {
+ eViewLayerCryptomatteFlags cryptoflags = static_cast<eViewLayerCryptomatteFlags>(
+ view_layer->cryptomatte_flag & VIEW_LAYER_CRYPTOMATTE_ALL);
+ if (cryptoflags == 0) {
+ cryptoflags = static_cast<eViewLayerCryptomatteFlags>(VIEW_LAYER_CRYPTOMATTE_ALL);
+ }
+
+ if (cryptoflags & VIEW_LAYER_CRYPTOMATTE_OBJECT) {
+ add_layer(blender::StringRefNull(view_layer->name) + ".CryptoObject");
+ }
+ if (cryptoflags & VIEW_LAYER_CRYPTOMATTE_ASSET) {
+ add_layer(blender::StringRefNull(view_layer->name) + ".CryptoAsset");
+ }
+ if (cryptoflags & VIEW_LAYER_CRYPTOMATTE_MATERIAL) {
+ add_layer(blender::StringRefNull(view_layer->name) + ".CryptoMaterial");
+ }
+ }
+}
+
blender::bke::cryptomatte::CryptomatteLayer &CryptomatteSession::add_layer(std::string layer_name)
{
+ if (!layer_names.contains(layer_name)) {
+ layer_names.append(layer_name);
+ }
return layers.lookup_or_add_default(layer_name);
}
@@ -128,6 +156,12 @@ struct CryptomatteSession *BKE_cryptomatte_init_from_render_result(
return session;
}
+struct CryptomatteSession *BKE_cryptomatte_init_from_scene(const struct Scene *scene)
+{
+ CryptomatteSession *session = new CryptomatteSession(scene);
+ return session;
+}
+
void BKE_cryptomatte_add_layer(struct CryptomatteSession *session, const char *layer_name)
{
session->add_layer(layer_name);
@@ -182,39 +216,57 @@ float BKE_cryptomatte_hash_to_float(uint32_t cryptomatte_hash)
return blender::bke::cryptomatte::CryptomatteHash(cryptomatte_hash).float_encoded();
}
-char *BKE_cryptomatte_entries_to_matte_id(NodeCryptomatte *node_storage)
+/* Find an ID in the given main that matches the given encoded float. */
+bool BKE_cryptomatte_find_name(const CryptomatteSession *session,
+ const float encoded_hash,
+ char *r_name,
+ int name_len)
{
- std::stringstream ss;
- ss.precision(9);
+ std::optional<std::string> name = (*session)[encoded_hash];
+ if (!name) {
+ return false;
+ }
+
+ BLI_strncpy(r_name, name->c_str(), name_len);
+ return true;
+}
+char *BKE_cryptomatte_entries_to_matte_id(NodeCryptomatte *node_storage)
+{
+ DynStr *matte_id = BLI_dynstr_new();
bool first = true;
LISTBASE_FOREACH (CryptomatteEntry *, entry, &node_storage->entries) {
if (!first) {
- ss << ',';
+ BLI_dynstr_append(matte_id, ",");
}
- blender::StringRef entry_name(entry->name, BLI_strnlen(entry->name, sizeof(entry->name)));
- if (!entry_name.is_empty()) {
- ss << entry_name;
+ if (BLI_strnlen(entry->name, sizeof(entry->name)) != 0) {
+ BLI_dynstr_nappend(matte_id, entry->name, sizeof(entry->name));
}
else {
- ss << '<' << std::scientific << entry->encoded_hash << '>';
+ BLI_dynstr_appendf(matte_id, "<%.9g>", entry->encoded_hash);
}
first = false;
}
-
- /* Convert result to C string. */
- const std::string result_string = ss.str();
- const char *c_str = result_string.c_str();
- size_t result_len = result_string.size() + 1;
- char *result = static_cast<char *>(MEM_mallocN(sizeof(char) * result_len, __func__));
- memcpy(result, c_str, result_len);
+ char *result = BLI_dynstr_get_cstring(matte_id);
+ BLI_dynstr_free(matte_id);
return result;
}
void BKE_cryptomatte_matte_id_to_entries(NodeCryptomatte *node_storage, const char *matte_id)
{
BLI_freelistN(&node_storage->entries);
- std::optional<CryptomatteSession> session = std::nullopt;
+
+ if (matte_id == nullptr) {
+ MEM_SAFE_FREE(node_storage->matte_id);
+ return;
+ }
+ /* Update the matte_id so the files can be opened in versions that don't
+ * use `CryptomatteEntry`. */
+ if (matte_id != node_storage->matte_id && node_storage->matte_id &&
+ STREQ(node_storage->matte_id, matte_id)) {
+ MEM_SAFE_FREE(node_storage->matte_id);
+ node_storage->matte_id = static_cast<char *>(MEM_dupallocN(matte_id));
+ }
std::istringstream ss(matte_id);
while (ss.good()) {
@@ -358,6 +410,9 @@ static bool from_manifest(CryptomatteLayer &layer, blender::StringRefNull manife
ref = ref.drop_prefix(quoted_name_len);
ref = skip_whitespaces_(ref);
+ if (ref.is_empty()) {
+ return false;
+ }
char colon = ref.front();
if (colon != ':') {
return false;
@@ -365,7 +420,7 @@ static bool from_manifest(CryptomatteLayer &layer, blender::StringRefNull manife
ref = ref.drop_prefix(1);
ref = skip_whitespaces_(ref);
- if (ref.front() != '\"') {
+ if (ref.is_empty() || ref.front() != '\"') {
return false;
}
@@ -500,7 +555,7 @@ std::unique_ptr<CryptomatteLayer> CryptomatteLayer::read_from_manifest(
blender::StringRefNull manifest)
{
std::unique_ptr<CryptomatteLayer> layer = std::make_unique<CryptomatteLayer>();
- blender::bke::cryptomatte::manifest::from_manifest(*layer.get(), manifest);
+ blender::bke::cryptomatte::manifest::from_manifest(*layer, manifest);
return layer;
}
@@ -597,4 +652,10 @@ void CryptomatteStampDataCallbackData::extract_layer_manifest(void *_data,
blender::bke::cryptomatte::manifest::from_manifest(layer, propvalue);
}
+const blender::Vector<std::string> &BKE_cryptomatte_layer_names_get(
+ const CryptomatteSession &session)
+{
+ return session.layer_names;
+}
+
} // namespace blender::bke::cryptomatte
diff --git a/source/blender/blenkernel/intern/cryptomatte_test.cc b/source/blender/blenkernel/intern/cryptomatte_test.cc
index 5481b97913c..85ab7e43d22 100644
--- a/source/blender/blenkernel/intern/cryptomatte_test.cc
+++ b/source/blender/blenkernel/intern/cryptomatte_test.cc
@@ -21,8 +21,6 @@
#include "BKE_cryptomatte.hh"
#include "BKE_image.h"
-#include "DNA_node_types.h"
-
#include "RE_pipeline.h"
#include "MEM_guardedalloc.h"
@@ -77,17 +75,15 @@ static void test_cryptomatte_manifest(std::string expected, std::string manifest
TEST(cryptomatte, layer_from_manifest)
{
test_cryptomatte_manifest("{}", "{}");
- test_cryptomatte_manifest("{\"Object\":\"12345678\"}", "{\"Object\": \"12345678\"}");
- test_cryptomatte_manifest("{\"Object\":\"12345678\",\"Object2\":\"87654321\"}",
- "{\"Object\":\"12345678\",\"Object2\":\"87654321\"}");
- test_cryptomatte_manifest(
- "{\"Object\":\"12345678\",\"Object2\":\"87654321\"}",
- " { \"Object\" : \"12345678\" , \"Object2\" : \"87654321\" } ");
- test_cryptomatte_manifest("{\"Object\\\"01\\\"\":\"12345678\"}",
- "{\"Object\\\"01\\\"\": \"12345678\"}");
+ test_cryptomatte_manifest(R"({"Object":"12345678"})", R"({"Object": "12345678"})");
+ test_cryptomatte_manifest(R"({"Object":"12345678","Object2":"87654321"})",
+ R"({"Object":"12345678","Object2":"87654321"})");
+ test_cryptomatte_manifest(R"({"Object":"12345678","Object2":"87654321"})",
+ R"( { "Object" : "12345678" , "Object2" : "87654321" } )");
+ test_cryptomatte_manifest(R"({"Object\"01\"":"12345678"})", R"({"Object\"01\"": "12345678"})");
test_cryptomatte_manifest(
- "{\"Object\\\"01\\\"\":\"12345678\",\"Object\":\"12345678\",\"Object2\":\"87654321\"}",
- "{\"Object\\\"01\\\"\":\"12345678\",\"Object\":\"12345678\", \"Object2\":\"87654321\"}");
+ R"({"Object\"01\"":"12345678","Object":"12345678","Object2":"87654321"})",
+ R"({"Object\"01\"":"12345678","Object":"12345678", "Object2":"87654321"})");
}
TEST(cryptomatte, extract_layer_hash_from_metadata_key)
@@ -127,7 +123,7 @@ static void validate_cryptomatte_session_from_stamp_data(void *UNUSED(data),
EXPECT_STREQ("uint32_to_float32", propvalue);
}
else if (prop_name == "cryptomatte/87f095e/manifest") {
- EXPECT_STREQ("{\"Object\":\"12345678\"}", propvalue);
+ EXPECT_STREQ(R"({"Object":"12345678"})", propvalue);
}
else if (prop_name == "cryptomatte/c42daa7/name") {
@@ -140,7 +136,7 @@ static void validate_cryptomatte_session_from_stamp_data(void *UNUSED(data),
EXPECT_STREQ("uint32_to_float32", propvalue);
}
else if (prop_name == "cryptomatte/c42daa7/manifest") {
- EXPECT_STREQ("{\"Object2\":\"87654321\"}", propvalue);
+ EXPECT_STREQ(R"({"Object2":"87654321"})", propvalue);
}
else {
@@ -155,12 +151,12 @@ TEST(cryptomatte, session_from_stamp_data)
MEM_callocN(sizeof(RenderResult), __func__));
BKE_render_result_stamp_data(render_result, "cryptomatte/qwerty/name", "layer1");
BKE_render_result_stamp_data(
- render_result, "cryptomatte/qwerty/manifest", "{\"Object\":\"12345678\"}");
+ render_result, "cryptomatte/qwerty/manifest", R"({"Object":"12345678"})");
BKE_render_result_stamp_data(render_result, "cryptomatte/uiop/name", "layer2");
BKE_render_result_stamp_data(
- render_result, "cryptomatte/uiop/manifest", "{\"Object2\":\"87654321\"}");
- CryptomatteSession *session = BKE_cryptomatte_init_from_render_result(render_result);
- EXPECT_NE(session, nullptr);
+ render_result, "cryptomatte/uiop/manifest", R"({"Object2":"87654321"})");
+ CryptomatteSessionPtr session(BKE_cryptomatte_init_from_render_result(render_result));
+ EXPECT_NE(session.get(), nullptr);
RE_FreeRenderResult(render_result);
/* Create StampData from CryptomatteSession. */
@@ -168,25 +164,23 @@ TEST(cryptomatte, session_from_stamp_data)
BLI_strncpy(view_layer.name, "viewlayername", sizeof(view_layer.name));
RenderResult *render_result2 = static_cast<RenderResult *>(
MEM_callocN(sizeof(RenderResult), __func__));
- BKE_cryptomatte_store_metadata(session, render_result2, &view_layer);
+ BKE_cryptomatte_store_metadata(session.get(), render_result2, &view_layer);
/* Validate StampData. */
BKE_stamp_info_callback(
nullptr, render_result2->stamp_data, validate_cryptomatte_session_from_stamp_data, false);
RE_FreeRenderResult(render_result2);
- BKE_cryptomatte_free(session);
}
-TEST(cryptomatte, T86026)
+/**
+ * Test method that contains known malformed manifests and makes sure that these can be parsed as
+ * best as possible. */
+TEST(cryptomatte, parsing_malformed_manifests)
{
- NodeCryptomatte storage = {{0.0f}};
- CryptomatteEntry entry = {nullptr};
- BLI_addtail(&storage.entries, &entry);
- entry.encoded_hash = 4.76190593e-07;
- char *matte_id = BKE_cryptomatte_entries_to_matte_id(&storage);
- EXPECT_STREQ("<4.761905927e-07>", matte_id);
- MEM_freeN(matte_id);
+ /* Manifest from multilayer.exr in the cryptomatte git-repository. */
+ test_cryptomatte_manifest(
+ R"({"/obj/instance1:instances:0":"0d54c6cc","/obj/instance1:instances:1":"293d9340","/obj/instance1:instances:110":"ccb9e1f2","/obj/instance1:instances:111":"f8dd3a48","/obj/instance1:instances:112":"a99e07a8","/obj/instance1:instances:113":"e75599a4","/obj/instance1:instances:114":"794200f3","/obj/instance1:instances:115":"2a3a1728","/obj/instance1:instances:116":"478544a1","/obj/instance1:instances:117":"b2bd969a","/obj/instance1:instances:10":"3a0c8681","/obj/instance1:instances:11":"01e5970d","/obj/box:polygons:1":"9d416418","/obj/instance1:instances:100":"2dcd2966","/obj/instance1:instances:101":"9331cd82","/obj/instance1:instances:102":"df50fccb","/obj/instance1:instances:103":"97f8590d","/obj/instance1:instances:104":"bbcd220d","/obj/instance1:instances:105":"4ae06139","/obj/instance1:instances:106":"8873d5ea","/obj/instance1:instances:107":"39d8af8d","/obj/instance1:instances:108":"bb11bd4e","/obj/instance1:instances:109":"a32bba35"})",
+ R"({"\/obj\/box:polygons:1":"9d416418","\/obj\/instance1:instances:0":"0d54c6cc","\/obj\/instance1:instances:1":"293d9340","\/obj\/instance1:instances:10":"3a0c8681","\/obj\/instance1:instances:100":"2dcd2966","\/obj\/instance1:instances:101":"9331cd82","\/obj\/instance1:instances:102":"df50fccb","\/obj\/instance1:instances:103":"97f8590d","\/obj\/instance1:instances:104":"bbcd220d","\/obj\/instance1:instances:105":"4ae06139","\/obj\/instance1:instances:106":"8873d5ea","\/obj\/instance1:instances:107":"39d8af8d","\/obj\/instance1:instances:108":"bb11bd4e","\/obj\/instance1:instances:109":"a32bba35","\/obj\/instance1:instances:11":"01e5970d","\/obj\/instance1:instances:110":"ccb9e1f2","\/obj\/instance1:instances:111":"f8dd3a48","\/obj\/instance1:instances:112":"a99e07a8","\/obj\/instance1:instances:113":"e75599a4","\/obj\/instance1:instances:114":"794200f3","\/obj\/instance1:instances:115":"2a3a1728","\/obj\/instance1:instances:116":"478544a1","\/obj\/instance1:instances:117":"b2bd969a","\/obj\/instance1:instance)");
}
-
} // namespace blender::bke::cryptomatte::tests
diff --git a/source/blender/blenkernel/intern/displist.c b/source/blender/blenkernel/intern/displist.c
index c860e57520d..708b1971bd5 100644
--- a/source/blender/blenkernel/intern/displist.c
+++ b/source/blender/blenkernel/intern/displist.c
@@ -875,9 +875,9 @@ static float (*displist_vert_coords_alloc(ListBase *dispbase, int *r_vert_len))[
allverts = MEM_mallocN(sizeof(float[3]) * (*r_vert_len), "displist_vert_coords_alloc allverts");
fp = (float *)allverts;
LISTBASE_FOREACH (DispList *, dl, dispbase) {
- int offs = 3 * ((dl->type == DL_INDEX3) ? dl->nr : dl->parts * dl->nr);
- memcpy(fp, dl->verts, sizeof(float) * offs);
- fp += offs;
+ int ofs = 3 * ((dl->type == DL_INDEX3) ? dl->nr : dl->parts * dl->nr);
+ memcpy(fp, dl->verts, sizeof(float) * ofs);
+ fp += ofs;
}
return allverts;
@@ -889,9 +889,9 @@ static void displist_vert_coords_apply(ListBase *dispbase, float (*allverts)[3])
fp = (float *)allverts;
LISTBASE_FOREACH (DispList *, dl, dispbase) {
- int offs = 3 * ((dl->type == DL_INDEX3) ? dl->nr : dl->parts * dl->nr);
- memcpy(dl->verts, fp, sizeof(float) * offs);
- fp += offs;
+ int ofs = 3 * ((dl->type == DL_INDEX3) ? dl->nr : dl->parts * dl->nr);
+ memcpy(dl->verts, fp, sizeof(float) * ofs);
+ fp += ofs;
}
}
diff --git a/source/blender/blenkernel/intern/geometry_component_instances.cc b/source/blender/blenkernel/intern/geometry_component_instances.cc
new file mode 100644
index 00000000000..68c551645d2
--- /dev/null
+++ b/source/blender/blenkernel/intern/geometry_component_instances.cc
@@ -0,0 +1,173 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "BLI_float4x4.hh"
+#include "BLI_map.hh"
+#include "BLI_rand.hh"
+#include "BLI_set.hh"
+#include "BLI_span.hh"
+#include "BLI_vector.hh"
+
+#include "DNA_collection_types.h"
+
+#include "BKE_geometry_set.hh"
+
+using blender::float4x4;
+using blender::Map;
+using blender::MutableSpan;
+using blender::Set;
+using blender::Span;
+
+/* -------------------------------------------------------------------- */
+/** \name Geometry Component Implementation
+ * \{ */
+
+InstancesComponent::InstancesComponent() : GeometryComponent(GEO_COMPONENT_TYPE_INSTANCES)
+{
+}
+
+GeometryComponent *InstancesComponent::copy() const
+{
+ InstancesComponent *new_component = new InstancesComponent();
+ new_component->transforms_ = transforms_;
+ new_component->instanced_data_ = instanced_data_;
+ return new_component;
+}
+
+void InstancesComponent::clear()
+{
+ instanced_data_.clear();
+ transforms_.clear();
+}
+
+void InstancesComponent::add_instance(Object *object, float4x4 transform, const int id)
+{
+ InstancedData data;
+ data.type = INSTANCE_DATA_TYPE_OBJECT;
+ data.data.object = object;
+ this->add_instance(data, transform, id);
+}
+
+void InstancesComponent::add_instance(Collection *collection, float4x4 transform, const int id)
+{
+ InstancedData data;
+ data.type = INSTANCE_DATA_TYPE_COLLECTION;
+ data.data.collection = collection;
+ this->add_instance(data, transform, id);
+}
+
+void InstancesComponent::add_instance(InstancedData data, float4x4 transform, const int id)
+{
+ instanced_data_.append(data);
+ transforms_.append(transform);
+ ids_.append(id);
+}
+
+Span<InstancedData> InstancesComponent::instanced_data() const
+{
+ return instanced_data_;
+}
+
+Span<float4x4> InstancesComponent::transforms() const
+{
+ return transforms_;
+}
+
+Span<int> InstancesComponent::ids() const
+{
+ return ids_;
+}
+
+MutableSpan<float4x4> InstancesComponent::transforms()
+{
+ return transforms_;
+}
+
+int InstancesComponent::instances_amount() const
+{
+ const int size = instanced_data_.size();
+ BLI_assert(transforms_.size() == size);
+ return size;
+}
+
+bool InstancesComponent::is_empty() const
+{
+ return transforms_.size() == 0;
+}
+
+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> InstancesComponent::almost_unique_ids() const
+{
+ std::lock_guard lock(almost_unique_ids_mutex_);
+ if (almost_unique_ids_.size() != ids_.size()) {
+ almost_unique_ids_ = generate_unique_instance_ids(ids_);
+ }
+ return almost_unique_ids_;
+}
+
+/** \} */
diff --git a/source/blender/blenkernel/intern/geometry_component_mesh.cc b/source/blender/blenkernel/intern/geometry_component_mesh.cc
new file mode 100644
index 00000000000..8ee2142799d
--- /dev/null
+++ b/source/blender/blenkernel/intern/geometry_component_mesh.cc
@@ -0,0 +1,979 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "BLI_listbase.h"
+
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+#include "DNA_object_types.h"
+
+#include "BKE_attribute_access.hh"
+#include "BKE_attribute_math.hh"
+#include "BKE_deform.h"
+#include "BKE_geometry_set.hh"
+#include "BKE_lib_id.h"
+#include "BKE_mesh.h"
+
+#include "attribute_access_intern.hh"
+
+/* Can't include BKE_object_deform.h right now, due to an enum forward declaration. */
+extern "C" MDeformVert *BKE_object_defgroup_data_create(ID *id);
+
+using blender::bke::ReadAttributePtr;
+
+/* -------------------------------------------------------------------- */
+/** \name Geometry Component Implementation
+ * \{ */
+
+MeshComponent::MeshComponent() : GeometryComponent(GEO_COMPONENT_TYPE_MESH)
+{
+}
+
+MeshComponent::~MeshComponent()
+{
+ this->clear();
+}
+
+GeometryComponent *MeshComponent::copy() const
+{
+ MeshComponent *new_component = new MeshComponent();
+ if (mesh_ != nullptr) {
+ new_component->mesh_ = BKE_mesh_copy_for_eval(mesh_, false);
+ new_component->ownership_ = GeometryOwnershipType::Owned;
+ new_component->vertex_group_names_ = blender::Map(vertex_group_names_);
+ }
+ return new_component;
+}
+
+void MeshComponent::clear()
+{
+ BLI_assert(this->is_mutable());
+ if (mesh_ != nullptr) {
+ if (ownership_ == GeometryOwnershipType::Owned) {
+ BKE_id_free(nullptr, mesh_);
+ }
+ mesh_ = nullptr;
+ }
+ vertex_group_names_.clear();
+}
+
+bool MeshComponent::has_mesh() const
+{
+ return mesh_ != nullptr;
+}
+
+/* Clear the component and replace it with the new mesh. */
+void MeshComponent::replace(Mesh *mesh, GeometryOwnershipType ownership)
+{
+ BLI_assert(this->is_mutable());
+ this->clear();
+ mesh_ = mesh;
+ ownership_ = ownership;
+}
+
+/* This function exists for the same reason as #vertex_group_names_. Non-nodes modifiers need to
+ * be able to replace the mesh data without losing the vertex group names, which may have come
+ * from another object. */
+void MeshComponent::replace_mesh_but_keep_vertex_group_names(Mesh *mesh,
+ GeometryOwnershipType ownership)
+{
+ BLI_assert(this->is_mutable());
+ if (mesh_ != nullptr) {
+ if (ownership_ == GeometryOwnershipType::Owned) {
+ BKE_id_free(nullptr, mesh_);
+ }
+ mesh_ = nullptr;
+ }
+ mesh_ = mesh;
+ ownership_ = ownership;
+}
+
+/* Return the mesh and clear the component. The caller takes over responsibility for freeing the
+ * mesh (if the component was responsible before). */
+Mesh *MeshComponent::release()
+{
+ BLI_assert(this->is_mutable());
+ Mesh *mesh = mesh_;
+ mesh_ = nullptr;
+ return mesh;
+}
+
+void MeshComponent::copy_vertex_group_names_from_object(const Object &object)
+{
+ BLI_assert(this->is_mutable());
+ vertex_group_names_.clear();
+ int index = 0;
+ LISTBASE_FOREACH (const bDeformGroup *, group, &object.defbase) {
+ vertex_group_names_.add(group->name, index);
+ index++;
+ }
+}
+
+const blender::Map<std::string, int> &MeshComponent::vertex_group_names() const
+{
+ return vertex_group_names_;
+}
+
+/* This is only exposed for the internal attribute API. */
+blender::Map<std::string, int> &MeshComponent::vertex_group_names()
+{
+ return vertex_group_names_;
+}
+
+/* Get the mesh from this component. This method can be used by multiple threads at the same
+ * time. Therefore, the returned mesh should not be modified. No ownership is transferred. */
+const Mesh *MeshComponent::get_for_read() const
+{
+ return mesh_;
+}
+
+/* Get the mesh from this component. This method can only be used when the component is mutable,
+ * i.e. it is not shared. The returned mesh can be modified. No ownership is transferred. */
+Mesh *MeshComponent::get_for_write()
+{
+ BLI_assert(this->is_mutable());
+ if (ownership_ == GeometryOwnershipType::ReadOnly) {
+ mesh_ = BKE_mesh_copy_for_eval(mesh_, false);
+ ownership_ = GeometryOwnershipType::Owned;
+ }
+ return mesh_;
+}
+
+bool MeshComponent::is_empty() const
+{
+ return mesh_ == nullptr;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Attribute Access
+ * \{ */
+
+int MeshComponent::attribute_domain_size(const AttributeDomain domain) const
+{
+ BLI_assert(this->attribute_domain_supported(domain));
+ if (mesh_ == nullptr) {
+ return 0;
+ }
+ switch (domain) {
+ case ATTR_DOMAIN_CORNER:
+ return mesh_->totloop;
+ case ATTR_DOMAIN_POINT:
+ return mesh_->totvert;
+ case ATTR_DOMAIN_EDGE:
+ return mesh_->totedge;
+ case ATTR_DOMAIN_POLYGON:
+ return mesh_->totpoly;
+ default:
+ BLI_assert(false);
+ break;
+ }
+ return 0;
+}
+
+namespace blender::bke {
+
+template<typename T>
+static void adapt_mesh_domain_corner_to_point_impl(const Mesh &mesh,
+ const TypedReadAttribute<T> &attribute,
+ MutableSpan<T> r_values)
+{
+ BLI_assert(r_values.size() == mesh.totvert);
+ attribute_math::DefaultMixer<T> mixer(r_values);
+
+ for (const int loop_index : IndexRange(mesh.totloop)) {
+ const T value = attribute[loop_index];
+ const MLoop &loop = mesh.mloop[loop_index];
+ const int point_index = loop.v;
+ mixer.mix_in(point_index, value);
+ }
+ mixer.finalize();
+}
+
+static ReadAttributePtr adapt_mesh_domain_corner_to_point(const Mesh &mesh,
+ ReadAttributePtr attribute)
+{
+ ReadAttributePtr new_attribute;
+ const CustomDataType data_type = attribute->custom_data_type();
+ attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
+ using T = decltype(dummy);
+ if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) {
+ /* We compute all interpolated values at once, because for this interpolation, one has to
+ * iterate over all loops anyway. */
+ Array<T> values(mesh.totvert);
+ adapt_mesh_domain_corner_to_point_impl<T>(mesh, *attribute, values);
+ new_attribute = std::make_unique<OwnedArrayReadAttribute<T>>(ATTR_DOMAIN_POINT,
+ std::move(values));
+ }
+ });
+ return new_attribute;
+}
+
+template<typename T>
+static void adapt_mesh_domain_point_to_corner_impl(const Mesh &mesh,
+ const TypedReadAttribute<T> &attribute,
+ MutableSpan<T> r_values)
+{
+ BLI_assert(r_values.size() == mesh.totloop);
+
+ for (const int loop_index : IndexRange(mesh.totloop)) {
+ const int vertex_index = mesh.mloop[loop_index].v;
+ r_values[loop_index] = attribute[vertex_index];
+ }
+}
+
+static ReadAttributePtr adapt_mesh_domain_point_to_corner(const Mesh &mesh,
+ ReadAttributePtr attribute)
+{
+ ReadAttributePtr new_attribute;
+ const CustomDataType data_type = attribute->custom_data_type();
+ attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
+ using T = decltype(dummy);
+ /* It is not strictly necessary to compute the value for all corners here. Instead one could
+ * lazily lookup the mesh topology when a specific index accessed. This can be more efficient
+ * when an algorithm only accesses very few of the corner values. However, for the algorithms
+ * we currently have, precomputing the array is fine. Also, it is easier to implement. */
+ Array<T> values(mesh.totloop);
+ adapt_mesh_domain_point_to_corner_impl<T>(mesh, *attribute, values);
+ new_attribute = std::make_unique<OwnedArrayReadAttribute<T>>(ATTR_DOMAIN_CORNER,
+ std::move(values));
+ });
+ return new_attribute;
+}
+
+/**
+ * \note Theoretically this interpolation does not need to compute all values at once.
+ * However, doing that makes the implementation simpler, and this can be optimized in the future if
+ * only some values are required.
+ */
+template<typename T>
+static void adapt_mesh_domain_corner_to_polygon_impl(const Mesh &mesh,
+ Span<T> old_values,
+ MutableSpan<T> r_values)
+{
+ BLI_assert(r_values.size() == mesh.totpoly);
+ attribute_math::DefaultMixer<T> mixer(r_values);
+
+ for (const int poly_index : IndexRange(mesh.totpoly)) {
+ const MPoly &poly = mesh.mpoly[poly_index];
+ for (const int loop_index : IndexRange(poly.loopstart, poly.totloop)) {
+ const T value = old_values[loop_index];
+ mixer.mix_in(poly_index, value);
+ }
+ }
+
+ mixer.finalize();
+}
+
+static ReadAttributePtr adapt_mesh_domain_corner_to_polygon(const Mesh &mesh,
+ ReadAttributePtr attribute)
+{
+ ReadAttributePtr new_attribute;
+ const CustomDataType data_type = attribute->custom_data_type();
+ attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
+ using T = decltype(dummy);
+ if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) {
+ Array<T> values(mesh.totpoly);
+ adapt_mesh_domain_corner_to_polygon_impl<T>(mesh, attribute->get_span<T>(), values);
+ new_attribute = std::make_unique<OwnedArrayReadAttribute<T>>(ATTR_DOMAIN_POINT,
+ std::move(values));
+ }
+ });
+ return new_attribute;
+}
+
+template<typename T>
+void adapt_mesh_domain_polygon_to_point_impl(const Mesh &mesh,
+ Span<T> old_values,
+ MutableSpan<T> r_values)
+{
+ BLI_assert(r_values.size() == mesh.totvert);
+ attribute_math::DefaultMixer<T> mixer(r_values);
+
+ for (const int poly_index : IndexRange(mesh.totpoly)) {
+ const MPoly &poly = mesh.mpoly[poly_index];
+ const T value = old_values[poly_index];
+ for (const int loop_index : IndexRange(poly.loopstart, poly.totloop)) {
+ const MLoop &loop = mesh.mloop[loop_index];
+ const int point_index = loop.v;
+ mixer.mix_in(point_index, value);
+ }
+ }
+
+ mixer.finalize();
+}
+
+static ReadAttributePtr adapt_mesh_domain_polygon_to_point(const Mesh &mesh,
+ ReadAttributePtr attribute)
+{
+ ReadAttributePtr new_attribute;
+ const CustomDataType data_type = attribute->custom_data_type();
+ attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
+ using T = decltype(dummy);
+ if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) {
+ Array<T> values(mesh.totvert);
+ adapt_mesh_domain_polygon_to_point_impl<T>(mesh, attribute->get_span<T>(), values);
+ new_attribute = std::make_unique<OwnedArrayReadAttribute<T>>(ATTR_DOMAIN_POINT,
+ std::move(values));
+ }
+ });
+ return new_attribute;
+}
+
+template<typename T>
+void adapt_mesh_domain_polygon_to_corner_impl(const Mesh &mesh,
+ const Span<T> old_values,
+ MutableSpan<T> r_values)
+{
+ BLI_assert(r_values.size() == mesh.totloop);
+
+ for (const int poly_index : IndexRange(mesh.totpoly)) {
+ const MPoly &poly = mesh.mpoly[poly_index];
+ MutableSpan<T> poly_corner_values = r_values.slice(poly.loopstart, poly.totloop);
+ poly_corner_values.fill(old_values[poly_index]);
+ }
+}
+
+static ReadAttributePtr adapt_mesh_domain_polygon_to_corner(const Mesh &mesh,
+ ReadAttributePtr attribute)
+{
+ ReadAttributePtr new_attribute;
+ const CustomDataType data_type = attribute->custom_data_type();
+ attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
+ using T = decltype(dummy);
+ if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) {
+ Array<T> values(mesh.totloop);
+ adapt_mesh_domain_polygon_to_corner_impl<T>(mesh, attribute->get_span<T>(), values);
+ new_attribute = std::make_unique<OwnedArrayReadAttribute<T>>(ATTR_DOMAIN_POINT,
+ std::move(values));
+ }
+ });
+ return new_attribute;
+}
+
+/**
+ * \note Theoretically this interpolation does not need to compute all values at once.
+ * However, doing that makes the implementation simpler, and this can be optimized in the future if
+ * only some values are required.
+ */
+template<typename T>
+static void adapt_mesh_domain_point_to_polygon_impl(const Mesh &mesh,
+ const Span<T> old_values,
+ MutableSpan<T> r_values)
+{
+ BLI_assert(r_values.size() == mesh.totpoly);
+ attribute_math::DefaultMixer<T> mixer(r_values);
+
+ for (const int poly_index : IndexRange(mesh.totpoly)) {
+ const MPoly &poly = mesh.mpoly[poly_index];
+ for (const int loop_index : IndexRange(poly.loopstart, poly.totloop)) {
+ MLoop &loop = mesh.mloop[loop_index];
+ const int point_index = loop.v;
+ mixer.mix_in(poly_index, old_values[point_index]);
+ }
+ }
+ mixer.finalize();
+}
+
+static ReadAttributePtr adapt_mesh_domain_point_to_polygon(const Mesh &mesh,
+ ReadAttributePtr attribute)
+{
+ ReadAttributePtr new_attribute;
+ const CustomDataType data_type = attribute->custom_data_type();
+ attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
+ using T = decltype(dummy);
+ if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) {
+ Array<T> values(mesh.totpoly);
+ adapt_mesh_domain_point_to_polygon_impl<T>(mesh, attribute->get_span<T>(), values);
+ new_attribute = std::make_unique<OwnedArrayReadAttribute<T>>(ATTR_DOMAIN_POINT,
+ std::move(values));
+ }
+ });
+ return new_attribute;
+}
+
+} // namespace blender::bke
+
+ReadAttributePtr MeshComponent::attribute_try_adapt_domain(ReadAttributePtr attribute,
+ const AttributeDomain new_domain) const
+{
+ if (!attribute) {
+ return {};
+ }
+ if (attribute->size() == 0) {
+ return {};
+ }
+ const AttributeDomain old_domain = attribute->domain();
+ if (old_domain == new_domain) {
+ return attribute;
+ }
+
+ switch (old_domain) {
+ case ATTR_DOMAIN_CORNER: {
+ switch (new_domain) {
+ case ATTR_DOMAIN_POINT:
+ return blender::bke::adapt_mesh_domain_corner_to_point(*mesh_, std::move(attribute));
+ case ATTR_DOMAIN_POLYGON:
+ return blender::bke::adapt_mesh_domain_corner_to_polygon(*mesh_, std::move(attribute));
+ default:
+ break;
+ }
+ break;
+ }
+ case ATTR_DOMAIN_POINT: {
+ switch (new_domain) {
+ case ATTR_DOMAIN_CORNER:
+ return blender::bke::adapt_mesh_domain_point_to_corner(*mesh_, std::move(attribute));
+ case ATTR_DOMAIN_POLYGON:
+ return blender::bke::adapt_mesh_domain_point_to_polygon(*mesh_, std::move(attribute));
+ default:
+ break;
+ }
+ break;
+ }
+ case ATTR_DOMAIN_POLYGON: {
+ switch (new_domain) {
+ case ATTR_DOMAIN_POINT:
+ return blender::bke::adapt_mesh_domain_polygon_to_point(*mesh_, std::move(attribute));
+ case ATTR_DOMAIN_CORNER:
+ return blender::bke::adapt_mesh_domain_polygon_to_corner(*mesh_, std::move(attribute));
+ default:
+ break;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+
+ return {};
+}
+
+static Mesh *get_mesh_from_component_for_write(GeometryComponent &component)
+{
+ BLI_assert(component.type() == GEO_COMPONENT_TYPE_MESH);
+ MeshComponent &mesh_component = static_cast<MeshComponent &>(component);
+ return mesh_component.get_for_write();
+}
+
+static const Mesh *get_mesh_from_component_for_read(const GeometryComponent &component)
+{
+ BLI_assert(component.type() == GEO_COMPONENT_TYPE_MESH);
+ const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component);
+ return mesh_component.get_for_read();
+}
+
+namespace blender::bke {
+
+static float3 get_vertex_position(const MVert &vert)
+{
+ return float3(vert.co);
+}
+
+static void set_vertex_position(MVert &vert, const float3 &position)
+{
+ copy_v3_v3(vert.co, position);
+}
+
+static ReadAttributePtr make_vertex_position_read_attribute(const void *data,
+ const int domain_size)
+{
+ return std::make_unique<DerivedArrayReadAttribute<MVert, float3, get_vertex_position>>(
+ ATTR_DOMAIN_POINT, Span<MVert>((const MVert *)data, domain_size));
+}
+
+static WriteAttributePtr make_vertex_position_write_attribute(void *data, const int domain_size)
+{
+ return std::make_unique<
+ DerivedArrayWriteAttribute<MVert, float3, get_vertex_position, set_vertex_position>>(
+ ATTR_DOMAIN_POINT, MutableSpan<MVert>((MVert *)data, domain_size));
+}
+
+static void tag_normals_dirty_when_writing_position(GeometryComponent &component)
+{
+ Mesh *mesh = get_mesh_from_component_for_write(component);
+ if (mesh != nullptr) {
+ mesh->runtime.cd_dirty_vert |= CD_MASK_NORMAL;
+ }
+}
+
+static int get_material_index(const MPoly &mpoly)
+{
+ return static_cast<int>(mpoly.mat_nr);
+}
+
+static void set_material_index(MPoly &mpoly, const int &index)
+{
+ mpoly.mat_nr = static_cast<short>(std::clamp(index, 0, SHRT_MAX));
+}
+
+static ReadAttributePtr make_material_index_read_attribute(const void *data, const int domain_size)
+{
+ return std::make_unique<DerivedArrayReadAttribute<MPoly, int, get_material_index>>(
+ ATTR_DOMAIN_POLYGON, Span<MPoly>((const MPoly *)data, domain_size));
+}
+
+static WriteAttributePtr make_material_index_write_attribute(void *data, const int domain_size)
+{
+ return std::make_unique<
+ DerivedArrayWriteAttribute<MPoly, int, get_material_index, set_material_index>>(
+ ATTR_DOMAIN_POLYGON, MutableSpan<MPoly>((MPoly *)data, domain_size));
+}
+
+static bool get_shade_smooth(const MPoly &mpoly)
+{
+ return mpoly.flag & ME_SMOOTH;
+}
+
+static void set_shade_smooth(MPoly &mpoly, const bool &value)
+{
+ SET_FLAG_FROM_TEST(mpoly.flag, value, ME_SMOOTH);
+}
+
+static ReadAttributePtr make_shade_smooth_read_attribute(const void *data, const int domain_size)
+{
+ return std::make_unique<DerivedArrayReadAttribute<MPoly, bool, get_shade_smooth>>(
+ ATTR_DOMAIN_POLYGON, Span<MPoly>((const MPoly *)data, domain_size));
+}
+
+static WriteAttributePtr make_shade_smooth_write_attribute(void *data, const int domain_size)
+{
+ return std::make_unique<
+ DerivedArrayWriteAttribute<MPoly, bool, get_shade_smooth, set_shade_smooth>>(
+ ATTR_DOMAIN_POLYGON, MutableSpan<MPoly>((MPoly *)data, domain_size));
+}
+
+static float2 get_loop_uv(const MLoopUV &uv)
+{
+ return float2(uv.uv);
+}
+
+static void set_loop_uv(MLoopUV &uv, const float2 &co)
+{
+ copy_v2_v2(uv.uv, co);
+}
+
+static ReadAttributePtr make_uvs_read_attribute(const void *data, const int domain_size)
+{
+ return std::make_unique<DerivedArrayReadAttribute<MLoopUV, float2, get_loop_uv>>(
+ ATTR_DOMAIN_CORNER, Span((const MLoopUV *)data, domain_size));
+}
+
+static WriteAttributePtr make_uvs_write_attribute(void *data, const int domain_size)
+{
+ return std::make_unique<DerivedArrayWriteAttribute<MLoopUV, float2, get_loop_uv, set_loop_uv>>(
+ ATTR_DOMAIN_CORNER, MutableSpan((MLoopUV *)data, domain_size));
+}
+
+static Color4f get_loop_color(const MLoopCol &col)
+{
+ Color4f value;
+ rgba_uchar_to_float(value, &col.r);
+ return value;
+}
+
+static void set_loop_color(MLoopCol &col, const Color4f &value)
+{
+ rgba_float_to_uchar(&col.r, value);
+}
+
+static ReadAttributePtr make_vertex_color_read_attribute(const void *data, const int domain_size)
+{
+ return std::make_unique<DerivedArrayReadAttribute<MLoopCol, Color4f, get_loop_color>>(
+ ATTR_DOMAIN_CORNER, Span((const MLoopCol *)data, domain_size));
+}
+
+static WriteAttributePtr make_vertex_color_write_attribute(void *data, const int domain_size)
+{
+ return std::make_unique<
+ DerivedArrayWriteAttribute<MLoopCol, Color4f, get_loop_color, set_loop_color>>(
+ ATTR_DOMAIN_CORNER, MutableSpan((MLoopCol *)data, domain_size));
+}
+
+static float get_crease(const MEdge &edge)
+{
+ return edge.crease / 255.0f;
+}
+
+static void set_crease(MEdge &edge, const float &value)
+{
+ edge.crease = round_fl_to_uchar_clamp(value * 255.0f);
+}
+
+static ReadAttributePtr make_crease_read_attribute(const void *data, const int domain_size)
+{
+ return std::make_unique<DerivedArrayReadAttribute<MEdge, float, get_crease>>(
+ ATTR_DOMAIN_EDGE, Span((const MEdge *)data, domain_size));
+}
+
+static WriteAttributePtr make_crease_write_attribute(void *data, const int domain_size)
+{
+ return std::make_unique<DerivedArrayWriteAttribute<MEdge, float, get_crease, set_crease>>(
+ ATTR_DOMAIN_EDGE, MutableSpan((MEdge *)data, domain_size));
+}
+
+class VertexWeightWriteAttribute final : public WriteAttribute {
+ private:
+ MDeformVert *dverts_;
+ const int dvert_index_;
+
+ public:
+ VertexWeightWriteAttribute(MDeformVert *dverts, const int totvert, const int dvert_index)
+ : WriteAttribute(ATTR_DOMAIN_POINT, CPPType::get<float>(), totvert),
+ dverts_(dverts),
+ dvert_index_(dvert_index)
+ {
+ }
+
+ void get_internal(const int64_t index, void *r_value) const override
+ {
+ get_internal(dverts_, dvert_index_, index, r_value);
+ }
+
+ void set_internal(const int64_t index, const void *value) override
+ {
+ MDeformWeight *weight = BKE_defvert_ensure_index(&dverts_[index], dvert_index_);
+ weight->weight = *reinterpret_cast<const float *>(value);
+ }
+
+ static void get_internal(const MDeformVert *dverts,
+ const int dvert_index,
+ const int64_t index,
+ void *r_value)
+ {
+ if (dverts == nullptr) {
+ *(float *)r_value = 0.0f;
+ return;
+ }
+ const MDeformVert &dvert = dverts[index];
+ for (const MDeformWeight &weight : Span(dvert.dw, dvert.totweight)) {
+ if (weight.def_nr == dvert_index) {
+ *(float *)r_value = weight.weight;
+ return;
+ }
+ }
+ *(float *)r_value = 0.0f;
+ }
+};
+
+class VertexWeightReadAttribute final : public ReadAttribute {
+ private:
+ const MDeformVert *dverts_;
+ const int dvert_index_;
+
+ public:
+ VertexWeightReadAttribute(const MDeformVert *dverts, const int totvert, const int dvert_index)
+ : ReadAttribute(ATTR_DOMAIN_POINT, CPPType::get<float>(), totvert),
+ dverts_(dverts),
+ dvert_index_(dvert_index)
+ {
+ }
+
+ void get_internal(const int64_t index, void *r_value) const override
+ {
+ VertexWeightWriteAttribute::get_internal(dverts_, dvert_index_, index, r_value);
+ }
+};
+
+/**
+ * This provider makes vertex groups available as float attributes.
+ */
+class VertexGroupsAttributeProvider final : public DynamicAttributesProvider {
+ public:
+ ReadAttributePtr try_get_for_read(const GeometryComponent &component,
+ const StringRef attribute_name) const final
+ {
+ BLI_assert(component.type() == GEO_COMPONENT_TYPE_MESH);
+ const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component);
+ const Mesh *mesh = mesh_component.get_for_read();
+ const int vertex_group_index = mesh_component.vertex_group_names().lookup_default_as(
+ attribute_name, -1);
+ if (vertex_group_index < 0) {
+ return {};
+ }
+ if (mesh == nullptr || mesh->dvert == nullptr) {
+ static const float default_value = 0.0f;
+ return std::make_unique<ConstantReadAttribute>(
+ ATTR_DOMAIN_POINT, mesh->totvert, CPPType::get<float>(), &default_value);
+ }
+ return std::make_unique<VertexWeightReadAttribute>(
+ mesh->dvert, mesh->totvert, vertex_group_index);
+ }
+
+ WriteAttributePtr try_get_for_write(GeometryComponent &component,
+ const StringRef attribute_name) const final
+ {
+ BLI_assert(component.type() == GEO_COMPONENT_TYPE_MESH);
+ MeshComponent &mesh_component = static_cast<MeshComponent &>(component);
+ Mesh *mesh = mesh_component.get_for_write();
+ if (mesh == nullptr) {
+ return {};
+ }
+ const int vertex_group_index = mesh_component.vertex_group_names().lookup_default_as(
+ attribute_name, -1);
+ if (vertex_group_index < 0) {
+ return {};
+ }
+ if (mesh->dvert == nullptr) {
+ BKE_object_defgroup_data_create(&mesh->id);
+ }
+ else {
+ /* Copy the data layer if it is shared with some other mesh. */
+ mesh->dvert = (MDeformVert *)CustomData_duplicate_referenced_layer(
+ &mesh->vdata, CD_MDEFORMVERT, mesh->totvert);
+ }
+ return std::make_unique<blender::bke::VertexWeightWriteAttribute>(
+ mesh->dvert, mesh->totvert, vertex_group_index);
+ }
+
+ bool try_delete(GeometryComponent &component, const StringRef attribute_name) const final
+ {
+ BLI_assert(component.type() == GEO_COMPONENT_TYPE_MESH);
+ MeshComponent &mesh_component = static_cast<MeshComponent &>(component);
+
+ const int vertex_group_index = mesh_component.vertex_group_names().pop_default_as(
+ attribute_name, -1);
+ if (vertex_group_index < 0) {
+ return false;
+ }
+ Mesh *mesh = mesh_component.get_for_write();
+ if (mesh == nullptr) {
+ return true;
+ }
+ if (mesh->dvert == nullptr) {
+ return true;
+ }
+ for (MDeformVert &dvert : MutableSpan(mesh->dvert, mesh->totvert)) {
+ MDeformWeight *weight = BKE_defvert_find_index(&dvert, vertex_group_index);
+ BKE_defvert_remove_group(&dvert, weight);
+ }
+ return true;
+ }
+
+ bool foreach_attribute(const GeometryComponent &component,
+ const AttributeForeachCallback callback) const final
+ {
+ BLI_assert(component.type() == GEO_COMPONENT_TYPE_MESH);
+ const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component);
+ for (const auto item : mesh_component.vertex_group_names().items()) {
+ const StringRefNull name = item.key;
+ const int vertex_group_index = item.value;
+ if (vertex_group_index >= 0) {
+ AttributeMetaData meta_data{ATTR_DOMAIN_POINT, CD_PROP_FLOAT};
+ if (!callback(name, meta_data)) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ void foreach_domain(const FunctionRef<void(AttributeDomain)> callback) const final
+ {
+ callback(ATTR_DOMAIN_POINT);
+ }
+};
+
+/**
+ * This provider makes face normals available as a read-only float3 attribute.
+ */
+class NormalAttributeProvider final : public BuiltinAttributeProvider {
+ public:
+ NormalAttributeProvider()
+ : BuiltinAttributeProvider(
+ "normal", ATTR_DOMAIN_POLYGON, CD_PROP_FLOAT3, NonCreatable, Readonly, NonDeletable)
+ {
+ }
+
+ ReadAttributePtr try_get_for_read(const GeometryComponent &component) const final
+ {
+ const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component);
+ const Mesh *mesh = mesh_component.get_for_read();
+ if (mesh == nullptr) {
+ return {};
+ }
+
+ /* Use existing normals if possible. */
+ if (!(mesh->runtime.cd_dirty_poly & CD_MASK_NORMAL) &&
+ CustomData_has_layer(&mesh->pdata, CD_NORMAL)) {
+ const void *data = CustomData_get_layer(&mesh->pdata, CD_NORMAL);
+
+ return std::make_unique<ArrayReadAttribute<float3>>(
+ ATTR_DOMAIN_POLYGON, Span<float3>((const float3 *)data, mesh->totpoly));
+ }
+
+ Array<float3> normals(mesh->totpoly);
+ for (const int i : IndexRange(mesh->totpoly)) {
+ const MPoly *poly = &mesh->mpoly[i];
+ BKE_mesh_calc_poly_normal(poly, &mesh->mloop[poly->loopstart], mesh->mvert, normals[i]);
+ }
+
+ return std::make_unique<OwnedArrayReadAttribute<float3>>(ATTR_DOMAIN_POLYGON,
+ std::move(normals));
+ }
+
+ WriteAttributePtr try_get_for_write(GeometryComponent &UNUSED(component)) const final
+ {
+ return {};
+ }
+
+ bool try_delete(GeometryComponent &UNUSED(component)) const final
+ {
+ return false;
+ }
+
+ bool try_create(GeometryComponent &UNUSED(component)) const final
+ {
+ return false;
+ }
+
+ bool exists(const GeometryComponent &component) const final
+ {
+ return component.attribute_domain_size(ATTR_DOMAIN_POLYGON) != 0;
+ }
+};
+
+/**
+ * In this function all the attribute providers for a mesh component are created. Most data in this
+ * function is statically allocated, because it does not change over time.
+ */
+static ComponentAttributeProviders create_attribute_providers_for_mesh()
+{
+ static auto update_custom_data_pointers = [](GeometryComponent &component) {
+ Mesh *mesh = get_mesh_from_component_for_write(component);
+ if (mesh != nullptr) {
+ BKE_mesh_update_customdata_pointers(mesh, false);
+ }
+ };
+
+#define MAKE_MUTABLE_CUSTOM_DATA_GETTER(NAME) \
+ [](GeometryComponent &component) -> CustomData * { \
+ Mesh *mesh = get_mesh_from_component_for_write(component); \
+ return mesh ? &mesh->NAME : nullptr; \
+ }
+#define MAKE_CONST_CUSTOM_DATA_GETTER(NAME) \
+ [](const GeometryComponent &component) -> const CustomData * { \
+ const Mesh *mesh = get_mesh_from_component_for_read(component); \
+ return mesh ? &mesh->NAME : nullptr; \
+ }
+
+ static CustomDataAccessInfo corner_access = {MAKE_MUTABLE_CUSTOM_DATA_GETTER(ldata),
+ MAKE_CONST_CUSTOM_DATA_GETTER(ldata),
+ update_custom_data_pointers};
+ static CustomDataAccessInfo point_access = {MAKE_MUTABLE_CUSTOM_DATA_GETTER(vdata),
+ MAKE_CONST_CUSTOM_DATA_GETTER(vdata),
+ update_custom_data_pointers};
+ static CustomDataAccessInfo edge_access = {MAKE_MUTABLE_CUSTOM_DATA_GETTER(edata),
+ MAKE_CONST_CUSTOM_DATA_GETTER(edata),
+ update_custom_data_pointers};
+ static CustomDataAccessInfo polygon_access = {MAKE_MUTABLE_CUSTOM_DATA_GETTER(pdata),
+ MAKE_CONST_CUSTOM_DATA_GETTER(pdata),
+ update_custom_data_pointers};
+
+#undef MAKE_CONST_CUSTOM_DATA_GETTER
+#undef MAKE_MUTABLE_CUSTOM_DATA_GETTER
+
+ static BuiltinCustomDataLayerProvider position("position",
+ ATTR_DOMAIN_POINT,
+ CD_PROP_FLOAT3,
+ CD_MVERT,
+ BuiltinAttributeProvider::NonCreatable,
+ BuiltinAttributeProvider::Writable,
+ BuiltinAttributeProvider::NonDeletable,
+ point_access,
+ make_vertex_position_read_attribute,
+ make_vertex_position_write_attribute,
+ tag_normals_dirty_when_writing_position);
+
+ static NormalAttributeProvider normal;
+
+ static BuiltinCustomDataLayerProvider material_index("material_index",
+ ATTR_DOMAIN_POLYGON,
+ CD_PROP_INT32,
+ CD_MPOLY,
+ BuiltinAttributeProvider::NonCreatable,
+ BuiltinAttributeProvider::Writable,
+ BuiltinAttributeProvider::NonDeletable,
+ polygon_access,
+ make_material_index_read_attribute,
+ make_material_index_write_attribute,
+ nullptr);
+
+ static BuiltinCustomDataLayerProvider shade_smooth("shade_smooth",
+ ATTR_DOMAIN_POLYGON,
+ CD_PROP_BOOL,
+ CD_MPOLY,
+ BuiltinAttributeProvider::NonCreatable,
+ BuiltinAttributeProvider::Writable,
+ BuiltinAttributeProvider::NonDeletable,
+ polygon_access,
+ make_shade_smooth_read_attribute,
+ make_shade_smooth_write_attribute,
+ nullptr);
+
+ static BuiltinCustomDataLayerProvider crease("crease",
+ ATTR_DOMAIN_EDGE,
+ CD_PROP_FLOAT,
+ CD_MEDGE,
+ BuiltinAttributeProvider::NonCreatable,
+ BuiltinAttributeProvider::Writable,
+ BuiltinAttributeProvider::NonDeletable,
+ edge_access,
+ make_crease_read_attribute,
+ make_crease_write_attribute,
+ nullptr);
+
+ static NamedLegacyCustomDataProvider uvs(ATTR_DOMAIN_CORNER,
+ CD_PROP_FLOAT2,
+ CD_MLOOPUV,
+ corner_access,
+ make_uvs_read_attribute,
+ make_uvs_write_attribute);
+
+ static NamedLegacyCustomDataProvider vertex_colors(ATTR_DOMAIN_CORNER,
+ CD_PROP_COLOR,
+ CD_MLOOPCOL,
+ corner_access,
+ make_vertex_color_read_attribute,
+ make_vertex_color_write_attribute);
+
+ static VertexGroupsAttributeProvider vertex_groups;
+ static CustomDataAttributeProvider corner_custom_data(ATTR_DOMAIN_CORNER, corner_access);
+ static CustomDataAttributeProvider point_custom_data(ATTR_DOMAIN_POINT, point_access);
+ static CustomDataAttributeProvider edge_custom_data(ATTR_DOMAIN_EDGE, edge_access);
+ static CustomDataAttributeProvider polygon_custom_data(ATTR_DOMAIN_POLYGON, polygon_access);
+
+ return ComponentAttributeProviders({&position, &material_index, &shade_smooth, &normal, &crease},
+ {&uvs,
+ &vertex_colors,
+ &corner_custom_data,
+ &vertex_groups,
+ &point_custom_data,
+ &edge_custom_data,
+ &polygon_custom_data});
+}
+
+} // namespace blender::bke
+
+const blender::bke::ComponentAttributeProviders *MeshComponent::get_attribute_providers() const
+{
+ static blender::bke::ComponentAttributeProviders providers =
+ blender::bke::create_attribute_providers_for_mesh();
+ return &providers;
+}
+
+/** \} */
diff --git a/source/blender/blenkernel/intern/geometry_component_pointcloud.cc b/source/blender/blenkernel/intern/geometry_component_pointcloud.cc
new file mode 100644
index 00000000000..073f457ae54
--- /dev/null
+++ b/source/blender/blenkernel/intern/geometry_component_pointcloud.cc
@@ -0,0 +1,205 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "DNA_pointcloud_types.h"
+
+#include "BKE_attribute_access.hh"
+#include "BKE_geometry_set.hh"
+#include "BKE_lib_id.h"
+#include "BKE_pointcloud.h"
+
+#include "attribute_access_intern.hh"
+
+/* -------------------------------------------------------------------- */
+/** \name Geometry Component Implementation
+ * \{ */
+
+PointCloudComponent::PointCloudComponent() : GeometryComponent(GEO_COMPONENT_TYPE_POINT_CLOUD)
+{
+}
+
+PointCloudComponent::~PointCloudComponent()
+{
+ this->clear();
+}
+
+GeometryComponent *PointCloudComponent::copy() const
+{
+ PointCloudComponent *new_component = new PointCloudComponent();
+ if (pointcloud_ != nullptr) {
+ new_component->pointcloud_ = BKE_pointcloud_copy_for_eval(pointcloud_, false);
+ new_component->ownership_ = GeometryOwnershipType::Owned;
+ }
+ return new_component;
+}
+
+void PointCloudComponent::clear()
+{
+ BLI_assert(this->is_mutable());
+ if (pointcloud_ != nullptr) {
+ if (ownership_ == GeometryOwnershipType::Owned) {
+ BKE_id_free(nullptr, pointcloud_);
+ }
+ pointcloud_ = nullptr;
+ }
+}
+
+bool PointCloudComponent::has_pointcloud() const
+{
+ return pointcloud_ != nullptr;
+}
+
+/* Clear the component and replace it with the new point cloud. */
+void PointCloudComponent::replace(PointCloud *pointcloud, GeometryOwnershipType ownership)
+{
+ BLI_assert(this->is_mutable());
+ this->clear();
+ pointcloud_ = pointcloud;
+ ownership_ = ownership;
+}
+
+/* Return the point cloud and clear the component. The caller takes over responsibility for freeing
+ * the point cloud (if the component was responsible before). */
+PointCloud *PointCloudComponent::release()
+{
+ BLI_assert(this->is_mutable());
+ PointCloud *pointcloud = pointcloud_;
+ pointcloud_ = nullptr;
+ return pointcloud;
+}
+
+/* Get the point cloud from this component. This method can be used by multiple threads at the same
+ * time. Therefore, the returned point cloud should not be modified. No ownership is transferred.
+ */
+const PointCloud *PointCloudComponent::get_for_read() const
+{
+ return pointcloud_;
+}
+
+/* Get the point cloud from this component. This method can only be used when the component is
+ * mutable, i.e. it is not shared. The returned point cloud can be modified. No ownership is
+ * transferred. */
+PointCloud *PointCloudComponent::get_for_write()
+{
+ BLI_assert(this->is_mutable());
+ if (ownership_ == GeometryOwnershipType::ReadOnly) {
+ pointcloud_ = BKE_pointcloud_copy_for_eval(pointcloud_, false);
+ ownership_ = GeometryOwnershipType::Owned;
+ }
+ return pointcloud_;
+}
+
+bool PointCloudComponent::is_empty() const
+{
+ return pointcloud_ == nullptr;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Attribute Access
+ * \{ */
+
+int PointCloudComponent::attribute_domain_size(const AttributeDomain domain) const
+{
+ BLI_assert(domain == ATTR_DOMAIN_POINT);
+ UNUSED_VARS_NDEBUG(domain);
+ if (pointcloud_ == nullptr) {
+ return 0;
+ }
+ return pointcloud_->totpoint;
+}
+
+namespace blender::bke {
+
+template<typename T, AttributeDomain Domain>
+static ReadAttributePtr make_array_read_attribute(const void *data, const int domain_size)
+{
+ return std::make_unique<ArrayReadAttribute<T>>(Domain, Span<T>((const T *)data, domain_size));
+}
+
+template<typename T, AttributeDomain Domain>
+static WriteAttributePtr make_array_write_attribute(void *data, const int domain_size)
+{
+ return std::make_unique<ArrayWriteAttribute<T>>(Domain, MutableSpan<T>((T *)data, domain_size));
+}
+
+/**
+ * In this function all the attribute providers for a point cloud component are created. Most data
+ * in this function is statically allocated, because it does not change over time.
+ */
+static ComponentAttributeProviders create_attribute_providers_for_point_cloud()
+{
+ static auto update_custom_data_pointers = [](GeometryComponent &component) {
+ PointCloudComponent &pointcloud_component = static_cast<PointCloudComponent &>(component);
+ PointCloud *pointcloud = pointcloud_component.get_for_write();
+ if (pointcloud != nullptr) {
+ BKE_pointcloud_update_customdata_pointers(pointcloud);
+ }
+ };
+ static CustomDataAccessInfo point_access = {
+ [](GeometryComponent &component) -> CustomData * {
+ PointCloudComponent &pointcloud_component = static_cast<PointCloudComponent &>(component);
+ PointCloud *pointcloud = pointcloud_component.get_for_write();
+ return pointcloud ? &pointcloud->pdata : nullptr;
+ },
+ [](const GeometryComponent &component) -> const CustomData * {
+ const PointCloudComponent &pointcloud_component = static_cast<const PointCloudComponent &>(
+ component);
+ const PointCloud *pointcloud = pointcloud_component.get_for_read();
+ return pointcloud ? &pointcloud->pdata : nullptr;
+ },
+ update_custom_data_pointers};
+
+ static BuiltinCustomDataLayerProvider position(
+ "position",
+ ATTR_DOMAIN_POINT,
+ CD_PROP_FLOAT3,
+ CD_PROP_FLOAT3,
+ BuiltinAttributeProvider::NonCreatable,
+ BuiltinAttributeProvider::Writable,
+ BuiltinAttributeProvider::NonDeletable,
+ point_access,
+ make_array_read_attribute<float3, ATTR_DOMAIN_POINT>,
+ make_array_write_attribute<float3, ATTR_DOMAIN_POINT>,
+ nullptr);
+ static BuiltinCustomDataLayerProvider radius(
+ "radius",
+ ATTR_DOMAIN_POINT,
+ CD_PROP_FLOAT,
+ CD_PROP_FLOAT,
+ BuiltinAttributeProvider::Creatable,
+ BuiltinAttributeProvider::Writable,
+ BuiltinAttributeProvider::Deletable,
+ point_access,
+ make_array_read_attribute<float, ATTR_DOMAIN_POINT>,
+ make_array_write_attribute<float, ATTR_DOMAIN_POINT>,
+ nullptr);
+ static CustomDataAttributeProvider point_custom_data(ATTR_DOMAIN_POINT, point_access);
+ return ComponentAttributeProviders({&position, &radius}, {&point_custom_data});
+}
+
+} // namespace blender::bke
+
+const blender::bke::ComponentAttributeProviders *PointCloudComponent::get_attribute_providers()
+ const
+{
+ static blender::bke::ComponentAttributeProviders providers =
+ blender::bke::create_attribute_providers_for_point_cloud();
+ return &providers;
+}
+
+/** \} */
diff --git a/source/blender/blenkernel/intern/geometry_component_volume.cc b/source/blender/blenkernel/intern/geometry_component_volume.cc
new file mode 100644
index 00000000000..fd2327e0bf5
--- /dev/null
+++ b/source/blender/blenkernel/intern/geometry_component_volume.cc
@@ -0,0 +1,100 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "DNA_volume_types.h"
+
+#include "BKE_geometry_set.hh"
+#include "BKE_lib_id.h"
+#include "BKE_volume.h"
+
+/* -------------------------------------------------------------------- */
+/** \name Geometry Component Implementation
+ * \{ */
+
+VolumeComponent::VolumeComponent() : GeometryComponent(GEO_COMPONENT_TYPE_VOLUME)
+{
+}
+
+VolumeComponent::~VolumeComponent()
+{
+ this->clear();
+}
+
+GeometryComponent *VolumeComponent::copy() const
+{
+ VolumeComponent *new_component = new VolumeComponent();
+ if (volume_ != nullptr) {
+ new_component->volume_ = BKE_volume_copy_for_eval(volume_, false);
+ new_component->ownership_ = GeometryOwnershipType::Owned;
+ }
+ return new_component;
+}
+
+void VolumeComponent::clear()
+{
+ BLI_assert(this->is_mutable());
+ if (volume_ != nullptr) {
+ if (ownership_ == GeometryOwnershipType::Owned) {
+ BKE_id_free(nullptr, volume_);
+ }
+ volume_ = nullptr;
+ }
+}
+
+bool VolumeComponent::has_volume() const
+{
+ return volume_ != nullptr;
+}
+
+/* Clear the component and replace it with the new volume. */
+void VolumeComponent::replace(Volume *volume, GeometryOwnershipType ownership)
+{
+ BLI_assert(this->is_mutable());
+ this->clear();
+ volume_ = volume;
+ ownership_ = ownership;
+}
+
+/* Return the volume and clear the component. The caller takes over responsibility for freeing the
+ * volume (if the component was responsible before). */
+Volume *VolumeComponent::release()
+{
+ BLI_assert(this->is_mutable());
+ Volume *volume = volume_;
+ volume_ = nullptr;
+ return volume;
+}
+
+/* Get the volume from this component. This method can be used by multiple threads at the same
+ * time. Therefore, the returned volume should not be modified. No ownership is transferred. */
+const Volume *VolumeComponent::get_for_read() const
+{
+ return volume_;
+}
+
+/* Get the volume from this component. This method can only be used when the component is mutable,
+ * i.e. it is not shared. The returned volume can be modified. No ownership is transferred. */
+Volume *VolumeComponent::get_for_write()
+{
+ BLI_assert(this->is_mutable());
+ if (ownership_ == GeometryOwnershipType::ReadOnly) {
+ volume_ = BKE_volume_copy_for_eval(volume_, false);
+ ownership_ = GeometryOwnershipType::Owned;
+ }
+ return volume_;
+}
+
+/** \} */
diff --git a/source/blender/blenkernel/intern/geometry_set.cc b/source/blender/blenkernel/intern/geometry_set.cc
index 0274dfdbd1c..f47d88cbeed 100644
--- a/source/blender/blenkernel/intern/geometry_set.cc
+++ b/source/blender/blenkernel/intern/geometry_set.cc
@@ -56,13 +56,13 @@ GeometryComponent ::~GeometryComponent()
GeometryComponent *GeometryComponent::create(GeometryComponentType component_type)
{
switch (component_type) {
- case GeometryComponentType::Mesh:
+ case GEO_COMPONENT_TYPE_MESH:
return new MeshComponent();
- case GeometryComponentType::PointCloud:
+ case GEO_COMPONENT_TYPE_POINT_CLOUD:
return new PointCloudComponent();
- case GeometryComponentType::Instances:
+ case GEO_COMPONENT_TYPE_INSTANCES:
return new InstancesComponent();
- case GeometryComponentType::Volume:
+ case GEO_COMPONENT_TYPE_VOLUME:
return new VolumeComponent();
}
BLI_assert(false);
@@ -311,437 +311,6 @@ Volume *GeometrySet::get_volume_for_write()
/** \} */
/* -------------------------------------------------------------------- */
-/** \name Mesh Component
- * \{ */
-
-MeshComponent::MeshComponent() : GeometryComponent(GeometryComponentType::Mesh)
-{
-}
-
-MeshComponent::~MeshComponent()
-{
- this->clear();
-}
-
-GeometryComponent *MeshComponent::copy() const
-{
- MeshComponent *new_component = new MeshComponent();
- if (mesh_ != nullptr) {
- new_component->mesh_ = BKE_mesh_copy_for_eval(mesh_, false);
- new_component->ownership_ = GeometryOwnershipType::Owned;
- new_component->vertex_group_names_ = blender::Map(vertex_group_names_);
- }
- return new_component;
-}
-
-void MeshComponent::clear()
-{
- BLI_assert(this->is_mutable());
- if (mesh_ != nullptr) {
- if (ownership_ == GeometryOwnershipType::Owned) {
- BKE_id_free(nullptr, mesh_);
- }
- mesh_ = nullptr;
- }
- vertex_group_names_.clear();
-}
-
-bool MeshComponent::has_mesh() const
-{
- return mesh_ != nullptr;
-}
-
-/* Clear the component and replace it with the new mesh. */
-void MeshComponent::replace(Mesh *mesh, GeometryOwnershipType ownership)
-{
- BLI_assert(this->is_mutable());
- this->clear();
- mesh_ = mesh;
- ownership_ = ownership;
-}
-
-/* This function exists for the same reason as #vertex_group_names_. Non-nodes modifiers need to
- * be able to replace the mesh data without losing the vertex group names, which may have come
- * from another object. */
-void MeshComponent::replace_mesh_but_keep_vertex_group_names(Mesh *mesh,
- GeometryOwnershipType ownership)
-{
- BLI_assert(this->is_mutable());
- if (mesh_ != nullptr) {
- if (ownership_ == GeometryOwnershipType::Owned) {
- BKE_id_free(nullptr, mesh_);
- }
- mesh_ = nullptr;
- }
- mesh_ = mesh;
- ownership_ = ownership;
-}
-
-/* Return the mesh and clear the component. The caller takes over responsibility for freeing the
- * mesh (if the component was responsible before). */
-Mesh *MeshComponent::release()
-{
- BLI_assert(this->is_mutable());
- Mesh *mesh = mesh_;
- mesh_ = nullptr;
- return mesh;
-}
-
-void MeshComponent::copy_vertex_group_names_from_object(const Object &object)
-{
- BLI_assert(this->is_mutable());
- vertex_group_names_.clear();
- int index = 0;
- LISTBASE_FOREACH (const bDeformGroup *, group, &object.defbase) {
- vertex_group_names_.add(group->name, index);
- index++;
- }
-}
-
-const blender::Map<std::string, int> &MeshComponent::vertex_group_names() const
-{
- return vertex_group_names_;
-}
-
-/* This is only exposed for the internal attribute API. */
-blender::Map<std::string, int> &MeshComponent::vertex_group_names()
-{
- return vertex_group_names_;
-}
-
-/* Get the mesh from this component. This method can be used by multiple threads at the same
- * time. Therefore, the returned mesh should not be modified. No ownership is transferred. */
-const Mesh *MeshComponent::get_for_read() const
-{
- return mesh_;
-}
-
-/* Get the mesh from this component. This method can only be used when the component is mutable,
- * i.e. it is not shared. The returned mesh can be modified. No ownership is transferred. */
-Mesh *MeshComponent::get_for_write()
-{
- BLI_assert(this->is_mutable());
- if (ownership_ == GeometryOwnershipType::ReadOnly) {
- mesh_ = BKE_mesh_copy_for_eval(mesh_, false);
- ownership_ = GeometryOwnershipType::Owned;
- }
- return mesh_;
-}
-
-bool MeshComponent::is_empty() const
-{
- return mesh_ == nullptr;
-}
-
-/** \} */
-
-/* -------------------------------------------------------------------- */
-/** \name Pointcloud Component
- * \{ */
-
-PointCloudComponent::PointCloudComponent() : GeometryComponent(GeometryComponentType::PointCloud)
-{
-}
-
-PointCloudComponent::~PointCloudComponent()
-{
- this->clear();
-}
-
-GeometryComponent *PointCloudComponent::copy() const
-{
- PointCloudComponent *new_component = new PointCloudComponent();
- if (pointcloud_ != nullptr) {
- new_component->pointcloud_ = BKE_pointcloud_copy_for_eval(pointcloud_, false);
- new_component->ownership_ = GeometryOwnershipType::Owned;
- }
- return new_component;
-}
-
-void PointCloudComponent::clear()
-{
- BLI_assert(this->is_mutable());
- if (pointcloud_ != nullptr) {
- if (ownership_ == GeometryOwnershipType::Owned) {
- BKE_id_free(nullptr, pointcloud_);
- }
- pointcloud_ = nullptr;
- }
-}
-
-bool PointCloudComponent::has_pointcloud() const
-{
- return pointcloud_ != nullptr;
-}
-
-/* Clear the component and replace it with the new point cloud. */
-void PointCloudComponent::replace(PointCloud *pointcloud, GeometryOwnershipType ownership)
-{
- BLI_assert(this->is_mutable());
- this->clear();
- pointcloud_ = pointcloud;
- ownership_ = ownership;
-}
-
-/* Return the point cloud and clear the component. The caller takes over responsibility for freeing
- * the point cloud (if the component was responsible before). */
-PointCloud *PointCloudComponent::release()
-{
- BLI_assert(this->is_mutable());
- PointCloud *pointcloud = pointcloud_;
- pointcloud_ = nullptr;
- return pointcloud;
-}
-
-/* Get the point cloud from this component. This method can be used by multiple threads at the same
- * time. Therefore, the returned point cloud should not be modified. No ownership is transferred.
- */
-const PointCloud *PointCloudComponent::get_for_read() const
-{
- return pointcloud_;
-}
-
-/* Get the point cloud from this component. This method can only be used when the component is
- * mutable, i.e. it is not shared. The returned point cloud can be modified. No ownership is
- * transferred. */
-PointCloud *PointCloudComponent::get_for_write()
-{
- BLI_assert(this->is_mutable());
- if (ownership_ == GeometryOwnershipType::ReadOnly) {
- pointcloud_ = BKE_pointcloud_copy_for_eval(pointcloud_, false);
- ownership_ = GeometryOwnershipType::Owned;
- }
- return pointcloud_;
-}
-
-bool PointCloudComponent::is_empty() const
-{
- return pointcloud_ == nullptr;
-}
-
-/** \} */
-
-/* -------------------------------------------------------------------- */
-/** \name Instances Component
- * \{ */
-
-InstancesComponent::InstancesComponent() : GeometryComponent(GeometryComponentType::Instances)
-{
-}
-
-GeometryComponent *InstancesComponent::copy() const
-{
- InstancesComponent *new_component = new InstancesComponent();
- new_component->transforms_ = transforms_;
- new_component->instanced_data_ = instanced_data_;
- return new_component;
-}
-
-void InstancesComponent::clear()
-{
- instanced_data_.clear();
- transforms_.clear();
-}
-
-void InstancesComponent::add_instance(Object *object, float4x4 transform, const int id)
-{
- InstancedData data;
- data.type = INSTANCE_DATA_TYPE_OBJECT;
- data.data.object = object;
- this->add_instance(data, transform, id);
-}
-
-void InstancesComponent::add_instance(Collection *collection, float4x4 transform, const int id)
-{
- InstancedData data;
- data.type = INSTANCE_DATA_TYPE_COLLECTION;
- data.data.collection = collection;
- this->add_instance(data, transform, id);
-}
-
-void InstancesComponent::add_instance(InstancedData data, float4x4 transform, const int id)
-{
- instanced_data_.append(data);
- transforms_.append(transform);
- ids_.append(id);
-}
-
-Span<InstancedData> InstancesComponent::instanced_data() const
-{
- return instanced_data_;
-}
-
-Span<float4x4> InstancesComponent::transforms() const
-{
- return transforms_;
-}
-
-Span<int> InstancesComponent::ids() const
-{
- return ids_;
-}
-
-MutableSpan<float4x4> InstancesComponent::transforms()
-{
- return transforms_;
-}
-
-int InstancesComponent::instances_amount() const
-{
- const int size = instanced_data_.size();
- BLI_assert(transforms_.size() == size);
- return size;
-}
-
-bool InstancesComponent::is_empty() const
-{
- return transforms_.size() == 0;
-}
-
-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> InstancesComponent::almost_unique_ids() const
-{
- std::lock_guard lock(almost_unique_ids_mutex_);
- if (almost_unique_ids_.size() != ids_.size()) {
- almost_unique_ids_ = generate_unique_instance_ids(ids_);
- }
- return almost_unique_ids_;
-}
-
-/** \} */
-
-/* -------------------------------------------------------------------- */
-/** \name Volume Component
- * \{ */
-
-VolumeComponent::VolumeComponent() : GeometryComponent(GeometryComponentType::Volume)
-{
-}
-
-VolumeComponent::~VolumeComponent()
-{
- this->clear();
-}
-
-GeometryComponent *VolumeComponent::copy() const
-{
- VolumeComponent *new_component = new VolumeComponent();
- if (volume_ != nullptr) {
- new_component->volume_ = BKE_volume_copy_for_eval(volume_, false);
- new_component->ownership_ = GeometryOwnershipType::Owned;
- }
- return new_component;
-}
-
-void VolumeComponent::clear()
-{
- BLI_assert(this->is_mutable());
- if (volume_ != nullptr) {
- if (ownership_ == GeometryOwnershipType::Owned) {
- BKE_id_free(nullptr, volume_);
- }
- volume_ = nullptr;
- }
-}
-
-bool VolumeComponent::has_volume() const
-{
- return volume_ != nullptr;
-}
-
-/* Clear the component and replace it with the new volume. */
-void VolumeComponent::replace(Volume *volume, GeometryOwnershipType ownership)
-{
- BLI_assert(this->is_mutable());
- this->clear();
- volume_ = volume;
- ownership_ = ownership;
-}
-
-/* Return the volume and clear the component. The caller takes over responsibility for freeing the
- * volume (if the component was responsible before). */
-Volume *VolumeComponent::release()
-{
- BLI_assert(this->is_mutable());
- Volume *volume = volume_;
- volume_ = nullptr;
- return volume;
-}
-
-/* Get the volume from this component. This method can be used by multiple threads at the same
- * time. Therefore, the returned volume should not be modified. No ownership is transferred. */
-const Volume *VolumeComponent::get_for_read() const
-{
- return volume_;
-}
-
-/* Get the volume from this component. This method can only be used when the component is mutable,
- * i.e. it is not shared. The returned volume can be modified. No ownership is transferred. */
-Volume *VolumeComponent::get_for_write()
-{
- BLI_assert(this->is_mutable());
- if (ownership_ == GeometryOwnershipType::ReadOnly) {
- volume_ = BKE_volume_copy_for_eval(volume_, false);
- ownership_ = GeometryOwnershipType::Owned;
- }
- return volume_;
-}
-
-/** \} */
-
-/* -------------------------------------------------------------------- */
/** \name C API
* \{ */
diff --git a/source/blender/blenkernel/intern/geometry_set_instances.cc b/source/blender/blenkernel/intern/geometry_set_instances.cc
index 1a260c5d48e..ce54ec7911f 100644
--- a/source/blender/blenkernel/intern/geometry_set_instances.cc
+++ b/source/blender/blenkernel/intern/geometry_set_instances.cc
@@ -371,14 +371,17 @@ static void join_instance_groups_mesh(Span<GeometryInstanceGroup> set_groups,
dst_component.replace(new_mesh);
Vector<GeometryComponentType> component_types;
- component_types.append(GeometryComponentType::Mesh);
+ component_types.append(GEO_COMPONENT_TYPE_MESH);
if (convert_points_to_vertices) {
- component_types.append(GeometryComponentType::PointCloud);
+ component_types.append(GEO_COMPONENT_TYPE_POINT_CLOUD);
}
/* Don't copy attributes that are stored directly in the mesh data structs. */
Map<std::string, AttributeKind> attributes;
- gather_attribute_info(attributes, component_types, set_groups, {"position", "material_index"});
+ gather_attribute_info(attributes,
+ component_types,
+ set_groups,
+ {"position", "material_index", "normal", "shade_smooth", "crease"});
join_attributes(
set_groups, component_types, attributes, static_cast<GeometryComponent &>(dst_component));
}
@@ -399,9 +402,9 @@ static void join_instance_groups_pointcloud(Span<GeometryInstanceGroup> set_grou
PointCloud *pointcloud = BKE_pointcloud_new_nomain(totpoint);
dst_component.replace(pointcloud);
Map<std::string, AttributeKind> attributes;
- gather_attribute_info(attributes, {GeometryComponentType::PointCloud}, set_groups, {});
+ gather_attribute_info(attributes, {GEO_COMPONENT_TYPE_POINT_CLOUD}, set_groups, {});
join_attributes(set_groups,
- {GeometryComponentType::PointCloud},
+ {GEO_COMPONENT_TYPE_POINT_CLOUD},
attributes,
static_cast<GeometryComponent &>(dst_component));
}
diff --git a/source/blender/blenkernel/intern/gpencil.c b/source/blender/blenkernel/intern/gpencil.c
index 00dcaad83db..3b46672f9cd 100644
--- a/source/blender/blenkernel/intern/gpencil.c
+++ b/source/blender/blenkernel/intern/gpencil.c
@@ -1663,6 +1663,31 @@ bGPDlayer *BKE_gpencil_layer_active_get(bGPdata *gpd)
return NULL;
}
+bGPDlayer *BKE_gpencil_layer_get_by_name(bGPdata *gpd, char *name, int first_if_not_found)
+{
+ bGPDlayer *gpl;
+ int i = 0;
+
+ /* error checking */
+ if (ELEM(NULL, gpd, gpd->layers.first)) {
+ return NULL;
+ }
+
+ /* loop over layers until found (assume only one active) */
+ for (gpl = gpd->layers.first; gpl; gpl = gpl->next) {
+ if (STREQ(name, gpl->info)) {
+ return gpl;
+ }
+ i++;
+ }
+
+ /* no such layer */
+ if (first_if_not_found) {
+ return gpd->layers.first;
+ }
+ return NULL;
+}
+
/**
* Set active grease pencil layer.
* \param gpd: Grease pencil data-block
@@ -2421,6 +2446,21 @@ int BKE_gpencil_object_material_index_get(Object *ob, Material *ma)
return -1;
}
+int BKE_gpencil_object_material_get_index_name(Object *ob, char *name)
+{
+ short *totcol = BKE_object_material_len_p(ob);
+ Material *read_ma = NULL;
+ for (short i = 0; i < *totcol; i++) {
+ read_ma = BKE_object_material_get(ob, i + 1);
+ /* Material names are like "MAMaterial.001" */
+ if (STREQ(name, &read_ma->id.name[2])) {
+ return i;
+ }
+ }
+
+ return -1;
+}
+
/**
* Create a default palette.
* \param bmain: Main pointer
diff --git a/source/blender/blenkernel/intern/idtype.c b/source/blender/blenkernel/intern/idtype.c
index 1889d1c4eb0..fee70922570 100644
--- a/source/blender/blenkernel/intern/idtype.c
+++ b/source/blender/blenkernel/intern/idtype.c
@@ -63,7 +63,7 @@ bool BKE_idtype_cache_key_cmp(const void *key_a_v, const void *key_b_v)
(key_a->offset_in_ID != key_b->offset_in_ID) || (key_a->cache_v != key_b->cache_v);
}
-static IDTypeInfo *id_types[MAX_LIBARRAY] = {NULL};
+static IDTypeInfo *id_types[INDEX_ID_MAX] = {NULL};
static void id_type_init(void)
{
diff --git a/source/blender/blenkernel/intern/lib_id.c b/source/blender/blenkernel/intern/lib_id.c
index 54c2f5f5565..af921307bfb 100644
--- a/source/blender/blenkernel/intern/lib_id.c
+++ b/source/blender/blenkernel/intern/lib_id.c
@@ -171,7 +171,9 @@ static void lib_id_clear_library_data_ex(Main *bmain, ID *id)
/* Conceptually, an ID made local is not the same as the linked one anymore. Reflect that by
* regenerating its session UUID. */
- BKE_lib_libblock_session_uuid_renew(id);
+ if ((id->tag & LIB_TAG_TEMP_MAIN) == 0) {
+ BKE_lib_libblock_session_uuid_renew(id);
+ }
/* We need to tag this IDs and all of its users, conceptually new local ID and original linked
* ones are two completely different data-blocks that were virtually remapped, even though in
@@ -916,7 +918,7 @@ void BKE_main_id_tag_idcode(struct Main *mainvar,
*/
void BKE_main_id_tag_all(struct Main *mainvar, const int tag, const bool value)
{
- ListBase *lbarray[MAX_LIBARRAY];
+ ListBase *lbarray[INDEX_ID_MAX];
int a;
a = set_listbasepointers(mainvar, lbarray);
@@ -949,7 +951,7 @@ void BKE_main_id_flag_listbase(ListBase *lb, const int flag, const bool value)
*/
void BKE_main_id_flag_all(Main *bmain, const int flag, const bool value)
{
- ListBase *lbarray[MAX_LIBARRAY];
+ ListBase *lbarray[INDEX_ID_MAX];
int a;
a = set_listbasepointers(bmain, lbarray);
while (a--) {
@@ -1139,6 +1141,7 @@ static uint global_session_uuid = 0;
void BKE_lib_libblock_session_uuid_ensure(ID *id)
{
if (id->session_uuid == MAIN_ID_SESSION_UUID_UNSET) {
+ BLI_assert((id->tag & LIB_TAG_TEMP_MAIN) == 0); /* Caller must ensure this. */
id->session_uuid = atomic_add_and_fetch_uint32(&global_session_uuid, 1);
/* In case overflow happens, still assign a valid ID. This way opening files many times works
* correctly. */
@@ -1870,7 +1873,7 @@ void BKE_library_make_local(Main *bmain,
const bool untagged_only,
const bool set_fake)
{
- ListBase *lbarray[MAX_LIBARRAY];
+ ListBase *lbarray[INDEX_ID_MAX];
LinkNode *todo_ids = NULL;
LinkNode *copied_ids = NULL;
diff --git a/source/blender/blenkernel/intern/lib_id_delete.c b/source/blender/blenkernel/intern/lib_id_delete.c
index 7c5032c97f4..67b2e4429d6 100644
--- a/source/blender/blenkernel/intern/lib_id_delete.c
+++ b/source/blender/blenkernel/intern/lib_id_delete.c
@@ -240,7 +240,7 @@ void BKE_id_free_us(Main *bmain, void *idv) /* test users */
static size_t id_delete(Main *bmain, const bool do_tagged_deletion)
{
const int tag = LIB_TAG_DOIT;
- ListBase *lbarray[MAX_LIBARRAY];
+ ListBase *lbarray[INDEX_ID_MAX];
Link dummy_link = {0};
int base_count, i;
@@ -300,11 +300,15 @@ static size_t id_delete(Main *bmain, const bool do_tagged_deletion)
* links, this can lead to nasty crashing here in second, actual deleting loop.
* Also, this will also flag users of deleted data that cannot be unlinked
* (object using deleted obdata, etc.), so that they also get deleted. */
- BKE_libblock_remap_locked(
- bmain, id, NULL, ID_REMAP_FLAG_NEVER_NULL_USAGE | ID_REMAP_FORCE_NEVER_NULL_USAGE);
+ BKE_libblock_remap_locked(bmain,
+ id,
+ NULL,
+ (ID_REMAP_FLAG_NEVER_NULL_USAGE |
+ ID_REMAP_FORCE_NEVER_NULL_USAGE |
+ ID_REMAP_FORCE_INTERNAL_RUNTIME_POINTERS));
/* Since we removed ID from Main,
* we also need to unlink its own other IDs usages ourself. */
- BKE_libblock_relink_ex(bmain, id, NULL, NULL, 0);
+ BKE_libblock_relink_ex(bmain, id, NULL, NULL, ID_REMAP_FORCE_INTERNAL_RUNTIME_POINTERS);
}
}
@@ -337,8 +341,12 @@ static size_t id_delete(Main *bmain, const bool do_tagged_deletion)
* actual deleting loop.
* Also, this will also flag users of deleted data that cannot be unlinked
* (object using deleted obdata, etc.), so that they also get deleted. */
- BKE_libblock_remap_locked(
- bmain, id, NULL, ID_REMAP_FLAG_NEVER_NULL_USAGE | ID_REMAP_FORCE_NEVER_NULL_USAGE);
+ BKE_libblock_remap_locked(bmain,
+ id,
+ NULL,
+ (ID_REMAP_FLAG_NEVER_NULL_USAGE |
+ ID_REMAP_FORCE_NEVER_NULL_USAGE |
+ ID_REMAP_FORCE_INTERNAL_RUNTIME_POINTERS));
}
}
}
diff --git a/source/blender/blenkernel/intern/lib_override.c b/source/blender/blenkernel/intern/lib_override.c
index 602c560cedd..4ec130f8388 100644
--- a/source/blender/blenkernel/intern/lib_override.c
+++ b/source/blender/blenkernel/intern/lib_override.c
@@ -24,6 +24,8 @@
#include <stdlib.h>
#include <string.h>
+#include "CLG_log.h"
+
#include "MEM_guardedalloc.h"
#include "DNA_ID.h"
@@ -46,6 +48,7 @@
#include "BKE_lib_query.h"
#include "BKE_lib_remap.h"
#include "BKE_main.h"
+#include "BKE_report.h"
#include "BKE_scene.h"
#include "BLI_ghash.h"
@@ -66,6 +69,8 @@
# include "PIL_time_utildefines.h"
#endif
+static CLG_LogRef LOG = {"bke.liboverride"};
+
static void lib_override_library_property_copy(IDOverrideLibraryProperty *op_dst,
IDOverrideLibraryProperty *op_src);
static void lib_override_library_property_operation_copy(
@@ -213,6 +218,32 @@ static ID *lib_override_library_create_from(Main *bmain, ID *reference_id)
return local_id;
}
+/** Check if given ID has some override rules that actually indicate the user edited it.
+ *
+ * TODO: This could be simplified by storing a flag in IDOverrideLibrary during the diffing
+ * process? */
+bool BKE_lib_override_library_is_user_edited(struct ID *id)
+{
+ if (!ID_IS_OVERRIDE_LIBRARY(id)) {
+ return false;
+ }
+
+ LISTBASE_FOREACH (IDOverrideLibraryProperty *, op, &id->override_library->properties) {
+ LISTBASE_FOREACH (IDOverrideLibraryPropertyOperation *, opop, &op->operations) {
+ if ((opop->flag & IDOVERRIDE_LIBRARY_FLAG_IDPOINTER_MATCH_REFERENCE) != 0) {
+ continue;
+ }
+ if (opop->operation == IDOVERRIDE_LIBRARY_OP_NOOP) {
+ continue;
+ }
+ /* If an operation does not match the filters above, it is considered as a user-editing one,
+ * therefore this override is user-edited. */
+ return true;
+ }
+ }
+ return false;
+}
+
/** Create an overridden local copy of linked reference. */
ID *BKE_lib_override_library_create_from_id(Main *bmain,
ID *reference_id,
@@ -379,7 +410,7 @@ typedef struct LibOverrideGroupTagData {
*
* Requires existing `Main.relations`.
*
- * Note: this is typically called to complete `lib_override_linked_group_tag()`.
+ * NOTE: This is typically called to complete `lib_override_linked_group_tag()`.
*/
static bool lib_override_hierarchy_dependencies_recursive_tag(LibOverrideGroupTagData *data)
{
@@ -615,58 +646,85 @@ static bool lib_override_library_create_do(Main *bmain, ID *id_root)
return BKE_lib_override_library_create_from_tag(bmain);
}
-static void lib_override_library_create_post_process(
- Main *bmain, Scene *scene, ViewLayer *view_layer, ID *id_root, ID *id_reference)
+BLI_INLINE bool lib_override_library_create_post_process_object_is_instantiated(
+ ViewLayer *view_layer, Object *object, const bool is_resync)
+{
+ /* We cannot rely on check for object being actually instantiated in resync case, because often
+ * the overridden collection is 'excluded' from the current viewlayer.
+ *
+ * Fallback to a basic usercount check then, this is weak (since it could lead to some object not
+ * being instantiated at all), but it should work fine in most common cases. */
+ return ((is_resync && ID_REAL_USERS(object) >= 1) ||
+ (!is_resync && BKE_view_layer_base_find(view_layer, object) != NULL));
+}
+
+static void lib_override_library_create_post_process(Main *bmain,
+ Scene *scene,
+ ViewLayer *view_layer,
+ ID *id_root,
+ ID *id_reference,
+ const bool is_resync)
{
BKE_main_collection_sync(bmain);
- switch (GS(id_root->name)) {
- case ID_GR: {
- Object *ob_reference = id_reference != NULL && GS(id_reference->name) == ID_OB ?
- (Object *)id_reference :
- NULL;
- Collection *collection_new = ((Collection *)id_root->newid);
- if (ob_reference != NULL) {
- BKE_collection_add_from_object(bmain, scene, ob_reference, collection_new);
- }
- else if (id_reference != NULL) {
- BKE_collection_add_from_collection(
- bmain, scene, ((Collection *)id_reference), collection_new);
- }
- else {
- BKE_collection_add_from_collection(bmain, scene, ((Collection *)id_root), collection_new);
- }
+ if (id_root->newid != NULL) {
+ switch (GS(id_root->name)) {
+ case ID_GR: {
+ Object *ob_reference = id_reference != NULL && GS(id_reference->name) == ID_OB ?
+ (Object *)id_reference :
+ NULL;
+ Collection *collection_new = ((Collection *)id_root->newid);
+ if (ob_reference != NULL) {
+ BKE_collection_add_from_object(bmain, scene, ob_reference, collection_new);
+ }
+ else if (id_reference != NULL) {
+ BLI_assert(GS(id_reference->name) == ID_GR);
+ BKE_collection_add_from_collection(
+ bmain, scene, ((Collection *)id_reference), collection_new);
+ }
+ else {
+ BKE_collection_add_from_collection(
+ bmain, scene, ((Collection *)id_root), collection_new);
+ }
- FOREACH_COLLECTION_OBJECT_RECURSIVE_BEGIN (collection_new, ob_new) {
- if (ob_new != NULL && ob_new->id.override_library != NULL) {
- if (ob_reference != NULL) {
- Base *base;
- if ((base = BKE_view_layer_base_find(view_layer, ob_new)) == NULL) {
- BKE_collection_object_add_from(bmain, scene, ob_reference, ob_new);
- base = BKE_view_layer_base_find(view_layer, ob_new);
- DEG_id_tag_update_ex(bmain, &ob_new->id, ID_RECALC_TRANSFORM | ID_RECALC_BASE_FLAGS);
- }
+ FOREACH_COLLECTION_OBJECT_RECURSIVE_BEGIN (collection_new, ob_new) {
+ if (ob_new != NULL && ob_new->id.override_library != NULL) {
+ if (ob_reference != NULL) {
+ Base *base = BKE_view_layer_base_find(view_layer, ob_new);
+ if (!lib_override_library_create_post_process_object_is_instantiated(
+ view_layer, ob_new, is_resync)) {
+ BKE_collection_object_add_from(bmain, scene, ob_reference, ob_new);
+ base = BKE_view_layer_base_find(view_layer, ob_new);
+ DEG_id_tag_update_ex(
+ bmain, &ob_new->id, ID_RECALC_TRANSFORM | ID_RECALC_BASE_FLAGS);
+ }
- if (ob_new == (Object *)ob_reference->id.newid) {
- /* TODO: is setting active needed? */
- BKE_view_layer_base_select_and_set_active(view_layer, base);
+ if (ob_new == (Object *)ob_reference->id.newid && base != NULL) {
+ /* TODO: is setting active needed? */
+ BKE_view_layer_base_select_and_set_active(view_layer, base);
+ }
+ }
+ else if (!lib_override_library_create_post_process_object_is_instantiated(
+ view_layer, ob_new, is_resync)) {
+ BKE_collection_object_add(bmain, collection_new, ob_new);
+ DEG_id_tag_update_ex(bmain, &ob_new->id, ID_RECALC_TRANSFORM | ID_RECALC_BASE_FLAGS);
}
- }
- else if (BKE_view_layer_base_find(view_layer, ob_new) == NULL) {
- BKE_collection_object_add(bmain, collection_new, ob_new);
- DEG_id_tag_update_ex(bmain, &ob_new->id, ID_RECALC_TRANSFORM | ID_RECALC_BASE_FLAGS);
}
}
+ FOREACH_COLLECTION_OBJECT_RECURSIVE_END;
+ break;
}
- FOREACH_COLLECTION_OBJECT_RECURSIVE_END;
- break;
- }
- case ID_OB: {
- BKE_collection_object_add_from(bmain, scene, (Object *)id_root, ((Object *)id_root->newid));
- break;
+ case ID_OB: {
+ Object *ob_new = (Object *)id_root->newid;
+ if (!lib_override_library_create_post_process_object_is_instantiated(
+ view_layer, ob_new, is_resync)) {
+ BKE_collection_object_add_from(bmain, scene, (Object *)id_root, ob_new);
+ }
+ break;
+ }
+ default:
+ break;
}
- default:
- break;
}
/* We need to ensure all new overrides of objects are properly instantiated. */
@@ -677,7 +735,8 @@ static void lib_override_library_create_post_process(
ob_new->id.override_library->reference == &ob->id);
Collection *default_instantiating_collection = NULL;
- if (BKE_view_layer_base_find(view_layer, ob_new) == NULL) {
+ if (!lib_override_library_create_post_process_object_is_instantiated(
+ view_layer, ob_new, is_resync)) {
if (default_instantiating_collection == NULL) {
switch (GS(id_root->name)) {
case ID_GR: {
@@ -740,7 +799,7 @@ bool BKE_lib_override_library_create(
return success;
}
- lib_override_library_create_post_process(bmain, scene, view_layer, id_root, id_reference);
+ lib_override_library_create_post_process(bmain, scene, view_layer, id_root, id_reference, false);
/* Cleanup. */
BKE_main_id_clear_newpoins(bmain);
@@ -799,7 +858,8 @@ bool BKE_lib_override_library_proxy_convert(Main *bmain,
* \param id_root: The root liboverride ID to resync from.
* \return true if override was successfully resynced.
*/
-bool BKE_lib_override_library_resync(Main *bmain, Scene *scene, ViewLayer *view_layer, ID *id_root)
+bool BKE_lib_override_library_resync(
+ Main *bmain, Scene *scene, ViewLayer *view_layer, ID *id_root, const bool do_hierarchy_enforce)
{
BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(id_root));
@@ -879,6 +939,8 @@ bool BKE_lib_override_library_resync(Main *bmain, Scene *scene, ViewLayer *view_
ID *id_override_new = id->newid;
ID *id_override_old = BLI_ghash_lookup(linkedref_to_old_override, id);
+ BLI_assert((id_override_new->tag & LIB_TAG_LIB_OVERRIDE_NEED_RESYNC) == 0);
+
if (id_override_old != NULL) {
/* Swap the names between old override ID and new one. */
char id_name_buf[MAX_ID_NAME];
@@ -946,8 +1008,14 @@ bool BKE_lib_override_library_resync(Main *bmain, Scene *scene, ViewLayer *view_
}
}
- RNA_struct_override_apply(
- bmain, &rnaptr_dst, &rnaptr_src, NULL, id_override_new->override_library);
+ RNA_struct_override_apply(bmain,
+ &rnaptr_dst,
+ &rnaptr_src,
+ NULL,
+ id_override_new->override_library,
+ do_hierarchy_enforce ?
+ RNA_OVERRIDE_APPLY_FLAG_IGNORE_ID_POINTERS :
+ RNA_OVERRIDE_APPLY_FLAG_NOP);
}
}
}
@@ -971,11 +1039,21 @@ bool BKE_lib_override_library_resync(Main *bmain, Scene *scene, ViewLayer *view_
}
id->tag &= ~LIB_TAG_DOIT;
}
- /* Also cleanup old overrides that went missing in new linked data. */
+ /* Also deal with old overrides that went missing in new linked data. */
else if (id->tag & LIB_TAG_MISSING && !ID_IS_LINKED(id)) {
BLI_assert(ID_IS_OVERRIDE_LIBRARY(id));
- id->tag |= LIB_TAG_DOIT;
- id->tag &= ~LIB_TAG_MISSING;
+ if (!BKE_lib_override_library_is_user_edited(id)) {
+ /* If user never edited them, we can delete them. */
+ id->tag |= LIB_TAG_DOIT;
+ id->tag &= ~LIB_TAG_MISSING;
+ CLOG_INFO(&LOG, 2, "Old override %s is being deleted", id->name);
+ }
+ else {
+ /* Otherwise, keep them, user needs to decide whether what to do with them. */
+ BLI_assert((id->tag & LIB_TAG_DOIT) == 0);
+ id_fake_user_set(id);
+ CLOG_INFO(&LOG, 2, "Old override %s is being kept around as it was user-edited", id->name);
+ }
}
}
FOREACH_MAIN_ID_END;
@@ -991,7 +1069,8 @@ bool BKE_lib_override_library_resync(Main *bmain, Scene *scene, ViewLayer *view_
* since we already relinked old root override collection to new resync'ed one above. So this
* call is not expected to instantiate this new resync'ed collection anywhere, just to ensure
* that we do not have any stray objects. */
- lib_override_library_create_post_process(bmain, scene, view_layer, id_root_reference, id_root);
+ lib_override_library_create_post_process(
+ bmain, scene, view_layer, id_root_reference, id_root, true);
/* Cleanup. */
BLI_ghash_free(linkedref_to_old_override, NULL, NULL);
@@ -1003,6 +1082,120 @@ bool BKE_lib_override_library_resync(Main *bmain, Scene *scene, ViewLayer *view_
}
/**
+ * Detect and handle required resync of overrides data, when relations between reference linked IDs
+ * have changed.
+ *
+ * This is a fairly complex and costly operation, typically it should be called after
+ * #BKE_lib_override_library_main_update, which would already detect and tag a lot of cases.
+ *
+ * This function will first detect the remaining cases requiring a resync (namely, either when an
+ * existing linked ID that did not require to be overridden before now would be, or when new IDs
+ * are added to the hierarchy).
+ *
+ * Then it will handle the resync of necessary IDs (through calls to
+ * #BKE_lib_override_library_resync).
+ */
+void BKE_lib_override_library_main_resync(Main *bmain, Scene *scene, ViewLayer *view_layer)
+{
+ BKE_main_relations_create(bmain, 0);
+ BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false);
+
+ /* NOTE: in code below, the order in which `FOREACH_MAIN_ID_BEGIN` processes ID types ensures
+ * that we always process 'higher-level' overrides first (i.e. scenes, then collections, then
+ * objects, then other types). */
+
+ /* Detect all linked data that would need to be overridden if we had to create an override from
+ * those used by current existing overrides. */
+ ID *id;
+ FOREACH_MAIN_ID_BEGIN (bmain, id) {
+ if (!ID_IS_OVERRIDE_LIBRARY_REAL(id)) {
+ continue;
+ }
+ if (id->tag & (LIB_TAG_DOIT | LIB_TAG_MISSING)) {
+ /* We already processed that ID as part of another ID's hierarchy. */
+ continue;
+ }
+
+ LibOverrideGroupTagData data = {.bmain = bmain,
+ .id_root = id->override_library->reference,
+ .tag = LIB_TAG_DOIT,
+ .missing_tag = LIB_TAG_MISSING};
+ lib_override_linked_group_tag(&data);
+ BKE_main_relations_tag_set(bmain, MAINIDRELATIONS_ENTRY_TAGS_PROCESSED, false);
+ lib_override_hierarchy_dependencies_recursive_tag(&data);
+ BKE_main_relations_tag_set(bmain, MAINIDRELATIONS_ENTRY_TAGS_PROCESSED, false);
+ }
+ FOREACH_MAIN_ID_END;
+
+ /* Now check existing overrides, those needing resync will be the one either already tagged as
+ * such, or the one using linked data that is now tagged as needing override. */
+ FOREACH_MAIN_ID_BEGIN (bmain, id) {
+ if (!ID_IS_OVERRIDE_LIBRARY_REAL(id)) {
+ continue;
+ }
+
+ if (id->tag & LIB_TAG_LIB_OVERRIDE_NEED_RESYNC) {
+ CLOG_INFO(&LOG, 4, "ID %s was already tagged as needing resync", id->name);
+ continue;
+ }
+
+ MainIDRelationsEntry *entry = BLI_ghash_lookup(bmain->relations->relations_from_pointers, id);
+ BLI_assert(entry != NULL);
+
+ for (MainIDRelationsEntryItem *entry_item = entry->to_ids; entry_item != NULL;
+ entry_item = entry_item->next) {
+ if (entry_item->usage_flag &
+ (IDWALK_CB_EMBEDDED | IDWALK_CB_LOOPBACK | IDWALK_CB_OVERRIDE_LIBRARY_REFERENCE)) {
+ continue;
+ }
+ ID *id_to = *entry_item->id_pointer.to;
+
+ /* Case where this ID pointer was to a linked ID, that now needs to be overridden. */
+ if (ID_IS_LINKED(id_to) && (id_to->tag & LIB_TAG_DOIT) != 0) {
+ id->tag |= LIB_TAG_LIB_OVERRIDE_NEED_RESYNC;
+ CLOG_INFO(&LOG,
+ 3,
+ "ID %s now tagged as needing resync because they use linked %s that now needs "
+ "to be overridden",
+ id->name,
+ id_to->name);
+ break;
+ }
+ }
+ }
+ FOREACH_MAIN_ID_END;
+
+ BKE_main_relations_free(bmain);
+ BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false);
+
+ /* And do the actual resync for all IDs detected as needing it.
+ * NOTE: Since this changes `bmain` (adding **and** removing IDs), we cannot use
+ * `FOREACH_MAIN_ID_BEGIN/END` here, and need special multi-loop processing. */
+ bool do_continue = true;
+ while (do_continue) {
+ ListBase *lb;
+ do_continue = false;
+ FOREACH_MAIN_LISTBASE_BEGIN (bmain, lb) {
+ FOREACH_MAIN_LISTBASE_ID_BEGIN (lb, id) {
+ if ((id->tag & LIB_TAG_LIB_OVERRIDE_NEED_RESYNC) == 0) {
+ continue;
+ }
+ do_continue = true;
+ CLOG_INFO(&LOG, 2, "Resyncing %s...", id->name);
+ const bool success = BKE_lib_override_library_resync(bmain, scene, view_layer, id, false);
+ CLOG_INFO(&LOG, 2, "\tSuccess: %d", success);
+ break;
+ }
+ FOREACH_MAIN_LISTBASE_ID_END;
+ if (do_continue) {
+ break;
+ }
+ }
+ FOREACH_MAIN_LISTBASE_END;
+ }
+}
+
+/**
* Advanced 'smart' function to delete library overrides (including their existing override
* hierarchy) and remap their usages to their linked reference IDs.
*
@@ -1372,6 +1565,53 @@ bool BKE_lib_override_library_property_operation_operands_validate(
return true;
}
+/** Check against potential \a bmain. */
+void BKE_lib_override_library_validate(Main *UNUSED(bmain), ID *id, ReportList *reports)
+{
+ if (id->override_library == NULL) {
+ return;
+ }
+ if (id->override_library->reference == NULL) {
+ /* This is a template ID, could be linked or local, not an override. */
+ return;
+ }
+ if (id->override_library->reference == id) {
+ /* Very serious data corruption, cannot do much about it besides removing the reference
+ * (therefore making the id a local override template one only). */
+ BKE_reportf(reports,
+ RPT_ERROR,
+ "Data corruption: data-block '%s' is using itself as library override reference",
+ id->name);
+ id->override_library->reference = NULL;
+ return;
+ }
+ if (id->override_library->reference->lib == NULL) {
+ /* Very serious data corruption, cannot do much about it besides removing the reference
+ * (therefore making the id a local override template one only). */
+ BKE_reportf(reports,
+ RPT_ERROR,
+ "Data corruption: data-block '%s' is using another local data-block ('%s') as "
+ "library override reference",
+ id->name,
+ id->override_library->reference->name);
+ id->override_library->reference = NULL;
+ return;
+ }
+}
+
+/** Check against potential \a bmain. */
+void BKE_lib_override_library_main_validate(Main *bmain, ReportList *reports)
+{
+ ID *id;
+
+ FOREACH_MAIN_ID_BEGIN (bmain, id) {
+ if (id->override_library != NULL) {
+ BKE_lib_override_library_validate(bmain, id, reports);
+ }
+ }
+ FOREACH_MAIN_ID_END;
+}
+
/**
* Check that status of local data-block is still valid against current reference one.
*
@@ -1557,17 +1797,15 @@ bool BKE_lib_override_library_operations_create(Main *bmain, ID *local)
created = true;
}
-#ifndef NDEBUG
if (report_flags & RNA_OVERRIDE_MATCH_RESULT_RESTORED) {
- printf("We did restore some properties of %s from its reference.\n", local->name);
+ CLOG_INFO(&LOG, 2, "We did restore some properties of %s from its reference", local->name);
}
if (report_flags & RNA_OVERRIDE_MATCH_RESULT_CREATED) {
- printf("We did generate library override rules for %s\n", local->name);
+ CLOG_INFO(&LOG, 2, "We did generate library override rules for %s", local->name);
}
else {
- printf("No new library override rules for %s\n", local->name);
+ CLOG_INFO(&LOG, 2, "No new library override rules for %s", local->name);
}
-#endif
}
return created;
}
@@ -1880,6 +2118,14 @@ void BKE_lib_override_library_main_unused_cleanup(struct Main *bmain)
FOREACH_MAIN_ID_END;
}
+static void lib_override_id_swap(Main *bmain, ID *id_local, ID *id_temp)
+{
+ BKE_lib_id_swap(bmain, id_local, id_temp);
+ /* We need to keep these tags from temp ID into orig one.
+ * ID swap does not swap most of ID data itself. */
+ id_local->tag |= (id_temp->tag & LIB_TAG_LIB_OVERRIDE_NEED_RESYNC);
+}
+
/** Update given override from its reference (re-applying overridden properties). */
void BKE_lib_override_library_update(Main *bmain, ID *local)
{
@@ -1943,16 +2189,20 @@ void BKE_lib_override_library_update(Main *bmain, ID *local)
RNA_id_pointer_create(local->override_library->storage, rnaptr_storage);
}
- RNA_struct_override_apply(
- bmain, &rnaptr_dst, &rnaptr_src, rnaptr_storage, local->override_library);
+ RNA_struct_override_apply(bmain,
+ &rnaptr_dst,
+ &rnaptr_src,
+ rnaptr_storage,
+ local->override_library,
+ RNA_OVERRIDE_APPLY_FLAG_NOP);
/* This also transfers all pointers (memory) owned by local to tmp_id, and vice-versa.
* So when we'll free tmp_id, we'll actually free old, outdated data from local. */
- BKE_lib_id_swap(bmain, local, tmp_id);
+ lib_override_id_swap(bmain, local, tmp_id);
if (local_key != NULL && tmp_key != NULL) {
/* This is some kind of hard-coded 'always enforced override'. */
- BKE_lib_id_swap(bmain, &local_key->id, &tmp_key->id);
+ lib_override_id_swap(bmain, &local_key->id, &tmp_key->id);
tmp_key->id.flag |= (local_key->id.flag & LIB_EMBEDDED_DATA_LIB_OVERRIDE);
/* The swap of local and tmp_id inverted those pointers, we need to redefine proper
* relationships. */
diff --git a/source/blender/blenkernel/intern/lib_query.c b/source/blender/blenkernel/intern/lib_query.c
index 796bc3dc3d0..e33743eb36b 100644
--- a/source/blender/blenkernel/intern/lib_query.c
+++ b/source/blender/blenkernel/intern/lib_query.c
@@ -183,8 +183,9 @@ static void library_foreach_ID_link(Main *bmain,
BLI_assert(inherit_data == NULL || data.bmain == inherit_data->bmain);
if (flag & IDWALK_RECURSE) {
- /* For now, recursion implies read-only. */
+ /* For now, recursion implies read-only, and no internal pointers. */
flag |= IDWALK_READONLY;
+ flag &= ~IDWALK_DO_INTERNAL_RUNTIME_POINTERS;
data.ids_handled = BLI_gset_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, __func__);
BLI_LINKSTACK_INIT(data.ids_todo);
@@ -230,6 +231,7 @@ static void library_foreach_ID_link(Main *bmain,
}
if (bmain != NULL && bmain->relations != NULL && (flag & IDWALK_READONLY) &&
+ (flag & IDWALK_DO_INTERNAL_RUNTIME_POINTERS) == 0 &&
(((bmain->relations->flag & MAINIDRELATIONS_INCLUDE_UI) == 0) ==
((data.flag & IDWALK_INCLUDE_UI) == 0))) {
/* Note that this is minor optimization, even in worst cases (like id being an object with
@@ -250,6 +252,11 @@ static void library_foreach_ID_link(Main *bmain,
/* Note: ID.lib pointer is purposefully fully ignored here...
* We may want to add it at some point? */
+ if (flag & IDWALK_DO_INTERNAL_RUNTIME_POINTERS) {
+ CALLBACK_INVOKE_ID(id->newid, IDWALK_CB_INTERNAL);
+ CALLBACK_INVOKE_ID(id->orig_id, IDWALK_CB_INTERNAL);
+ }
+
if (id->override_library != NULL) {
CALLBACK_INVOKE_ID(id->override_library->reference,
IDWALK_CB_USER | IDWALK_CB_OVERRIDE_LIBRARY_REFERENCE);
@@ -440,7 +447,7 @@ bool BKE_library_id_can_use_idtype(ID *id_owner, const short id_type_used)
typedef struct IDUsersIter {
ID *id;
- ListBase *lb_array[MAX_LIBARRAY];
+ ListBase *lb_array[INDEX_ID_MAX];
int lb_idx;
ID *curr_id;
@@ -514,7 +521,7 @@ int BKE_library_ID_use_ID(ID *id_user, ID *id_used)
static bool library_ID_is_used(Main *bmain, void *idv, const bool check_linked)
{
IDUsersIter iter;
- ListBase *lb_array[MAX_LIBARRAY];
+ ListBase *lb_array[INDEX_ID_MAX];
ID *id = idv;
int i = set_listbasepointers(bmain, lb_array);
bool is_defined = false;
@@ -567,7 +574,7 @@ bool BKE_library_ID_is_indirectly_used(Main *bmain, void *idv)
void BKE_library_ID_test_usages(Main *bmain, void *idv, bool *is_used_local, bool *is_used_linked)
{
IDUsersIter iter;
- ListBase *lb_array[MAX_LIBARRAY];
+ ListBase *lb_array[INDEX_ID_MAX];
ID *id = idv;
int i = set_listbasepointers(bmain, lb_array);
bool is_defined = false;
@@ -805,7 +812,7 @@ void BKE_library_unused_linked_data_set_tag(Main *bmain, const bool do_init_tag)
*/
void BKE_library_indirectly_used_data_tag_clear(Main *bmain)
{
- ListBase *lb_array[MAX_LIBARRAY];
+ ListBase *lb_array[INDEX_ID_MAX];
bool do_loop = true;
while (do_loop) {
diff --git a/source/blender/blenkernel/intern/lib_remap.c b/source/blender/blenkernel/intern/lib_remap.c
index 56f7bb0be6f..1f597bbb9a6 100644
--- a/source/blender/blenkernel/intern/lib_remap.c
+++ b/source/blender/blenkernel/intern/lib_remap.c
@@ -373,9 +373,12 @@ static void libblock_remap_data(
Main *bmain, ID *id, ID *old_id, ID *new_id, const short remap_flags, IDRemap *r_id_remap_data)
{
IDRemap id_remap_data;
- const int foreach_id_flags = (remap_flags & ID_REMAP_NO_INDIRECT_PROXY_DATA_USAGE) != 0 ?
- IDWALK_NO_INDIRECT_PROXY_DATA_USAGE :
- IDWALK_NOP;
+ const int foreach_id_flags = ((remap_flags & ID_REMAP_NO_INDIRECT_PROXY_DATA_USAGE) != 0 ?
+ IDWALK_NO_INDIRECT_PROXY_DATA_USAGE :
+ IDWALK_NOP) |
+ ((remap_flags & ID_REMAP_FORCE_INTERNAL_RUNTIME_POINTERS) != 0 ?
+ IDWALK_DO_INTERNAL_RUNTIME_POINTERS :
+ IDWALK_NOP);
if (r_id_remap_data == NULL) {
r_id_remap_data = &id_remap_data;
@@ -422,15 +425,17 @@ static void libblock_remap_data(
FOREACH_MAIN_ID_END;
}
- /* XXX We may not want to always 'transfer' fake-user from old to new id...
- * Think for now it's desired behavior though,
- * we can always add an option (flag) to control this later if needed. */
- if (old_id && (old_id->flag & LIB_FAKEUSER)) {
- id_fake_user_clear(old_id);
- id_fake_user_set(new_id);
- }
+ if ((remap_flags & ID_REMAP_SKIP_USER_CLEAR) == 0) {
+ /* XXX We may not want to always 'transfer' fake-user from old to new id...
+ * Think for now it's desired behavior though,
+ * we can always add an option (flag) to control this later if needed. */
+ if (old_id && (old_id->flag & LIB_FAKEUSER)) {
+ id_fake_user_clear(old_id);
+ id_fake_user_set(new_id);
+ }
- id_us_clear_real(old_id);
+ id_us_clear_real(old_id);
+ }
if (new_id && (new_id->tag & LIB_TAG_INDIRECT) &&
(r_id_remap_data->status & ID_REMAP_IS_LINKED_DIRECT)) {
@@ -479,12 +484,14 @@ void BKE_libblock_remap_locked(Main *bmain, void *old_idv, void *new_idv, const
skipped_direct = id_remap_data.skipped_direct;
skipped_refcounted = id_remap_data.skipped_refcounted;
- /* If old_id was used by some ugly 'user_one' stuff (like Image or Clip editors...), and user
- * count has actually been incremented for that, we have to decrease once more its user count...
- * unless we had to skip some 'user_one' cases. */
- if ((old_id->tag & LIB_TAG_EXTRAUSER_SET) &&
- !(id_remap_data.status & ID_REMAP_IS_USER_ONE_SKIPPED)) {
- id_us_clear_real(old_id);
+ if ((remap_flags & ID_REMAP_SKIP_USER_CLEAR) == 0) {
+ /* If old_id was used by some ugly 'user_one' stuff (like Image or Clip editors...), and user
+ * count has actually been incremented for that, we have to decrease once more its user
+ * count... unless we had to skip some 'user_one' cases. */
+ if ((old_id->tag & LIB_TAG_EXTRAUSER_SET) &&
+ !(id_remap_data.status & ID_REMAP_IS_USER_ONE_SKIPPED)) {
+ id_us_clear_real(old_id);
+ }
}
if (old_id->us - skipped_refcounted < 0) {
diff --git a/source/blender/blenkernel/intern/main.c b/source/blender/blenkernel/intern/main.c
index 6f94b3355fa..d1f34ad8ce9 100644
--- a/source/blender/blenkernel/intern/main.c
+++ b/source/blender/blenkernel/intern/main.c
@@ -53,7 +53,7 @@ Main *BKE_main_new(void)
void BKE_main_free(Main *mainvar)
{
/* also call when reading a file, erase all, etc */
- ListBase *lbarray[MAX_LIBARRAY];
+ ListBase *lbarray[INDEX_ID_MAX];
int a;
/* Since we are removing whole main, no need to bother 'properly'
@@ -532,18 +532,17 @@ ListBase *which_libbase(Main *bmain, short type)
}
/**
- * puts into array *lb pointers to all the #ListBase structs in main,
- * and returns the number of them as the function result. This is useful for
- * generic traversal of all the blocks in a Main (by traversing all the
- * lists in turn), without worrying about block types.
+ * Put the pointers to all the #ListBase structs in given `bmain` into the `*lb[INDEX_ID_MAX]`
+ * array, and return the number of those for convenience.
*
- * \note #MAX_LIBARRAY define should match this code */
-int set_listbasepointers(Main *bmain, ListBase **lb)
+ * This is useful for generic traversal of all the blocks in a #Main (by traversing all the lists
+ * in turn), without worrying about block types.
+ *
+ * \note The order of each ID type #ListBase in the array is determined by the `INDEX_ID_<IDTYPE>`
+ * enum definitions in `DNA_ID.h`. See also the #FOREACH_MAIN_ID_BEGIN macro in `BKE_main.h`
+ */
+int set_listbasepointers(Main *bmain, ListBase *lb[INDEX_ID_MAX])
{
- /* BACKWARDS! also watch order of free-ing! (mesh<->mat), first items freed last.
- * This is important because freeing data decreases user-counts of other data-blocks,
- * if this data is its self freed it can crash. */
-
/* Libraries may be accessed from pretty much any other ID. */
lb[INDEX_ID_LI] = &(bmain->libraries);
@@ -606,5 +605,5 @@ int set_listbasepointers(Main *bmain, ListBase **lb)
lb[INDEX_ID_NULL] = NULL;
- return (MAX_LIBARRAY - 1);
+ return (INDEX_ID_MAX - 1);
}
diff --git a/source/blender/blenkernel/intern/main_idmap.c b/source/blender/blenkernel/intern/main_idmap.c
index 21f5e9c6fb2..1d362db4432 100644
--- a/source/blender/blenkernel/intern/main_idmap.c
+++ b/source/blender/blenkernel/intern/main_idmap.c
@@ -66,7 +66,7 @@ struct IDNameLib_TypeMap {
* Opaque structure, external API users only see this.
*/
struct IDNameLib_Map {
- struct IDNameLib_TypeMap type_maps[MAX_LIBARRAY];
+ struct IDNameLib_TypeMap type_maps[INDEX_ID_MAX];
struct GHash *uuid_map;
struct Main *bmain;
struct GSet *valid_id_pointers;
@@ -77,7 +77,7 @@ static struct IDNameLib_TypeMap *main_idmap_from_idcode(struct IDNameLib_Map *id
short id_type)
{
if (id_map->idmap_types & MAIN_IDMAP_TYPE_NAME) {
- for (int i = 0; i < MAX_LIBARRAY; i++) {
+ for (int i = 0; i < INDEX_ID_MAX; i++) {
if (id_map->type_maps[i].id_type == id_type) {
return &id_map->type_maps[i];
}
@@ -108,13 +108,13 @@ struct IDNameLib_Map *BKE_main_idmap_create(struct Main *bmain,
id_map->idmap_types = idmap_types;
int index = 0;
- while (index < MAX_LIBARRAY) {
+ while (index < INDEX_ID_MAX) {
struct IDNameLib_TypeMap *type_map = &id_map->type_maps[index];
type_map->map = NULL;
type_map->id_type = BKE_idtype_idcode_iter_step(&index);
BLI_assert(type_map->id_type != 0);
}
- BLI_assert(index == MAX_LIBARRAY);
+ BLI_assert(index == INDEX_ID_MAX);
if (idmap_types & MAIN_IDMAP_TYPE_UUID) {
ID *id;
@@ -231,7 +231,7 @@ void BKE_main_idmap_destroy(struct IDNameLib_Map *id_map)
{
if (id_map->idmap_types & MAIN_IDMAP_TYPE_NAME) {
struct IDNameLib_TypeMap *type_map = id_map->type_maps;
- for (int i = 0; i < MAX_LIBARRAY; i++, type_map++) {
+ for (int i = 0; i < INDEX_ID_MAX; i++, type_map++) {
if (type_map->map) {
BLI_ghash_free(type_map->map, NULL, NULL);
type_map->map = NULL;
diff --git a/source/blender/blenkernel/intern/mesh_boolean_convert.cc b/source/blender/blenkernel/intern/mesh_boolean_convert.cc
index 299b1ff1c71..824f791d400 100644
--- a/source/blender/blenkernel/intern/mesh_boolean_convert.cc
+++ b/source/blender/blenkernel/intern/mesh_boolean_convert.cc
@@ -32,6 +32,7 @@
#include "BLI_alloca.h"
#include "BLI_float2.hh"
+#include "BLI_float4x4.hh"
#include "BLI_math.h"
#include "BLI_mesh_boolean.hh"
#include "BLI_mesh_intersect.hh"
@@ -50,12 +51,12 @@ constexpr int estimated_max_facelen = 100; /* Used for initial size of some Vect
* so this is a hack to clean up such matrices.
* Would be better to change the transformation code itself.
*/
-static void clean_obmat(float cleaned[4][4], const float mat[4][4])
+static void clean_obmat(float4x4 &cleaned, const float4x4 &mat)
{
const float fuzz = 1e-6f;
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) {
- float f = mat[i][j];
+ float f = mat.values[i][j];
if (fabsf(f) <= fuzz) {
f = 0.0f;
}
@@ -65,17 +66,11 @@ static void clean_obmat(float cleaned[4][4], const float mat[4][4])
else if (fabsf(f + 1.0f) <= fuzz) {
f = -1.0f;
}
- cleaned[i][j] = f;
+ cleaned.values[i][j] = f;
}
}
}
-/* Need to wrap this in a class to use it in an Array. */
-class TransMat {
- public:
- float mat[4][4];
-};
-
/* `MeshesToIMeshInfo` keeps track of information used when combining a number
* of `Mesh`es into a single `IMesh` for doing boolean on.
* Mostly this means keeping track of the index offsets for various mesh elements. */
@@ -97,7 +92,10 @@ class MeshesToIMeshInfo {
Array<Face *> mesh_to_imesh_face;
/* Transformation matrix to transform a coordinate in the corresponding
* Mesh to the local space of the first Mesh. */
- Array<TransMat> to_obj0;
+ Array<float4x4> to_obj0;
+ /* For each input mesh, how to remap the material slot numbers to
+ * the material slots in the first mesh. */
+ Array<const short *> material_remaps;
/* Total number of input mesh vertices. */
int tot_meshes_verts;
/* Total number of input mesh edges. */
@@ -242,7 +240,8 @@ const MEdge *MeshesToIMeshInfo::input_medge_for_orig_index(int orig_index,
* All allocation of memory for the IMesh comes from `arena`.
*/
static IMesh meshes_to_imesh(Span<const Mesh *> meshes,
- const float (*obmats[])[4][4],
+ Span<const float4x4 *> obmats,
+ Span<const short *> material_remaps,
IMeshArena &arena,
MeshesToIMeshInfo *r_info)
{
@@ -271,7 +270,8 @@ static IMesh meshes_to_imesh(Span<const Mesh *> meshes,
r_info->mesh_vert_offset = Array<int>(nmeshes);
r_info->mesh_edge_offset = Array<int>(nmeshes);
r_info->mesh_poly_offset = Array<int>(nmeshes);
- r_info->to_obj0 = Array<TransMat>(nmeshes);
+ r_info->to_obj0 = Array<float4x4>(nmeshes);
+ r_info->material_remaps = Array<const short *>(nmeshes);
int v = 0;
int e = 0;
int f = 0;
@@ -286,15 +286,15 @@ static IMesh meshes_to_imesh(Span<const Mesh *> meshes,
* of object 0, we multiply each object's `obmat` by the inverse of
* object 0's `obmat`. Exact Boolean works better if these matrices
* are 'cleaned' -- see the comment for the `clean_obmat` function, above. */
- float obj0_mat[4][4];
- float inv_obj0_mat[4][4];
+ float4x4 obj0_mat;
+ float4x4 inv_obj0_mat;
if (obmats[0] == nullptr) {
- unit_m4(obj0_mat);
- unit_m4(inv_obj0_mat);
+ unit_m4(obj0_mat.values);
+ unit_m4(inv_obj0_mat.values);
}
else {
clean_obmat(obj0_mat, *obmats[0]);
- invert_m4_m4(inv_obj0_mat, obj0_mat);
+ invert_m4_m4(inv_obj0_mat.values, obj0_mat.values);
}
/* For each input `Mesh`, make `Vert`s and `Face`s for the corresponding
@@ -303,13 +303,14 @@ static IMesh meshes_to_imesh(Span<const Mesh *> meshes,
* When making `Face`s, we also put in the original indices for `MEdge`s that
* make up the `MPoly`s using the same scheme. */
for (int mi : meshes.index_range()) {
- float objn_to_obj0_mat[4][4];
+ float4x4 objn_to_obj0_mat;
const Mesh *me = meshes[mi];
if (mi == 0) {
r_info->mesh_vert_offset[mi] = 0;
r_info->mesh_edge_offset[mi] = 0;
r_info->mesh_poly_offset[mi] = 0;
- unit_m4(r_info->to_obj0[0].mat);
+ unit_m4(r_info->to_obj0[0].values);
+ r_info->material_remaps[0] = nullptr;
}
else {
r_info->mesh_vert_offset[mi] = v;
@@ -317,23 +318,28 @@ static IMesh meshes_to_imesh(Span<const Mesh *> meshes,
r_info->mesh_poly_offset[mi] = f;
/* Get matrix that transforms a coordinate in objects[mi]'s local space
* to object[0]'s local space.*/
- float objn_mat[4][4];
+ float4x4 objn_mat;
if (obmats[mi] == nullptr) {
- unit_m4(objn_mat);
+ unit_m4(objn_mat.values);
}
else {
clean_obmat(objn_mat, *obmats[mi]);
}
- mul_m4_m4m4(objn_to_obj0_mat, inv_obj0_mat, objn_mat);
- copy_m4_m4(r_info->to_obj0[mi].mat, objn_to_obj0_mat);
+ objn_to_obj0_mat = inv_obj0_mat * objn_mat;
+ r_info->to_obj0[mi] = objn_to_obj0_mat;
+ if (mi < material_remaps.size()) {
+ r_info->material_remaps[mi] = material_remaps[mi];
+ }
+ else {
+ r_info->material_remaps[mi] = nullptr;
+ }
}
for (int vi = 0; vi < me->totvert; ++vi) {
- float co[3];
- copy_v3_v3(co, me->mvert[vi].co);
+ float3 co = me->mvert[vi].co;
if (mi > 0) {
- mul_m4_v3(objn_to_obj0_mat, co);
+ co = objn_to_obj0_mat * co;
}
- r_info->mesh_to_imesh_vert[v] = arena.add_or_find_vert(mpq3(co[0], co[1], co[2]), v);
+ r_info->mesh_to_imesh_vert[v] = arena.add_or_find_vert(mpq3(co.x, co.y, co.z), v);
++v;
}
for (const MPoly &poly : Span(me->mpoly, me->totpoly)) {
@@ -396,12 +402,21 @@ static void copy_poly_attributes(Mesh *dest_mesh,
const MPoly *orig_mp,
const Mesh *orig_me,
int mp_index,
- int index_in_orig_me)
+ int index_in_orig_me,
+ const short *material_remap)
{
mp->mat_nr = orig_mp->mat_nr;
if (mp->mat_nr >= dest_mesh->totcol) {
mp->mat_nr = 0;
}
+ else {
+ if (material_remap) {
+ short mat_nr = material_remap[orig_mp->mat_nr];
+ if (mat_nr >= 0 && mat_nr < dest_mesh->totcol) {
+ mp->mat_nr = mat_nr;
+ }
+ }
+ }
mp->flag = orig_mp->flag;
CustomData *target_cd = &dest_mesh->pdata;
const CustomData *source_cd = &orig_me->pdata;
@@ -534,7 +549,7 @@ static int fill_orig_loops(const Face *f,
static void get_poly2d_cos(const Mesh *me,
const MPoly *mp,
float (*cos_2d)[2],
- const TransMat &trans_mat,
+ const float4x4 &trans_mat,
float r_axis_mat[3][3])
{
int n = mp->totloop;
@@ -546,9 +561,8 @@ static void get_poly2d_cos(const Mesh *me,
MLoop *ml = &me->mloop[mp->loopstart];
const MVert *mverts = me->mvert;
for (int i = 0; i < n; ++i) {
- float co[3];
- copy_v3_v3(co, mverts[ml->v].co);
- mul_m4_v3(trans_mat.mat, co);
+ float3 co = mverts[ml->v].co;
+ co = trans_mat * co;
mul_v2_m3v3(cos_2d[i], r_axis_mat, co);
++ml;
}
@@ -618,6 +632,9 @@ static void copy_or_interp_loop_attributes(Mesh *dest_mesh,
* A non bmesh version could have the benefit of not copying data into src_blocks_ofs -
* using the contiguous data instead. TODO: add to the custom data API. */
int target_layer_type_index = CustomData_get_named_layer(target_cd, ty, name);
+ if (!CustomData_layer_has_interp(source_cd, source_layer_i)) {
+ continue;
+ }
int source_layer_type_index = source_layer_i - source_cd->typemap[ty];
BLI_assert(target_layer_type_index != -1 && source_layer_type_index >= 0);
for (int j = 0; j < orig_mp->totloop; ++j) {
@@ -725,7 +742,9 @@ static Mesh *imesh_to_mesh(IMesh *im, MeshesToIMeshInfo &mim)
++l;
++cur_loop_index;
}
- copy_poly_attributes(result, mp, orig_mp, orig_me, fi, index_in_orig_me);
+
+ copy_poly_attributes(
+ result, mp, orig_mp, orig_me, fi, index_in_orig_me, mim.material_remaps[orig_me_index]);
copy_or_interp_loop_attributes(result, f, mp, orig_mp, orig_me, orig_me_index, mim);
}
@@ -763,23 +782,25 @@ static Mesh *imesh_to_mesh(IMesh *im, MeshesToIMeshInfo &mim)
* Do Exact Boolean directly, without a round trip through #BMesh.
* The Mesh operands are in `meshes`, with corresponding transforms in in `obmats`.
*/
-static Mesh *direct_mesh_boolean(const Mesh **meshes,
- const float (*obmats[])[4][4],
- const int meshes_len,
+static Mesh *direct_mesh_boolean(Span<const Mesh *> meshes,
+ Span<const float4x4 *> obmats,
+ Span<const short *> material_remaps,
const bool use_self,
+ const bool hole_tolerant,
const BoolOpType boolean_mode)
{
const int dbg_level = 0;
+ BLI_assert(meshes.size() == obmats.size());
+ const int meshes_len = meshes.size();
if (meshes_len <= 0) {
return nullptr;
}
if (dbg_level > 0) {
std::cout << "\nDIRECT_MESH_INTERSECT, nmeshes = " << meshes_len << "\n";
}
- Span<const Mesh *> mesh_span(meshes, meshes_len);
MeshesToIMeshInfo mim;
IMeshArena arena;
- IMesh m_in = meshes_to_imesh(mesh_span, obmats, arena, &mim);
+ IMesh m_in = meshes_to_imesh(meshes, obmats, material_remaps, arena, &mim);
std::function<int(int)> shape_fn = [&mim](int f) {
for (int mi = 0; mi < mim.mesh_poly_offset.size() - 1; ++mi) {
if (f < mim.mesh_poly_offset[mi + 1]) {
@@ -788,7 +809,8 @@ static Mesh *direct_mesh_boolean(const Mesh **meshes,
}
return static_cast<int>(mim.mesh_poly_offset.size()) - 1;
};
- IMesh m_out = boolean_mesh(m_in, boolean_mode, meshes_len, shape_fn, use_self, nullptr, &arena);
+ IMesh m_out = boolean_mesh(
+ m_in, boolean_mode, meshes_len, shape_fn, use_self, hole_tolerant, nullptr, &arena);
if (dbg_level > 1) {
std::cout << m_out;
write_obj_mesh(m_out, "m_out");
@@ -806,27 +828,36 @@ extern "C" {
/* Do a mesh boolean directly on meshes (without going back and forth to BMesh).
* The \a meshes argument is an array of \a meshes_len of Mesh pointers.
* The \a obmats argument is an array of \a meshes_len of pointers to the obmat
+ * The \a material_remaps is an array of pointers to arrays of maps from material
+ * slot numbers in the corresponding mesh to the material slot in the first mesh.
+ * It is OK for material_remaps or any of its constituent arrays to be NULL.
* matrices that transform local coordinates to global ones. It is allowed
* for the pointers to be nullptr, meaning the transformation is the identity. */
Mesh *BKE_mesh_boolean(const Mesh **meshes,
const float (*obmats[])[4][4],
+ const short **material_remaps,
const int meshes_len,
const bool use_self,
+ const bool hole_tolerant,
const int boolean_mode)
{
+ const blender::float4x4 **transforms = (const blender::float4x4 **)obmats;
return blender::meshintersect::direct_mesh_boolean(
- meshes,
- obmats,
- meshes_len,
+ blender::Span(meshes, meshes_len),
+ blender::Span(transforms, meshes_len),
+ blender::Span(material_remaps, material_remaps ? meshes_len : 0),
use_self,
+ hole_tolerant,
static_cast<blender::meshintersect::BoolOpType>(boolean_mode));
}
#else
Mesh *BKE_mesh_boolean(const Mesh **UNUSED(meshes),
const float (*obmats[])[4][4],
+ const short **UNUSED(material_remaps),
const int UNUSED(meshes_len),
const bool UNUSED(use_self),
+ const bool UNUSED(hole_tolerant),
const int UNUSED(boolean_mode))
{
UNUSED_VARS(obmats);
diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc
index 55cb0d5cce4..5041f914ef9 100644
--- a/source/blender/blenkernel/intern/node.cc
+++ b/source/blender/blenkernel/intern/node.cc
@@ -107,6 +107,9 @@ static void node_free_node(bNodeTree *ntree, bNode *node);
static void node_socket_interface_free(bNodeTree *UNUSED(ntree),
bNodeSocket *sock,
const bool do_id_user);
+static void nodeMuteRerouteOutputLinks(struct bNodeTree *ntree,
+ struct bNode *node,
+ const bool mute);
static void ntree_init_data(ID *id)
{
@@ -538,20 +541,14 @@ void ntreeBlendWrite(BlendWriter *writer, bNodeTree *ntree)
}
BLO_write_struct_by_name(writer, node->typeinfo->storagename, node->storage);
}
- else if ((ntree->type == NTREE_COMPOSIT) && (node->type == CMP_NODE_CRYPTOMATTE)) {
+ else if ((ntree->type == NTREE_COMPOSIT) &&
+ (ELEM(node->type, CMP_NODE_CRYPTOMATTE, CMP_NODE_CRYPTOMATTE_LEGACY))) {
NodeCryptomatte *nc = (NodeCryptomatte *)node->storage;
- /* Update the matte_id so the files can be opened in versions that don't
- * use `CryptomatteEntry`. */
- MEM_SAFE_FREE(nc->matte_id);
- nc->matte_id = BKE_cryptomatte_entries_to_matte_id(nc);
- if (nc->matte_id) {
- BLO_write_string(writer, nc->matte_id);
- }
+ BLO_write_string(writer, nc->matte_id);
LISTBASE_FOREACH (CryptomatteEntry *, entry, &nc->entries) {
BLO_write_struct(writer, CryptomatteEntry, entry);
}
BLO_write_struct_by_name(writer, node->typeinfo->storagename, node->storage);
- MEM_SAFE_FREE(nc->matte_id);
}
else if (node->type == FN_NODE_INPUT_STRING) {
NodeInputString *storage = (NodeInputString *)node->storage;
@@ -710,10 +707,12 @@ void ntreeBlendReadData(BlendDataReader *reader, bNodeTree *ntree)
iuser->scene = nullptr;
break;
}
+ case CMP_NODE_CRYPTOMATTE_LEGACY:
case CMP_NODE_CRYPTOMATTE: {
NodeCryptomatte *nc = (NodeCryptomatte *)node->storage;
BLO_read_data_address(reader, &nc->matte_id);
BLO_read_list(reader, &nc->entries);
+ BLI_listbase_clear(&nc->runtime.layers);
break;
}
case TEX_NODE_IMAGE: {
@@ -910,7 +909,8 @@ void ntreeBlendReadExpand(BlendExpander *expander, bNodeTree *ntree)
}
LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
- if (node->id && node->type != CMP_NODE_R_LAYERS) {
+ if (node->id && !(node->type == CMP_NODE_R_LAYERS) &&
+ !(node->type == CMP_NODE_CRYPTOMATTE && node->custom1 == CMP_CRYPTOMATTE_SRC_RENDER)) {
BLO_expand(expander, node->id);
}
@@ -1575,6 +1575,8 @@ const char *nodeStaticSocketType(int type, int subtype)
return "NodeSocketFloatAngle";
case PROP_TIME:
return "NodeSocketFloatTime";
+ case PROP_DISTANCE:
+ return "NodeSocketFloatDistance";
case PROP_NONE:
default:
return "NodeSocketFloat";
@@ -1644,6 +1646,8 @@ const char *nodeStaticSocketInterfaceType(int type, int subtype)
return "NodeSocketInterfaceFloatAngle";
case PROP_TIME:
return "NodeSocketInterfaceFloatTime";
+ case PROP_DISTANCE:
+ return "NodeSocketInterfaceFloatDistance";
case PROP_NONE:
default:
return "NodeSocketInterfaceFloat";
@@ -2214,6 +2218,106 @@ void nodeRemLink(bNodeTree *ntree, bNodeLink *link)
}
}
+/* Check if all output links are muted or not. */
+static bool nodeMuteFromSocketLinks(const bNodeTree *ntree, const bNodeSocket *sock)
+{
+ int tot = 0;
+ int muted = 0;
+ LISTBASE_FOREACH (const bNodeLink *, link, &ntree->links) {
+ if (link->fromsock == sock) {
+ tot++;
+ if (link->flag & NODE_LINK_MUTED) {
+ muted++;
+ }
+ }
+ }
+ return tot == muted;
+}
+
+static void nodeMuteLink(bNodeLink *link)
+{
+ link->flag |= NODE_LINK_MUTED;
+ link->flag |= NODE_LINK_TEST;
+ if (!(link->tosock->flag & SOCK_MULTI_INPUT)) {
+ link->tosock->flag &= ~SOCK_IN_USE;
+ }
+}
+
+static void nodeUnMuteLink(bNodeLink *link)
+{
+ link->flag &= ~NODE_LINK_MUTED;
+ link->flag |= NODE_LINK_TEST;
+ link->tosock->flag |= SOCK_IN_USE;
+}
+
+/* Upstream muting. Always happens when unmuting but checks when muting. O(n^2) algorithm.*/
+static void nodeMuteRerouteInputLinks(bNodeTree *ntree, bNode *node, const bool mute)
+{
+ if (node->type != NODE_REROUTE) {
+ return;
+ }
+ if (!mute || nodeMuteFromSocketLinks(ntree, (bNodeSocket *)node->outputs.first)) {
+ bNodeSocket *sock = (bNodeSocket *)node->inputs.first;
+ LISTBASE_FOREACH (bNodeLink *, link, &ntree->links) {
+ if (!(link->flag & NODE_LINK_VALID) || (link->tosock != sock)) {
+ continue;
+ }
+ if (mute) {
+ nodeMuteLink(link);
+ }
+ else {
+ nodeUnMuteLink(link);
+ }
+ nodeMuteRerouteInputLinks(ntree, link->fromnode, mute);
+ }
+ }
+}
+
+/* Downstream muting propagates when reaching reroute nodes. O(n^2) algorithm.*/
+static void nodeMuteRerouteOutputLinks(bNodeTree *ntree, bNode *node, const bool mute)
+{
+ if (node->type != NODE_REROUTE) {
+ return;
+ }
+ bNodeSocket *sock;
+ sock = (bNodeSocket *)node->outputs.first;
+ LISTBASE_FOREACH (bNodeLink *, link, &ntree->links) {
+ if (!(link->flag & NODE_LINK_VALID) || (link->fromsock != sock)) {
+ continue;
+ }
+ if (mute) {
+ nodeMuteLink(link);
+ }
+ else {
+ nodeUnMuteLink(link);
+ }
+ nodeMuteRerouteOutputLinks(ntree, link->tonode, mute);
+ }
+}
+
+void nodeMuteLinkToggle(bNodeTree *ntree, bNodeLink *link)
+{
+ if (link->tosock) {
+ bool mute = !(link->flag & NODE_LINK_MUTED);
+ if (mute) {
+ nodeMuteLink(link);
+ }
+ else {
+ nodeUnMuteLink(link);
+ }
+ if (link->tonode->type == NODE_REROUTE) {
+ nodeMuteRerouteOutputLinks(ntree, link->tonode, mute);
+ }
+ if (link->fromnode->type == NODE_REROUTE) {
+ nodeMuteRerouteInputLinks(ntree, link->fromnode, mute);
+ }
+ }
+
+ if (ntree) {
+ ntree->update |= NTREE_UPDATE_LINKS;
+ }
+}
+
void nodeRemSocketLinks(bNodeTree *ntree, bNodeSocket *sock)
{
LISTBASE_FOREACH_MUTABLE (bNodeLink *, link, &ntree->links) {
@@ -2256,6 +2360,10 @@ void nodeInternalRelink(bNodeTree *ntree, bNode *node)
link->flag &= ~NODE_LINK_VALID;
}
+ if (fromlink->flag & NODE_LINK_MUTED) {
+ link->flag |= NODE_LINK_MUTED;
+ }
+
ntree->update |= NTREE_UPDATE_LINKS;
}
else {
@@ -4013,7 +4121,9 @@ void ntreeTagUsedSockets(bNodeTree *ntree)
LISTBASE_FOREACH (bNodeLink *, link, &ntree->links) {
link->fromsock->flag |= SOCK_IN_USE;
- link->tosock->flag |= SOCK_IN_USE;
+ if (!(link->flag & NODE_LINK_MUTED)) {
+ link->tosock->flag |= SOCK_IN_USE;
+ }
}
}
@@ -4482,18 +4592,18 @@ void node_type_group_update(struct bNodeType *ntype,
}
void node_type_exec(struct bNodeType *ntype,
- NodeInitExecFunction initexecfunc,
- NodeFreeExecFunction freeexecfunc,
- NodeExecFunction execfunc)
+ NodeInitExecFunction init_exec_fn,
+ NodeFreeExecFunction free_exec_fn,
+ NodeExecFunction exec_fn)
{
- ntype->initexecfunc = initexecfunc;
- ntype->freeexecfunc = freeexecfunc;
- ntype->execfunc = execfunc;
+ ntype->init_exec_fn = init_exec_fn;
+ ntype->free_exec_fn = free_exec_fn;
+ ntype->exec_fn = exec_fn;
}
-void node_type_gpu(struct bNodeType *ntype, NodeGPUExecFunction gpufunc)
+void node_type_gpu(struct bNodeType *ntype, NodeGPUExecFunction gpu_fn)
{
- ntype->gpufunc = gpufunc;
+ ntype->gpu_fn = gpu_fn;
}
void node_type_internal_links(bNodeType *ntype,
@@ -4610,6 +4720,7 @@ static void registerCompositNodes()
register_node_type_cmp_keyingscreen();
register_node_type_cmp_keying();
register_node_type_cmp_cryptomatte();
+ register_node_type_cmp_cryptomatte_legacy();
register_node_type_cmp_translate();
register_node_type_cmp_rotate();
@@ -4795,6 +4906,7 @@ static void registerGeometryNodes()
register_node_type_geo_attribute_color_ramp();
register_node_type_geo_attribute_combine_xyz();
register_node_type_geo_attribute_compare();
+ register_node_type_geo_attribute_convert();
register_node_type_geo_attribute_fill();
register_node_type_geo_attribute_math();
register_node_type_geo_attribute_mix();
@@ -4802,11 +4914,20 @@ static void registerGeometryNodes()
register_node_type_geo_attribute_randomize();
register_node_type_geo_attribute_separate_xyz();
register_node_type_geo_attribute_vector_math();
+ register_node_type_geo_attribute_remove();
register_node_type_geo_boolean();
register_node_type_geo_collection_info();
register_node_type_geo_edge_split();
register_node_type_geo_is_viewport();
register_node_type_geo_join_geometry();
+ register_node_type_geo_mesh_primitive_circle();
+ register_node_type_geo_mesh_primitive_cone();
+ register_node_type_geo_mesh_primitive_cube();
+ register_node_type_geo_mesh_primitive_cylinder();
+ register_node_type_geo_mesh_primitive_ico_sphere();
+ register_node_type_geo_mesh_primitive_line();
+ register_node_type_geo_mesh_primitive_plane();
+ register_node_type_geo_mesh_primitive_uv_sphere();
register_node_type_geo_object_info();
register_node_type_geo_point_distribute();
register_node_type_geo_point_instance();
@@ -4816,8 +4937,8 @@ static void registerGeometryNodes()
register_node_type_geo_point_translate();
register_node_type_geo_points_to_volume();
register_node_type_geo_sample_texture();
+ register_node_type_geo_subdivide();
register_node_type_geo_subdivision_surface();
- register_node_type_geo_subdivision_surface_simple();
register_node_type_geo_transform();
register_node_type_geo_triangulate();
register_node_type_geo_volume_to_mesh();
diff --git a/source/blender/blenkernel/intern/node_ui_storage.cc b/source/blender/blenkernel/intern/node_ui_storage.cc
index 6e0253eca31..f2a152ac00d 100644
--- a/source/blender/blenkernel/intern/node_ui_storage.cc
+++ b/source/blender/blenkernel/intern/node_ui_storage.cc
@@ -62,8 +62,12 @@ const NodeUIStorage *BKE_node_tree_ui_storage_get_from_context(const bContext *C
}
const Object *active_object = CTX_data_active_object(C);
+ if (active_object == nullptr) {
+ return nullptr;
+ }
+
const ModifierData *active_modifier = BKE_object_active_modifier(active_object);
- if (active_object == nullptr || active_modifier == nullptr) {
+ if (active_modifier == nullptr) {
return nullptr;
}
@@ -154,8 +158,11 @@ void BKE_nodetree_error_message_add(bNodeTree &ntree,
void BKE_nodetree_attribute_hint_add(bNodeTree &ntree,
const NodeTreeEvaluationContext &context,
const bNode &node,
- const StringRef attribute_name)
+ const StringRef attribute_name,
+ const AttributeDomain domain,
+ const CustomDataType data_type)
{
NodeUIStorage &node_ui_storage = node_ui_storage_ensure(ntree, context, node);
- node_ui_storage.attribute_name_hints.add_as(attribute_name);
+ node_ui_storage.attribute_hints.add_as(attribute_name,
+ AvailableAttributeInfo{domain, data_type});
}
diff --git a/source/blender/blenkernel/intern/object_update.c b/source/blender/blenkernel/intern/object_update.c
index 69442b7646c..1e6a099040f 100644
--- a/source/blender/blenkernel/intern/object_update.c
+++ b/source/blender/blenkernel/intern/object_update.c
@@ -176,12 +176,14 @@ void BKE_object_handle_data_update(Depsgraph *depsgraph, Scene *scene, Object *o
CustomData_MeshMasks cddata_masks = scene->customdata_mask;
CustomData_MeshMasks_update(&cddata_masks, &CD_MASK_BAREMESH);
- if (DEG_get_mode(depsgraph) == DAG_EVAL_RENDER) {
- /* Make sure Freestyle edge/face marks appear in DM for render (see T40315). */
+ /* Make sure Freestyle edge/face marks appear in DM for render (see T40315). Due to Line Art
+ * impementation, edge marks should also be shown in viewport. */
#ifdef WITH_FREESTYLE
- cddata_masks.emask |= CD_MASK_FREESTYLE_EDGE;
- cddata_masks.pmask |= CD_MASK_FREESTYLE_FACE;
+ cddata_masks.emask |= CD_MASK_FREESTYLE_EDGE;
+ cddata_masks.pmask |= CD_MASK_FREESTYLE_FACE;
+ cddata_masks.vmask |= CD_MASK_MDEFORMVERT;
#endif
+ if (DEG_get_mode(depsgraph) == DAG_EVAL_RENDER) {
/* Always compute UVs, vertex colors as orcos for render. */
cddata_masks.lmask |= CD_MASK_MLOOPUV | CD_MASK_MLOOPCOL;
cddata_masks.vmask |= CD_MASK_ORCO | CD_MASK_PROP_COLOR;
diff --git a/source/blender/blenkernel/intern/outliner_treehash.c b/source/blender/blenkernel/intern/outliner_treehash.c
index 05873d20f7f..b9497d389e7 100644
--- a/source/blender/blenkernel/intern/outliner_treehash.c
+++ b/source/blender/blenkernel/intern/outliner_treehash.c
@@ -101,7 +101,7 @@ static unsigned int tse_hash(const void *ptr)
unsigned int u_int;
} hash;
- BLI_assert(tse->type || !tse->nr);
+ BLI_assert((tse->type != TSE_SOME_ID) || !tse->nr);
hash.h_pair[0] = tse->type;
hash.h_pair[1] = tse->nr;
@@ -193,7 +193,7 @@ static TseGroup *BKE_outliner_treehash_lookup_group(GHash *th, short type, short
{
TreeStoreElem tse_template;
tse_template.type = type;
- tse_template.nr = type ? nr : 0; /* we're picky! :) */
+ tse_template.nr = (type == TSE_SOME_ID) ? 0 : nr; /* we're picky! :) */
tse_template.id = id;
BLI_assert(th);
diff --git a/source/blender/blenkernel/intern/paint.c b/source/blender/blenkernel/intern/paint.c
index 08c5beedbf3..2e81b61ad8c 100644
--- a/source/blender/blenkernel/intern/paint.c
+++ b/source/blender/blenkernel/intern/paint.c
@@ -1760,7 +1760,7 @@ void BKE_sculpt_update_object_before_eval(Object *ob)
SculptSession *ss = ob->sculpt;
if (ss && ss->building_vp_handle == false) {
- if (!ss->cache && !ss->filter_cache) {
+ if (!ss->cache && !ss->filter_cache && !ss->expand_cache) {
/* We free pbvh on changes, except in the middle of drawing a stroke
* since it can't deal with changing PVBH node organization, we hope
* topology does not change in the meantime .. weak. */
diff --git a/source/blender/blenkernel/intern/particle.c b/source/blender/blenkernel/intern/particle.c
index acda59ce96c..e50b321900a 100644
--- a/source/blender/blenkernel/intern/particle.c
+++ b/source/blender/blenkernel/intern/particle.c
@@ -3925,7 +3925,7 @@ static ModifierData *object_add_or_copy_particle_system(
}
if (name == NULL) {
- name = (psys_orig != NULL) ? psys_orig->name : DATA_("ParticleSettings");
+ name = (psys_orig != NULL) ? psys_orig->name : DATA_("ParticleSystem");
}
psys = ob->particlesystem.first;
@@ -3943,7 +3943,7 @@ static ModifierData *object_add_or_copy_particle_system(
id_us_plus(&psys->part->id);
}
else {
- psys->part = BKE_particlesettings_add(bmain, psys->name);
+ psys->part = BKE_particlesettings_add(bmain, DATA_("ParticleSettings"));
}
md = BKE_modifier_new(eModifierType_ParticleSystem);
BLI_strncpy(md->name, psys->name, sizeof(md->name));
diff --git a/source/blender/blenkernel/intern/particle_distribute.c b/source/blender/blenkernel/intern/particle_distribute.c
index c3cc9136057..ad617b4198b 100644
--- a/source/blender/blenkernel/intern/particle_distribute.c
+++ b/source/blender/blenkernel/intern/particle_distribute.c
@@ -338,18 +338,18 @@ static void hammersley_create(float *out, int n, int seed, float amount)
{
RNG *rng;
- double offs[2], t;
+ double ofs[2], t;
rng = BLI_rng_new(31415926 + n + seed);
- offs[0] = BLI_rng_get_double(rng) + (double)amount;
- offs[1] = BLI_rng_get_double(rng) + (double)amount;
+ ofs[0] = BLI_rng_get_double(rng) + (double)amount;
+ ofs[1] = BLI_rng_get_double(rng) + (double)amount;
BLI_rng_free(rng);
for (int k = 0; k < n; k++) {
BLI_hammersley_1d(k, &t);
- out[2 * k + 0] = fmod((double)k / (double)n + offs[0], 1.0);
- out[2 * k + 1] = fmod(t + offs[1], 1.0);
+ out[2 * k + 0] = fmod((double)k / (double)n + ofs[0], 1.0);
+ out[2 * k + 1] = fmod(t + ofs[1], 1.0);
}
}
diff --git a/source/blender/blenkernel/intern/pbvh.c b/source/blender/blenkernel/intern/pbvh.c
index 8a98780d918..77dde3a921a 100644
--- a/source/blender/blenkernel/intern/pbvh.c
+++ b/source/blender/blenkernel/intern/pbvh.c
@@ -137,8 +137,7 @@ static void update_node_vb(PBVH *pbvh, PBVHNode *node)
if (node->flag & PBVH_Leaf) {
PBVHVertexIter vd;
- BKE_pbvh_vertex_iter_begin(pbvh, node, vd, PBVH_ITER_ALL)
- {
+ BKE_pbvh_vertex_iter_begin (pbvh, node, vd, PBVH_ITER_ALL) {
BB_expand(&vb, vd.co);
}
BKE_pbvh_vertex_iter_end;
@@ -1143,8 +1142,7 @@ static void pbvh_update_mask_redraw_task_cb(void *__restrict userdata,
if (node->flag & PBVH_Leaf) {
PBVHVertexIter vd;
- BKE_pbvh_vertex_iter_begin(pbvh, node, vd, PBVH_ITER_ALL)
- {
+ BKE_pbvh_vertex_iter_begin (pbvh, node, vd, PBVH_ITER_ALL) {
if (vd.mask && *vd.mask < 1.0f) {
has_unmasked = true;
}
@@ -1191,8 +1189,7 @@ static void pbvh_update_visibility_redraw_task_cb(void *__restrict userdata,
BKE_pbvh_node_fully_hidden_set(node, true);
if (node->flag & PBVH_Leaf) {
PBVHVertexIter vd;
- BKE_pbvh_vertex_iter_begin(pbvh, node, vd, PBVH_ITER_ALL)
- {
+ BKE_pbvh_vertex_iter_begin (pbvh, node, vd, PBVH_ITER_ALL) {
if (vd.visible) {
BKE_pbvh_node_fully_hidden_set(node, false);
return;
diff --git a/source/blender/blenkernel/intern/scene.c b/source/blender/blenkernel/intern/scene.c
index 3633dff8690..de3e1023b08 100644
--- a/source/blender/blenkernel/intern/scene.c
+++ b/source/blender/blenkernel/intern/scene.c
@@ -1377,9 +1377,10 @@ static void scene_blend_read_data(BlendDataReader *reader, ID *id)
/* patch for missing scene IDs, can't be in do-versions */
static void composite_patch(bNodeTree *ntree, Scene *scene)
{
-
LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
- if (node->id == NULL && node->type == CMP_NODE_R_LAYERS) {
+ if (node->id == NULL &&
+ ((node->type == CMP_NODE_R_LAYERS) ||
+ (node->type == CMP_NODE_CRYPTOMATTE && node->custom1 == CMP_CRYPTOMATTE_SRC_RENDER))) {
node->id = &scene->id;
}
}
diff --git a/source/blender/blenkernel/intern/screen.c b/source/blender/blenkernel/intern/screen.c
index 8b911143668..1766ac5b85f 100644
--- a/source/blender/blenkernel/intern/screen.c
+++ b/source/blender/blenkernel/intern/screen.c
@@ -224,6 +224,12 @@ void BKE_screen_foreach_id_screen_area(LibraryForeachIDData *data, ScrArea *area
BKE_LIB_FOREACHID_PROCESS(data, sclip->mask_info.mask, IDWALK_CB_USER_ONE);
break;
}
+ case SPACE_SPREADSHEET: {
+ SpaceSpreadsheet *sspreadsheet = (SpaceSpreadsheet *)sl;
+
+ BKE_LIB_FOREACHID_PROCESS_ID(data, sspreadsheet->pinned_id, IDWALK_CB_NOP);
+ break;
+ }
default:
break;
}
@@ -1217,7 +1223,7 @@ static void write_panel_list(BlendWriter *writer, ListBase *lb)
}
}
-static void write_area_regions(BlendWriter *writer, ScrArea *area)
+static void write_area(BlendWriter *writer, ScrArea *area)
{
LISTBASE_FOREACH (ARegion *, region, &area->regionbase) {
write_region(writer, region, area->spacetype);
@@ -1342,6 +1348,9 @@ static void write_area_regions(BlendWriter *writer, ScrArea *area)
else if (sl->spacetype == SPACE_INFO) {
BLO_write_struct(writer, SpaceInfo, sl);
}
+ else if (sl->spacetype == SPACE_SPREADSHEET) {
+ BLO_write_struct(writer, SpaceSpreadsheet, sl);
+ }
}
}
@@ -1356,7 +1365,7 @@ void BKE_screen_area_map_blend_write(BlendWriter *writer, ScrAreaMap *area_map)
BLO_write_struct(writer, ScrGlobalAreaData, area->global);
- write_area_regions(writer, area);
+ write_area(writer, area);
area->butspacetype = SPACE_EMPTY; /* Unset again, was changed above. */
}
@@ -1681,6 +1690,7 @@ static void direct_link_area(BlendDataReader *reader, ScrArea *area)
sfile->op = NULL;
sfile->previews_timer = NULL;
sfile->tags = 0;
+ sfile->runtime = NULL;
BLO_read_data_address(reader, &sfile->params);
BLO_read_data_address(reader, &sfile->asset_params);
}
@@ -1691,6 +1701,11 @@ static void direct_link_area(BlendDataReader *reader, ScrArea *area)
sclip->scopes.track_preview = NULL;
sclip->scopes.ok = 0;
}
+ else if (sl->spacetype == SPACE_SPREADSHEET) {
+ SpaceSpreadsheet *sspreadsheet = (SpaceSpreadsheet *)sl;
+
+ sspreadsheet->runtime = NULL;
+ }
}
BLI_listbase_clear(&area->actionzones);
@@ -1904,6 +1919,11 @@ void BKE_screen_area_blend_read_lib(BlendLibReader *reader, ID *parent_id, ScrAr
BLO_read_id_address(reader, parent_id->lib, &sclip->mask_info.mask);
break;
}
+ case SPACE_SPREADSHEET: {
+ SpaceSpreadsheet *sspreadsheet = (SpaceSpreadsheet *)sl;
+ BLO_read_id_address(reader, parent_id->lib, &sspreadsheet->pinned_id);
+ break;
+ }
default:
break;
}
diff --git a/source/blender/blenkernel/intern/simulation.cc b/source/blender/blenkernel/intern/simulation.cc
index 6b46804c251..216563b860d 100644
--- a/source/blender/blenkernel/intern/simulation.cc
+++ b/source/blender/blenkernel/intern/simulation.cc
@@ -54,7 +54,6 @@
#include "BLI_map.hh"
#include "BLT_translation.h"
-#include "FN_attributes_ref.hh"
#include "FN_multi_function_network_evaluation.hh"
#include "FN_multi_function_network_optimization.hh"
diff --git a/source/blender/blenkernel/intern/sound.c b/source/blender/blenkernel/intern/sound.c
index a33fedb3ea6..6dd1f66f6b5 100644
--- a/source/blender/blenkernel/intern/sound.c
+++ b/source/blender/blenkernel/intern/sound.c
@@ -413,7 +413,7 @@ void BKE_sound_init(Main *bmain)
}
if (!(sound_device = AUD_init(device_name, specs, buffersize, "Blender"))) {
- sound_device = AUD_init("Null", specs, buffersize, "Blender");
+ sound_device = AUD_init("None", specs, buffersize, "Blender");
}
BKE_sound_init_main(bmain);
@@ -994,7 +994,7 @@ int BKE_sound_scene_playing(Scene *scene)
return -1;
}
- /* In case of a "Null" audio device, we have no playback information. */
+ /* In case of a "None" audio device, we have no playback information. */
if (AUD_Device_getRate(sound_device) == AUD_RATE_INVALID) {
return -1;
}
diff --git a/source/blender/blenkernel/intern/studiolight.c b/source/blender/blenkernel/intern/studiolight.c
index 5bffcd4d9e7..4cc2d101b02 100644
--- a/source/blender/blenkernel/intern/studiolight.c
+++ b/source/blender/blenkernel/intern/studiolight.c
@@ -514,7 +514,7 @@ static void studiolight_create_matcap_gputexture(StudioLightImage *sli)
ImBuf *ibuf = sli->ibuf;
float *gpu_matcap_3components = MEM_callocN(sizeof(float[3]) * ibuf->x * ibuf->y, __func__);
- float(*offset4)[4] = (float(*)[4])ibuf->rect_float;
+ const float(*offset4)[4] = (const float(*)[4])ibuf->rect_float;
float(*offset3)[3] = (float(*)[3])gpu_matcap_3components;
for (int i = 0; i < ibuf->x * ibuf->y; i++, offset4++, offset3++) {
copy_v3_v3(*offset3, *offset4);
diff --git a/source/blender/blenkernel/intern/tracking_auto.c b/source/blender/blenkernel/intern/tracking_auto.c
index d5e878a9a75..f107fc4d6bc 100644
--- a/source/blender/blenkernel/intern/tracking_auto.c
+++ b/source/blender/blenkernel/intern/tracking_auto.c
@@ -386,7 +386,8 @@ static void autotrack_context_init_tracks_for_clip(AutoTrackContext *context, in
autotrack_track->track = track;
autotrack_track->is_trackable = autotrack_is_track_trackable(context, autotrack_track);
- tracking_configure_tracker(track, NULL, &autotrack_track->track_region_options);
+ tracking_configure_tracker(
+ track, NULL, context->is_backwards, &autotrack_track->track_region_options);
}
}
diff --git a/source/blender/blenkernel/intern/tracking_region_tracker.c b/source/blender/blenkernel/intern/tracking_region_tracker.c
index 7e37e438e24..ef36acdc3d5 100644
--- a/source/blender/blenkernel/intern/tracking_region_tracker.c
+++ b/source/blender/blenkernel/intern/tracking_region_tracker.c
@@ -183,8 +183,13 @@ static ImBuf *tracking_context_get_reference_ibuf(MovieClip *clip,
/* Fill in libmv tracker options structure with settings need to be used to perform track. */
void tracking_configure_tracker(const MovieTrackingTrack *track,
float *mask,
+ const bool is_backwards,
libmv_TrackRegionOptions *options)
{
+ options->direction = is_backwards ? LIBMV_TRACK_REGION_BACKWARD : LIBMV_TRACK_REGION_FORWARD;
+
+ /* TODO(sergey): Use explicit conversion, so that options are decoupled between the Libmv library
+ * and enumerator values in DNA. */
options->motion_model = track->motion_model;
options->use_brute = ((track->algorithm_flag & TRACK_ALGORITHM_FLAG_USE_BRUTE) != 0);
@@ -218,6 +223,7 @@ static bool configure_and_run_tracker(ImBuf *destination_ibuf,
int reference_search_area_width,
int reference_search_area_height,
float *mask,
+ const bool is_backward,
double dst_pixel_x[5],
double dst_pixel_y[5])
{
@@ -246,7 +252,7 @@ static bool configure_and_run_tracker(ImBuf *destination_ibuf,
destination_ibuf, track, marker, &new_search_area_width, &new_search_area_height);
/* configure the tracker */
- tracking_configure_tracker(track, mask, &options);
+ tracking_configure_tracker(track, mask, is_backward, &options);
/* Convert the marker corners and center into pixel coordinates in the
* search/destination images. */
@@ -372,6 +378,7 @@ void BKE_tracking_refine_marker(MovieClip *clip,
search_area_width,
search_area_height,
mask,
+ backwards,
dst_pixel_x,
dst_pixel_y);
diff --git a/source/blender/blenkernel/tracking_private.h b/source/blender/blenkernel/tracking_private.h
index 35c5221efa3..8de6ec93a7c 100644
--- a/source/blender/blenkernel/tracking_private.h
+++ b/source/blender/blenkernel/tracking_private.h
@@ -111,6 +111,7 @@ struct libmv_TrackRegionOptions;
void tracking_configure_tracker(const MovieTrackingTrack *track,
float *mask,
+ bool is_backwards,
struct libmv_TrackRegionOptions *options);
struct MovieTrackingMarker *tracking_get_keyframed_marker(struct MovieTrackingTrack *track,