diff options
Diffstat (limited to 'source/blender/blenkernel')
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, ¶ms, NULL); + if (bfd != NULL) { + BKE_blendfile_read_setup(C, bfd, ¶ms, 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, ¶ms, NULL); + struct BlendFileData *bfd = BKE_blendfile_read_from_memfile( + bmain, &mfu->memfile, ¶ms, NULL); + if (bfd != NULL) { + BKE_blendfile_read_setup(C, bfd, ¶ms, 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, |