diff options
Diffstat (limited to 'source/blender/blenkernel')
93 files changed, 3498 insertions, 2541 deletions
diff --git a/source/blender/blenkernel/BKE_DerivedMesh.h b/source/blender/blenkernel/BKE_DerivedMesh.h index 269d90b868e..4526bc38a70 100644 --- a/source/blender/blenkernel/BKE_DerivedMesh.h +++ b/source/blender/blenkernel/BKE_DerivedMesh.h @@ -104,14 +104,6 @@ typedef enum DerivedMeshType { DM_TYPE_CCGDM, } DerivedMeshType; -typedef enum DMDirtyFlag { - /* dm has valid tessellated faces, but tessellated CDDATA need to be updated. */ - DM_DIRTY_TESS_CDLAYERS = 1 << 0, - - /* check this with modifier dependsOnNormals callback to see if normals need recalculation */ - DM_DIRTY_NORMALS = 1 << 1, -} DMDirtyFlag; - typedef struct DerivedMesh DerivedMesh; struct DerivedMesh { /** Private DerivedMesh data, only for internal DerivedMesh use */ @@ -120,7 +112,6 @@ struct DerivedMesh { int needsFree; /* checked on ->release, is set to 0 for cached results */ int deformedOnly; /* set by modifier stack if only deformed from original */ DerivedMeshType type; - DMDirtyFlag dirty; /** * \warning Typical access is done via #getLoopTriArray, #getNumLoopTri. @@ -139,9 +130,6 @@ struct DerivedMesh { short tangent_mask; /* which tangent layers are calculated */ - /** Calculate vert and face normals */ - void (*calcNormals)(DerivedMesh *dm); - /** Loop tessellation cache (WARNING! Only call inside threading-protected code!) */ void (*recalcLoopTri)(DerivedMesh *dm); /** accessor functions */ @@ -164,7 +152,6 @@ struct DerivedMesh { */ struct MVert *(*getVertArray)(DerivedMesh *dm); struct MEdge *(*getEdgeArray)(DerivedMesh *dm); - struct MFace *(*getTessFaceArray)(DerivedMesh *dm); struct MLoop *(*getLoopArray)(DerivedMesh *dm); struct MPoly *(*getPolyArray)(DerivedMesh *dm); @@ -173,7 +160,6 @@ struct DerivedMesh { */ void (*copyVertArray)(DerivedMesh *dm, struct MVert *r_vert); void (*copyEdgeArray)(DerivedMesh *dm, struct MEdge *r_edge); - void (*copyTessFaceArray)(DerivedMesh *dm, struct MFace *r_face); void (*copyLoopArray)(DerivedMesh *dm, struct MLoop *r_loop); void (*copyPolyArray)(DerivedMesh *dm, struct MPoly *r_poly); @@ -182,37 +168,18 @@ struct DerivedMesh { */ struct MVert *(*dupVertArray)(DerivedMesh *dm); struct MEdge *(*dupEdgeArray)(DerivedMesh *dm); - struct MFace *(*dupTessFaceArray)(DerivedMesh *dm); struct MLoop *(*dupLoopArray)(DerivedMesh *dm); struct MPoly *(*dupPolyArray)(DerivedMesh *dm); - /** Return a pointer to a single element of vert/edge/face custom data - * from the derived mesh (this gives a pointer to the actual data, not - * a copy) - */ - void *(*getVertData)(DerivedMesh *dm, int index, int type); - void *(*getEdgeData)(DerivedMesh *dm, int index, int type); - void *(*getTessFaceData)(DerivedMesh *dm, int index, int type); - void *(*getPolyData)(DerivedMesh *dm, int index, int type); - /** Return a pointer to the entire array of vert/edge/face custom data * from the derived mesh (this gives a pointer to the actual data, not * a copy) */ void *(*getVertDataArray)(DerivedMesh *dm, int type); void *(*getEdgeDataArray)(DerivedMesh *dm, int type); - void *(*getTessFaceDataArray)(DerivedMesh *dm, int type); void *(*getLoopDataArray)(DerivedMesh *dm, int type); void *(*getPolyDataArray)(DerivedMesh *dm, int type); - /** Retrieves the base CustomData structures for - * verts/edges/tessfaces/loops/faces. */ - CustomData *(*getVertDataLayout)(DerivedMesh *dm); - CustomData *(*getEdgeDataLayout)(DerivedMesh *dm); - CustomData *(*getTessFaceDataLayout)(DerivedMesh *dm); - CustomData *(*getLoopDataLayout)(DerivedMesh *dm); - CustomData *(*getPolyDataLayout)(DerivedMesh *dm); - /** Optional grid access for subsurf */ int (*getNumGrids)(DerivedMesh *dm); int (*getGridSize)(DerivedMesh *dm); @@ -231,11 +198,6 @@ struct DerivedMesh { /** Get smooth vertex normal, undefined if index is not valid */ void (*getVertNo)(DerivedMesh *dm, int index, float r_no[3]); - void (*getPolyNo)(DerivedMesh *dm, int index, float r_no[3]); - - /** Get a map of vertices to faces - */ - const struct MeshElemMap *(*getPolyMap)(struct Object *ob, DerivedMesh *dm); /** Release reference to the DerivedMesh. This function decides internally * if the DerivedMesh will be freed, or cached for later use. */ @@ -265,15 +227,6 @@ void DM_init(DerivedMesh *dm, * Utility function to initialize a DerivedMesh for the desired number * of vertices, edges and faces, with a layer setup copied from source */ -void DM_from_template_ex(DerivedMesh *dm, - DerivedMesh *source, - DerivedMeshType type, - int numVerts, - int numEdges, - int numTessFaces, - int numLoops, - int numPolys, - const struct CustomData_MeshMasks *mask); void DM_from_template(DerivedMesh *dm, DerivedMesh *source, DerivedMeshType type, @@ -303,26 +256,9 @@ void DM_set_only_copy(DerivedMesh *dm, const struct CustomData_MeshMasks *mask); void DM_add_vert_layer(struct DerivedMesh *dm, int type, eCDAllocType alloctype, void *layer); void DM_add_edge_layer(struct DerivedMesh *dm, int type, eCDAllocType alloctype, void *layer); -void DM_add_tessface_layer(struct DerivedMesh *dm, int type, eCDAllocType alloctype, void *layer); -void DM_add_loop_layer(DerivedMesh *dm, int type, eCDAllocType alloctype, void *layer); void DM_add_poly_layer(struct DerivedMesh *dm, int type, eCDAllocType alloctype, void *layer); /* -------------------------------------------------------------------- */ -/** \name Custom Data Access Functions - * - * \return pointer to data from first layer which matches type - * if they return NULL for valid indices, data doesn't exist. - * \note these return pointers - any change modifies the internals of the mesh. - * \{ */ - -void *DM_get_vert_data(struct DerivedMesh *dm, int index, int type); -void *DM_get_edge_data(struct DerivedMesh *dm, int index, int type); -void *DM_get_tessface_data(struct DerivedMesh *dm, int index, int type); -void *DM_get_poly_data(struct DerivedMesh *dm, int index, int type); - -/** \} */ - -/* -------------------------------------------------------------------- */ /** \name Custom Data Layer Access Functions * * \return pointer to first data layer which matches type (a flat array) @@ -332,7 +268,6 @@ void *DM_get_poly_data(struct DerivedMesh *dm, int index, int type); void *DM_get_vert_data_layer(struct DerivedMesh *dm, int type); void *DM_get_edge_data_layer(struct DerivedMesh *dm, int type); -void *DM_get_tessface_data_layer(struct DerivedMesh *dm, int type); void *DM_get_poly_data_layer(struct DerivedMesh *dm, int type); void *DM_get_loop_data_layer(struct DerivedMesh *dm, int type); @@ -354,8 +289,6 @@ void DM_copy_vert_data(struct DerivedMesh *source, */ void DM_DupPolys(DerivedMesh *source, DerivedMesh *target); -void DM_ensure_normals(DerivedMesh *dm); - /** * Ensure the array is large enough. * @@ -399,14 +332,8 @@ bool editbmesh_modifier_is_enabled(struct Scene *scene, void makeDerivedMesh(struct Depsgraph *depsgraph, struct Scene *scene, struct Object *ob, - struct BMEditMesh *em, const struct CustomData_MeshMasks *dataMask); -void DM_calc_loop_tangents(DerivedMesh *dm, - bool calc_active_tangent, - const char (*tangent_names)[MAX_NAME], - int tangent_names_len); - #ifdef __cplusplus } #endif diff --git a/source/blender/blenkernel/BKE_action.h b/source/blender/blenkernel/BKE_action.h index 0b09bfd8730..5d1b4baedfd 100644 --- a/source/blender/blenkernel/BKE_action.h +++ b/source/blender/blenkernel/BKE_action.h @@ -365,7 +365,6 @@ void what_does_obaction(struct Object *ob, char groupname[], const struct AnimationEvalContext *anim_eval_context); -/* for proxy */ void BKE_pose_copy_pchan_result(struct bPoseChannel *pchanto, const struct bPoseChannel *pchanfrom); /** diff --git a/source/blender/blenkernel/BKE_armature.h b/source/blender/blenkernel/BKE_armature.h index 8584ce6f508..12d8135ba55 100644 --- a/source/blender/blenkernel/BKE_armature.h +++ b/source/blender/blenkernel/BKE_armature.h @@ -619,14 +619,6 @@ void BKE_pose_eval_cleanup(struct Depsgraph *depsgraph, struct Scene *scene, struct Object *object); -void BKE_pose_eval_proxy_init(struct Depsgraph *depsgraph, struct Object *object); -void BKE_pose_eval_proxy_done(struct Depsgraph *depsgraph, struct Object *object); -void BKE_pose_eval_proxy_cleanup(struct Depsgraph *depsgraph, struct Object *object); - -void BKE_pose_eval_proxy_copy_bone(struct Depsgraph *depsgraph, - struct Object *object, - int pchan_index); - /* -------------------------------------------------------------------- */ /** \name Deform 3D Coordinates by Armature (armature_deform.c) * \{ */ diff --git a/source/blender/blenkernel/BKE_attribute.h b/source/blender/blenkernel/BKE_attribute.h index 6020da08f51..ff207997e79 100644 --- a/source/blender/blenkernel/BKE_attribute.h +++ b/source/blender/blenkernel/BKE_attribute.h @@ -40,11 +40,11 @@ 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_POINT = 0, /* Mesh, Curve or Point Cloud Point */ ATTR_DOMAIN_EDGE = 1, /* Mesh Edge */ ATTR_DOMAIN_FACE = 2, /* Mesh Face */ ATTR_DOMAIN_CORNER = 3, /* Mesh Corner */ - ATTR_DOMAIN_CURVE = 4, /* Hair Curve */ + ATTR_DOMAIN_CURVE = 4, /* A single curve in a larger curve data-block */ ATTR_DOMAIN_INSTANCE = 5, /* Instance */ ATTR_DOMAIN_NUM diff --git a/source/blender/blenkernel/BKE_attribute_math.hh b/source/blender/blenkernel/BKE_attribute_math.hh index 90f349125c9..bf773cd6d75 100644 --- a/source/blender/blenkernel/BKE_attribute_math.hh +++ b/source/blender/blenkernel/BKE_attribute_math.hh @@ -50,6 +50,9 @@ inline void convert_to_static_type(const CustomDataType data_type, const Func &f case CD_PROP_BOOL: func(bool()); break; + case CD_PROP_INT8: + func(int8_t()); + break; case CD_PROP_COLOR: func(ColorGeometry4f()); break; @@ -77,6 +80,9 @@ inline void convert_to_static_type(const fn::CPPType &cpp_type, const Func &func else if (cpp_type.is<bool>()) { func(bool()); } + else if (cpp_type.is<int8_t>()) { + func(int8_t()); + } else if (cpp_type.is<ColorGeometry4f>()) { func(ColorGeometry4f()); } @@ -93,6 +99,12 @@ inline void convert_to_static_type(const fn::CPPType &cpp_type, const Func &func template<typename T> T mix3(const float3 &weights, const T &v0, const T &v1, const T &v2); +template<> +inline int8_t mix3(const float3 &weights, const int8_t &v0, const int8_t &v1, const int8_t &v2) +{ + return static_cast<int8_t>(weights.x * v0 + weights.y * v1 + weights.z * v2); +} + template<> inline bool mix3(const float3 &weights, const bool &v0, const bool &v1, const bool &v2) { return (weights.x * v0 + weights.y * v1 + weights.z * v2) >= 0.5f; @@ -147,6 +159,11 @@ template<> inline bool mix2(const float factor, const bool &a, const bool &b) return ((1.0f - factor) * a + factor * b) >= 0.5f; } +template<> inline int8_t mix2(const float factor, const int8_t &a, const int8_t &b) +{ + return static_cast<int8_t>((1.0f - factor) * a + factor * b); +} + template<> inline int mix2(const float factor, const int &a, const int &b) { return static_cast<int>((1.0f - factor) * a + factor * b); @@ -364,6 +381,15 @@ template<> struct DefaultMixerStruct<bool> { using type = SimpleMixerWithAccumulationType<bool, float, float_to_bool>; }; +template<> struct DefaultMixerStruct<int8_t> { + static int8_t float_to_int8_t(const float &value) + { + return static_cast<int8_t>(value); + } + /* Store interpolated 8 bit integers in a float temporarily to increase accuracy. */ + using type = SimpleMixerWithAccumulationType<int8_t, float, float_to_int8_t>; +}; + template<typename T> struct DefaultPropatationMixerStruct { /* Use void by default. This can be checked for in `if constexpr` statements. */ using type = typename DefaultMixerStruct<T>::type; diff --git a/source/blender/blenkernel/BKE_blender_version.h b/source/blender/blenkernel/BKE_blender_version.h index d0ab8be9a29..fe656166ada 100644 --- a/source/blender/blenkernel/BKE_blender_version.h +++ b/source/blender/blenkernel/BKE_blender_version.h @@ -31,7 +31,7 @@ extern "C" { */ /* Blender major and minor version. */ -#define BLENDER_VERSION 301 +#define BLENDER_VERSION 302 /* Blender patch version for bugfix releases. */ #define BLENDER_VERSION_PATCH 0 /** Blender release cycle stage: alpha/beta/rc/release. */ @@ -39,7 +39,7 @@ extern "C" { /* Blender file format version. */ #define BLENDER_FILE_VERSION BLENDER_VERSION -#define BLENDER_FILE_SUBVERSION 5 +#define BLENDER_FILE_SUBVERSION 2 /* 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_camera.h b/source/blender/blenkernel/BKE_camera.h index ee78621c11f..550ce4eb601 100644 --- a/source/blender/blenkernel/BKE_camera.h +++ b/source/blender/blenkernel/BKE_camera.h @@ -46,7 +46,7 @@ void *BKE_camera_add(struct Main *bmain, const char *name); /** * Get the camera's DOF value, takes the DOF object into account. */ -float BKE_camera_object_dof_distance(struct Object *ob); +float BKE_camera_object_dof_distance(const struct Object *ob); int BKE_camera_sensor_fit(int sensor_fit, float sizex, float sizey); float BKE_camera_sensor_size(int sensor_fit, float sensor_x, float sensor_y); diff --git a/source/blender/blenkernel/BKE_collection.h b/source/blender/blenkernel/BKE_collection.h index 402bffea91d..467d74b17da 100644 --- a/source/blender/blenkernel/BKE_collection.h +++ b/source/blender/blenkernel/BKE_collection.h @@ -123,12 +123,21 @@ struct Collection *BKE_collection_object_find(struct Main *bmain, bool BKE_collection_is_empty(const struct Collection *collection); /** - * Add object to collection + * Add object to given collection, ensuring this collection is 'editable' (i.e. local and not a + * liboverride), and finding a suitable parent one otherwise. */ bool BKE_collection_object_add(struct Main *bmain, struct Collection *collection, struct Object *ob); /** + * Same as #BKE_collection_object_add, but unconditionally adds the object to the given collection. + * + * NOTE: required in certain cases, like do-versioning or complex ID management tasks. + */ +bool BKE_collection_object_add_notest(struct Main *bmain, + struct Collection *collection, + struct Object *ob); +/** * Add \a ob_dst to all scene collections that reference object \a ob_src is in. * Used for copying objects. * diff --git a/source/blender/blenkernel/BKE_constraint.h b/source/blender/blenkernel/BKE_constraint.h index 55e5cd0a149..2da79e27576 100644 --- a/source/blender/blenkernel/BKE_constraint.h +++ b/source/blender/blenkernel/BKE_constraint.h @@ -278,18 +278,6 @@ bool BKE_constraint_apply_and_remove_for_pose(struct Depsgraph *depsgraph, void BKE_constraint_panel_expand(struct bConstraint *con); -/* Constraints + Proxies function prototypes */ - -/** - * Rescue all constraints tagged as being #CONSTRAINT_PROXY_LOCAL - * (i.e. added to bone that's proxy-synced in this file). - */ -void BKE_constraints_proxylocal_extract(struct ListBase *dst, struct ListBase *src); -/** - * Returns if the owner of the constraint is proxy-protected. - */ -bool BKE_constraints_proxylocked_owner(struct Object *ob, struct bPoseChannel *pchan); - /* Constraint Evaluation function prototypes */ /** diff --git a/source/blender/blenkernel/BKE_crazyspace.h b/source/blender/blenkernel/BKE_crazyspace.h index c00e397f552..42d85c70bc1 100644 --- a/source/blender/blenkernel/BKE_crazyspace.h +++ b/source/blender/blenkernel/BKE_crazyspace.h @@ -26,10 +26,12 @@ #ifdef __cplusplus extern "C" { #endif + struct BMEditMesh; struct Depsgraph; struct Mesh; struct Object; +struct ReportList; struct Scene; /* crazyspace.c */ @@ -69,6 +71,31 @@ void BKE_crazyspace_build_sculpt(struct Depsgraph *depsgraph, float (**deformmats)[3][3], float (**deformcos)[3]); +/* -------------------------------------------------------------------- */ +/** \name Crazy-Space API + * \{ */ + +void BKE_crazyspace_api_eval(struct Depsgraph *depsgraph, + struct Scene *scene, + struct Object *object, + struct ReportList *reports); + +void BKE_crazyspace_api_displacement_to_deformed(struct Object *object, + struct ReportList *reports, + int vertex_index, + float displacement[3], + float r_displacement_deformed[3]); + +void BKE_crazyspace_api_displacement_to_original(struct Object *object, + struct ReportList *reports, + int vertex_index, + float displacement_deformed[3], + float r_displacement[3]); + +void BKE_crazyspace_api_eval_clear(struct Object *object); + +/** \} */ + #ifdef __cplusplus } #endif diff --git a/source/blender/blenkernel/BKE_curves.h b/source/blender/blenkernel/BKE_curves.h new file mode 100644 index 00000000000..99839b20121 --- /dev/null +++ b/source/blender/blenkernel/BKE_curves.h @@ -0,0 +1,68 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#pragma once + +/** \file + * \ingroup bke + * \brief Low-level operations for curves. + */ + +#ifdef __cplusplus +extern "C" { +#endif + +struct BoundBox; +struct CustomDataLayer; +struct Depsgraph; +struct Curves; +struct Main; +struct Object; +struct Scene; + +void *BKE_curves_add(struct Main *bmain, const char *name); + +struct BoundBox *BKE_curves_boundbox_get(struct Object *ob); + +void BKE_curves_update_customdata_pointers(struct Curves *curves); +bool BKE_curves_customdata_required(struct Curves *curves, struct CustomDataLayer *layer); + +/* Depsgraph */ + +struct Curves *BKE_curves_new_for_eval(const struct Curves *curves_src, + int totpoint, + int totcurve); +struct Curves *BKE_curves_copy_for_eval(struct Curves *curves_src, bool reference); + +void BKE_curves_data_update(struct Depsgraph *depsgraph, + struct Scene *scene, + struct Object *object); + +/* Draw Cache */ + +enum { + BKE_CURVES_BATCH_DIRTY_ALL = 0, +}; + +void BKE_curves_batch_cache_dirty_tag(struct Curves *curves, int mode); +void BKE_curves_batch_cache_free(struct Curves *curves); + +extern void (*BKE_curves_batch_cache_dirty_tag_cb)(struct Curves *curves, int mode); +extern void (*BKE_curves_batch_cache_free_cb)(struct Curves *curves); + +#ifdef __cplusplus +} +#endif diff --git a/source/blender/blenkernel/BKE_customdata.h b/source/blender/blenkernel/BKE_customdata.h index 00eae2e8e6e..38b43e36feb 100644 --- a/source/blender/blenkernel/BKE_customdata.h +++ b/source/blender/blenkernel/BKE_customdata.h @@ -254,6 +254,11 @@ bool CustomData_free_layer_active(struct CustomData *data, int type, int totelem void CustomData_free_layers(struct CustomData *data, int type, int totelem); /** + * Free all anonymous attributes. + */ +void CustomData_free_layers_anonymous(struct CustomData *data, int totelem); + +/** * Returns true if a layer with the specified type exists. */ bool CustomData_has_layer(const struct CustomData *data, int type); @@ -437,6 +442,12 @@ int CustomData_get_clone_layer(const struct CustomData *data, int type); int CustomData_get_stencil_layer(const struct CustomData *data, int type); /** + * Returns name of the active layer of the given type or NULL + * if no such active layer is defined. + */ +const char *CustomData_get_active_layer_name(const struct CustomData *data, int type); + +/** * Copies the data from source to the data element at index in the first layer of type * no effect if there is no layer of type. */ diff --git a/source/blender/blenkernel/BKE_editmesh.h b/source/blender/blenkernel/BKE_editmesh.h index 5be06dcc5c3..1da7ae3da8a 100644 --- a/source/blender/blenkernel/BKE_editmesh.h +++ b/source/blender/blenkernel/BKE_editmesh.h @@ -62,14 +62,6 @@ typedef struct BMEditMesh { struct BMLoop *(*looptris)[3]; int tottri; - struct Mesh *mesh_eval_final, *mesh_eval_cage; - - /** Cached cage bounding box of `mesh_eval_cage` for selection. */ - struct BoundBox *bb_cage; - - /** Evaluated mesh data-mask. */ - CustomData_MeshMasks lastDataMask; - /** Selection mode (#SCE_SELECT_VERTEX, #SCE_SELECT_EDGE & #SCE_SELECT_FACE). */ short selectmode; /** The active material (assigned to newly created faces). */ @@ -121,7 +113,6 @@ BMEditMesh *BKE_editmesh_copy(BMEditMesh *em); * don't add NULL data check here. caller must do that */ BMEditMesh *BKE_editmesh_from_object(struct Object *ob); -void BKE_editmesh_free_derived_caches(BMEditMesh *em); /** * \note Does not free the #BMEditMesh struct itself. */ @@ -145,7 +136,7 @@ void BKE_editmesh_lnorspace_update(BMEditMesh *em, struct Mesh *me); * If auto-smooth not already set, set it. */ void BKE_editmesh_ensure_autosmooth(BMEditMesh *em, struct Mesh *me); -struct BoundBox *BKE_editmesh_cage_boundbox_get(BMEditMesh *em); +struct BoundBox *BKE_editmesh_cage_boundbox_get(struct Object *object, BMEditMesh *em); #ifdef __cplusplus } diff --git a/source/blender/blenkernel/BKE_hair.h b/source/blender/blenkernel/BKE_hair.h deleted file mode 100644 index 403e461a9dc..00000000000 --- a/source/blender/blenkernel/BKE_hair.h +++ /dev/null @@ -1,63 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -#pragma once - -/** \file - * \ingroup bke - * \brief General operations for hairs. - */ -#ifdef __cplusplus -extern "C" { -#endif - -struct BoundBox; -struct CustomDataLayer; -struct Depsgraph; -struct Hair; -struct Main; -struct Object; -struct Scene; - -void *BKE_hair_add(struct Main *bmain, const char *name); - -struct BoundBox *BKE_hair_boundbox_get(struct Object *ob); - -void BKE_hair_update_customdata_pointers(struct Hair *hair); -bool BKE_hair_customdata_required(struct Hair *hair, struct CustomDataLayer *layer); - -/* Depsgraph */ - -struct Hair *BKE_hair_new_for_eval(const struct Hair *hair_src, int totpoint, int totcurve); -struct Hair *BKE_hair_copy_for_eval(struct Hair *hair_src, bool reference); - -void BKE_hair_data_update(struct Depsgraph *depsgraph, struct Scene *scene, struct Object *object); - -/* Draw Cache */ - -enum { - BKE_HAIR_BATCH_DIRTY_ALL = 0, -}; - -void BKE_hair_batch_cache_dirty_tag(struct Hair *hair, int mode); -void BKE_hair_batch_cache_free(struct Hair *hair); - -extern void (*BKE_hair_batch_cache_dirty_tag_cb)(struct Hair *hair, int mode); -extern void (*BKE_hair_batch_cache_free_cb)(struct Hair *hair); - -#ifdef __cplusplus -} -#endif diff --git a/source/blender/blenkernel/BKE_idtype.h b/source/blender/blenkernel/BKE_idtype.h index df50f773a46..e9e5b183e4a 100644 --- a/source/blender/blenkernel/BKE_idtype.h +++ b/source/blender/blenkernel/BKE_idtype.h @@ -278,7 +278,7 @@ extern IDTypeInfo IDType_ID_PC; extern IDTypeInfo IDType_ID_CF; extern IDTypeInfo IDType_ID_WS; extern IDTypeInfo IDType_ID_LP; -extern IDTypeInfo IDType_ID_HA; +extern IDTypeInfo IDType_ID_CV; extern IDTypeInfo IDType_ID_PT; extern IDTypeInfo IDType_ID_VO; extern IDTypeInfo IDType_ID_SIM; diff --git a/source/blender/blenkernel/BKE_image.h b/source/blender/blenkernel/BKE_image.h index 80c6b155be0..598818ba3c0 100644 --- a/source/blender/blenkernel/BKE_image.h +++ b/source/blender/blenkernel/BKE_image.h @@ -24,6 +24,8 @@ #include "BLI_utildefines.h" +#include "BLI_rect.h" + #ifdef __cplusplus extern "C" { #endif @@ -561,19 +563,27 @@ struct GPUTexture *BKE_image_get_gpu_tilemap(struct Image *image, * Is the alpha of the `GPUTexture` for a given image/ibuf premultiplied. */ bool BKE_image_has_gpu_texture_premultiplied_alpha(struct Image *image, struct ImBuf *ibuf); + /** * Partial update of texture for texture painting. * This is often much quicker than fully updating the texture for high resolution images. */ void BKE_image_update_gputexture( struct Image *ima, struct ImageUser *iuser, int x, int y, int w, int h); + /** * Mark areas on the #GPUTexture that needs to be updated. The areas are marked in chunks. * The next time the #GPUTexture is used these tiles will be refreshes. This saves time * when writing to the same place multiple times This happens for during foreground rendering. */ -void BKE_image_update_gputexture_delayed( - struct Image *ima, struct ImBuf *ibuf, int x, int y, int w, int h); +void BKE_image_update_gputexture_delayed(struct Image *ima, + struct ImageTile *image_tile, + struct ImBuf *ibuf, + int x, + int y, + int w, + int h); + /** * Called on entering and exiting texture paint mode, * temporary disabling/enabling mipmapping on all images for quick texture @@ -591,6 +601,32 @@ bool BKE_image_remove_renderslot(struct Image *ima, struct ImageUser *iuser, int struct RenderSlot *BKE_image_get_renderslot(struct Image *ima, int index); bool BKE_image_clear_renderslot(struct Image *ima, struct ImageUser *iuser, int slot); +/* --- image_partial_update.cc --- */ +/** Image partial updates. */ +struct PartialUpdateUser; + +/** + * \brief Create a new PartialUpdateUser. An Object that contains data to use partial updates. + */ +struct PartialUpdateUser *BKE_image_partial_update_create(const struct Image *image); + +/** + * \brief free a partial update user. + */ +void BKE_image_partial_update_free(struct PartialUpdateUser *user); + +/* --- partial updater (image side) --- */ +struct PartialUpdateRegister; + +void BKE_image_partial_update_register_free(struct Image *image); +/** \brief Mark a region of the image to update. */ +void BKE_image_partial_update_mark_region(struct Image *image, + const struct ImageTile *image_tile, + const struct ImBuf *image_buffer, + const rcti *updated_region); +/** \brief Mark the whole image to be updated. */ +void BKE_image_partial_update_mark_full_update(struct Image *image); + #ifdef __cplusplus } #endif diff --git a/source/blender/blenkernel/BKE_image_partial_update.hh b/source/blender/blenkernel/BKE_image_partial_update.hh new file mode 100644 index 00000000000..0fc05809bbd --- /dev/null +++ b/source/blender/blenkernel/BKE_image_partial_update.hh @@ -0,0 +1,298 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Copyright 2021, Blender Foundation. + */ + +/** \file + * \ingroup bke + * + * To reduce the overhead of image processing this file contains a mechanism to detect areas of the + * image that are changed. These areas are organized in chunks. Changes that happen over time are + * organized in changesets. + * + * A common use case is to update #GPUTexture for drawing where only that part is uploaded that + * only changed. + */ + +#pragma once + +#include "BLI_utildefines.h" + +#include "BLI_rect.h" + +#include "DNA_image_types.h" + +extern "C" { +struct PartialUpdateUser; +struct PartialUpdateRegister; +} + +namespace blender::bke::image { + +using TileNumber = int; + +namespace partial_update { + +/* --- image_partial_update.cc --- */ +/** Image partial updates. */ + +/** + * \brief Result codes of #BKE_image_partial_update_collect_changes. + */ +enum class ePartialUpdateCollectResult { + /** \brief Unable to construct partial updates. Caller should perform a full update. */ + FullUpdateNeeded, + + /** \brief No changes detected since the last time requested. */ + NoChangesDetected, + + /** \brief Changes detected since the last time requested. */ + PartialChangesDetected, +}; + +/** + * \brief A region to update. + * + * Data is organized in tiles. These tiles are in texel space (1 unit is a single texel). When + * tiles are requested they are merged with neighboring tiles. + */ +struct PartialUpdateRegion { + /** \brief region of the image that has been updated. Region can be bigger than actual changes. + */ + struct rcti region; + + /** + * \brief Tile number (UDIM) that this region belongs to. + */ + TileNumber tile_number; +}; + +/** + * \brief Return codes of #BKE_image_partial_update_get_next_change. + */ +enum class ePartialUpdateIterResult { + /** \brief no tiles left when iterating over tiles. */ + Finished = 0, + + /** \brief a chunk was available and has been loaded. */ + ChangeAvailable = 1, +}; + +/** + * \brief collect the partial update since the last request. + * + * Invoke #BKE_image_partial_update_get_next_change to iterate over the collected tiles. + * + * \returns ePartialUpdateCollectResult::FullUpdateNeeded: called should not use partial updates + * but recalculate the full image. This result can be expected when called for the first time for a + * user and when it isn't possible to reconstruct the changes as the internal state doesn't have + * enough data stored. ePartialUpdateCollectResult::NoChangesDetected: The have been no changes + * detected since last invoke for the same user. + * ePartialUpdateCollectResult::PartialChangesDetected: Parts of the image has been updated since + * last invoke for the same user. The changes can be read by using + * #BKE_image_partial_update_get_next_change. + */ +ePartialUpdateCollectResult BKE_image_partial_update_collect_changes( + struct Image *image, struct PartialUpdateUser *user); + +ePartialUpdateIterResult BKE_image_partial_update_get_next_change( + struct PartialUpdateUser *user, struct PartialUpdateRegion *r_region); + +/** \brief Abstract class to load tile data when using the PartialUpdateChecker. */ +class AbstractTileData { + protected: + virtual ~AbstractTileData() = default; + + public: + /** + * \brief Load the data for the given tile_number. + * + * Invoked when changes are on a different tile compared to the previous tile.. + */ + virtual void init_data(TileNumber tile_number) = 0; + /** + * \brief Unload the data that has been loaded. + * + * Invoked when changes are on a different tile compared to the previous tile or when finished + * iterating over the changes. + */ + virtual void free_data() = 0; +}; + +/** + * \brief Class to not load any tile specific data when iterating over changes. + */ +class NoTileData : AbstractTileData { + public: + NoTileData(Image *UNUSED(image), ImageUser *UNUSED(image_user)) + { + } + + void init_data(TileNumber UNUSED(new_tile_number)) override + { + } + + void free_data() override + { + } +}; + +/** + * \brief Load the ImageTile and ImBuf associated with the partial change. + */ +class ImageTileData : AbstractTileData { + public: + /** + * \brief Not owned Image that is being iterated over. + */ + Image *image; + + /** + * \brief Local copy of the image user. + * + * The local copy is required so we don't change the image user of the caller. + * We need to change it in order to request data for a specific tile. + */ + ImageUser image_user = {0}; + + /** + * \brief ImageTile associated with the loaded tile. + * Data is not owned by this instance but by the `image`. + */ + ImageTile *tile = nullptr; + + /** + * \brief ImBuf of the loaded tile. + * + * Can be nullptr when the file doesn't exist or when the tile hasn't been initialized. + */ + ImBuf *tile_buffer = nullptr; + + ImageTileData(Image *image, ImageUser *image_user) : image(image) + { + if (image_user != nullptr) { + this->image_user = *image_user; + } + } + + void init_data(TileNumber new_tile_number) override + { + image_user.tile = new_tile_number; + tile = BKE_image_get_tile(image, new_tile_number); + tile_buffer = BKE_image_acquire_ibuf(image, &image_user, NULL); + } + + void free_data() override + { + BKE_image_release_ibuf(image, tile_buffer, nullptr); + tile = nullptr; + tile_buffer = nullptr; + } +}; + +template<typename TileData = NoTileData> struct PartialUpdateChecker { + + /** + * \brief Not owned Image that is being iterated over. + */ + Image *image; + ImageUser *image_user; + + /** + * \brief the collected changes are stored inside the PartialUpdateUser. + */ + PartialUpdateUser *user; + + struct CollectResult { + PartialUpdateChecker<TileData> *checker; + + /** + * \brief Tile specific data. + */ + TileData tile_data; + PartialUpdateRegion changed_region; + ePartialUpdateCollectResult result_code; + + private: + TileNumber last_tile_number; + + public: + CollectResult(PartialUpdateChecker<TileData> *checker, ePartialUpdateCollectResult result_code) + : checker(checker), + tile_data(checker->image, checker->image_user), + result_code(result_code) + { + } + + const ePartialUpdateCollectResult get_result_code() const + { + return result_code; + } + + /** + * \brief Load the next changed region. + * + * This member function can only be called when partial changes are detected. + * (`get_result_code()` returns `ePartialUpdateCollectResult::PartialChangesDetected`). + * + * When changes for another tile than the previous tile is loaded the #tile_data will be + * updated. + */ + ePartialUpdateIterResult get_next_change() + { + BLI_assert(result_code == ePartialUpdateCollectResult::PartialChangesDetected); + ePartialUpdateIterResult result = BKE_image_partial_update_get_next_change(checker->user, + &changed_region); + switch (result) { + case ePartialUpdateIterResult::Finished: + tile_data.free_data(); + return result; + + case ePartialUpdateIterResult::ChangeAvailable: + if (last_tile_number == changed_region.tile_number) { + return result; + } + tile_data.free_data(); + tile_data.init_data(changed_region.tile_number); + last_tile_number = changed_region.tile_number; + return result; + + default: + BLI_assert_unreachable(); + return result; + } + } + }; + + public: + PartialUpdateChecker(Image *image, ImageUser *image_user, PartialUpdateUser *user) + : image(image), image_user(image_user), user(user) + { + } + + /** + * \brief Check for new changes since the last time this method was invoked for this #user. + */ + CollectResult collect_changes() + { + ePartialUpdateCollectResult collect_result = BKE_image_partial_update_collect_changes(image, + user); + return CollectResult(this, collect_result); + } +}; + +} // namespace partial_update +} // namespace blender::bke::image diff --git a/source/blender/blenkernel/BKE_layer.h b/source/blender/blenkernel/BKE_layer.h index bc2249b93b9..accdfe1ca25 100644 --- a/source/blender/blenkernel/BKE_layer.h +++ b/source/blender/blenkernel/BKE_layer.h @@ -157,6 +157,14 @@ int BKE_layer_collection_findindex(struct ViewLayer *view_layer, const struct La void BKE_layer_collection_resync_forbid(void); void BKE_layer_collection_resync_allow(void); +/** + * Helper to fix older pre-2.80 blend-files. + * + * Ensures the given `view_layer` as a valid first-level layer collection, i.e. a single one + * matching the scene's master collection. This is a requirement for `BKE_layer_collection_sync`. + */ +void BKE_layer_collection_doversion_2_80(const struct Scene *scene, struct ViewLayer *view_layer); + void BKE_main_collection_sync(const struct Main *bmain); void BKE_scene_collection_sync(const struct Scene *scene); /** diff --git a/source/blender/blenkernel/BKE_lib_id.h b/source/blender/blenkernel/BKE_lib_id.h index 1d905ad85b1..daf13590ca2 100644 --- a/source/blender/blenkernel/BKE_lib_id.h +++ b/source/blender/blenkernel/BKE_lib_id.h @@ -376,13 +376,19 @@ enum { /** Clear asset data (in case the ID can actually be made local, in copy case asset data is never * copied over). */ LIB_ID_MAKELOCAL_ASSET_DATA_CLEAR = 1 << 3, - - /* Special type-specific options. */ - /** For Objects, do not clear the proxy pointers while making the data-block local. */ - LIB_ID_MAKELOCAL_OBJECT_NO_PROXY_CLEARING = 1 << 16, }; /** + * Helper to decide whether given `id` can be directly made local, or needs to be copied. + * `r_force_local` and `r_force_copy` cannot be true together. But both can be false, in case no + * action should be performed. + * + * \note low-level helper to de-duplicate logic between `BKE_lib_id_make_local_generic` and the + * specific corner-cases implementations needed for objects and brushes. + */ +void BKE_lib_id_make_local_generic_action_define( + struct Main *bmain, struct ID *id, int flags, bool *r_force_local, bool *r_force_copy); +/** * Generic 'make local' function, works for most of data-block types. */ void BKE_lib_id_make_local_generic(struct Main *bmain, struct ID *id, int flags); diff --git a/source/blender/blenkernel/BKE_lib_override.h b/source/blender/blenkernel/BKE_lib_override.h index 30e75259967..e8065566c97 100644 --- a/source/blender/blenkernel/BKE_lib_override.h +++ b/source/blender/blenkernel/BKE_lib_override.h @@ -100,6 +100,9 @@ struct ID *BKE_lib_override_library_create_from_id(struct Main *bmain, * main. You can add more local IDs to be remapped to use new overriding ones by setting their * LIB_TAG_DOIT tag. * + * \param owner_library: the library in which the overrides should be created. Besides versioning + * and resync code path, this should always be NULL (i.e. the local .blend file). + * * \param reference_library: the library from which the linked data being overridden come from * (i.e. the library of the linked reference ID). * @@ -109,6 +112,7 @@ struct ID *BKE_lib_override_library_create_from_id(struct Main *bmain, * \return \a true on success, \a false otherwise. */ bool BKE_lib_override_library_create_from_tag(struct Main *bmain, + struct Library *owner_library, const struct Library *reference_library, bool do_no_main); /** @@ -122,16 +126,24 @@ bool BKE_lib_override_library_create_from_tag(struct Main *bmain, * * \param view_layer: the active view layer to search instantiated collections in, can be NULL (in * which case \a scene's master collection children hierarchy is used instead). + * + * \param owner_library: the library in which the overrides should be created. Besides versioning + * and resync code path, this should always be NULL (i.e. the local .blend file). + * * \param id_root: The root ID to create an override from. + * * \param id_reference: Some reference ID used to do some post-processing after overrides have been * created, may be NULL. Typically, the Empty object instantiating the linked collection we * override, currently. + * * \param r_id_root_override: if not NULL, the override generated for the given \a id_root. + * * \return true if override was successfully created. */ bool BKE_lib_override_library_create(struct Main *bmain, struct Scene *scene, struct ViewLayer *view_layer, + struct Library *owner_library, struct ID *id_root, struct ID *id_reference, struct ID **r_id_root_override); diff --git a/source/blender/blenkernel/BKE_lib_query.h b/source/blender/blenkernel/BKE_lib_query.h index d853cb16b13..dd2e2a2f8e5 100644 --- a/source/blender/blenkernel/BKE_lib_query.h +++ b/source/blender/blenkernel/BKE_lib_query.h @@ -63,7 +63,7 @@ enum { /** * That ID is not really used by its owner, it's just an internal hint/helper. - * This addresses Their Highest Ugliness the 'from' pointers: Object->from_proxy and Key->from. + * This marks the 'from' pointers issue, like Key->from. * How to handle that kind of cases totally depends on what caller code is doing... */ IDWALK_CB_LOOPBACK = (1 << 4), @@ -135,7 +135,6 @@ enum { /** Do not process ID pointers inside embedded IDs. Needed by depsgraph processing e.g. */ 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), diff --git a/source/blender/blenkernel/BKE_lib_remap.h b/source/blender/blenkernel/BKE_lib_remap.h index d8842dbce7f..f14cd75c5c6 100644 --- a/source/blender/blenkernel/BKE_lib_remap.h +++ b/source/blender/blenkernel/BKE_lib_remap.h @@ -38,6 +38,9 @@ extern "C" { #endif +struct ID; +struct IDRemapper; + /* BKE_libblock_free, delete are declared in BKE_lib_id.h for convenience. */ /* Also IDRemap->flag. */ @@ -65,15 +68,6 @@ enum { * and can cause crashes very easily! */ ID_REMAP_FORCE_NEVER_NULL_USAGE = 1 << 3, - /** - * Do not consider proxy/_group pointers of local objects as indirect usages... - * Our oh-so-beloved proxies again... - * Do not consider data used by local proxy object as indirect usage. - * This is needed e.g. in reload scenario, - * since we have to ensure remapping of Armature data of local proxy - * is also performed. Usual nightmare... - */ - 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). */ @@ -98,6 +92,19 @@ enum { }; /** + * Replace all references in given Main using the given \a mappings + * + * \note Is preferred over BKE_libblock_remap_locked due to performance. + */ +void BKE_libblock_remap_multiple_locked(struct Main *bmain, + const struct IDRemapper *mappings, + const short remap_flags); + +void BKE_libblock_remap_multiple(struct Main *bmain, + const struct IDRemapper *mappings, + const short remap_flags); + +/** * Replace all references in given Main to \a old_id by \a new_id * (if \a new_id is NULL, it unlinks \a old_id). * @@ -146,12 +153,61 @@ void BKE_libblock_relink_to_newid(struct Main *bmain, struct ID *id, int remap_f ATTR_NONNULL(); typedef void (*BKE_library_free_notifier_reference_cb)(const void *); -typedef void (*BKE_library_remap_editor_id_reference_cb)(struct ID *, struct ID *); +typedef void (*BKE_library_remap_editor_id_reference_cb)(const struct IDRemapper *mappings); void BKE_library_callback_free_notifier_reference_set(BKE_library_free_notifier_reference_cb func); void BKE_library_callback_remap_editor_id_reference_set( BKE_library_remap_editor_id_reference_cb func); +/* IDRemapper */ +struct IDRemapper; +typedef enum IDRemapperApplyResult { + /** No remapping rules available for the source. */ + ID_REMAP_RESULT_SOURCE_UNAVAILABLE, + /** Source isn't mappable (e.g. NULL). */ + ID_REMAP_RESULT_SOURCE_NOT_MAPPABLE, + /** Source has been remapped to a new pointer. */ + ID_REMAP_RESULT_SOURCE_REMAPPED, + /** Source has been set to NULL. */ + ID_REMAP_RESULT_SOURCE_UNASSIGNED, +} IDRemapperApplyResult; + +typedef enum IDRemapperApplyOptions { + ID_REMAP_APPLY_UPDATE_REFCOUNT = (1 << 0), + ID_REMAP_APPLY_ENSURE_REAL = (1 << 1), + + ID_REMAP_APPLY_DEFAULT = 0, +} IDRemapperApplyOptions; + +typedef void (*IDRemapperIterFunction)(struct ID *old_id, struct ID *new_id, void *user_data); + +/** + * Create a new ID Remapper. + * + * An ID remapper stores multiple remapping rules. + */ +struct IDRemapper *BKE_id_remapper_create(void); + +void BKE_id_remapper_clear(struct IDRemapper *id_remapper); +bool BKE_id_remapper_is_empty(const struct IDRemapper *id_remapper); +/** Free the given ID Remapper. */ +void BKE_id_remapper_free(struct IDRemapper *id_remapper); +/** Add a new remapping. */ +void BKE_id_remapper_add(struct IDRemapper *id_remapper, struct ID *old_id, struct ID *new_id); + +/** + * Apply a remapping. + * + * Update the id pointer stored in the given r_id_ptr if a remapping rule exists. + */ +IDRemapperApplyResult BKE_id_remapper_apply(const struct IDRemapper *id_remapper, + struct ID **r_id_ptr, + IDRemapperApplyOptions options); +bool BKE_id_remapper_has_mapping_for(const struct IDRemapper *id_remapper, uint64_t type_filter); +void BKE_id_remapper_iter(const struct IDRemapper *id_remapper, + IDRemapperIterFunction func, + void *user_data); + #ifdef __cplusplus } #endif diff --git a/source/blender/blenkernel/BKE_main.h b/source/blender/blenkernel/BKE_main.h index 4c6eb31db4b..e4f94110eb1 100644 --- a/source/blender/blenkernel/BKE_main.h +++ b/source/blender/blenkernel/BKE_main.h @@ -182,7 +182,11 @@ typedef struct Main { ListBase linestyles; ListBase cachefiles; ListBase workspaces; - ListBase hairs; + /** + * \note The name `hair_curves` is chosen to be different than `curves`, + * but they are generic curve data-blocks, not just for hair. + */ + ListBase hair_curves; ListBase pointclouds; ListBase volumes; ListBase simulations; diff --git a/source/blender/blenkernel/BKE_mesh.h b/source/blender/blenkernel/BKE_mesh.h index 6554a9c72aa..e1c706a82dc 100644 --- a/source/blender/blenkernel/BKE_mesh.h +++ b/source/blender/blenkernel/BKE_mesh.h @@ -119,6 +119,9 @@ void BKE_mesh_looptri_get_real_edges(const struct Mesh *mesh, void BKE_mesh_free_data_for_undo(struct Mesh *me); void BKE_mesh_clear_geometry(struct Mesh *me); struct Mesh *BKE_mesh_add(struct Main *bmain, const char *name); + +void BKE_mesh_free_editmesh(struct Mesh *mesh); + /** * A version of #BKE_mesh_copy_parameters that is intended for evaluated output * (the modifier stack for example). diff --git a/source/blender/blenkernel/BKE_modifier.h b/source/blender/blenkernel/BKE_modifier.h index 80889813b34..a05ed67063a 100644 --- a/source/blender/blenkernel/BKE_modifier.h +++ b/source/blender/blenkernel/BKE_modifier.h @@ -245,10 +245,6 @@ typedef struct ModifierTypeInfo { const struct ModifierEvalContext *ctx, struct Mesh *mesh); - struct Hair *(*modifyHair)(struct ModifierData *md, - const struct ModifierEvalContext *ctx, - struct Hair *hair); - /** * The modifier has to change the geometry set in-place. The geometry set can contain zero or * more geometry components. This callback can be used by modifiers that don't work on any @@ -470,6 +466,8 @@ void BKE_modifiers_foreach_tex_link(struct Object *ob, TexWalkFunc walk, void *u struct ModifierData *BKE_modifiers_findby_type(const struct Object *ob, ModifierType type); struct ModifierData *BKE_modifiers_findby_name(const struct Object *ob, const char *name); +struct ModifierData *BKE_modifiers_findby_session_uuid(const struct Object *ob, + const SessionUUID *session_uuid); void BKE_modifiers_clear_errors(struct Object *ob); /** * used for buttons, to find out if the 'draw deformed in edit-mode option is there. @@ -568,7 +566,8 @@ const char *BKE_modifier_path_relbase_from_global(struct Object *ob); * For a given modifier data, get corresponding original one. * If the modifier data is already original, return it as-is. */ -struct ModifierData *BKE_modifier_get_original(struct ModifierData *md); +struct ModifierData *BKE_modifier_get_original(const struct Object *object, + struct ModifierData *md); struct ModifierData *BKE_modifier_get_evaluated(struct Depsgraph *depsgraph, struct Object *object, struct ModifierData *md); diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h index 2f9034f6438..7ffa180b523 100644 --- a/source/blender/blenkernel/BKE_node.h +++ b/source/blender/blenkernel/BKE_node.h @@ -274,6 +274,9 @@ typedef struct bNodeType { char *label, int maxlen); + /** Optional override for node class, used for drawing node header. */ + int (*ui_class)(const struct bNode *node); + /** Called when the node is updated in the editor. */ void (*updatefunc)(struct bNodeTree *ntree, struct bNode *node); /** Check and update if internal ID data has changed. */ @@ -1178,30 +1181,7 @@ void BKE_nodetree_remove_layer_n(struct bNodeTree *ntree, struct Scene *scene, i #define SH_NODE_OUTPUT_AOV 707 #define SH_NODE_VECTOR_ROTATE 708 #define SH_NODE_CURVE_FLOAT 709 - -/* API */ - -struct bNodeTreeExec *ntreeShaderBeginExecTree(struct bNodeTree *ntree); -void ntreeShaderEndExecTree(struct bNodeTreeExec *exec); -/** - Find an output node of the shader tree. - * - * \note it will only return output which is NOT in the group, which isn't how - * render engines works but it's how the GPU shader compilation works. This we - * can change in the future and make it a generic function, but for now it stays - * private here. - */ -struct bNode *ntreeShaderOutputNode(struct bNodeTree *ntree, int target); -/** - * This one needs to work on a local tree. - * - * TODO: This is *not* part of `blenkernel`, it's defined under "source/blender/nodes/". - * This declaration should be moved out of BKE. - */ -void ntreeGPUMaterialNodes(struct bNodeTree *localtree, - struct GPUMaterial *mat, - bool *has_surface_output, - bool *has_volume_output); +#define SH_NODE_POINT_INFO 710 /** \} */ @@ -1314,6 +1294,8 @@ void ntreeGPUMaterialNodes(struct bNodeTree *localtree, #define CMP_NODE_POSTERIZE 327 #define CMP_NODE_CONVERT_COLOR_SPACE 328 #define CMP_NODE_SCENE_TIME 329 +#define CMP_NODE_SEPARATE_XYZ 330 +#define CMP_NODE_COMBINE_XYZ 331 /* channel toggles */ #define CMP_CHAN_RGB 1 @@ -1352,75 +1334,6 @@ void ntreeGPUMaterialNodes(struct bNodeTree *localtree, #define CMP_DEFAULT_SMAA_CONTRAST_LIMIT 0.2f #define CMP_DEFAULT_SMAA_CORNER_ROUNDING 0.25f -/* API */ -void ntreeCompositExecTree(struct Scene *scene, - struct bNodeTree *ntree, - struct RenderData *rd, - int rendering, - int do_previews, - const struct ColorManagedViewSettings *view_settings, - const struct ColorManagedDisplaySettings *display_settings, - const char *view_name); - -/** - * Called from render pipeline, to tag render input and output. - * need to do all scenes, to prevent errors when you re-render 1 scene. - */ -void ntreeCompositTagRender(struct Scene *scene); -/** - * Update the outputs of the render layer nodes. - * Since the outputs depend on the render engine, this part is a bit complex: - * - #ntreeCompositUpdateRLayers is called and loops over all render layer nodes. - * - Each render layer node calls the update function of the - * render engine that's used for its scene. - * - The render engine calls RE_engine_register_pass for each pass. - * - #RE_engine_register_pass calls #node_cmp_rlayers_register_pass. - * - * TODO: This is *not* part of `blenkernel`, it's defined under "source/blender/nodes/". - * This declaration should be moved out of BKE. - */ -void ntreeCompositUpdateRLayers(struct bNodeTree *ntree); -void ntreeCompositClearTags(struct bNodeTree *ntree); - -struct bNodeSocket *ntreeCompositOutputFileAddSocket(struct bNodeTree *ntree, - struct bNode *node, - const char *name, - struct ImageFormatData *im_format); -int ntreeCompositOutputFileRemoveActiveSocket(struct bNodeTree *ntree, struct bNode *node); -void ntreeCompositOutputFileSetPath(struct bNode *node, - struct bNodeSocket *sock, - const char *name); -void ntreeCompositOutputFileSetLayer(struct bNode *node, - struct bNodeSocket *sock, - const char *name); -/* needed in do_versions */ -void ntreeCompositOutputFileUniquePath(struct ListBase *list, - struct bNodeSocket *sock, - const char defname[], - char delim); -void ntreeCompositOutputFileUniqueLayer(struct ListBase *list, - struct bNodeSocket *sock, - const char defname[], - char delim); - -void ntreeCompositColorBalanceSyncFromLGG(bNodeTree *ntree, bNode *node); -void ntreeCompositColorBalanceSyncFromCDL(bNodeTree *ntree, bNode *node); - -void ntreeCompositCryptomatteSyncFromAdd(const Scene *scene, bNode *node); -void ntreeCompositCryptomatteSyncFromRemove(bNode *node); -bNodeSocket *ntreeCompositCryptomatteAddSocket(bNodeTree *ntree, bNode *node); -int ntreeCompositCryptomatteRemoveSocket(bNodeTree *ntree, bNode *node); -void ntreeCompositCryptomatteLayerPrefix(const Scene *scene, - const bNode *node, - char *r_prefix, - size_t prefix_len); -/** - * Update the runtime layer names with the crypto-matte layer names of the references render layer - * or image. - */ -void ntreeCompositCryptomatteUpdateLayerNames(const Scene *scene, bNode *node); -struct CryptomatteSession *ntreeCompositCryptomatteSession(const Scene *scene, bNode *node); - /** \} */ /* -------------------------------------------------------------------- */ @@ -1457,24 +1370,6 @@ struct TexResult; #define TEX_NODE_PROC 500 #define TEX_NODE_PROC_MAX 600 -/* API */ -void ntreeTexCheckCyclics(struct bNodeTree *ntree); - -struct bNodeTreeExec *ntreeTexBeginExecTree(struct bNodeTree *ntree); -void ntreeTexEndExecTree(struct bNodeTreeExec *exec); -int ntreeTexExecTree(struct bNodeTree *ntree, - struct TexResult *target, - const float co[3], - float dxt[3], - float dyt[3], - int osatex, - short thread, - const struct Tex *tex, - short which_output, - int cfra, - int preview, - struct MTex *mtex); - /** \} */ /* -------------------------------------------------------------------- */ @@ -1633,6 +1528,7 @@ int ntreeTexExecTree(struct bNodeTree *ntree, #define GEO_NODE_FLIP_FACES 1150 #define GEO_NODE_SCALE_ELEMENTS 1151 #define GEO_NODE_EXTRUDE_MESH 1152 +#define GEO_NODE_MERGE_BY_DISTANCE 1153 /** \} */ diff --git a/source/blender/blenkernel/BKE_object.h b/source/blender/blenkernel/BKE_object.h index da8dba0c86b..96ed7942067 100644 --- a/source/blender/blenkernel/BKE_object.h +++ b/source/blender/blenkernel/BKE_object.h @@ -144,18 +144,6 @@ void BKE_object_link_modifiers(struct Object *ob_dst, const struct Object *ob_sr void BKE_object_free_modifiers(struct Object *ob, int flag); void BKE_object_free_shaderfx(struct Object *ob, int flag); -/** - * Proxy rule: - * - `lib_object->proxy_from` == the one we borrow from, set temporally while object_update. - * - `local_object->proxy` == pointer to library object, saved in files and read. - * - `local_object->proxy_group` == pointer to collection dupli-object, saved in files and read. - */ -void BKE_object_make_proxy(struct Main *bmain, - struct Object *ob, - struct Object *target, - struct Object *cob); -void BKE_object_copy_proxy_drivers(struct Object *ob, struct Object *target); - bool BKE_object_exists_check(struct Main *bmain, const struct Object *obtest); /** * Actual check for internal data, not context or flags. @@ -444,7 +432,6 @@ void BKE_object_eval_constraints(struct Depsgraph *depsgraph, struct Object *ob); void BKE_object_eval_transform_final(struct Depsgraph *depsgraph, struct Object *ob); -bool BKE_object_eval_proxy_copy(struct Depsgraph *depsgraph, struct Object *object); void BKE_object_eval_uber_transform(struct Depsgraph *depsgraph, struct Object *ob); void BKE_object_eval_uber_data(struct Depsgraph *depsgraph, struct Scene *scene, @@ -486,12 +473,6 @@ void BKE_object_handle_data_update(struct Depsgraph *depsgraph, */ void BKE_object_handle_update(struct Depsgraph *depsgraph, struct Scene *scene, struct Object *ob); /** - * Proxy rule: - * - lib_object->proxy_from == the one we borrow from, only set temporal and cleared here. - * - local_object->proxy == pointer to library object, saved in files and read. - * - * Function below is polluted with proxy exceptions, cleanup will follow! - * * The main object update call, for object matrix, constraints, keys and #DispList (modifiers) * requires flags to be set! * @@ -501,8 +482,7 @@ void BKE_object_handle_update(struct Depsgraph *depsgraph, struct Scene *scene, void BKE_object_handle_update_ex(struct Depsgraph *depsgraph, struct Scene *scene, struct Object *ob, - struct RigidBodyWorld *rbw, - bool do_proxy_update); + struct RigidBodyWorld *rbw); void BKE_object_sculpt_data_create(struct Object *ob); @@ -530,6 +510,9 @@ struct Mesh *BKE_object_get_pre_modified_mesh(const struct Object *object); */ struct Mesh *BKE_object_get_original_mesh(const struct Object *object); +struct Mesh *BKE_object_get_editmesh_eval_final(const struct Object *object); +struct Mesh *BKE_object_get_editmesh_eval_cage(const struct Object *object); + /* Lattice accessors. * These functions return either the regular lattice, or the edit-mode lattice, * whichever is currently in use. */ diff --git a/source/blender/blenkernel/BKE_paint.h b/source/blender/blenkernel/BKE_paint.h index 4019c4d62c4..8291be9d7e8 100644 --- a/source/blender/blenkernel/BKE_paint.h +++ b/source/blender/blenkernel/BKE_paint.h @@ -499,7 +499,6 @@ typedef struct SculptSession { /* These are always assigned to base mesh data when using PBVH_FACES and PBVH_GRIDS. */ struct MVert *mvert; - const float (*vert_normals)[3]; struct MPoly *mpoly; struct MLoop *mloop; diff --git a/source/blender/blenkernel/BKE_screen.h b/source/blender/blenkernel/BKE_screen.h index 63f6fca2a9d..c85ae04a492 100644 --- a/source/blender/blenkernel/BKE_screen.h +++ b/source/blender/blenkernel/BKE_screen.h @@ -38,6 +38,7 @@ struct BlendLibReader; struct BlendWriter; struct Header; struct ID; +struct IDRemapper; struct LibraryForeachIDData; struct ListBase; struct Menu; @@ -117,10 +118,7 @@ typedef struct SpaceType { bContextDataCallback context; /* Used when we want to replace an ID by another (or NULL). */ - void (*id_remap)(struct ScrArea *area, - struct SpaceLink *sl, - struct ID *old_id, - struct ID *new_id); + void (*id_remap)(struct ScrArea *area, struct SpaceLink *sl, const struct IDRemapper *mappings); int (*space_subtype_get)(struct ScrArea *area); void (*space_subtype_set)(struct ScrArea *area, int value); diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt index d89588b649a..11dab0ecaad 100644 --- a/source/blender/blenkernel/CMakeLists.txt +++ b/source/blender/blenkernel/CMakeLists.txt @@ -119,6 +119,7 @@ set(SRC intern/crazyspace.c intern/cryptomatte.cc intern/curve.cc + intern/curves.cc intern/curve_bevel.c intern/curve_convert.c intern/curve_decimate.c @@ -156,7 +157,6 @@ set(SRC intern/gpencil_curve.c intern/gpencil_geom.cc intern/gpencil_modifier.c - intern/hair.cc intern/icons.cc intern/icons_rasterize.c intern/idprop.c @@ -165,6 +165,7 @@ set(SRC intern/idprop_utils.c intern/idtype.c intern/image.c + intern/image_partial_update.cc intern/image_gen.c intern/image_gpu.cc intern/image_save.c @@ -179,7 +180,9 @@ set(SRC intern/lib_id.c intern/lib_id_delete.c intern/lib_id_eval.c + intern/lib_id_remapper.cc intern/lib_override.c + intern/lib_override_proxy_conversion.c intern/lib_query.c intern/lib_remap.c intern/library.c @@ -353,6 +356,7 @@ set(SRC BKE_cryptomatte.h BKE_cryptomatte.hh BKE_curve.h + BKE_curves.h BKE_curve_to_mesh.hh BKE_curveprofile.h BKE_customdata.h @@ -381,7 +385,6 @@ set(SRC BKE_gpencil_curve.h BKE_gpencil_geom.h BKE_gpencil_modifier.h - BKE_hair.h BKE_icons.h BKE_idprop.h BKE_idprop.hh @@ -522,7 +525,7 @@ set(LIB bf_simulation # For `vfontdata_freetype.c`. - ${FREETYPE_LIBRARIES} + ${FREETYPE_LIBRARIES} ${BROTLI_LIBRARIES} ) if(WITH_BINRELOC) @@ -721,10 +724,6 @@ if(WITH_FFTW3) add_definitions(-DFFTW3=1) endif() -if(WITH_INTERNATIONAL) - add_definitions(-DWITH_INTERNATIONAL) -endif() - if(WITH_FREESTYLE) add_definitions(-DWITH_FREESTYLE) endif() @@ -825,8 +824,10 @@ if(WITH_GTESTS) intern/cryptomatte_test.cc intern/fcurve_test.cc intern/idprop_serialize_test.cc + intern/image_partial_update_test.cc intern/lattice_deform_test.cc intern/layer_test.cc + intern/lib_id_remapper_test.cc intern/lib_id_test.cc intern/lib_remap_test.cc intern/tracking_test.cc diff --git a/source/blender/blenkernel/intern/DerivedMesh.cc b/source/blender/blenkernel/intern/DerivedMesh.cc index 13131c24eda..2e779d6fad7 100644 --- a/source/blender/blenkernel/intern/DerivedMesh.cc +++ b/source/blender/blenkernel/intern/DerivedMesh.cc @@ -127,30 +127,6 @@ static MEdge *dm_getEdgeArray(DerivedMesh *dm) return medge; } -static MFace *dm_getTessFaceArray(DerivedMesh *dm) -{ - MFace *mface = (MFace *)CustomData_get_layer(&dm->faceData, CD_MFACE); - - if (!mface) { - int numTessFaces = dm->getNumTessFaces(dm); - - if (!numTessFaces) { - /* Do not add layer if there's no elements in it, this leads to issues later when - * this layer is needed with non-zero size, but currently CD stuff does not check - * for requested layer size on creation and just returns layer which was previously - * added (sergey) */ - return nullptr; - } - - mface = (MFace *)CustomData_add_layer( - &dm->faceData, CD_MFACE, CD_CALLOC, nullptr, numTessFaces); - CustomData_set_layer_flag(&dm->faceData, CD_MFACE, CD_FLAG_TEMPORARY); - dm->copyTessFaceArray(dm, mface); - } - - return mface; -} - static MLoop *dm_getLoopArray(DerivedMesh *dm) { MLoop *mloop = (MLoop *)CustomData_get_layer(&dm->loopData, CD_MLOOP); @@ -203,18 +179,6 @@ static MEdge *dm_dupEdgeArray(DerivedMesh *dm) return tmp; } -static MFace *dm_dupFaceArray(DerivedMesh *dm) -{ - MFace *tmp = (MFace *)MEM_malloc_arrayN( - dm->getNumTessFaces(dm), sizeof(*tmp), "dm_dupFaceArray tmp"); - - if (tmp) { - dm->copyTessFaceArray(dm, tmp); - } - - return tmp; -} - static MLoop *dm_dupLoopArray(DerivedMesh *dm) { MLoop *tmp = (MLoop *)MEM_malloc_arrayN( @@ -270,42 +234,15 @@ static const MLoopTri *dm_getLoopTriArray(DerivedMesh *dm) return looptri; } -static CustomData *dm_getVertCData(DerivedMesh *dm) -{ - return &dm->vertData; -} - -static CustomData *dm_getEdgeCData(DerivedMesh *dm) -{ - return &dm->edgeData; -} - -static CustomData *dm_getTessFaceCData(DerivedMesh *dm) -{ - return &dm->faceData; -} - -static CustomData *dm_getLoopCData(DerivedMesh *dm) -{ - return &dm->loopData; -} - -static CustomData *dm_getPolyCData(DerivedMesh *dm) -{ - return &dm->polyData; -} - void DM_init_funcs(DerivedMesh *dm) { /* default function implementations */ dm->getVertArray = dm_getVertArray; dm->getEdgeArray = dm_getEdgeArray; - dm->getTessFaceArray = dm_getTessFaceArray; dm->getLoopArray = dm_getLoopArray; dm->getPolyArray = dm_getPolyArray; dm->dupVertArray = dm_dupVertArray; dm->dupEdgeArray = dm_dupEdgeArray; - dm->dupTessFaceArray = dm_dupFaceArray; dm->dupLoopArray = dm_dupLoopArray; dm->dupPolyArray = dm_dupPolyArray; @@ -314,19 +251,8 @@ void DM_init_funcs(DerivedMesh *dm) /* subtypes handle getting actual data */ dm->getNumLoopTri = dm_getNumLoopTri; - dm->getVertDataLayout = dm_getVertCData; - dm->getEdgeDataLayout = dm_getEdgeCData; - dm->getTessFaceDataLayout = dm_getTessFaceCData; - dm->getLoopDataLayout = dm_getLoopCData; - dm->getPolyDataLayout = dm_getPolyCData; - - dm->getVertData = DM_get_vert_data; - dm->getEdgeData = DM_get_edge_data; - dm->getTessFaceData = DM_get_tessface_data; - dm->getPolyData = DM_get_poly_data; dm->getVertDataArray = DM_get_vert_data_layer; dm->getEdgeDataArray = DM_get_edge_data_layer; - dm->getTessFaceDataArray = DM_get_tessface_data_layer; dm->getPolyDataArray = DM_get_poly_data_layer; dm->getLoopDataArray = DM_get_loop_data_layer; } @@ -349,7 +275,6 @@ void DM_init(DerivedMesh *dm, DM_init_funcs(dm); dm->needsFree = 1; - dm->dirty = (DMDirtyFlag)0; /* Don't use #CustomData_reset because we don't want to touch custom-data. */ copy_vn_i(dm->vertData.typemap, CD_NUMTYPES, -1); @@ -359,16 +284,16 @@ void DM_init(DerivedMesh *dm, copy_vn_i(dm->polyData.typemap, CD_NUMTYPES, -1); } -void DM_from_template_ex(DerivedMesh *dm, - DerivedMesh *source, - DerivedMeshType type, - int numVerts, - int numEdges, - int numTessFaces, - int numLoops, - int numPolys, - const CustomData_MeshMasks *mask) +void DM_from_template(DerivedMesh *dm, + DerivedMesh *source, + DerivedMeshType type, + int numVerts, + int numEdges, + int numTessFaces, + int numLoops, + int numPolys) { + const CustomData_MeshMasks *mask = &CD_MASK_DERIVEDMESH; CustomData_copy(&source->vertData, &dm->vertData, mask->vmask, CD_CALLOC, numVerts); CustomData_copy(&source->edgeData, &dm->edgeData, mask->emask, CD_CALLOC, numEdges); CustomData_copy(&source->faceData, &dm->faceData, mask->fmask, CD_CALLOC, numTessFaces); @@ -387,26 +312,6 @@ void DM_from_template_ex(DerivedMesh *dm, DM_init_funcs(dm); dm->needsFree = 1; - dm->dirty = (DMDirtyFlag)0; -} -void DM_from_template(DerivedMesh *dm, - DerivedMesh *source, - DerivedMeshType type, - int numVerts, - int numEdges, - int numTessFaces, - int numLoops, - int numPolys) -{ - DM_from_template_ex(dm, - source, - type, - numVerts, - numEdges, - numTessFaces, - numLoops, - numPolys, - &CD_MASK_DERIVEDMESH); } bool DM_release(DerivedMesh *dm) @@ -464,14 +369,6 @@ void DM_DupPolys(DerivedMesh *source, DerivedMesh *target) } } -void DM_ensure_normals(DerivedMesh *dm) -{ - if (dm->dirty & DM_DIRTY_NORMALS) { - dm->calcNormals(dm); - } - BLI_assert((dm->dirty & DM_DIRTY_NORMALS) == 0); -} - void DM_ensure_looptri_data(DerivedMesh *dm) { const unsigned int totpoly = dm->numPolyData; @@ -524,7 +421,7 @@ void DM_set_only_copy(DerivedMesh *dm, const CustomData_MeshMasks *mask) * see replies to r50969, Campbell */ #if 0 CustomData_set_only_copy(&dm->loopData, mask->lmask); - CustomData_set_only_copy(&dm->polyData, mask->pmask); + Custom(&dm->polyData, mask->pmask); #endif } @@ -552,45 +449,11 @@ void DM_add_edge_layer(DerivedMesh *dm, int type, eCDAllocType alloctype, void * CustomData_add_layer(&dm->edgeData, type, alloctype, layer, dm->numEdgeData); } -void DM_add_tessface_layer(DerivedMesh *dm, int type, eCDAllocType alloctype, void *layer) -{ - CustomData_add_layer(&dm->faceData, type, alloctype, layer, dm->numTessFaceData); -} - -void DM_add_loop_layer(DerivedMesh *dm, int type, eCDAllocType alloctype, void *layer) -{ - CustomData_add_layer(&dm->loopData, type, alloctype, layer, dm->numLoopData); -} - void DM_add_poly_layer(DerivedMesh *dm, int type, eCDAllocType alloctype, void *layer) { CustomData_add_layer(&dm->polyData, type, alloctype, layer, dm->numPolyData); } -void *DM_get_vert_data(DerivedMesh *dm, int index, int type) -{ - BLI_assert(index >= 0 && index < dm->getNumVerts(dm)); - return CustomData_get(&dm->vertData, index, type); -} - -void *DM_get_edge_data(DerivedMesh *dm, int index, int type) -{ - BLI_assert(index >= 0 && index < dm->getNumEdges(dm)); - return CustomData_get(&dm->edgeData, index, type); -} - -void *DM_get_tessface_data(DerivedMesh *dm, int index, int type) -{ - BLI_assert(index >= 0 && index < dm->getNumTessFaces(dm)); - return CustomData_get(&dm->faceData, index, type); -} - -void *DM_get_poly_data(DerivedMesh *dm, int index, int type) -{ - BLI_assert(index >= 0 && index < dm->getNumPolys(dm)); - return CustomData_get(&dm->polyData, index, type); -} - void *DM_get_vert_data_layer(DerivedMesh *dm, int type) { if (type == CD_MVERT) { @@ -609,15 +472,6 @@ void *DM_get_edge_data_layer(DerivedMesh *dm, int type) return CustomData_get_layer(&dm->edgeData, type); } -void *DM_get_tessface_data_layer(DerivedMesh *dm, int type) -{ - if (type == CD_MFACE) { - return dm->getTessFaceArray(dm); - } - - return CustomData_get_layer(&dm->faceData, type); -} - void *DM_get_poly_data_layer(DerivedMesh *dm, int type) { return CustomData_get_layer(&dm->polyData, type); @@ -1731,13 +1585,6 @@ static void editbmesh_calc_modifiers(struct Depsgraph *depsgraph, BKE_id_free(nullptr, mesh_orco); } - /* Ensure normals calculation below is correct (normal settings have transferred properly). - * However, nodes modifiers might create meshes from scratch or transfer meshes from other - * objects with different settings, and in general it doesn't make sense to guarantee that - * the settings are the same as the original mesh. If necessary, this could become a modifier - * type flag. */ - BLI_assert(mesh_input->smoothresh == mesh_cage->smoothresh); - /* Compute normals. */ editbmesh_calc_modifier_final_normals(mesh_final, &final_datamask); if (mesh_cage && (mesh_cage != mesh_final)) { @@ -1769,17 +1616,6 @@ static void mesh_build_data(struct Depsgraph *depsgraph, const CustomData_MeshMasks *dataMask, const bool need_mapping) { - BLI_assert(ob->type == OB_MESH); - - /* Evaluated meshes aren't supposed to be created on original instances. If you do, - * they aren't cleaned up properly on mode switch, causing crashes, e.g T58150. */ - BLI_assert(ob->id.tag & LIB_TAG_COPIED_ON_WRITE); - - BKE_object_free_derived_caches(ob); - if (DEG_is_active(depsgraph)) { - BKE_sculpt_update_object_before_eval(ob); - } - #if 0 /* XXX This is already taken care of in mesh_calc_modifiers()... */ if (need_mapping) { /* Also add the flag so that it is recorded in lastDataMask. */ @@ -1846,15 +1682,7 @@ static void editbmesh_build_data(struct Depsgraph *depsgraph, BMEditMesh *em, CustomData_MeshMasks *dataMask) { - BLI_assert(obedit->id.tag & LIB_TAG_COPIED_ON_WRITE); - - BKE_object_free_derived_caches(obedit); - if (DEG_is_active(depsgraph)) { - BKE_sculpt_update_object_before_eval(obedit); - } - - BKE_editmesh_free_derived_caches(em); - + Mesh *mesh = static_cast<Mesh *>(obedit->data); Mesh *me_cage; Mesh *me_final; GeometrySet *non_mesh_components; @@ -1862,13 +1690,33 @@ static void editbmesh_build_data(struct Depsgraph *depsgraph, editbmesh_calc_modifiers( depsgraph, scene, obedit, em, dataMask, &me_cage, &me_final, &non_mesh_components); - em->mesh_eval_final = me_final; - em->mesh_eval_cage = me_cage; + /* The modifier stack result is expected to share edit mesh pointer with the input. + * This is similar `mesh_calc_finalize()`. */ + BKE_mesh_free_editmesh(me_final); + BKE_mesh_free_editmesh(me_cage); + me_final->edit_mesh = me_cage->edit_mesh = em; + + /* Object has edit_mesh but is not in edit mode (object shares mesh datablock with another object + * with is in edit mode). + * Convert edit mesh to mesh until the draw manager can draw mesh wrapper which is not in the + * edit mode. */ + if (!(obedit->mode & OB_MODE_EDIT)) { + BKE_mesh_wrapper_ensure_mdata(me_final); + if (me_final != me_cage) { + BKE_mesh_wrapper_ensure_mdata(me_cage); + } + } + + const bool is_mesh_eval_owned = (me_final != mesh->runtime.mesh_eval); + BKE_object_eval_assign_data(obedit, &me_final->id, is_mesh_eval_owned); + + obedit->runtime.editmesh_eval_cage = me_cage; + obedit->runtime.geometry_set_eval = non_mesh_components; - BKE_object_boundbox_calc_from_mesh(obedit, em->mesh_eval_final); + BKE_object_boundbox_calc_from_mesh(obedit, me_final); - em->lastDataMask = *dataMask; + obedit->runtime.last_data_mask = *dataMask; } static void object_get_datamask(const Depsgraph *depsgraph, @@ -1924,9 +1772,25 @@ static void object_get_datamask(const Depsgraph *depsgraph, void makeDerivedMesh(struct Depsgraph *depsgraph, Scene *scene, Object *ob, - BMEditMesh *em, const CustomData_MeshMasks *dataMask) { + BLI_assert(ob->type == OB_MESH); + + /* Evaluated meshes aren't supposed to be created on original instances. If you do, + * they aren't cleaned up properly on mode switch, causing crashes, e.g T58150. */ + BLI_assert(ob->id.tag & LIB_TAG_COPIED_ON_WRITE); + + BKE_object_free_derived_caches(ob); + if (DEG_is_active(depsgraph)) { + BKE_sculpt_update_object_before_eval(ob); + } + + /* NOTE: Access the `edit_mesh` after freeing the derived caches, so that `ob->data` is restored + * to the pre-evaluated state. This is because the evaluated state is not necessarily sharing the + * `edit_mesh` pointer with the input. For example, if the object is first evaluated in the + * object mode, and then user in another scene moves object to edit mode. */ + BMEditMesh *em = ((Mesh *)ob->data)->edit_mesh; + bool need_mapping; CustomData_MeshMasks cddata_masks = *dataMask; object_get_datamask(depsgraph, ob, &cddata_masks, &need_mapping); @@ -1965,8 +1829,9 @@ Mesh *mesh_get_eval_final(struct Depsgraph *depsgraph, !CustomData_MeshMasks_are_matching(&(ob->runtime.last_data_mask), &cddata_masks) || (need_mapping && !ob->runtime.last_need_mapping)) { CustomData_MeshMasks_update(&cddata_masks, &ob->runtime.last_data_mask); - mesh_build_data( - depsgraph, scene, ob, &cddata_masks, need_mapping || ob->runtime.last_need_mapping); + + makeDerivedMesh(depsgraph, scene, ob, dataMask); + mesh_eval = BKE_object_get_evaluated_mesh(ob); } @@ -1981,6 +1846,15 @@ Mesh *mesh_get_eval_deform(struct Depsgraph *depsgraph, Object *ob, const CustomData_MeshMasks *dataMask) { + BMEditMesh *em = ((Mesh *)ob->data)->edit_mesh; + if (em != nullptr) { + /* There is no such a concept as deformed mesh in edit mode. + * Explicitly disallow this request so that the evaluated result is not modified with evaluated + * result from the wrong mode. */ + BLI_assert_msg(0, "Request of derformed mesh of object which is in edit mode"); + return nullptr; + } + /* This function isn't thread-safe and can't be used during evaluation. */ BLI_assert(DEG_is_evaluating(depsgraph) == false); @@ -2055,12 +1929,12 @@ Mesh *editbmesh_get_eval_cage(struct Depsgraph *depsgraph, */ object_get_datamask(depsgraph, obedit, &cddata_masks, nullptr); - if (!em->mesh_eval_cage || - !CustomData_MeshMasks_are_matching(&(em->lastDataMask), &cddata_masks)) { + if (!obedit->runtime.editmesh_eval_cage || + !CustomData_MeshMasks_are_matching(&(obedit->runtime.last_data_mask), &cddata_masks)) { editbmesh_build_data(depsgraph, scene, obedit, em, &cddata_masks); } - return em->mesh_eval_cage; + return obedit->runtime.editmesh_eval_cage; } Mesh *editbmesh_get_eval_cage_from_orig(struct Depsgraph *depsgraph, @@ -2117,32 +1991,6 @@ void mesh_get_mapped_verts_coords(Mesh *me_eval, float (*r_cos)[3], const int to } } -void DM_calc_loop_tangents(DerivedMesh *dm, - bool calc_active_tangent, - const char (*tangent_names)[MAX_NAME], - int tangent_names_len) -{ - BKE_mesh_calc_loop_tangent_ex( - dm->getVertArray(dm), - dm->getPolyArray(dm), - dm->getNumPolys(dm), - dm->getLoopArray(dm), - dm->getLoopTriArray(dm), - dm->getNumLoopTri(dm), - &dm->loopData, - calc_active_tangent, - tangent_names, - tangent_names_len, - (const float(*)[3])CustomData_get_layer(&dm->vertData, CD_NORMAL), - (const float(*)[3])CustomData_get_layer(&dm->polyData, CD_NORMAL), - (const float(*)[3])dm->getLoopDataArray(dm, CD_NORMAL), - (const float(*)[3])dm->getVertDataArray(dm, CD_ORCO), /* may be nullptr */ - /* result */ - &dm->loopData, - dm->getNumLoops(dm), - &dm->tangent_mask); -} - static void mesh_init_origspace(Mesh *mesh) { const float default_osf[4][2] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}}; diff --git a/source/blender/blenkernel/intern/action.c b/source/blender/blenkernel/intern/action.c index fde42304185..8426f6f4676 100644 --- a/source/blender/blenkernel/intern/action.c +++ b/source/blender/blenkernel/intern/action.c @@ -1956,30 +1956,15 @@ void BKE_pose_blend_read_lib(BlendLibReader *reader, Object *ob, bPose *pose) return; } - /* always rebuild to match proxy or lib changes, but on Undo */ + /* Always rebuild to match library changes, except on Undo. */ bool rebuild = false; if (!BLO_read_lib_is_undo(reader)) { - if (ob->proxy || ob->id.lib != arm->id.lib) { + if (ob->id.lib != arm->id.lib) { rebuild = true; } } - if (ob->proxy) { - /* sync proxy layer */ - if (pose->proxy_layer) { - arm->layer = pose->proxy_layer; - } - - /* sync proxy active bone */ - if (pose->proxy_act_bone[0]) { - Bone *bone = BKE_armature_find_bone_name(arm, pose->proxy_act_bone); - if (bone) { - arm->act_bone = bone; - } - } - } - LISTBASE_FOREACH (bPoseChannel *, pchan, &pose->chanbase) { BKE_constraint_blend_read_lib(reader, (ID *)ob, &pchan->constraints); diff --git a/source/blender/blenkernel/intern/anim_data.c b/source/blender/blenkernel/intern/anim_data.c index 42b72a7cd66..1c0b465d202 100644 --- a/source/blender/blenkernel/intern/anim_data.c +++ b/source/blender/blenkernel/intern/anim_data.c @@ -1284,8 +1284,8 @@ void BKE_animdata_main_cb(Main *bmain, ID_AnimData_Edit_Callback func, void *use /* cache files */ ANIMDATA_IDS_CB(bmain->cachefiles.first); - /* hairs */ - ANIMDATA_IDS_CB(bmain->hairs.first); + /* Hair Curves. */ + ANIMDATA_IDS_CB(bmain->hair_curves.first); /* pointclouds */ ANIMDATA_IDS_CB(bmain->pointclouds.first); @@ -1413,8 +1413,8 @@ void BKE_animdata_fix_paths_rename_all_ex(Main *bmain, /* cache files */ RENAMEFIX_ANIM_IDS(bmain->cachefiles.first); - /* hairs */ - RENAMEFIX_ANIM_IDS(bmain->hairs.first); + /* Hair Curves. */ + RENAMEFIX_ANIM_IDS(bmain->hair_curves.first); /* pointclouds */ RENAMEFIX_ANIM_IDS(bmain->pointclouds.first); diff --git a/source/blender/blenkernel/intern/anim_sys.c b/source/blender/blenkernel/intern/anim_sys.c index b5ea68aaadc..c45856adbda 100644 --- a/source/blender/blenkernel/intern/anim_sys.c +++ b/source/blender/blenkernel/intern/anim_sys.c @@ -3382,8 +3382,8 @@ void BKE_animsys_evaluate_all_animation(Main *main, Depsgraph *depsgraph, float /* cache files */ EVAL_ANIM_IDS(main->cachefiles.first, ADT_RECALC_ANIM); - /* hairs */ - EVAL_ANIM_IDS(main->hairs.first, ADT_RECALC_ANIM); + /* Hair Curves. */ + EVAL_ANIM_IDS(main->hair_curves.first, ADT_RECALC_ANIM); /* pointclouds */ EVAL_ANIM_IDS(main->pointclouds.first, ADT_RECALC_ANIM); diff --git a/source/blender/blenkernel/intern/armature.c b/source/blender/blenkernel/intern/armature.c index 5704ef6e42f..7feb9d08915 100644 --- a/source/blender/blenkernel/intern/armature.c +++ b/source/blender/blenkernel/intern/armature.c @@ -69,8 +69,6 @@ #include "CLG_log.h" -static CLG_LogRef LOG = {"bke.armature"}; - /* -------------------------------------------------------------------- */ /** \name Prototypes * \{ */ @@ -2296,161 +2294,6 @@ void BKE_armature_where_is(bArmature *arm) /** \name Pose Rebuild * \{ */ -/* if bone layer is protected, copy the data from from->pose - * when used with linked libraries this copies from the linked pose into the local pose */ -static void pose_proxy_sync(Object *ob, Object *from, int layer_protected) -{ - bPose *pose = ob->pose, *frompose = from->pose; - bPoseChannel *pchan, *pchanp; - bConstraint *con; - int error = 0; - - if (frompose == NULL) { - return; - } - - /* in some cases when rigs change, we can't synchronize - * to avoid crashing check for possible errors here */ - for (pchan = pose->chanbase.first; pchan; pchan = pchan->next) { - if (pchan->bone->layer & layer_protected) { - if (BKE_pose_channel_find_name(frompose, pchan->name) == NULL) { - CLOG_ERROR(&LOG, - "failed to sync proxy armature because '%s' is missing pose channel '%s'", - from->id.name, - pchan->name); - error = 1; - } - } - } - - if (error) { - return; - } - - /* clear all transformation values from library */ - BKE_pose_rest(frompose, false); - - /* copy over all of the proxy's bone groups */ - /* TODO: for later - * - implement 'local' bone groups as for constraints - * NOTE: this isn't trivial, as bones reference groups by index not by pointer, - * so syncing things correctly needs careful attention */ - BLI_freelistN(&pose->agroups); - BLI_duplicatelist(&pose->agroups, &frompose->agroups); - pose->active_group = frompose->active_group; - - for (pchan = pose->chanbase.first; pchan; pchan = pchan->next) { - pchanp = BKE_pose_channel_find_name(frompose, pchan->name); - - if (UNLIKELY(pchanp == NULL)) { - /* happens for proxies that become invalid because of a missing link - * for regular cases it shouldn't happen at all */ - } - else if (pchan->bone->layer & layer_protected) { - ListBase proxylocal_constraints = {NULL, NULL}; - bPoseChannel pchanw; - - /* copy posechannel to temp, but restore important pointers */ - pchanw = *pchanp; - pchanw.bone = pchan->bone; - pchanw.prev = pchan->prev; - pchanw.next = pchan->next; - pchanw.parent = pchan->parent; - pchanw.child = pchan->child; - pchanw.custom_tx = pchan->custom_tx; - pchanw.bbone_prev = pchan->bbone_prev; - pchanw.bbone_next = pchan->bbone_next; - - pchanw.mpath = pchan->mpath; - pchan->mpath = NULL; - - /* Reset runtime data, we don't want to share that with the proxy. */ - BKE_pose_channel_runtime_reset_on_copy(&pchanw.runtime); - - /* this is freed so copy a copy, else undo crashes */ - if (pchanw.prop) { - pchanw.prop = IDP_CopyProperty(pchanw.prop); - - /* use the values from the existing props */ - if (pchan->prop) { - IDP_SyncGroupValues(pchanw.prop, pchan->prop); - } - } - - /* Constraints - proxy constraints are flushed... local ones are added after - * 1: extract constraints not from proxy (CONSTRAINT_PROXY_LOCAL) from pchan's constraints. - * 2: copy proxy-pchan's constraints on-to new. - * 3: add extracted local constraints back on top. - * - * Note for BKE_constraints_copy: - * When copying constraints, disable 'do_extern' otherwise - * we get the libs direct linked in this blend. - */ - BKE_constraints_proxylocal_extract(&proxylocal_constraints, &pchan->constraints); - BKE_constraints_copy(&pchanw.constraints, &pchanp->constraints, false); - BLI_movelisttolist(&pchanw.constraints, &proxylocal_constraints); - - /* constraints - set target ob pointer to own object */ - for (con = pchanw.constraints.first; con; con = con->next) { - const bConstraintTypeInfo *cti = BKE_constraint_typeinfo_get(con); - ListBase targets = {NULL, NULL}; - bConstraintTarget *ct; - - if (cti && cti->get_constraint_targets) { - cti->get_constraint_targets(con, &targets); - - for (ct = targets.first; ct; ct = ct->next) { - if (ct->tar == from) { - ct->tar = ob; - } - } - - if (cti->flush_constraint_targets) { - cti->flush_constraint_targets(con, &targets, 0); - } - } - } - - /* free stuff from current channel */ - BKE_pose_channel_free(pchan); - - /* copy data in temp back over to the cleaned-out (but still allocated) original channel */ - *pchan = pchanw; - if (pchan->custom) { - id_us_plus(&pchan->custom->id); - } - } - else { - /* always copy custom shape */ - pchan->custom = pchanp->custom; - if (pchan->custom) { - id_us_plus(&pchan->custom->id); - } - if (pchanp->custom_tx) { - pchan->custom_tx = BKE_pose_channel_find_name(pose, pchanp->custom_tx->name); - } - - /* ID-Property Syncing */ - { - IDProperty *prop_orig = pchan->prop; - if (pchanp->prop) { - pchan->prop = IDP_CopyProperty(pchanp->prop); - if (prop_orig) { - /* copy existing values across when types match */ - IDP_SyncGroupValues(pchan->prop, prop_orig); - } - } - else { - pchan->prop = NULL; - } - if (prop_orig) { - IDP_FreeProperty(prop_orig); - } - } - } - } -} - /** * \param r_last_visited_bone_p: The last bone handled by the last call to this function. */ @@ -2579,16 +2422,6 @@ void BKE_pose_rebuild(Main *bmain, Object *ob, bArmature *arm, const bool do_id_ // printf("rebuild pose %s, %d bones\n", ob->id.name, counter); - /* synchronize protected layers with proxy */ - /* HACK! To preserve 2.7x behavior that you always can pose even locked bones, - * do not do any restoration if this is a COW temp copy! */ - /* Switched back to just NO_MAIN tag, for some reasons (c) - * using COW tag was working this morning, but not anymore... */ - if (ob->proxy != NULL && (ob->id.tag & LIB_TAG_NO_MAIN) == 0) { - BKE_object_copy_proxy_drivers(ob, ob->proxy); - pose_proxy_sync(ob, ob->proxy, arm->layer_protected); - } - BKE_pose_update_constraint_flags(pose); /* for IK detection for example */ pose->flag &= ~POSE_RECALC; diff --git a/source/blender/blenkernel/intern/armature_update.c b/source/blender/blenkernel/intern/armature_update.c index 05c318663e9..73a396b2cdd 100644 --- a/source/blender/blenkernel/intern/armature_update.c +++ b/source/blender/blenkernel/intern/armature_update.c @@ -850,10 +850,6 @@ void BKE_pose_eval_init(struct Depsgraph *depsgraph, Scene *UNUSED(scene), Objec } BLI_assert(pose->chan_array != NULL || BLI_listbase_is_empty(&pose->chanbase)); - - if (object->proxy != NULL) { - object->proxy->proxy_from = object; - } } void BKE_pose_eval_init_ik(struct Depsgraph *depsgraph, Scene *scene, Object *object) @@ -1070,57 +1066,3 @@ void BKE_pose_eval_cleanup(struct Depsgraph *depsgraph, Scene *scene, Object *ob BIK_release_tree(scene, object, ctime); pose_eval_cleanup_common(object); } - -void BKE_pose_eval_proxy_init(struct Depsgraph *depsgraph, Object *object) -{ - BLI_assert(ID_IS_LINKED(object) && object->proxy_from != NULL); - DEG_debug_print_eval(depsgraph, __func__, object->id.name, object); - - BLI_assert(object->pose->chan_array != NULL || BLI_listbase_is_empty(&object->pose->chanbase)); -} - -void BKE_pose_eval_proxy_done(struct Depsgraph *depsgraph, Object *object) -{ - BLI_assert(ID_IS_LINKED(object) && object->proxy_from != NULL); - DEG_debug_print_eval(depsgraph, __func__, object->id.name, object); -} - -void BKE_pose_eval_proxy_cleanup(struct Depsgraph *depsgraph, Object *object) -{ - BLI_assert(ID_IS_LINKED(object) && object->proxy_from != NULL); - DEG_debug_print_eval(depsgraph, __func__, object->id.name, object); - pose_eval_cleanup_common(object); -} - -void BKE_pose_eval_proxy_copy_bone(struct Depsgraph *depsgraph, Object *object, int pchan_index) -{ - const bArmature *armature = (bArmature *)object->data; - if (armature->edbo != NULL) { - return; - } - BLI_assert(ID_IS_LINKED(object) && object->proxy_from != NULL); - bPoseChannel *pchan = pose_pchan_get_indexed(object, pchan_index); - BLI_assert(pchan != NULL); - DEG_debug_print_eval_subdata( - depsgraph, __func__, object->id.name, object, "pchan", pchan->name, pchan); - /* TODO(sergey): Use indexed lookup, once it's guaranteed to be kept - * around for the time while proxies are evaluating. - */ -#if 0 - bPoseChannel *pchan_from = pose_pchan_get_indexed(object->proxy_from, pchan_index); -#else - bPoseChannel *pchan_from = BKE_pose_channel_find_name(object->proxy_from->pose, pchan->name); -#endif - if (pchan_from == NULL) { - printf( - "WARNING: Could not find bone %s in linked ID anymore... " - "You should delete and re-generate your proxy.\n", - pchan->name); - return; - } - BKE_pose_copy_pchan_result(pchan, pchan_from); - copy_dq_dq(&pchan->runtime.deform_dual_quat, &pchan_from->runtime.deform_dual_quat); - BKE_pchan_bbone_segments_cache_copy(pchan, pchan_from); - - pose_channel_flush_to_orig_if_needed(depsgraph, object, pchan); -} diff --git a/source/blender/blenkernel/intern/attribute.c b/source/blender/blenkernel/intern/attribute.c index ee8ef5e97f7..74eb95add51 100644 --- a/source/blender/blenkernel/intern/attribute.c +++ b/source/blender/blenkernel/intern/attribute.c @@ -29,8 +29,8 @@ #include "MEM_guardedalloc.h" #include "DNA_ID.h" +#include "DNA_curves_types.h" #include "DNA_customdata_types.h" -#include "DNA_hair_types.h" #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" #include "DNA_pointcloud_types.h" @@ -38,9 +38,9 @@ #include "BLI_string_utf8.h" #include "BKE_attribute.h" +#include "BKE_curves.h" #include "BKE_customdata.h" #include "BKE_editmesh.h" -#include "BKE_hair.h" #include "BKE_pointcloud.h" #include "BKE_report.h" @@ -88,12 +88,12 @@ static void get_domains(const ID *id, DomainInfo info[ATTR_DOMAIN_NUM]) } break; } - case ID_HA: { - Hair *hair = (Hair *)id; - info[ATTR_DOMAIN_POINT].customdata = &hair->pdata; - info[ATTR_DOMAIN_POINT].length = hair->totpoint; - info[ATTR_DOMAIN_CURVE].customdata = &hair->cdata; - info[ATTR_DOMAIN_CURVE].length = hair->totcurve; + case ID_CV: { + Curves *curves = (Curves *)id; + info[ATTR_DOMAIN_POINT].customdata = &curves->geometry.point_data; + info[ATTR_DOMAIN_POINT].length = curves->geometry.point_size; + info[ATTR_DOMAIN_CURVE].customdata = &curves->geometry.curve_data; + info[ATTR_DOMAIN_CURVE].length = curves->geometry.curve_size; break; } default: @@ -301,8 +301,8 @@ bool BKE_id_attribute_required(ID *id, CustomDataLayer *layer) case ID_PT: { return BKE_pointcloud_customdata_required((PointCloud *)id, layer); } - case ID_HA: { - return BKE_hair_customdata_required((Hair *)id, layer); + case ID_CV: { + return BKE_curves_customdata_required((Curves *)id, layer); } default: return false; @@ -372,8 +372,8 @@ int *BKE_id_attributes_active_index_p(ID *id) case ID_ME: { return &((Mesh *)id)->attributes_active_index; } - case ID_HA: { - return &((Hair *)id)->attributes_active_index; + case ID_CV: { + return &((Curves *)id)->attributes_active_index; } default: return NULL; diff --git a/source/blender/blenkernel/intern/attribute_access.cc b/source/blender/blenkernel/intern/attribute_access.cc index cc43a3e26a8..68ab11a013b 100644 --- a/source/blender/blenkernel/intern/attribute_access.cc +++ b/source/blender/blenkernel/intern/attribute_access.cc @@ -83,6 +83,8 @@ const blender::fn::CPPType *custom_data_type_to_cpp_type(const CustomDataType ty return &CPPType::get<ColorGeometry4f>(); case CD_PROP_BOOL: return &CPPType::get<bool>(); + case CD_PROP_INT8: + return &CPPType::get<int8_t>(); default: return nullptr; } @@ -109,6 +111,9 @@ CustomDataType cpp_type_to_custom_data_type(const blender::fn::CPPType &type) if (type.is<bool>()) { return CD_PROP_BOOL; } + if (type.is<int8_t>()) { + return CD_PROP_INT8; + } return static_cast<CustomDataType>(-1); } @@ -117,16 +122,18 @@ static int attribute_data_type_complexity(const CustomDataType data_type) switch (data_type) { case CD_PROP_BOOL: return 0; - case CD_PROP_INT32: + case CD_PROP_INT8: return 1; - case CD_PROP_FLOAT: + case CD_PROP_INT32: return 2; - case CD_PROP_FLOAT2: + case CD_PROP_FLOAT: return 3; - case CD_PROP_FLOAT3: + case CD_PROP_FLOAT2: return 4; - case CD_PROP_COLOR: + case CD_PROP_FLOAT3: return 5; + case CD_PROP_COLOR: + return 6; #if 0 /* These attribute types are not supported yet. */ case CD_MLOOPCOL: return 3; diff --git a/source/blender/blenkernel/intern/attribute_access_intern.hh b/source/blender/blenkernel/intern/attribute_access_intern.hh index 2cd128081eb..5341266e182 100644 --- a/source/blender/blenkernel/intern/attribute_access_intern.hh +++ b/source/blender/blenkernel/intern/attribute_access_intern.hh @@ -140,7 +140,8 @@ 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; + CD_MASK_PROP_COLOR | CD_MASK_PROP_BOOL | + CD_MASK_PROP_INT8; const AttributeDomain domain_; const CustomDataAccessInfo custom_data_access_; diff --git a/source/blender/blenkernel/intern/blendfile.c b/source/blender/blenkernel/intern/blendfile.c index 6ae19c8036f..86c2593e2e6 100644 --- a/source/blender/blenkernel/intern/blendfile.c +++ b/source/blender/blenkernel/intern/blendfile.c @@ -78,6 +78,23 @@ /** \name High Level `.blend` file read/write. * \{ */ +static bool blendfile_or_libraries_versions_atleast(Main *bmain, + const short versionfile, + const short subversionfile) +{ + if (!MAIN_VERSION_ATLEAST(bmain, versionfile, subversionfile)) { + return false; + } + + LISTBASE_FOREACH (Library *, library, &bmain->libraries) { + if (!MAIN_VERSION_ATLEAST(library, versionfile, subversionfile)) { + return false; + } + } + + return true; +} + static bool foreach_path_clean_cb(BPathForeachPathData *UNUSED(bpath_data), char *path_dst, const char *path_src) @@ -349,10 +366,11 @@ static void setup_app_data(bContext *C, do_versions_ipos_to_animato(bmain); } - /* FIXME: Same as above, readfile's `do_version` do not allow to create new IDs. */ - /* TODO: Once this is definitively validated for 3.0 and option to not do it is removed, add a - * version bump and check here. */ - if (mode != LOAD_UNDO && !USER_EXPERIMENTAL_TEST(&U, no_proxy_to_override_conversion)) { + /* NOTE: readfile's `do_version` does not allow to create new IDs, and only operates on a single + * library at a time. This code needs to operate on the whole Main at once. */ + /* NOTE: Check bmain version (i.e. current blend file version), AND the versions of all the + * linked libraries. */ + if (mode != LOAD_UNDO && !blendfile_or_libraries_versions_atleast(bmain, 302, 1)) { BKE_lib_override_library_main_proxy_convert(bmain, reports); } @@ -603,12 +621,12 @@ UserDef *BKE_blendfile_userdef_from_defaults(void) const char *addons[] = { "io_anim_bvh", "io_curve_svg", + "io_import_obj", "io_mesh_ply", "io_mesh_stl", "io_mesh_uv_layout", "io_scene_fbx", "io_scene_gltf2", - "io_scene_obj", "io_scene_x3d", "cycles", "pose_library", diff --git a/source/blender/blenkernel/intern/blendfile_link_append.c b/source/blender/blenkernel/intern/blendfile_link_append.c index 9b3f4c2fae8..025d16007d8 100644 --- a/source/blender/blenkernel/intern/blendfile_link_append.c +++ b/source/blender/blenkernel/intern/blendfile_link_append.c @@ -993,6 +993,27 @@ static int foreach_libblock_link_append_callback(LibraryIDLinkCallbackData *cb_d /** \name Library link/append code. * \{ */ +static void blendfile_link_append_proxies_convert(Main *bmain, ReportList *reports) +{ + /* NOTE: Do not bother checking file versions here, if there are no proxies to convert this code + * is quite fast anyway. */ + + BlendFileReadReport bf_reports = {.reports = reports}; + BKE_lib_override_library_main_proxy_convert(bmain, &bf_reports); + + if (bf_reports.count.proxies_to_lib_overrides_success != 0 || + bf_reports.count.proxies_to_lib_overrides_failures != 0) { + BKE_reportf( + bf_reports.reports, + RPT_WARNING, + "Proxies have been removed from Blender (%d proxies were automatically converted " + "to library overrides, %d proxies could not be converted and were cleared). " + "Please consider re-saving any library .blend file with the newest Blender version.", + bf_reports.count.proxies_to_lib_overrides_success, + bf_reports.count.proxies_to_lib_overrides_failures); + } +} + void BKE_blendfile_append(BlendfileLinkAppendContext *lapp_context, ReportList *reports) { if (lapp_context->num_items == 0) { @@ -1040,10 +1061,6 @@ void BKE_blendfile_append(BlendfileLinkAppendContext *lapp_context, ReportList * if (item->action != LINK_APPEND_ACT_UNSET) { /* Already set, pass. */ } - if (GS(id->name) == ID_OB && ((Object *)id)->proxy_from != NULL) { - CLOG_INFO(&LOG, 3, "Appended ID '%s' is proxified, keeping it linked...", id->name); - item->action = LINK_APPEND_ACT_KEEP_LINKED; - } else if (do_reuse_local_id && existing_local_id != NULL) { CLOG_INFO(&LOG, 3, "Appended ID '%s' as a matching local one, re-using it...", id->name); item->action = LINK_APPEND_ACT_REUSE_LOCAL; @@ -1098,10 +1115,7 @@ void BKE_blendfile_append(BlendfileLinkAppendContext *lapp_context, ReportList * local_appended_new_id = id->newid; break; case LINK_APPEND_ACT_MAKE_LOCAL: - BKE_lib_id_make_local(bmain, - id, - make_local_common_flags | LIB_ID_MAKELOCAL_FORCE_LOCAL | - LIB_ID_MAKELOCAL_OBJECT_NO_PROXY_CLEARING); + BKE_lib_id_make_local(bmain, id, make_local_common_flags | LIB_ID_MAKELOCAL_FORCE_LOCAL); BLI_assert(id->newid == NULL); local_appended_new_id = id; break; @@ -1210,55 +1224,11 @@ void BKE_blendfile_append(BlendfileLinkAppendContext *lapp_context, ReportList * continue; } BLI_assert(ID_IS_LINKED(id)); - - /* Attempt to re-link copied proxy objects. This allows appending of an entire scene - * from another blend file into this one, even when that blend file contains proxified - * armatures that have local references. Since the proxified object needs to be linked - * (not local), this will only work when the "Localize all" checkbox is disabled. - * TL;DR: this is a dirty hack on top of an already weak feature (proxies). */ - if (GS(id->name) == ID_OB && ((Object *)id)->proxy != NULL) { - Object *ob = (Object *)id; - Object *ob_new = (Object *)id->newid; - bool is_local = false, is_lib = false; - - /* Proxies only work when the proxified object is linked-in from a library. */ - if (!ID_IS_LINKED(ob->proxy)) { - CLOG_WARN(&LOG, - "Proxy object %s will lose its link to %s, because the " - "proxified object is local", - id->newid->name, - ob->proxy->id.name); - continue; - } - - BKE_library_ID_test_usages(bmain, id, &is_local, &is_lib); - - /* We can only switch the proxy'ing to a made-local proxy if it is no longer - * referred to from a library. Not checking for local use; if new local proxy - * was not used locally would be a nasty bug! */ - if (is_local || is_lib) { - CLOG_WARN(&LOG, - "Made-local proxy object %s will lose its link to %s, " - "because the linked-in proxy is referenced (is_local=%i, is_lib=%i)", - id->newid->name, - ob->proxy->id.name, - is_local, - is_lib); - } - else { - /* we can switch the proxy'ing from the linked-in to the made-local proxy. - * BKE_object_make_proxy() shouldn't be used here, as it allocates memory that - * was already allocated by object_make_local() (which called BKE_object_copy). */ - ob_new->proxy = ob->proxy; - ob_new->proxy_group = ob->proxy_group; - ob_new->proxy_from = ob->proxy_from; - ob_new->proxy->proxy_from = ob_new; - ob->proxy = ob->proxy_from = ob->proxy_group = NULL; - } - } } BKE_main_id_newptr_and_tag_clear(bmain); + + blendfile_link_append_proxies_convert(bmain, reports); } void BKE_blendfile_link(BlendfileLinkAppendContext *lapp_context, ReportList *reports) @@ -1361,6 +1331,10 @@ void BKE_blendfile_link(BlendfileLinkAppendContext *lapp_context, ReportList *re .active_collection = NULL}; loose_data_instantiate(&instantiate_context); } + + if ((lapp_context->params->flag & FILE_LINK) != 0) { + blendfile_link_append_proxies_convert(lapp_context->params->bmain, reports); + } } /** \} */ @@ -1541,7 +1515,6 @@ void BKE_blendfile_library_relocate(BlendfileLinkAppendContext *lapp_context, /* Note that in reload case, we also want to replace indirect usages. */ const short remap_flags = ID_REMAP_SKIP_NEVER_NULL_USAGE | - ID_REMAP_NO_INDIRECT_PROXY_DATA_USAGE | (do_reload ? 0 : ID_REMAP_SKIP_INDIRECT_USAGE); for (item_idx = 0, itemlink = lapp_context->items.list; itemlink; item_idx++, itemlink = itemlink->next) { diff --git a/source/blender/blenkernel/intern/brush.c b/source/blender/blenkernel/intern/brush.c index 153a65d67db..c86d4658cc9 100644 --- a/source/blender/blenkernel/intern/brush.c +++ b/source/blender/blenkernel/intern/brush.c @@ -149,16 +149,9 @@ static void brush_make_local(Main *bmain, ID *id, const int flags) Brush *brush = (Brush *)id; const bool lib_local = (flags & LIB_ID_MAKELOCAL_FULL_LIBRARY) != 0; - bool force_local = (flags & LIB_ID_MAKELOCAL_FORCE_LOCAL) != 0; - bool force_copy = (flags & LIB_ID_MAKELOCAL_FORCE_COPY) != 0; - BLI_assert(force_copy == false || force_copy != force_local); - bool is_local = false, is_lib = false; - - /* - only lib users: do nothing (unless force_local is set) - * - only local users: set flag - * - mixed: make copy - */ + bool force_local, force_copy; + BKE_lib_id_make_local_generic_action_define(bmain, id, flags, &force_local, &force_copy); if (brush->clone.image) { /* Special case: ima always local immediately. Clone image should only have one user anyway. */ @@ -171,18 +164,6 @@ static void brush_make_local(Main *bmain, ID *id, const int flags) BLI_assert(brush->clone.image->id.lib == NULL && brush->clone.image->id.newid == NULL); } - if (!force_local && !force_copy) { - BKE_library_ID_test_usages(bmain, brush, &is_local, &is_lib); - if (lib_local || is_local) { - if (!is_lib) { - force_local = true; - } - else { - force_copy = true; - } - } - } - if (force_local) { BKE_lib_id_clear_library_data(bmain, &brush->id, flags); BKE_lib_id_expand_local(bmain, &brush->id, flags); diff --git a/source/blender/blenkernel/intern/camera.c b/source/blender/blenkernel/intern/camera.c index 7940936b64a..d9c637eb177 100644 --- a/source/blender/blenkernel/intern/camera.c +++ b/source/blender/blenkernel/intern/camera.c @@ -218,7 +218,7 @@ void *BKE_camera_add(Main *bmain, const char *name) return cam; } -float BKE_camera_object_dof_distance(Object *ob) +float BKE_camera_object_dof_distance(const Object *ob) { Camera *cam = (Camera *)ob->data; if (ob->type != OB_CAMERA) { diff --git a/source/blender/blenkernel/intern/cdderivedmesh.c b/source/blender/blenkernel/intern/cdderivedmesh.c index a4f3e84a2bf..275500ba2f6 100644 --- a/source/blender/blenkernel/intern/cdderivedmesh.c +++ b/source/blender/blenkernel/intern/cdderivedmesh.c @@ -116,12 +116,6 @@ static void cdDM_copyEdgeArray(DerivedMesh *dm, MEdge *r_edge) memcpy(r_edge, cddm->medge, sizeof(*r_edge) * dm->numEdgeData); } -static void cdDM_copyTessFaceArray(DerivedMesh *dm, MFace *r_face) -{ - CDDerivedMesh *cddm = (CDDerivedMesh *)dm; - memcpy(r_face, cddm->mface, sizeof(*r_face) * dm->numTessFaceData); -} - static void cdDM_copyLoopArray(DerivedMesh *dm, MLoop *r_loop) { CDDerivedMesh *cddm = (CDDerivedMesh *)dm; @@ -147,20 +141,6 @@ static void cdDM_getVertNo(DerivedMesh *dm, int index, float r_no[3]) copy_v3_v3(r_no, cddm->vert_normals[index]); } -static const MeshElemMap *cdDM_getPolyMap(Object *ob, DerivedMesh *dm) -{ - CDDerivedMesh *cddm = (CDDerivedMesh *)dm; - - if (!cddm->pmap && ob->type == OB_MESH) { - Mesh *me = ob->data; - - BKE_mesh_vert_poly_map_create( - &cddm->pmap, &cddm->pmap_mem, me->mpoly, me->mloop, me->totvert, me->totpoly, me->totloop); - } - - return cddm->pmap; -} - static void cdDM_recalc_looptri(DerivedMesh *dm) { CDDerivedMesh *cddm = (CDDerivedMesh *)dm; @@ -216,24 +196,17 @@ static CDDerivedMesh *cdDM_create(const char *desc) dm->copyVertArray = cdDM_copyVertArray; dm->copyEdgeArray = cdDM_copyEdgeArray; - dm->copyTessFaceArray = cdDM_copyTessFaceArray; dm->copyLoopArray = cdDM_copyLoopArray; dm->copyPolyArray = cdDM_copyPolyArray; - dm->getVertData = DM_get_vert_data; - dm->getEdgeData = DM_get_edge_data; - dm->getTessFaceData = DM_get_tessface_data; dm->getVertDataArray = DM_get_vert_data_layer; dm->getEdgeDataArray = DM_get_edge_data_layer; - dm->getTessFaceDataArray = DM_get_tessface_data_layer; dm->recalcLoopTri = cdDM_recalc_looptri; dm->getVertCo = cdDM_getVertCo; dm->getVertNo = cdDM_getVertNo; - dm->getPolyMap = cdDM_getPolyMap; - dm->release = cdDM_release; return cddm; @@ -265,12 +238,6 @@ static DerivedMesh *cdDM_from_mesh_ex(Mesh *mesh, dm->deformedOnly = 1; dm->cd_flag = mesh->cd_flag; - if (mesh->runtime.cd_dirty_vert & CD_MASK_NORMAL) { - dm->dirty |= DM_DIRTY_NORMALS; - } - /* TODO: DM_DIRTY_TESS_CDLAYERS ? Maybe not though, - * since we probably want to switch to looptris? */ - CustomData_merge(&mesh->vdata, &dm->vertData, cddata_masks.vmask, alloctype, mesh->totvert); CustomData_merge(&mesh->edata, &dm->edgeData, cddata_masks.emask, alloctype, mesh->totedge); CustomData_merge(&mesh->fdata, @@ -282,7 +249,9 @@ static DerivedMesh *cdDM_from_mesh_ex(Mesh *mesh, CustomData_merge(&mesh->pdata, &dm->polyData, cddata_masks.pmask, alloctype, mesh->totpoly); cddm->mvert = CustomData_get_layer(&dm->vertData, CD_MVERT); - cddm->vert_normals = CustomData_get_layer(&dm->vertData, CD_NORMAL); + /* Though this may be an unnecessary calculation, simply retrieving the layer may return nothing + * or dirty normals. */ + cddm->vert_normals = BKE_mesh_vertex_normals_ensure(mesh); cddm->medge = CustomData_get_layer(&dm->edgeData, CD_MEDGE); cddm->mloop = CustomData_get_layer(&dm->loopData, CD_MLOOP); cddm->mpoly = CustomData_get_layer(&dm->polyData, CD_MPOLY); @@ -327,12 +296,6 @@ DerivedMesh *CDDM_copy(DerivedMesh *source) DM_from_template(dm, source, DM_TYPE_CDDM, numVerts, numEdges, numTessFaces, numLoops, numPolys); dm->deformedOnly = source->deformedOnly; dm->cd_flag = source->cd_flag; - dm->dirty = source->dirty; - - /* Tessellation data is never copied, so tag it here. - * Only tag dirty layers if we really ignored tessellation faces. - */ - dm->dirty |= DM_DIRTY_TESS_CDLAYERS; CustomData_copy_data(&source->vertData, &dm->vertData, 0, 0, numVerts); CustomData_copy_data(&source->edgeData, &dm->edgeData, 0, 0, numEdges); diff --git a/source/blender/blenkernel/intern/collection.c b/source/blender/blenkernel/intern/collection.c index e6ce4eb9440..79f40c1c888 100644 --- a/source/blender/blenkernel/intern/collection.c +++ b/source/blender/blenkernel/intern/collection.c @@ -1094,14 +1094,12 @@ static bool collection_object_remove(Main *bmain, return true; } -bool BKE_collection_object_add(Main *bmain, Collection *collection, Object *ob) +bool BKE_collection_object_add_notest(Main *bmain, Collection *collection, Object *ob) { - if (ELEM(NULL, collection, ob)) { + if (ob == NULL) { return false; } - collection = collection_parent_editable_find_recursive(collection); - /* Only case where this pointer can be NULL is when scene itself is linked, this case should * never be reached. */ BLI_assert(collection != NULL); @@ -1122,6 +1120,17 @@ bool BKE_collection_object_add(Main *bmain, Collection *collection, Object *ob) return true; } +bool BKE_collection_object_add(Main *bmain, Collection *collection, Object *ob) +{ + if (collection == NULL) { + return false; + } + + collection = collection_parent_editable_find_recursive(collection); + + return BKE_collection_object_add_notest(bmain, collection, ob); +} + void BKE_collection_object_add_from(Main *bmain, Scene *scene, Object *ob_src, Object *ob_dst) { bool is_instantiated = false; diff --git a/source/blender/blenkernel/intern/constraint.c b/source/blender/blenkernel/intern/constraint.c index f013ef99dde..3b7f91b93c0 100644 --- a/source/blender/blenkernel/intern/constraint.c +++ b/source/blender/blenkernel/intern/constraint.c @@ -5850,14 +5850,6 @@ static void add_new_constraint_to_list(Object *ob, bPoseChannel *pchan, bConstra BLI_addtail(list, con); BKE_constraint_unique_name(con, list); - /* if the target list is a list on some PoseChannel belonging to a proxy-protected - * Armature layer, we must tag newly added constraints with a flag which allows them - * to persist after proxy syncing has been done - */ - if (BKE_constraints_proxylocked_owner(ob, pchan)) { - con->flag |= CONSTRAINT_PROXY_LOCAL; - } - /* make this constraint the active one */ BKE_constraints_active_set(list, con); } @@ -6213,45 +6205,6 @@ bool BKE_constraint_is_nonlocal_in_liboverride(const Object *ob, const bConstrai (con == NULL || (con->flag & CONSTRAINT_OVERRIDE_LIBRARY_LOCAL) == 0)); } -/* -------- Constraints and Proxies ------- */ - -void BKE_constraints_proxylocal_extract(ListBase *dst, ListBase *src) -{ - bConstraint *con, *next; - - /* for each tagged constraint, remove from src and move to dst */ - for (con = src->first; con; con = next) { - next = con->next; - - /* check if tagged */ - if (con->flag & CONSTRAINT_PROXY_LOCAL) { - BLI_remlink(src, con); - BLI_addtail(dst, con); - } - } -} - -bool BKE_constraints_proxylocked_owner(Object *ob, bPoseChannel *pchan) -{ - /* Currently, constraints can only be on object or bone level */ - if (ob && ob->proxy) { - if (ob->pose && pchan) { - bArmature *arm = ob->data; - - /* On bone-level, check if bone is on proxy-protected layer */ - if ((pchan->bone) && (pchan->bone->layer & arm->layer_protected)) { - return true; - } - } - else { - /* FIXME: constraints on object-level are not handled well yet */ - return true; - } - } - - return false; -} - /* -------- Target-Matrix Stuff ------- */ void BKE_constraint_target_matrix_get(struct Depsgraph *depsgraph, diff --git a/source/blender/blenkernel/intern/crazyspace.c b/source/blender/blenkernel/intern/crazyspace.c index 6bbb9957b03..0bf83ed5036 100644 --- a/source/blender/blenkernel/intern/crazyspace.c +++ b/source/blender/blenkernel/intern/crazyspace.c @@ -41,6 +41,7 @@ #include "BKE_mesh_wrapper.h" #include "BKE_modifier.h" #include "BKE_multires.h" +#include "BKE_report.h" #include "DEG_depsgraph_query.h" @@ -109,7 +110,7 @@ float (*BKE_crazyspace_get_mapped_editverts(struct Depsgraph *depsgraph, Object /* disable subsurf temporal, get mapped cos, and enable it */ if (modifiers_disable_subsurf_temporary(scene_eval, obedit_eval)) { /* need to make new derivemesh */ - makeDerivedMesh(depsgraph, scene_eval, obedit_eval, editmesh_eval, &CD_MASK_BAREMESH); + makeDerivedMesh(depsgraph, scene_eval, obedit_eval, &CD_MASK_BAREMESH); } /* now get the cage */ @@ -193,13 +194,10 @@ void BKE_crazyspace_set_quats_mesh(Mesh *me, float (*mappedcos)[3], float (*quats)[4]) { - MVert *mvert = me->mvert; - for (int i = 0; i < me->totvert; i++, mvert++) { - mvert->flag &= ~ME_VERT_TMP_TAG; - } + BLI_bitmap *vert_tag = BLI_BITMAP_NEW(me->totvert, __func__); /* first store two sets of tangent vectors in vertices, we derive it just from the face-edges */ - mvert = me->mvert; + MVert *mvert = me->mvert; MPoly *mp = me->mpoly; MLoop *mloop = me->mloop; @@ -209,7 +207,7 @@ void BKE_crazyspace_set_quats_mesh(Mesh *me, MLoop *ml_prev = &ml_next[mp->totloop - 2]; for (int j = 0; j < mp->totloop; j++) { - if ((mvert[ml_curr->v].flag & ME_VERT_TMP_TAG) == 0) { + if (!BLI_BITMAP_TEST(vert_tag, ml_curr->v)) { const float *co_prev, *co_curr, *co_next; /* orig */ const float *vd_prev, *vd_curr, *vd_next; /* deform */ @@ -232,7 +230,7 @@ void BKE_crazyspace_set_quats_mesh(Mesh *me, set_crazy_vertex_quat( quats[ml_curr->v], co_curr, co_next, co_prev, vd_curr, vd_next, vd_prev); - mvert[ml_curr->v].flag |= ME_VERT_TMP_TAG; + BLI_BITMAP_ENABLE(vert_tag, ml_curr->v); } ml_prev = ml_curr; @@ -240,6 +238,8 @@ void BKE_crazyspace_set_quats_mesh(Mesh *me, ml_next++; } } + + MEM_freeN(vert_tag); } int BKE_crazyspace_get_first_deform_matrices_editbmesh(struct Depsgraph *depsgraph, @@ -516,3 +516,85 @@ void BKE_crazyspace_build_sculpt(struct Depsgraph *depsgraph, } } } + +/* -------------------------------------------------------------------- */ +/** \name Crazyspace API + * \{ */ + +void BKE_crazyspace_api_eval(Depsgraph *depsgraph, + Scene *scene, + Object *object, + struct ReportList *reports) +{ + if (object->runtime.crazyspace_deform_imats != NULL || + object->runtime.crazyspace_deform_cos != NULL) { + return; + } + + if (object->type != OB_MESH) { + BKE_report(reports, + RPT_ERROR, + "Crazyspace transformation is only available for Mesh type of objects"); + return; + } + + const Mesh *mesh = (const Mesh *)object->data; + object->runtime.crazyspace_num_verts = mesh->totvert; + BKE_crazyspace_build_sculpt(depsgraph, + scene, + object, + &object->runtime.crazyspace_deform_imats, + &object->runtime.crazyspace_deform_cos); +} + +void BKE_crazyspace_api_displacement_to_deformed(struct Object *object, + struct ReportList *reports, + int vertex_index, + float displacement[3], + float r_displacement_deformed[3]) +{ + if (vertex_index < 0 || vertex_index >= object->runtime.crazyspace_num_verts) { + BKE_reportf(reports, + RPT_ERROR, + "Invalid vertex index %d (expected to be within 0 to %d range)", + vertex_index, + object->runtime.crazyspace_num_verts); + return; + } + + mul_v3_m3v3(r_displacement_deformed, + object->runtime.crazyspace_deform_imats[vertex_index], + displacement); +} + +void BKE_crazyspace_api_displacement_to_original(struct Object *object, + struct ReportList *reports, + int vertex_index, + float displacement_deformed[3], + float r_displacement[3]) +{ + if (vertex_index < 0 || vertex_index >= object->runtime.crazyspace_num_verts) { + BKE_reportf(reports, + RPT_ERROR, + "Invalid vertex index %d (expected to be within 0 to %d range))", + vertex_index, + object->runtime.crazyspace_num_verts); + return; + } + + float mat[3][3]; + if (!invert_m3_m3(mat, object->runtime.crazyspace_deform_imats[vertex_index])) { + copy_v3_v3(r_displacement, displacement_deformed); + return; + } + + mul_v3_m3v3(r_displacement, mat, displacement_deformed); +} + +void BKE_crazyspace_api_eval_clear(Object *object) +{ + MEM_SAFE_FREE(object->runtime.crazyspace_deform_imats); + MEM_SAFE_FREE(object->runtime.crazyspace_deform_cos); +} + +/** \} */ diff --git a/source/blender/blenkernel/intern/curve.cc b/source/blender/blenkernel/intern/curve.cc index 70edaccb244..dda2b5076a8 100644 --- a/source/blender/blenkernel/intern/curve.cc +++ b/source/blender/blenkernel/intern/curve.cc @@ -899,7 +899,7 @@ float BKE_nurb_calc_length(const Nurb *nu, int resolution) pntsit = points + 3; } - while (--b) { + while (--b > 0) { length += len_v3v3(prevpntsit, pntsit); prevpntsit = pntsit; pntsit += 3; diff --git a/source/blender/blenkernel/intern/curve_to_mesh_convert.cc b/source/blender/blenkernel/intern/curve_to_mesh_convert.cc index 073d9d18a04..833b2fe99ec 100644 --- a/source/blender/blenkernel/intern/curve_to_mesh_convert.cc +++ b/source/blender/blenkernel/intern/curve_to_mesh_convert.cc @@ -66,8 +66,8 @@ static void vert_extrude_to_mesh_data(const Spline &spline, if (spline.is_cyclic() && spline.evaluated_edges_size() > 1) { MEdge &edge = r_edges[edge_offset + spline.evaluated_edges_size() - 1]; - edge.v1 = vert_offset; - edge.v2 = vert_offset + eval_size - 1; + edge.v1 = vert_offset + eval_size - 1; + edge.v2 = vert_offset; edge.flag = ME_LOOSEEDGE; } diff --git a/source/blender/blenkernel/intern/curves.cc b/source/blender/blenkernel/intern/curves.cc new file mode 100644 index 00000000000..f5672e9b288 --- /dev/null +++ b/source/blender/blenkernel/intern/curves.cc @@ -0,0 +1,473 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup bke + */ + +#include <cmath> +#include <cstring> + +#include "MEM_guardedalloc.h" + +#include "DNA_curves_types.h" +#include "DNA_defaults.h" +#include "DNA_material_types.h" +#include "DNA_object_types.h" + +#include "BLI_index_range.hh" +#include "BLI_listbase.h" +#include "BLI_math_base.h" +#include "BLI_math_vec_types.hh" +#include "BLI_rand.hh" +#include "BLI_string.h" +#include "BLI_utildefines.h" + +#include "BKE_anim_data.h" +#include "BKE_curves.h" +#include "BKE_customdata.h" +#include "BKE_global.h" +#include "BKE_idtype.h" +#include "BKE_lib_id.h" +#include "BKE_lib_query.h" +#include "BKE_lib_remap.h" +#include "BKE_main.h" +#include "BKE_modifier.h" +#include "BKE_object.h" + +#include "BLT_translation.h" + +#include "DEG_depsgraph_query.h" + +#include "BLO_read_write.h" + +using blender::float3; +using blender::IndexRange; +using blender::MutableSpan; +using blender::RandomNumberGenerator; + +static const char *ATTR_POSITION = "position"; +static const char *ATTR_RADIUS = "radius"; + +static void curves_random(Curves *curves); + +static void curves_init_data(ID *id) +{ + Curves *curves = (Curves *)id; + BLI_assert(MEMCMP_STRUCT_AFTER_IS_ZERO(curves, id)); + + MEMCPY_STRUCT_AFTER(curves, DNA_struct_default_get(Curves), id); + + CustomData_reset(&curves->geometry.point_data); + CustomData_reset(&curves->geometry.curve_data); + + CustomData_add_layer_named(&curves->geometry.point_data, + CD_PROP_FLOAT3, + CD_CALLOC, + nullptr, + curves->geometry.point_size, + ATTR_POSITION); + CustomData_add_layer_named(&curves->geometry.point_data, + CD_PROP_FLOAT, + CD_CALLOC, + nullptr, + curves->geometry.point_size, + ATTR_RADIUS); + + BKE_curves_update_customdata_pointers(curves); + + curves_random(curves); +} + +static void curves_copy_data(Main *UNUSED(bmain), ID *id_dst, const ID *id_src, const int flag) +{ + Curves *curves_dst = (Curves *)id_dst; + const Curves *curves_src = (const Curves *)id_src; + curves_dst->mat = static_cast<Material **>(MEM_dupallocN(curves_src->mat)); + + curves_dst->geometry.point_size = curves_src->geometry.point_size; + curves_dst->geometry.curve_size = curves_src->geometry.curve_size; + + const eCDAllocType alloc_type = (flag & LIB_ID_COPY_CD_REFERENCE) ? CD_REFERENCE : CD_DUPLICATE; + CustomData_copy(&curves_src->geometry.point_data, + &curves_dst->geometry.point_data, + CD_MASK_ALL, + alloc_type, + curves_dst->geometry.point_size); + CustomData_copy(&curves_src->geometry.curve_data, + &curves_dst->geometry.curve_data, + CD_MASK_ALL, + alloc_type, + curves_dst->geometry.curve_size); + BKE_curves_update_customdata_pointers(curves_dst); + + curves_dst->geometry.offsets = static_cast<int *>(MEM_dupallocN(curves_src->geometry.offsets)); + + curves_dst->batch_cache = nullptr; +} + +static void curves_free_data(ID *id) +{ + Curves *curves = (Curves *)id; + BKE_animdata_free(&curves->id, false); + + BKE_curves_batch_cache_free(curves); + + CustomData_free(&curves->geometry.point_data, curves->geometry.point_size); + CustomData_free(&curves->geometry.curve_data, curves->geometry.curve_size); + + MEM_SAFE_FREE(curves->geometry.offsets); + + MEM_SAFE_FREE(curves->mat); +} + +static void curves_foreach_id(ID *id, LibraryForeachIDData *data) +{ + Curves *curves = (Curves *)id; + for (int i = 0; i < curves->totcol; i++) { + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, curves->mat[i], IDWALK_CB_USER); + } +} + +static void curves_blend_write(BlendWriter *writer, ID *id, const void *id_address) +{ + Curves *curves = (Curves *)id; + + CustomDataLayer *players = nullptr, players_buff[CD_TEMP_CHUNK_SIZE]; + CustomDataLayer *clayers = nullptr, clayers_buff[CD_TEMP_CHUNK_SIZE]; + CustomData_blend_write_prepare( + &curves->geometry.point_data, &players, players_buff, ARRAY_SIZE(players_buff)); + CustomData_blend_write_prepare( + &curves->geometry.curve_data, &clayers, clayers_buff, ARRAY_SIZE(clayers_buff)); + + /* Write LibData */ + BLO_write_id_struct(writer, Curves, id_address, &curves->id); + BKE_id_blend_write(writer, &curves->id); + + /* Direct data */ + CustomData_blend_write(writer, + &curves->geometry.point_data, + players, + curves->geometry.point_size, + CD_MASK_ALL, + &curves->id); + CustomData_blend_write(writer, + &curves->geometry.curve_data, + clayers, + curves->geometry.curve_size, + CD_MASK_ALL, + &curves->id); + + BLO_write_int32_array(writer, curves->geometry.curve_size + 1, curves->geometry.offsets); + + BLO_write_pointer_array(writer, curves->totcol, curves->mat); + if (curves->adt) { + BKE_animdata_blend_write(writer, curves->adt); + } + + /* Remove temporary data. */ + if (players && players != players_buff) { + MEM_freeN(players); + } + if (clayers && clayers != clayers_buff) { + MEM_freeN(clayers); + } +} + +static void curves_blend_read_data(BlendDataReader *reader, ID *id) +{ + Curves *curves = (Curves *)id; + BLO_read_data_address(reader, &curves->adt); + BKE_animdata_blend_read_data(reader, curves->adt); + + /* Geometry */ + CustomData_blend_read(reader, &curves->geometry.point_data, curves->geometry.point_size); + CustomData_blend_read(reader, &curves->geometry.curve_data, curves->geometry.point_size); + BKE_curves_update_customdata_pointers(curves); + + BLO_read_int32_array(reader, curves->geometry.curve_size + 1, &curves->geometry.offsets); + + /* Materials */ + BLO_read_pointer_array(reader, (void **)&curves->mat); +} + +static void curves_blend_read_lib(BlendLibReader *reader, ID *id) +{ + Curves *curves = (Curves *)id; + for (int a = 0; a < curves->totcol; a++) { + BLO_read_id_address(reader, curves->id.lib, &curves->mat[a]); + } +} + +static void curves_blend_read_expand(BlendExpander *expander, ID *id) +{ + Curves *curves = (Curves *)id; + for (int a = 0; a < curves->totcol; a++) { + BLO_expand(expander, curves->mat[a]); + } +} + +IDTypeInfo IDType_ID_CV = { + /*id_code */ ID_CV, + /*id_filter */ FILTER_ID_CV, + /*main_listbase_index */ INDEX_ID_CV, + /*struct_size */ sizeof(Curves), + /*name */ "Hair Curves", + /*name_plural */ "Hair Curves", + /*translation_context */ BLT_I18NCONTEXT_ID_CURVES, + /*flags */ IDTYPE_FLAGS_APPEND_IS_REUSABLE, + /*asset_type_info */ nullptr, + + /*init_data */ curves_init_data, + /*copy_data */ curves_copy_data, + /*free_data */ curves_free_data, + /*make_local */ nullptr, + /*foreach_id */ curves_foreach_id, + /*foreach_cache */ nullptr, + /*foreach_path */ nullptr, + /*owner_get */ nullptr, + + /*blend_write */ curves_blend_write, + /*blend_read_data */ curves_blend_read_data, + /*blend_read_lib */ curves_blend_read_lib, + /*blend_read_expand */ curves_blend_read_expand, + + /*blend_read_undo_preserve */ nullptr, + + /*lib_override_apply_post */ nullptr, +}; + +static void curves_random(Curves *curves) +{ + CurvesGeometry &geometry = curves->geometry; + const int numpoints = 8; + + geometry.curve_size = 500; + + geometry.curve_size = 500; + geometry.point_size = geometry.curve_size * numpoints; + + curves->geometry.offsets = (int *)MEM_calloc_arrayN( + curves->geometry.curve_size + 1, sizeof(int), __func__); + CustomData_realloc(&geometry.point_data, geometry.point_size); + CustomData_realloc(&geometry.curve_data, geometry.curve_size); + BKE_curves_update_customdata_pointers(curves); + + MutableSpan<int> offsets{geometry.offsets, geometry.curve_size + 1}; + MutableSpan<float3> positions{(float3 *)geometry.position, geometry.point_size}; + MutableSpan<float> radii{geometry.radius, geometry.point_size}; + + for (const int i : offsets.index_range()) { + geometry.offsets[i] = numpoints * i; + } + + RandomNumberGenerator rng; + + for (int i = 0; i < geometry.curve_size; i++) { + const IndexRange curve_range(offsets[i], offsets[i + 1] - offsets[i]); + MutableSpan<float3> curve_positions = positions.slice(curve_range); + MutableSpan<float> curve_radii = radii.slice(curve_range); + + const float theta = 2.0f * M_PI * rng.get_float(); + const float phi = saacosf(2.0f * rng.get_float() - 1.0f); + + float3 no = {std::sin(theta) * std::sin(phi), std::cos(theta) * std::sin(phi), std::cos(phi)}; + no = blender::math::normalize(no); + + float3 co = no; + for (int key = 0; key < numpoints; key++) { + float t = key / (float)(numpoints - 1); + curve_positions[key] = co; + curve_radii[key] = 0.02f * (1.0f - t); + + float3 offset = float3(rng.get_float(), rng.get_float(), rng.get_float()) * 2.0f - 1.0f; + co += (offset + no) / numpoints; + } + } +} + +void *BKE_curves_add(Main *bmain, const char *name) +{ + Curves *curves = static_cast<Curves *>(BKE_id_new(bmain, ID_CV, name)); + + return curves; +} + +BoundBox *BKE_curves_boundbox_get(Object *ob) +{ + BLI_assert(ob->type == OB_CURVES); + Curves *curves = static_cast<Curves *>(ob->data); + + if (ob->runtime.bb != nullptr && (ob->runtime.bb->flag & BOUNDBOX_DIRTY) == 0) { + return ob->runtime.bb; + } + + if (ob->runtime.bb == nullptr) { + ob->runtime.bb = MEM_cnew<BoundBox>(__func__); + + float min[3], max[3]; + INIT_MINMAX(min, max); + + float(*curves_co)[3] = curves->geometry.position; + float *curves_radius = curves->geometry.radius; + for (int a = 0; a < curves->geometry.point_size; a++) { + float *co = curves_co[a]; + float radius = (curves_radius) ? curves_radius[a] : 0.0f; + const float co_min[3] = {co[0] - radius, co[1] - radius, co[2] - radius}; + const float co_max[3] = {co[0] + radius, co[1] + radius, co[2] + radius}; + DO_MIN(co_min, min); + DO_MAX(co_max, max); + } + + BKE_boundbox_init_from_minmax(ob->runtime.bb, min, max); + } + + return ob->runtime.bb; +} + +void BKE_curves_update_customdata_pointers(Curves *curves) +{ + curves->geometry.position = (float(*)[3])CustomData_get_layer_named( + &curves->geometry.point_data, CD_PROP_FLOAT3, ATTR_POSITION); + curves->geometry.radius = (float *)CustomData_get_layer_named( + &curves->geometry.point_data, CD_PROP_FLOAT, ATTR_RADIUS); +} + +bool BKE_curves_customdata_required(Curves *UNUSED(curves), CustomDataLayer *layer) +{ + return layer->type == CD_PROP_FLOAT3 && STREQ(layer->name, ATTR_POSITION); +} + +/* Dependency Graph */ + +Curves *BKE_curves_new_for_eval(const Curves *curves_src, int totpoint, int totcurve) +{ + Curves *curves_dst = static_cast<Curves *>(BKE_id_new_nomain(ID_CV, nullptr)); + + STRNCPY(curves_dst->id.name, curves_src->id.name); + curves_dst->mat = static_cast<Material **>(MEM_dupallocN(curves_src->mat)); + curves_dst->totcol = curves_src->totcol; + + curves_dst->geometry.point_size = totpoint; + curves_dst->geometry.curve_size = totcurve; + CustomData_copy(&curves_src->geometry.point_data, + &curves_dst->geometry.point_data, + CD_MASK_ALL, + CD_CALLOC, + totpoint); + CustomData_copy(&curves_src->geometry.curve_data, + &curves_dst->geometry.curve_data, + CD_MASK_ALL, + CD_CALLOC, + totcurve); + BKE_curves_update_customdata_pointers(curves_dst); + + return curves_dst; +} + +Curves *BKE_curves_copy_for_eval(Curves *curves_src, bool reference) +{ + int flags = LIB_ID_COPY_LOCALIZE; + + if (reference) { + flags |= LIB_ID_COPY_CD_REFERENCE; + } + + Curves *result = (Curves *)BKE_id_copy_ex(nullptr, &curves_src->id, nullptr, flags); + return result; +} + +static Curves *curves_evaluate_modifiers(struct Depsgraph *depsgraph, + struct Scene *scene, + Object *object, + Curves *curves_input) +{ + Curves *curves = curves_input; + + /* Modifier evaluation modes. */ + const bool use_render = (DEG_get_mode(depsgraph) == DAG_EVAL_RENDER); + const int required_mode = use_render ? eModifierMode_Render : eModifierMode_Realtime; + ModifierApplyFlag apply_flag = use_render ? MOD_APPLY_RENDER : MOD_APPLY_USECACHE; + const ModifierEvalContext mectx = {depsgraph, object, apply_flag}; + + /* Get effective list of modifiers to execute. Some effects like shape keys + * are added as virtual modifiers before the user created modifiers. */ + VirtualModifierData virtualModifierData; + ModifierData *md = BKE_modifiers_get_virtual_modifierlist(object, &virtualModifierData); + + /* Evaluate modifiers. */ + for (; md; md = md->next) { + const ModifierTypeInfo *mti = BKE_modifier_get_info(static_cast<ModifierType>(md->type)); + + if (!BKE_modifier_is_enabled(scene, md, required_mode)) { + continue; + } + + if ((mti->type == eModifierTypeType_OnlyDeform) && + (mti->flags & eModifierTypeFlag_AcceptsVertexCosOnly)) { + /* Ensure we are not modifying the input. */ + if (curves == curves_input) { + curves = BKE_curves_copy_for_eval(curves, true); + } + + /* Ensure we are not overwriting referenced data. */ + CustomData_duplicate_referenced_layer_named(&curves->geometry.point_data, + CD_PROP_FLOAT3, + ATTR_POSITION, + curves->geometry.point_size); + BKE_curves_update_customdata_pointers(curves); + + /* Created deformed coordinates array on demand. */ + mti->deformVerts( + md, &mectx, nullptr, curves->geometry.position, curves->geometry.point_size); + } + } + + return curves; +} + +void BKE_curves_data_update(struct Depsgraph *depsgraph, struct Scene *scene, Object *object) +{ + /* Free any evaluated data and restore original data. */ + BKE_object_free_derived_caches(object); + + /* Evaluate modifiers. */ + Curves *curves = static_cast<Curves *>(object->data); + Curves *curves_eval = curves_evaluate_modifiers(depsgraph, scene, object, curves); + + /* Assign evaluated object. */ + const bool is_owned = (curves != curves_eval); + BKE_object_eval_assign_data(object, &curves_eval->id, is_owned); +} + +/* Draw Cache */ + +void (*BKE_curves_batch_cache_dirty_tag_cb)(Curves *curves, int mode) = nullptr; +void (*BKE_curves_batch_cache_free_cb)(Curves *curves) = nullptr; + +void BKE_curves_batch_cache_dirty_tag(Curves *curves, int mode) +{ + if (curves->batch_cache) { + BKE_curves_batch_cache_dirty_tag_cb(curves, mode); + } +} + +void BKE_curves_batch_cache_free(Curves *curves) +{ + if (curves->batch_cache) { + BKE_curves_batch_cache_free_cb(curves); + } +} diff --git a/source/blender/blenkernel/intern/customdata.cc b/source/blender/blenkernel/intern/customdata.cc index 5e3beab9b72..c5cc077c8ae 100644 --- a/source/blender/blenkernel/intern/customdata.cc +++ b/source/blender/blenkernel/intern/customdata.cc @@ -31,7 +31,6 @@ #include "DNA_ID.h" #include "DNA_customdata_types.h" -#include "DNA_hair_types.h" #include "DNA_meshdata_types.h" #include "BLI_bitmap.h" @@ -1793,10 +1792,10 @@ static const LayerTypeInfo LAYERTYPEINFO[CD_NUMTYPES] = { {sizeof(float[3]), "vec3f", 1, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr}, /* 44: CD_RADIUS */ {sizeof(float), "MFloatProperty", 1, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr}, - /* 45: CD_HAIRCURVE */ - {sizeof(HairCurve), "HairCurve", 1, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr}, - /* 46: CD_HAIRMAPPING */ - {sizeof(HairMapping), "HairMapping", 1, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr}, + /* 45: CD_PROP_INT8 */ + {sizeof(int8_t), "MInt8Property", 1, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr}, + /* 46: CD_HAIRMAPPING */ /* UNUSED */ + {-1, "", 1, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr}, /* 47: CD_PROP_COLOR */ {sizeof(MPropCol), "MPropCol", @@ -1914,7 +1913,7 @@ static const char *LAYERTYPENAMES[CD_NUMTYPES] = { "CDCustomLoopNormal", "CDSculptFaceGroups", /* 43-46 */ "CDHairPoint", - "CDHairCurve", + "CDPropInt8", "CDHairMapping", "CDPoint", "CDPropCol", @@ -2427,6 +2426,13 @@ int CustomData_get_stencil_layer(const CustomData *data, int type) return (layer_index != -1) ? data->layers[layer_index].active_mask : -1; } +const char *CustomData_get_active_layer_name(const struct CustomData *data, const int type) +{ + /* Get the layer index of the active layer of this type. */ + const int layer_index = CustomData_get_active_layer_index(data, type); + return layer_index < 0 ? nullptr : data->layers[layer_index].name; +} + void CustomData_set_layer_active(CustomData *data, int type, int n) { for (int i = 0; i < data->totlayer; i++) { @@ -2773,6 +2779,24 @@ void CustomData_free_layers(CustomData *data, int type, int totelem) } } +void CustomData_free_layers_anonymous(struct CustomData *data, int totelem) +{ + while (true) { + bool found_anonymous_layer = false; + for (int i = 0; i < data->totlayer; i++) { + const CustomDataLayer *layer = &data->layers[i]; + if (layer->anonymous_id != nullptr) { + CustomData_free_layer(data, layer->type, totelem, i); + found_anonymous_layer = true; + break; + } + } + if (!found_anonymous_layer) { + break; + } + } +} + bool CustomData_has_layer(const CustomData *data, int type) { return (CustomData_get_layer_index(data, type) != -1); diff --git a/source/blender/blenkernel/intern/dynamicpaint.c b/source/blender/blenkernel/intern/dynamicpaint.c index 64e0427a810..bc5a0ed1538 100644 --- a/source/blender/blenkernel/intern/dynamicpaint.c +++ b/source/blender/blenkernel/intern/dynamicpaint.c @@ -1518,7 +1518,7 @@ static void dynamic_paint_set_init_color_tex_to_vcol_cb( multitex_ext_safe(tex, uv, &texres, pool, scene_color_manage, false); if (texres.tin > pPoint[vert].color[3]) { - copy_v3_v3(pPoint[vert].color, &texres.tr); + copy_v3_v3(pPoint[vert].color, texres.trgba); pPoint[vert].color[3] = texres.tin; } } @@ -1559,7 +1559,7 @@ static void dynamic_paint_set_init_color_tex_to_imseq_cb( multitex_ext_safe(tex, uv_final, &texres, NULL, scene_color_manage, false); /* apply color */ - copy_v3_v3(pPoint[i].color, &texres.tr); + copy_v3_v3(pPoint[i].color, texres.trgba); pPoint[i].color[3] = texres.tin; } @@ -1985,9 +1985,6 @@ static Mesh *dynamicPaint_Modifier_apply(DynamicPaintModifierData *pmd, Object * } MEM_freeN(fcolor); - - /* Mark tessellated CD layers as dirty. */ - // result->dirty |= DM_DIRTY_TESS_CDLAYERS; } /* vertex group paint */ else if (surface->type == MOD_DPAINT_SURFACE_T_WEIGHT) { diff --git a/source/blender/blenkernel/intern/editmesh.c b/source/blender/blenkernel/intern/editmesh.c index 6ef811c46c7..0774a1a3d88 100644 --- a/source/blender/blenkernel/intern/editmesh.c +++ b/source/blender/blenkernel/intern/editmesh.c @@ -39,6 +39,8 @@ #include "BKE_mesh_wrapper.h" #include "BKE_object.h" +#include "DEG_depsgraph_query.h" + BMEditMesh *BKE_editmesh_create(BMesh *bm) { BMEditMesh *em = MEM_callocN(sizeof(BMEditMesh), __func__); @@ -51,9 +53,6 @@ BMEditMesh *BKE_editmesh_copy(BMEditMesh *em) BMEditMesh *em_copy = MEM_callocN(sizeof(BMEditMesh), __func__); *em_copy = *em; - em_copy->mesh_eval_cage = em_copy->mesh_eval_final = NULL; - em_copy->bb_cage = NULL; - em_copy->bm = BM_mesh_copy(em->bm); /* The tessellation is NOT calculated on the copy here, @@ -194,22 +193,8 @@ void BKE_editmesh_looptri_and_normals_calc_with_partial(BMEditMesh *em, }); } -void BKE_editmesh_free_derived_caches(BMEditMesh *em) -{ - if (em->mesh_eval_cage) { - BKE_id_free(NULL, em->mesh_eval_cage); - } - if (em->mesh_eval_final && em->mesh_eval_final != em->mesh_eval_cage) { - BKE_id_free(NULL, em->mesh_eval_final); - } - em->mesh_eval_cage = em->mesh_eval_final = NULL; - - MEM_SAFE_FREE(em->bb_cage); -} - void BKE_editmesh_free_data(BMEditMesh *em) { - BKE_editmesh_free_derived_caches(em); if (em->looptris) { MEM_freeN(em->looptris); @@ -283,13 +268,15 @@ const float (*BKE_editmesh_vert_coords_when_deformed(struct Depsgraph *depsgraph *r_is_alloc = false; Mesh *me = ob->data; + Object *object_eval = DEG_get_evaluated_object(depsgraph, ob); + Mesh *editmesh_eval_final = BKE_object_get_editmesh_eval_final(object_eval); if ((me->runtime.edit_data != NULL) && (me->runtime.edit_data->vertexCos != NULL)) { /* Deformed, and we have deformed coords already. */ coords = me->runtime.edit_data->vertexCos; } - else if ((em->mesh_eval_final != NULL) && - (em->mesh_eval_final->runtime.wrapper_type == ME_WRAPPER_TYPE_BMESH)) { + else if ((editmesh_eval_final != NULL) && + (editmesh_eval_final->runtime.wrapper_type == ME_WRAPPER_TYPE_BMESH)) { /* If this is an edit-mesh type, leave NULL as we can use the vertex coords. */ } else { @@ -334,18 +321,18 @@ void BKE_editmesh_ensure_autosmooth(BMEditMesh *em, Mesh *me) } } -BoundBox *BKE_editmesh_cage_boundbox_get(BMEditMesh *em) +BoundBox *BKE_editmesh_cage_boundbox_get(struct Object *object, BMEditMesh *UNUSED(em)) { - if (em->bb_cage == NULL) { + if (object->runtime.editmesh_bb_cage == NULL) { float min[3], max[3]; INIT_MINMAX(min, max); - if (em->mesh_eval_cage) { - BKE_mesh_wrapper_minmax(em->mesh_eval_cage, min, max); + if (object->runtime.editmesh_eval_cage) { + BKE_mesh_wrapper_minmax(object->runtime.editmesh_eval_cage, min, max); } - em->bb_cage = MEM_callocN(sizeof(BoundBox), "BMEditMesh.bb_cage"); - BKE_boundbox_init_from_minmax(em->bb_cage, min, max); + object->runtime.editmesh_bb_cage = MEM_callocN(sizeof(BoundBox), "BMEditMesh.bb_cage"); + BKE_boundbox_init_from_minmax(object->runtime.editmesh_bb_cage, min, max); } - return em->bb_cage; + return object->runtime.editmesh_bb_cage; } diff --git a/source/blender/blenkernel/intern/effect.c b/source/blender/blenkernel/intern/effect.c index bbf9e9edfd2..1f1915f60ca 100644 --- a/source/blender/blenkernel/intern/effect.c +++ b/source/blender/blenkernel/intern/effect.c @@ -908,9 +908,9 @@ static void do_texture_effector(EffectorCache *eff, eff->pd->tex, tex_co, NULL, NULL, 0, result, 0, NULL, scene_color_manage, false); if (hasrgb && mode == PFIELD_TEX_RGB) { - force[0] = (0.5f - result->tr) * strength; - force[1] = (0.5f - result->tg) * strength; - force[2] = (0.5f - result->tb) * strength; + force[0] = (0.5f - result->trgba[0]) * strength; + force[1] = (0.5f - result->trgba[1]) * strength; + force[2] = (0.5f - result->trgba[2]) * strength; } else if (nabla != 0) { strength /= nabla; @@ -933,7 +933,8 @@ static void do_texture_effector(EffectorCache *eff, /* generate intensity if texture only has rgb value */ if (hasrgb & TEX_RGB) { for (int i = 0; i < 4; i++) { - result[i].tin = (1.0f / 3.0f) * (result[i].tr + result[i].tg + result[i].tb); + result[i].tin = (1.0f / 3.0f) * + (result[i].trgba[0] + result[i].trgba[1] + result[i].trgba[2]); } } force[0] = (result[0].tin - result[1].tin) * strength; @@ -943,12 +944,12 @@ static void do_texture_effector(EffectorCache *eff, else { /*PFIELD_TEX_CURL*/ float dbdy, dgdz, drdz, dbdx, dgdx, drdy; - dbdy = result[2].tb - result[0].tb; - dgdz = result[3].tg - result[0].tg; - drdz = result[3].tr - result[0].tr; - dbdx = result[1].tb - result[0].tb; - dgdx = result[1].tg - result[0].tg; - drdy = result[2].tr - result[0].tr; + dbdy = result[2].trgba[2] - result[0].trgba[2]; + dgdz = result[3].trgba[1] - result[0].trgba[1]; + drdz = result[3].trgba[0] - result[0].trgba[0]; + dbdx = result[1].trgba[2] - result[0].trgba[2]; + dgdx = result[1].trgba[1] - result[0].trgba[1]; + drdy = result[2].trgba[0] - result[0].trgba[0]; force[0] = (dbdy - dgdz) * strength; force[1] = (drdz - dbdx) * strength; diff --git a/source/blender/blenkernel/intern/fcurve_driver.c b/source/blender/blenkernel/intern/fcurve_driver.c index ce30f80ba65..e71217fea9e 100644 --- a/source/blender/blenkernel/intern/fcurve_driver.c +++ b/source/blender/blenkernel/intern/fcurve_driver.c @@ -88,14 +88,6 @@ typedef struct DriverVarTypeInfo { /** \name Driver Target Utilities * \{ */ -static ID *dtar_id_ensure_proxy_from(ID *id) -{ - if (id && GS(id->name) == ID_OB && ((Object *)id)->proxy_from) { - return (ID *)(((Object *)id)->proxy_from); - } - return id; -} - /** * Helper function to obtain a value using RNA from the specified source * (for evaluating drivers). @@ -113,7 +105,7 @@ static float dtar_get_prop_val(ChannelDriver *driver, DriverTarget *dtar) return 0.0f; } - id = dtar_id_ensure_proxy_from(dtar->id); + id = dtar->id; /* Error check for missing pointer. */ if (id == NULL) { @@ -217,7 +209,7 @@ bool driver_get_variable_property(ChannelDriver *driver, return false; } - id = dtar_id_ensure_proxy_from(dtar->id); + id = dtar->id; /* Error check for missing pointer. */ if (id == NULL) { @@ -273,7 +265,7 @@ static short driver_check_valid_targets(ChannelDriver *driver, DriverVar *dvar) short valid_targets = 0; DRIVER_TARGETS_USED_LOOPER_BEGIN (dvar) { - Object *ob = (Object *)dtar_id_ensure_proxy_from(dtar->id); + Object *ob = (Object *)dtar->id; /* Check if this target has valid data. */ if ((ob == NULL) || (GS(ob->id.name) != ID_OB)) { @@ -328,7 +320,7 @@ static float dvar_eval_rotDiff(ChannelDriver *driver, DriverVar *dvar) for (int i = 0; i < 2; i++) { /* Get pointer to loc values to store in. */ DriverTarget *dtar = &dvar->targets[i]; - Object *ob = (Object *)dtar_id_ensure_proxy_from(dtar->id); + Object *ob = (Object *)dtar->id; bPoseChannel *pchan; /* After the checks above, the targets should be valid here. */ @@ -389,7 +381,7 @@ static float dvar_eval_locDiff(ChannelDriver *driver, DriverVar *dvar) /* NOTE: for now, these are all just world-space */ DRIVER_TARGETS_USED_LOOPER_BEGIN (dvar) { /* Get pointer to loc values to store in. */ - Object *ob = (Object *)dtar_id_ensure_proxy_from(dtar->id); + Object *ob = (Object *)dtar->id; bPoseChannel *pchan; float tmp_loc[3]; @@ -472,7 +464,7 @@ static float dvar_eval_locDiff(ChannelDriver *driver, DriverVar *dvar) static float dvar_eval_transChan(ChannelDriver *driver, DriverVar *dvar) { DriverTarget *dtar = &dvar->targets[0]; - Object *ob = (Object *)dtar_id_ensure_proxy_from(dtar->id); + Object *ob = (Object *)dtar->id; bPoseChannel *pchan; float mat[4][4]; float oldEul[3] = {0.0f, 0.0f, 0.0f}; diff --git a/source/blender/blenkernel/intern/hair.cc b/source/blender/blenkernel/intern/hair.cc deleted file mode 100644 index b7ba159f631..00000000000 --- a/source/blender/blenkernel/intern/hair.cc +++ /dev/null @@ -1,435 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -/** \file - * \ingroup bke - */ - -#include <cmath> -#include <cstring> - -#include "MEM_guardedalloc.h" - -#include "DNA_defaults.h" -#include "DNA_hair_types.h" -#include "DNA_material_types.h" -#include "DNA_object_types.h" - -#include "BLI_listbase.h" -#include "BLI_math_base.h" -#include "BLI_math_vec_types.hh" -#include "BLI_rand.h" -#include "BLI_string.h" -#include "BLI_utildefines.h" - -#include "BKE_anim_data.h" -#include "BKE_customdata.h" -#include "BKE_global.h" -#include "BKE_hair.h" -#include "BKE_idtype.h" -#include "BKE_lib_id.h" -#include "BKE_lib_query.h" -#include "BKE_lib_remap.h" -#include "BKE_main.h" -#include "BKE_modifier.h" -#include "BKE_object.h" - -#include "BLT_translation.h" - -#include "DEG_depsgraph_query.h" - -#include "BLO_read_write.h" - -using blender::float3; - -static const char *HAIR_ATTR_POSITION = "position"; -static const char *HAIR_ATTR_RADIUS = "radius"; - -/* Hair datablock */ - -static void hair_random(Hair *hair); - -static void hair_init_data(ID *id) -{ - Hair *hair = (Hair *)id; - BLI_assert(MEMCMP_STRUCT_AFTER_IS_ZERO(hair, id)); - - MEMCPY_STRUCT_AFTER(hair, DNA_struct_default_get(Hair), id); - - CustomData_reset(&hair->pdata); - CustomData_reset(&hair->cdata); - - CustomData_add_layer_named( - &hair->pdata, CD_PROP_FLOAT3, CD_CALLOC, nullptr, hair->totpoint, HAIR_ATTR_POSITION); - CustomData_add_layer_named( - &hair->pdata, CD_PROP_FLOAT, CD_CALLOC, nullptr, hair->totpoint, HAIR_ATTR_RADIUS); - CustomData_add_layer(&hair->cdata, CD_HAIRCURVE, CD_CALLOC, nullptr, hair->totcurve); - BKE_hair_update_customdata_pointers(hair); - - hair_random(hair); -} - -static void hair_copy_data(Main *UNUSED(bmain), ID *id_dst, const ID *id_src, const int flag) -{ - Hair *hair_dst = (Hair *)id_dst; - const Hair *hair_src = (const Hair *)id_src; - hair_dst->mat = static_cast<Material **>(MEM_dupallocN(hair_src->mat)); - - const eCDAllocType alloc_type = (flag & LIB_ID_COPY_CD_REFERENCE) ? CD_REFERENCE : CD_DUPLICATE; - CustomData_copy(&hair_src->pdata, &hair_dst->pdata, CD_MASK_ALL, alloc_type, hair_dst->totpoint); - CustomData_copy(&hair_src->cdata, &hair_dst->cdata, CD_MASK_ALL, alloc_type, hair_dst->totcurve); - BKE_hair_update_customdata_pointers(hair_dst); - - hair_dst->batch_cache = nullptr; -} - -static void hair_free_data(ID *id) -{ - Hair *hair = (Hair *)id; - BKE_animdata_free(&hair->id, false); - - BKE_hair_batch_cache_free(hair); - - CustomData_free(&hair->pdata, hair->totpoint); - CustomData_free(&hair->cdata, hair->totcurve); - - MEM_SAFE_FREE(hair->mat); -} - -static void hair_foreach_id(ID *id, LibraryForeachIDData *data) -{ - Hair *hair = (Hair *)id; - for (int i = 0; i < hair->totcol; i++) { - BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, hair->mat[i], IDWALK_CB_USER); - } -} - -static void hair_blend_write(BlendWriter *writer, ID *id, const void *id_address) -{ - Hair *hair = (Hair *)id; - - CustomDataLayer *players = nullptr, players_buff[CD_TEMP_CHUNK_SIZE]; - CustomDataLayer *clayers = nullptr, clayers_buff[CD_TEMP_CHUNK_SIZE]; - CustomData_blend_write_prepare(&hair->pdata, &players, players_buff, ARRAY_SIZE(players_buff)); - CustomData_blend_write_prepare(&hair->cdata, &clayers, clayers_buff, ARRAY_SIZE(clayers_buff)); - - /* Write LibData */ - BLO_write_id_struct(writer, Hair, id_address, &hair->id); - BKE_id_blend_write(writer, &hair->id); - - /* Direct data */ - CustomData_blend_write(writer, &hair->pdata, players, hair->totpoint, CD_MASK_ALL, &hair->id); - CustomData_blend_write(writer, &hair->cdata, clayers, hair->totcurve, CD_MASK_ALL, &hair->id); - - BLO_write_pointer_array(writer, hair->totcol, hair->mat); - if (hair->adt) { - BKE_animdata_blend_write(writer, hair->adt); - } - - /* Remove temporary data. */ - if (players && players != players_buff) { - MEM_freeN(players); - } - if (clayers && clayers != clayers_buff) { - MEM_freeN(clayers); - } -} - -static void hair_blend_read_data(BlendDataReader *reader, ID *id) -{ - Hair *hair = (Hair *)id; - BLO_read_data_address(reader, &hair->adt); - BKE_animdata_blend_read_data(reader, hair->adt); - - /* Geometry */ - CustomData_blend_read(reader, &hair->pdata, hair->totpoint); - CustomData_blend_read(reader, &hair->cdata, hair->totcurve); - BKE_hair_update_customdata_pointers(hair); - - /* Materials */ - BLO_read_pointer_array(reader, (void **)&hair->mat); -} - -static void hair_blend_read_lib(BlendLibReader *reader, ID *id) -{ - Hair *hair = (Hair *)id; - for (int a = 0; a < hair->totcol; a++) { - BLO_read_id_address(reader, hair->id.lib, &hair->mat[a]); - } -} - -static void hair_blend_read_expand(BlendExpander *expander, ID *id) -{ - Hair *hair = (Hair *)id; - for (int a = 0; a < hair->totcol; a++) { - BLO_expand(expander, hair->mat[a]); - } -} - -IDTypeInfo IDType_ID_HA = { - /*id_code */ ID_HA, - /*id_filter */ FILTER_ID_HA, - /*main_listbase_index */ INDEX_ID_HA, - /*struct_size */ sizeof(Hair), - /*name */ "Hair", - /*name_plural */ "hairs", - /*translation_context */ BLT_I18NCONTEXT_ID_HAIR, - /*flags */ IDTYPE_FLAGS_APPEND_IS_REUSABLE, - /*asset_type_info */ nullptr, - - /*init_data */ hair_init_data, - /*copy_data */ hair_copy_data, - /*free_data */ hair_free_data, - /*make_local */ nullptr, - /*foreach_id */ hair_foreach_id, - /*foreach_cache */ nullptr, - /*foreach_path */ nullptr, - /*owner_get */ nullptr, - - /*blend_write */ hair_blend_write, - /*blend_read_data */ hair_blend_read_data, - /*blend_read_lib */ hair_blend_read_lib, - /*blend_read_expand */ hair_blend_read_expand, - - /*blend_read_undo_preserve */ nullptr, - - /*lib_override_apply_post */ nullptr, -}; - -static void hair_random(Hair *hair) -{ - const int numpoints = 8; - - hair->totcurve = 500; - hair->totpoint = hair->totcurve * numpoints; - - CustomData_realloc(&hair->pdata, hair->totpoint); - CustomData_realloc(&hair->cdata, hair->totcurve); - BKE_hair_update_customdata_pointers(hair); - - RNG *rng = BLI_rng_new(0); - - for (int i = 0; i < hair->totcurve; i++) { - HairCurve *curve = &hair->curves[i]; - curve->firstpoint = i * numpoints; - curve->numpoints = numpoints; - - float theta = 2.0f * M_PI * BLI_rng_get_float(rng); - float phi = saacosf(2.0f * BLI_rng_get_float(rng) - 1.0f); - - float no[3] = {sinf(theta) * sinf(phi), cosf(theta) * sinf(phi), cosf(phi)}; - normalize_v3(no); - - float co[3]; - copy_v3_v3(co, no); - - float(*curve_co)[3] = hair->co + curve->firstpoint; - float *curve_radius = hair->radius + curve->firstpoint; - for (int key = 0; key < numpoints; key++) { - float t = key / (float)(numpoints - 1); - copy_v3_v3(curve_co[key], co); - curve_radius[key] = 0.02f * (1.0f - t); - - float offset[3] = {2.0f * BLI_rng_get_float(rng) - 1.0f, - 2.0f * BLI_rng_get_float(rng) - 1.0f, - 2.0f * BLI_rng_get_float(rng) - 1.0f}; - add_v3_v3(offset, no); - madd_v3_v3fl(co, offset, 1.0f / numpoints); - } - } - - BLI_rng_free(rng); -} - -void *BKE_hair_add(Main *bmain, const char *name) -{ - Hair *hair = static_cast<Hair *>(BKE_id_new(bmain, ID_HA, name)); - - return hair; -} - -BoundBox *BKE_hair_boundbox_get(Object *ob) -{ - BLI_assert(ob->type == OB_HAIR); - Hair *hair = static_cast<Hair *>(ob->data); - - if (ob->runtime.bb != nullptr && (ob->runtime.bb->flag & BOUNDBOX_DIRTY) == 0) { - return ob->runtime.bb; - } - - if (ob->runtime.bb == nullptr) { - ob->runtime.bb = MEM_cnew<BoundBox>(__func__); - - float min[3], max[3]; - INIT_MINMAX(min, max); - - float(*hair_co)[3] = hair->co; - float *hair_radius = hair->radius; - for (int a = 0; a < hair->totpoint; a++) { - float *co = hair_co[a]; - float radius = (hair_radius) ? hair_radius[a] : 0.0f; - const float co_min[3] = {co[0] - radius, co[1] - radius, co[2] - radius}; - const float co_max[3] = {co[0] + radius, co[1] + radius, co[2] + radius}; - DO_MIN(co_min, min); - DO_MAX(co_max, max); - } - - BKE_boundbox_init_from_minmax(ob->runtime.bb, min, max); - } - - return ob->runtime.bb; -} - -void BKE_hair_update_customdata_pointers(Hair *hair) -{ - hair->co = (float(*)[3])CustomData_get_layer_named( - &hair->pdata, CD_PROP_FLOAT3, HAIR_ATTR_POSITION); - hair->radius = (float *)CustomData_get_layer_named( - &hair->pdata, CD_PROP_FLOAT, HAIR_ATTR_RADIUS); - hair->curves = (HairCurve *)CustomData_get_layer(&hair->cdata, CD_HAIRCURVE); - hair->mapping = (HairMaping *)CustomData_get_layer(&hair->cdata, CD_HAIRMAPPING); -} - -bool BKE_hair_customdata_required(Hair *UNUSED(hair), CustomDataLayer *layer) -{ - return layer->type == CD_PROP_FLOAT3 && STREQ(layer->name, HAIR_ATTR_POSITION); -} - -/* Dependency Graph */ - -Hair *BKE_hair_new_for_eval(const Hair *hair_src, int totpoint, int totcurve) -{ - Hair *hair_dst = static_cast<Hair *>(BKE_id_new_nomain(ID_HA, nullptr)); - - STRNCPY(hair_dst->id.name, hair_src->id.name); - hair_dst->mat = static_cast<Material **>(MEM_dupallocN(hair_src->mat)); - hair_dst->totcol = hair_src->totcol; - - hair_dst->totpoint = totpoint; - hair_dst->totcurve = totcurve; - CustomData_copy(&hair_src->pdata, &hair_dst->pdata, CD_MASK_ALL, CD_CALLOC, totpoint); - CustomData_copy(&hair_src->cdata, &hair_dst->cdata, CD_MASK_ALL, CD_CALLOC, totcurve); - BKE_hair_update_customdata_pointers(hair_dst); - - return hair_dst; -} - -Hair *BKE_hair_copy_for_eval(Hair *hair_src, bool reference) -{ - int flags = LIB_ID_COPY_LOCALIZE; - - if (reference) { - flags |= LIB_ID_COPY_CD_REFERENCE; - } - - Hair *result = (Hair *)BKE_id_copy_ex(nullptr, &hair_src->id, nullptr, flags); - return result; -} - -static Hair *hair_evaluate_modifiers(struct Depsgraph *depsgraph, - struct Scene *scene, - Object *object, - Hair *hair_input) -{ - Hair *hair = hair_input; - - /* Modifier evaluation modes. */ - const bool use_render = (DEG_get_mode(depsgraph) == DAG_EVAL_RENDER); - const int required_mode = use_render ? eModifierMode_Render : eModifierMode_Realtime; - ModifierApplyFlag apply_flag = use_render ? MOD_APPLY_RENDER : MOD_APPLY_USECACHE; - const ModifierEvalContext mectx = {depsgraph, object, apply_flag}; - - /* Get effective list of modifiers to execute. Some effects like shape keys - * are added as virtual modifiers before the user created modifiers. */ - VirtualModifierData virtualModifierData; - ModifierData *md = BKE_modifiers_get_virtual_modifierlist(object, &virtualModifierData); - - /* Evaluate modifiers. */ - for (; md; md = md->next) { - const ModifierTypeInfo *mti = BKE_modifier_get_info(static_cast<ModifierType>(md->type)); - - if (!BKE_modifier_is_enabled(scene, md, required_mode)) { - continue; - } - - if ((mti->type == eModifierTypeType_OnlyDeform) && - (mti->flags & eModifierTypeFlag_AcceptsVertexCosOnly)) { - /* Ensure we are not modifying the input. */ - if (hair == hair_input) { - hair = BKE_hair_copy_for_eval(hair, true); - } - - /* Ensure we are not overwriting referenced data. */ - CustomData_duplicate_referenced_layer_named( - &hair->pdata, CD_PROP_FLOAT3, HAIR_ATTR_POSITION, hair->totpoint); - BKE_hair_update_customdata_pointers(hair); - - /* Created deformed coordinates array on demand. */ - mti->deformVerts(md, &mectx, nullptr, hair->co, hair->totpoint); - } - else if (mti->modifyHair) { - /* Ensure we are not modifying the input. */ - if (hair == hair_input) { - hair = BKE_hair_copy_for_eval(hair, true); - } - - Hair *hair_next = mti->modifyHair(md, &mectx, hair); - - if (hair_next && hair_next != hair) { - /* If the modifier returned a new hair, release the old one. */ - if (hair != hair_input) { - BKE_id_free(nullptr, hair); - } - hair = hair_next; - } - } - } - - return hair; -} - -void BKE_hair_data_update(struct Depsgraph *depsgraph, struct Scene *scene, Object *object) -{ - /* Free any evaluated data and restore original data. */ - BKE_object_free_derived_caches(object); - - /* Evaluate modifiers. */ - Hair *hair = static_cast<Hair *>(object->data); - Hair *hair_eval = hair_evaluate_modifiers(depsgraph, scene, object, hair); - - /* Assign evaluated object. */ - const bool is_owned = (hair != hair_eval); - BKE_object_eval_assign_data(object, &hair_eval->id, is_owned); -} - -/* Draw Cache */ - -void (*BKE_hair_batch_cache_dirty_tag_cb)(Hair *hair, int mode) = nullptr; -void (*BKE_hair_batch_cache_free_cb)(Hair *hair) = nullptr; - -void BKE_hair_batch_cache_dirty_tag(Hair *hair, int mode) -{ - if (hair->batch_cache) { - BKE_hair_batch_cache_dirty_tag_cb(hair, mode); - } -} - -void BKE_hair_batch_cache_free(Hair *hair) -{ - if (hair->batch_cache) { - BKE_hair_batch_cache_free_cb(hair); - } -} diff --git a/source/blender/blenkernel/intern/idtype.c b/source/blender/blenkernel/intern/idtype.c index e6fd6c14d42..e9c8df76351 100644 --- a/source/blender/blenkernel/intern/idtype.c +++ b/source/blender/blenkernel/intern/idtype.c @@ -110,7 +110,7 @@ static void id_type_init(void) INIT_TYPE(ID_CF); INIT_TYPE(ID_WS); INIT_TYPE(ID_LP); - INIT_TYPE(ID_HA); + INIT_TYPE(ID_CV); INIT_TYPE(ID_PT); INIT_TYPE(ID_VO); INIT_TYPE(ID_SIM); @@ -237,7 +237,7 @@ uint64_t BKE_idtype_idcode_to_idfilter(const short idcode) CASE_IDFILTER(CU); CASE_IDFILTER(GD); CASE_IDFILTER(GR); - CASE_IDFILTER(HA); + CASE_IDFILTER(CV); CASE_IDFILTER(IM); CASE_IDFILTER(LA); CASE_IDFILTER(LS); @@ -286,7 +286,7 @@ short BKE_idtype_idcode_from_idfilter(const uint64_t idfilter) CASE_IDFILTER(CU); CASE_IDFILTER(GD); CASE_IDFILTER(GR); - CASE_IDFILTER(HA); + CASE_IDFILTER(CV); CASE_IDFILTER(IM); CASE_IDFILTER(LA); CASE_IDFILTER(LS); @@ -334,7 +334,7 @@ int BKE_idtype_idcode_to_index(const short idcode) CASE_IDINDEX(CU); CASE_IDINDEX(GD); CASE_IDINDEX(GR); - CASE_IDINDEX(HA); + CASE_IDINDEX(CV); CASE_IDINDEX(IM); CASE_IDINDEX(IP); CASE_IDINDEX(KE); @@ -393,7 +393,7 @@ short BKE_idtype_idcode_from_index(const int index) CASE_IDCODE(CU); CASE_IDCODE(GD); CASE_IDCODE(GR); - CASE_IDCODE(HA); + CASE_IDCODE(CV); CASE_IDCODE(IM); CASE_IDCODE(IP); CASE_IDCODE(KE); diff --git a/source/blender/blenkernel/intern/image.c b/source/blender/blenkernel/intern/image.c index 6947fcc16a3..130691dc7ff 100644 --- a/source/blender/blenkernel/intern/image.c +++ b/source/blender/blenkernel/intern/image.c @@ -134,6 +134,22 @@ static void image_runtime_reset_on_copy(struct Image *image) { image->runtime.cache_mutex = MEM_mallocN(sizeof(ThreadMutex), "image runtime cache_mutex"); BLI_mutex_init(image->runtime.cache_mutex); + + image->runtime.partial_update_register = NULL; + image->runtime.partial_update_user = NULL; +} + +static void image_runtime_free_data(struct Image *image) +{ + BLI_mutex_end(image->runtime.cache_mutex); + MEM_freeN(image->runtime.cache_mutex); + image->runtime.cache_mutex = NULL; + + if (image->runtime.partial_update_user != NULL) { + BKE_image_partial_update_free(image->runtime.partial_update_user); + image->runtime.partial_update_user = NULL; + } + BKE_image_partial_update_register_free(image); } static void image_init_data(ID *id) @@ -213,10 +229,8 @@ static void image_free_data(ID *id) BKE_previewimg_free(&image->preview); BLI_freelistN(&image->tiles); - BLI_freelistN(&image->gpu_refresh_areas); - BLI_mutex_end(image->runtime.cache_mutex); - MEM_freeN(image->runtime.cache_mutex); + image_runtime_free_data(image); } static void image_foreach_cache(ID *id, @@ -321,7 +335,8 @@ static void image_blend_write(BlendWriter *writer, ID *id, const void *id_addres ima->cache = NULL; ima->gpuflag = 0; BLI_listbase_clear(&ima->anims); - BLI_listbase_clear(&ima->gpu_refresh_areas); + ima->runtime.partial_update_register = NULL; + ima->runtime.partial_update_user = NULL; for (int i = 0; i < 3; i++) { for (int j = 0; j < 2; j++) { for (int resolution = 0; resolution < IMA_TEXTURE_RESOLUTION_LEN; resolution++) { @@ -401,7 +416,6 @@ static void image_blend_read_data(BlendDataReader *reader, ID *id) ima->lastused = 0; ima->gpuflag = 0; - BLI_listbase_clear(&ima->gpu_refresh_areas); image_runtime_reset(ima); } diff --git a/source/blender/blenkernel/intern/image_gpu.cc b/source/blender/blenkernel/intern/image_gpu.cc index c82de02e52a..eaee1fd2c30 100644 --- a/source/blender/blenkernel/intern/image_gpu.cc +++ b/source/blender/blenkernel/intern/image_gpu.cc @@ -38,6 +38,7 @@ #include "BKE_global.h" #include "BKE_image.h" +#include "BKE_image_partial_update.hh" #include "BKE_main.h" #include "GPU_capabilities.h" @@ -46,6 +47,10 @@ #include "PIL_time.h" +using namespace blender::bke::image::partial_update; + +extern "C" { + /* Prototypes. */ static void gpu_free_unused_buffers(); static void image_free_gpu(Image *ima, const bool immediate); @@ -53,14 +58,6 @@ static void image_free_gpu_limited_scale(Image *ima); static void image_update_gputexture_ex( Image *ima, ImageTile *tile, ImBuf *ibuf, int x, int y, int w, int h); -/* Internal structs. */ -#define IMA_PARTIAL_REFRESH_TILE_SIZE 256 -struct ImagePartialRefresh { - struct ImagePartialRefresh *next, *prev; - int tile_x; - int tile_y; -}; - bool BKE_image_has_gpu_texture_premultiplied_alpha(Image *image, ImBuf *ibuf) { if (image) { @@ -337,6 +334,48 @@ static void image_update_reusable_textures(Image *ima, } } +static void image_gpu_texture_partial_update_changes_available( + Image *image, PartialUpdateChecker<ImageTileData>::CollectResult &changes) +{ + while (changes.get_next_change() == ePartialUpdateIterResult::ChangeAvailable) { + const int tile_offset_x = changes.changed_region.region.xmin; + const int tile_offset_y = changes.changed_region.region.ymin; + const int tile_width = min_ii(changes.tile_data.tile_buffer->x, + BLI_rcti_size_x(&changes.changed_region.region)); + const int tile_height = min_ii(changes.tile_data.tile_buffer->y, + BLI_rcti_size_y(&changes.changed_region.region)); + image_update_gputexture_ex(image, + changes.tile_data.tile, + changes.tile_data.tile_buffer, + tile_offset_x, + tile_offset_y, + tile_width, + tile_height); + } +} + +static void image_gpu_texture_try_partial_update(Image *image, ImageUser *iuser) +{ + PartialUpdateChecker<ImageTileData> checker(image, iuser, image->runtime.partial_update_user); + PartialUpdateChecker<ImageTileData>::CollectResult changes = checker.collect_changes(); + switch (changes.get_result_code()) { + case ePartialUpdateCollectResult::FullUpdateNeeded: { + image_free_gpu(image, true); + break; + } + + case ePartialUpdateCollectResult::PartialChangesDetected: { + image_gpu_texture_partial_update_changes_available(image, changes); + break; + } + + case ePartialUpdateCollectResult::NoChangesDetected: { + /* GPUTextures are up to date. */ + break; + } + } +} + static GPUTexture *image_get_gpu_texture(Image *ima, ImageUser *iuser, ImBuf *ibuf, @@ -370,31 +409,20 @@ static GPUTexture *image_get_gpu_texture(Image *ima, } #undef GPU_FLAGS_TO_CHECK - /* Check if image has been updated and tagged to be updated (full or partial). */ - ImageTile *tile = BKE_image_get_tile(ima, 0); - if (((ima->gpuflag & IMA_GPU_REFRESH) != 0) || - ((ibuf == nullptr || tile == nullptr) && ((ima->gpuflag & IMA_GPU_PARTIAL_REFRESH) != 0))) { - image_free_gpu(ima, true); - BLI_freelistN(&ima->gpu_refresh_areas); - ima->gpuflag &= ~(IMA_GPU_REFRESH | IMA_GPU_PARTIAL_REFRESH); - } - else if (ima->gpuflag & IMA_GPU_PARTIAL_REFRESH) { - BLI_assert(ibuf); - BLI_assert(tile); - ImagePartialRefresh *refresh_area; - while (( - refresh_area = static_cast<ImagePartialRefresh *>(BLI_pophead(&ima->gpu_refresh_areas)))) { - const int tile_offset_x = refresh_area->tile_x * IMA_PARTIAL_REFRESH_TILE_SIZE; - const int tile_offset_y = refresh_area->tile_y * IMA_PARTIAL_REFRESH_TILE_SIZE; - const int tile_width = MIN2(IMA_PARTIAL_REFRESH_TILE_SIZE, ibuf->x - tile_offset_x); - const int tile_height = MIN2(IMA_PARTIAL_REFRESH_TILE_SIZE, ibuf->y - tile_offset_y); - image_update_gputexture_ex( - ima, tile, ibuf, tile_offset_x, tile_offset_y, tile_width, tile_height); - MEM_freeN(refresh_area); - } - ima->gpuflag &= ~IMA_GPU_PARTIAL_REFRESH; + /* TODO(jbakker): We should replace the IMA_GPU_REFRESH flag with a call to + * BKE_image-partial_update_mark_full_update. Although the flag is quicker it leads to double + * administration. */ + if ((ima->gpuflag & IMA_GPU_REFRESH) != 0) { + BKE_image_partial_update_mark_full_update(ima); + ima->gpuflag &= ~IMA_GPU_REFRESH; + } + + if (ima->runtime.partial_update_user == nullptr) { + ima->runtime.partial_update_user = BKE_image_partial_update_create(ima); } + image_gpu_texture_try_partial_update(ima, iuser); + /* Tag as in active use for garbage collector. */ BKE_image_tag_time(ima); @@ -417,6 +445,7 @@ static GPUTexture *image_get_gpu_texture(Image *ima, /* Check if we have a valid image. If not, we return a dummy * texture with zero bind-code so we don't keep trying. */ + ImageTile *tile = BKE_image_get_tile(ima, 0); if (tile == nullptr) { *tex = image_gpu_texture_error_create(textarget); return *tex; @@ -427,8 +456,7 @@ static GPUTexture *image_get_gpu_texture(Image *ima, if (ibuf_intern == nullptr) { ibuf_intern = BKE_image_acquire_ibuf(ima, iuser, nullptr); if (ibuf_intern == nullptr) { - *tex = image_gpu_texture_error_create(textarget); - return *tex; + return image_gpu_texture_error_create(textarget); } } @@ -477,15 +505,14 @@ static GPUTexture *image_get_gpu_texture(Image *ima, break; } - /* if `ibuf` was given, we don't own the `ibuf_intern` */ - if (ibuf == nullptr) { - BKE_image_release_ibuf(ima, ibuf_intern, nullptr); - } - if (*tex) { GPU_texture_orig_size_set(*tex, ibuf_intern->x, ibuf_intern->y); } + if (ibuf != ibuf_intern) { + BKE_image_release_ibuf(ima, ibuf_intern, nullptr); + } + return *tex; } @@ -903,87 +930,29 @@ static void image_update_gputexture_ex( void BKE_image_update_gputexture(Image *ima, ImageUser *iuser, int x, int y, int w, int h) { + ImageTile *image_tile = BKE_image_get_tile_from_iuser(ima, iuser); ImBuf *ibuf = BKE_image_acquire_ibuf(ima, iuser, nullptr); - ImageTile *tile = BKE_image_get_tile_from_iuser(ima, iuser); - - if ((ibuf == nullptr) || (w == 0) || (h == 0)) { - /* Full reload of texture. */ - BKE_image_free_gputextures(ima); - } - image_update_gputexture_ex(ima, tile, ibuf, x, y, w, h); + BKE_image_update_gputexture_delayed(ima, image_tile, ibuf, x, y, w, h); BKE_image_release_ibuf(ima, ibuf, nullptr); } -void BKE_image_update_gputexture_delayed( - struct Image *ima, struct ImBuf *ibuf, int x, int y, int w, int h) +void BKE_image_update_gputexture_delayed(struct Image *ima, + struct ImageTile *image_tile, + struct ImBuf *ibuf, + int x, + int y, + int w, + int h) { /* Check for full refresh. */ - if (ibuf && x == 0 && y == 0 && w == ibuf->x && h == ibuf->y) { - ima->gpuflag |= IMA_GPU_REFRESH; - } - /* Check if we can promote partial refresh to a full refresh. */ - if ((ima->gpuflag & (IMA_GPU_REFRESH | IMA_GPU_PARTIAL_REFRESH)) == - (IMA_GPU_REFRESH | IMA_GPU_PARTIAL_REFRESH)) { - ima->gpuflag &= ~IMA_GPU_PARTIAL_REFRESH; - BLI_freelistN(&ima->gpu_refresh_areas); - } - /* Image is already marked for complete refresh. */ - if (ima->gpuflag & IMA_GPU_REFRESH) { - return; - } - - /* Schedule the tiles that covers the requested area. */ - const int start_tile_x = x / IMA_PARTIAL_REFRESH_TILE_SIZE; - const int start_tile_y = y / IMA_PARTIAL_REFRESH_TILE_SIZE; - const int end_tile_x = (x + w) / IMA_PARTIAL_REFRESH_TILE_SIZE; - const int end_tile_y = (y + h) / IMA_PARTIAL_REFRESH_TILE_SIZE; - const int num_tiles_x = (end_tile_x + 1) - (start_tile_x); - const int num_tiles_y = (end_tile_y + 1) - (start_tile_y); - const int num_tiles = num_tiles_x * num_tiles_y; - const bool allocate_on_heap = BLI_BITMAP_SIZE(num_tiles) > 16; - BLI_bitmap *requested_tiles = nullptr; - if (allocate_on_heap) { - requested_tiles = BLI_BITMAP_NEW(num_tiles, __func__); + if (ibuf != nullptr && ima->source != IMA_SRC_TILED && x == 0 && y == 0 && w == ibuf->x && + h == ibuf->y) { + BKE_image_partial_update_mark_full_update(ima); } else { - requested_tiles = BLI_BITMAP_NEW_ALLOCA(num_tiles); - } - - /* Mark the tiles that have already been requested. They don't need to be requested again. */ - int num_tiles_not_scheduled = num_tiles; - LISTBASE_FOREACH (ImagePartialRefresh *, area, &ima->gpu_refresh_areas) { - if (area->tile_x < start_tile_x || area->tile_x > end_tile_x || area->tile_y < start_tile_y || - area->tile_y > end_tile_y) { - continue; - } - int requested_tile_index = (area->tile_x - start_tile_x) + - (area->tile_y - start_tile_y) * num_tiles_x; - BLI_BITMAP_ENABLE(requested_tiles, requested_tile_index); - num_tiles_not_scheduled--; - if (num_tiles_not_scheduled == 0) { - break; - } - } - - /* Schedule the tiles that aren't requested yet. */ - if (num_tiles_not_scheduled) { - int tile_index = 0; - for (int tile_y = start_tile_y; tile_y <= end_tile_y; tile_y++) { - for (int tile_x = start_tile_x; tile_x <= end_tile_x; tile_x++) { - if (!BLI_BITMAP_TEST_BOOL(requested_tiles, tile_index)) { - ImagePartialRefresh *area = static_cast<ImagePartialRefresh *>( - MEM_mallocN(sizeof(ImagePartialRefresh), __func__)); - area->tile_x = tile_x; - area->tile_y = tile_y; - BLI_addtail(&ima->gpu_refresh_areas, area); - } - tile_index++; - } - } - ima->gpuflag |= IMA_GPU_PARTIAL_REFRESH; - } - if (allocate_on_heap) { - MEM_freeN(requested_tiles); + rcti dirty_region; + BLI_rcti_init(&dirty_region, x, x + w, y, y + h); + BKE_image_partial_update_mark_region(ima, image_tile, ibuf, &dirty_region); } } @@ -1016,3 +985,4 @@ void BKE_image_paint_set_mipmap(Main *bmain, bool mipmap) } /** \} */ +} diff --git a/source/blender/blenkernel/intern/image_partial_update.cc b/source/blender/blenkernel/intern/image_partial_update.cc new file mode 100644 index 00000000000..876e5276e5a --- /dev/null +++ b/source/blender/blenkernel/intern/image_partial_update.cc @@ -0,0 +1,599 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Copyright 2021, Blender Foundation. + */ +/** + * \file image_gpu_partial_update.cc + * \ingroup bke + * + * To reduce the overhead of image processing this file contains a mechanism to detect areas of the + * image that are changed. These areas are organized in chunks. Changes that happen over time are + * organized in changesets. + * + * A common use case is to update #GPUTexture for drawing where only that part is uploaded that + * only changed. + * + * Usage: + * + * ``` + * Image *image = ...; + * ImBuf *image_buffer = ...; + * + * // Partial_update_user should be kept for the whole session where the changes needs to be + * // tracked. Keep this instance alive as long as you need to track image changes. + * + * PartialUpdateUser *partial_update_user = BKE_image_partial_update_create(image); + * + * ... + * + * switch (BKE_image_partial_update_collect_changes(image, image_buffer)) + * { + * case ePartialUpdateCollectResult::FullUpdateNeeded: + * // Unable to do partial updates. Perform a full update. + * break; + * case ePartialUpdateCollectResult::PartialChangesDetected: + * PartialUpdateRegion change; + * while (BKE_image_partial_update_get_next_change(partial_update_user, &change) == + * ePartialUpdateIterResult::ChangeAvailable){ + * // Do something with the change. + * } + * case ePartialUpdateCollectResult::NoChangesDetected: + * break; + * } + * + * ... + * + * // Free partial_update_user. + * BKE_image_partial_update_free(partial_update_user); + * + * ``` + */ + +#include <optional> + +#include "BKE_image.h" +#include "BKE_image_partial_update.hh" + +#include "DNA_image_types.h" + +#include "IMB_imbuf.h" +#include "IMB_imbuf_types.h" + +#include "BLI_vector.hh" + +namespace blender::bke::image::partial_update { + +/** \brief Size of chunks to track changes. */ +constexpr int CHUNK_SIZE = 256; + +/** + * \brief Max number of changesets to keep in history. + * + * A higher number would need more memory and processing + * to calculate a changeset, but would lead to do partial updates for requests that don't happen + * every frame. + * + * A to small number would lead to more full updates when changes couldn't be reconstructed from + * the available history. + */ +constexpr int MAX_HISTORY_LEN = 4; + +/** + * \brief get the chunk number for the give pixel coordinate. + * + * As chunks are squares the this member can be used for both x and y axis. + */ +static int chunk_number_for_pixel(int pixel_offset) +{ + int chunk_offset = pixel_offset / CHUNK_SIZE; + if (pixel_offset < 0) { + chunk_offset -= 1; + } + return chunk_offset; +} + +struct PartialUpdateUserImpl; +struct PartialUpdateRegisterImpl; + +/** + * Wrap PartialUpdateUserImpl to its C-struct (PartialUpdateUser). + */ +static struct PartialUpdateUser *wrap(PartialUpdateUserImpl *user) +{ + return static_cast<struct PartialUpdateUser *>(static_cast<void *>(user)); +} + +/** + * Unwrap the PartialUpdateUser C-struct to its CPP counterpart (PartialUpdateUserImpl). + */ +static PartialUpdateUserImpl *unwrap(struct PartialUpdateUser *user) +{ + return static_cast<PartialUpdateUserImpl *>(static_cast<void *>(user)); +} + +/** + * Wrap PartialUpdateRegisterImpl to its C-struct (PartialUpdateRegister). + */ +static struct PartialUpdateRegister *wrap(PartialUpdateRegisterImpl *partial_update_register) +{ + return static_cast<struct PartialUpdateRegister *>(static_cast<void *>(partial_update_register)); +} + +/** + * Unwrap the PartialUpdateRegister C-struct to its CPP counterpart (PartialUpdateRegisterImpl). + */ +static PartialUpdateRegisterImpl *unwrap(struct PartialUpdateRegister *partial_update_register) +{ + return static_cast<PartialUpdateRegisterImpl *>(static_cast<void *>(partial_update_register)); +} + +using TileNumber = int32_t; +using ChangesetID = int64_t; +constexpr ChangesetID UnknownChangesetID = -1; + +struct PartialUpdateUserImpl { + /** \brief last changeset id that was seen by this user. */ + ChangesetID last_changeset_id = UnknownChangesetID; + + /** \brief regions that have been updated. */ + Vector<PartialUpdateRegion> updated_regions; + +#ifdef NDEBUG + /** \brief reference to image to validate correct API usage. */ + const void *debug_image_; +#endif + + /** + * \brief Clear the list of updated regions. + * + * Updated regions should be cleared at the start of #BKE_image_partial_update_collect_changes so + * the + */ + void clear_updated_regions() + { + updated_regions.clear(); + } +}; + +/** + * \brief Dirty chunks of an ImageTile. + * + * Internally dirty tiles are grouped together in change sets to make sure that the correct + * answer can be built for different users reducing the amount of merges. + */ +struct TileChangeset { + private: + /** \brief Dirty flag for each chunk. */ + std::vector<bool> chunk_dirty_flags_; + /** \brief are there dirty/ */ + bool has_dirty_chunks_ = false; + + public: + /** \brief Width of the tile in pixels. */ + int tile_width; + /** \brief Height of the tile in pixels. */ + int tile_height; + /** \brief Number of chunks along the x-axis. */ + int chunk_x_len; + /** \brief Number of chunks along the y-axis. */ + int chunk_y_len; + + TileNumber tile_number; + + void clear() + { + init_chunks(chunk_x_len, chunk_y_len); + } + + /** + * \brief Update the resolution of the tile. + * + * \returns true: resolution has been updated. + * false: resolution was unchanged. + */ + bool update_resolution(const ImBuf *image_buffer) + { + if (tile_width == image_buffer->x && tile_height == image_buffer->y) { + return false; + } + + tile_width = image_buffer->x; + tile_height = image_buffer->y; + + int chunk_x_len = tile_width / CHUNK_SIZE; + int chunk_y_len = tile_height / CHUNK_SIZE; + init_chunks(chunk_x_len, chunk_y_len); + return true; + } + + void mark_region(const rcti *updated_region) + { + int start_x_chunk = chunk_number_for_pixel(updated_region->xmin); + int end_x_chunk = chunk_number_for_pixel(updated_region->xmax - 1); + int start_y_chunk = chunk_number_for_pixel(updated_region->ymin); + int end_y_chunk = chunk_number_for_pixel(updated_region->ymax - 1); + + /* Clamp tiles to tiles in image. */ + start_x_chunk = max_ii(0, start_x_chunk); + start_y_chunk = max_ii(0, start_y_chunk); + end_x_chunk = min_ii(chunk_x_len - 1, end_x_chunk); + end_y_chunk = min_ii(chunk_y_len - 1, end_y_chunk); + + /* Early exit when no tiles need to be updated. */ + if (start_x_chunk >= chunk_x_len) { + return; + } + if (start_y_chunk >= chunk_y_len) { + return; + } + if (end_x_chunk < 0) { + return; + } + if (end_y_chunk < 0) { + return; + } + + mark_chunks_dirty(start_x_chunk, start_y_chunk, end_x_chunk, end_y_chunk); + } + + void mark_chunks_dirty(int start_x_chunk, int start_y_chunk, int end_x_chunk, int end_y_chunk) + { + for (int chunk_y = start_y_chunk; chunk_y <= end_y_chunk; chunk_y++) { + for (int chunk_x = start_x_chunk; chunk_x <= end_x_chunk; chunk_x++) { + int chunk_index = chunk_y * chunk_x_len + chunk_x; + chunk_dirty_flags_[chunk_index] = true; + } + } + has_dirty_chunks_ = true; + } + + bool has_dirty_chunks() const + { + return has_dirty_chunks_; + } + + void init_chunks(int chunk_x_len_, int chunk_y_len_) + { + chunk_x_len = chunk_x_len_; + chunk_y_len = chunk_y_len_; + const int chunk_len = chunk_x_len * chunk_y_len; + const int previous_chunk_len = chunk_dirty_flags_.size(); + + chunk_dirty_flags_.resize(chunk_len); + /* Fast exit. When the changeset was already empty no need to + * re-initialize the chunk_validity. */ + if (!has_dirty_chunks()) { + return; + } + for (int index = 0; index < min_ii(chunk_len, previous_chunk_len); index++) { + chunk_dirty_flags_[index] = false; + } + has_dirty_chunks_ = false; + } + + /** \brief Merge the given changeset into the receiver. */ + void merge(const TileChangeset &other) + { + BLI_assert(chunk_x_len == other.chunk_x_len); + BLI_assert(chunk_y_len == other.chunk_y_len); + const int chunk_len = chunk_x_len * chunk_y_len; + + for (int chunk_index = 0; chunk_index < chunk_len; chunk_index++) { + chunk_dirty_flags_[chunk_index] = chunk_dirty_flags_[chunk_index] | + other.chunk_dirty_flags_[chunk_index]; + } + has_dirty_chunks_ |= other.has_dirty_chunks_; + } + + /** \brief has a chunk changed inside this changeset. */ + bool is_chunk_dirty(int chunk_x, int chunk_y) const + { + const int chunk_index = chunk_y * chunk_x_len + chunk_x; + return chunk_dirty_flags_[chunk_index]; + } +}; + +/** \brief Changeset keeping track of changes for an image */ +struct Changeset { + private: + Vector<TileChangeset> tiles; + + public: + /** \brief Keep track if any of the tiles have dirty chunks. */ + bool has_dirty_chunks; + + /** + * \brief Retrieve the TileChangeset for the given ImageTile. + * + * When the TileChangeset isn't found, it will be added. + */ + TileChangeset &operator[](const ImageTile *image_tile) + { + for (TileChangeset &tile_changeset : tiles) { + if (tile_changeset.tile_number == image_tile->tile_number) { + return tile_changeset; + } + } + + TileChangeset tile_changeset; + tile_changeset.tile_number = image_tile->tile_number; + tiles.append_as(tile_changeset); + + return tiles.last(); + } + + /** \brief Does this changeset contain data for the given tile. */ + bool has_tile(const ImageTile *image_tile) + { + for (TileChangeset &tile_changeset : tiles) { + if (tile_changeset.tile_number == image_tile->tile_number) { + return true; + } + } + return false; + } + + /** \brief Clear this changeset. */ + void clear() + { + tiles.clear(); + has_dirty_chunks = false; + } +}; + +/** + * \brief Partial update changes stored inside the image runtime. + * + * The PartialUpdateRegisterImpl will keep track of changes over time. Changes are groups inside + * TileChangesets. + */ +struct PartialUpdateRegisterImpl { + /** \brief changeset id of the first changeset kept in #history. */ + ChangesetID first_changeset_id; + /** \brief changeset id of the top changeset kept in #history. */ + ChangesetID last_changeset_id; + + /** \brief history of changesets. */ + Vector<Changeset> history; + /** \brief The current changeset. New changes will be added to this changeset. */ + Changeset current_changeset; + + void update_resolution(const ImageTile *image_tile, const ImBuf *image_buffer) + { + TileChangeset &tile_changeset = current_changeset[image_tile]; + const bool has_dirty_chunks = tile_changeset.has_dirty_chunks(); + const bool resolution_changed = tile_changeset.update_resolution(image_buffer); + + if (has_dirty_chunks && resolution_changed && !history.is_empty()) { + mark_full_update(); + } + } + + void mark_full_update() + { + history.clear(); + last_changeset_id++; + current_changeset.clear(); + first_changeset_id = last_changeset_id; + } + + void mark_region(const ImageTile *image_tile, const rcti *updated_region) + { + TileChangeset &tile_changeset = current_changeset[image_tile]; + tile_changeset.mark_region(updated_region); + current_changeset.has_dirty_chunks |= tile_changeset.has_dirty_chunks(); + } + + void ensure_empty_changeset() + { + if (!current_changeset.has_dirty_chunks) { + /* No need to create a new changeset when previous changeset does not contain any dirty + * tiles. */ + return; + } + commit_current_changeset(); + limit_history(); + } + + /** \brief Move the current changeset to the history and resets the current changeset. */ + void commit_current_changeset() + { + history.append_as(std::move(current_changeset)); + current_changeset.clear(); + last_changeset_id++; + } + + /** \brief Limit the number of items in the changeset. */ + void limit_history() + { + const int num_items_to_remove = max_ii(history.size() - MAX_HISTORY_LEN, 0); + if (num_items_to_remove == 0) { + return; + } + history.remove(0, num_items_to_remove); + first_changeset_id += num_items_to_remove; + } + + /** + * /brief Check if data is available to construct the update tiles for the given + * changeset_id. + * + * The update tiles can be created when changeset id is between + */ + bool can_construct(ChangesetID changeset_id) + { + return changeset_id >= first_changeset_id; + } + + /** + * \brief collect all historic changes since a given changeset. + */ + std::optional<TileChangeset> changed_tile_chunks_since(const ImageTile *image_tile, + const ChangesetID from_changeset) + { + std::optional<TileChangeset> changed_chunks = std::nullopt; + for (int index = from_changeset - first_changeset_id; index < history.size(); index++) { + if (!history[index].has_tile(image_tile)) { + continue; + } + + TileChangeset &tile_changeset = history[index][image_tile]; + if (!changed_chunks.has_value()) { + changed_chunks = std::make_optional<TileChangeset>(); + changed_chunks->init_chunks(tile_changeset.chunk_x_len, tile_changeset.chunk_y_len); + changed_chunks->tile_number = image_tile->tile_number; + } + + changed_chunks->merge(tile_changeset); + } + return changed_chunks; + } +}; + +static PartialUpdateRegister *image_partial_update_register_ensure(Image *image) +{ + if (image->runtime.partial_update_register == nullptr) { + PartialUpdateRegisterImpl *partial_update_register = MEM_new<PartialUpdateRegisterImpl>( + __func__); + image->runtime.partial_update_register = wrap(partial_update_register); + } + return image->runtime.partial_update_register; +} + +ePartialUpdateCollectResult BKE_image_partial_update_collect_changes(Image *image, + PartialUpdateUser *user) +{ + PartialUpdateUserImpl *user_impl = unwrap(user); +#ifdef NDEBUG + BLI_assert(image == user_impl->debug_image_); +#endif + + user_impl->clear_updated_regions(); + + PartialUpdateRegisterImpl *partial_updater = unwrap(image_partial_update_register_ensure(image)); + partial_updater->ensure_empty_changeset(); + + if (!partial_updater->can_construct(user_impl->last_changeset_id)) { + user_impl->last_changeset_id = partial_updater->last_changeset_id; + return ePartialUpdateCollectResult::FullUpdateNeeded; + } + + /* Check if there are changes since last invocation for the user. */ + if (user_impl->last_changeset_id == partial_updater->last_changeset_id) { + return ePartialUpdateCollectResult::NoChangesDetected; + } + + /* Collect changed tiles. */ + LISTBASE_FOREACH (ImageTile *, tile, &image->tiles) { + std::optional<TileChangeset> changed_chunks = partial_updater->changed_tile_chunks_since( + tile, user_impl->last_changeset_id); + /* Check if chunks of this tile are dirty. */ + if (!changed_chunks.has_value()) { + continue; + } + if (!changed_chunks->has_dirty_chunks()) { + continue; + } + + /* Convert tiles in the changeset to rectangles that are dirty. */ + for (int chunk_y = 0; chunk_y < changed_chunks->chunk_y_len; chunk_y++) { + for (int chunk_x = 0; chunk_x < changed_chunks->chunk_x_len; chunk_x++) { + if (!changed_chunks->is_chunk_dirty(chunk_x, chunk_y)) { + continue; + } + + PartialUpdateRegion region; + region.tile_number = tile->tile_number; + BLI_rcti_init(®ion.region, + chunk_x * CHUNK_SIZE, + (chunk_x + 1) * CHUNK_SIZE, + chunk_y * CHUNK_SIZE, + (chunk_y + 1) * CHUNK_SIZE); + user_impl->updated_regions.append_as(region); + } + } + } + + user_impl->last_changeset_id = partial_updater->last_changeset_id; + return ePartialUpdateCollectResult::PartialChangesDetected; +} + +ePartialUpdateIterResult BKE_image_partial_update_get_next_change(PartialUpdateUser *user, + PartialUpdateRegion *r_region) +{ + PartialUpdateUserImpl *user_impl = unwrap(user); + if (user_impl->updated_regions.is_empty()) { + return ePartialUpdateIterResult::Finished; + } + PartialUpdateRegion region = user_impl->updated_regions.pop_last(); + *r_region = region; + return ePartialUpdateIterResult::ChangeAvailable; +} + +} // namespace blender::bke::image::partial_update + +extern "C" { + +using namespace blender::bke::image::partial_update; + +// TODO(jbakker): cleanup parameter. +struct PartialUpdateUser *BKE_image_partial_update_create(const struct Image *image) +{ + PartialUpdateUserImpl *user_impl = MEM_new<PartialUpdateUserImpl>(__func__); + +#ifdef NDEBUG + user_impl->debug_image_ = image; +#else + UNUSED_VARS(image); +#endif + + return wrap(user_impl); +} + +void BKE_image_partial_update_free(PartialUpdateUser *user) +{ + PartialUpdateUserImpl *user_impl = unwrap(user); + MEM_delete<PartialUpdateUserImpl>(user_impl); +} + +/* --- Image side --- */ + +void BKE_image_partial_update_register_free(Image *image) +{ + PartialUpdateRegisterImpl *partial_update_register = unwrap( + image->runtime.partial_update_register); + if (partial_update_register) { + MEM_delete<PartialUpdateRegisterImpl>(partial_update_register); + } + image->runtime.partial_update_register = nullptr; +} + +void BKE_image_partial_update_mark_region(Image *image, + const ImageTile *image_tile, + const ImBuf *image_buffer, + const rcti *updated_region) +{ + PartialUpdateRegisterImpl *partial_updater = unwrap(image_partial_update_register_ensure(image)); + partial_updater->update_resolution(image_tile, image_buffer); + partial_updater->mark_region(image_tile, updated_region); +} + +void BKE_image_partial_update_mark_full_update(Image *image) +{ + PartialUpdateRegisterImpl *partial_updater = unwrap(image_partial_update_register_ensure(image)); + partial_updater->mark_full_update(); +} +} diff --git a/source/blender/blenkernel/intern/image_partial_update_test.cc b/source/blender/blenkernel/intern/image_partial_update_test.cc new file mode 100644 index 00000000000..bf12c27241e --- /dev/null +++ b/source/blender/blenkernel/intern/image_partial_update_test.cc @@ -0,0 +1,393 @@ +/* + * 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) 2020 by Blender Foundation. + */ +#include "testing/testing.h" + +#include "CLG_log.h" + +#include "BKE_appdir.h" +#include "BKE_idtype.h" +#include "BKE_image.h" +#include "BKE_image_partial_update.hh" +#include "BKE_main.h" + +#include "IMB_imbuf.h" +#include "IMB_moviecache.h" + +#include "DNA_image_types.h" + +#include "MEM_guardedalloc.h" + +namespace blender::bke::image::partial_update { + +constexpr float black_color[4] = {0.0f, 0.0f, 0.0f, 1.0f}; + +class ImagePartialUpdateTest : public testing::Test { + protected: + Main *bmain; + Image *image; + ImageTile *image_tile; + ImageUser image_user = {nullptr}; + ImBuf *image_buffer; + PartialUpdateUser *partial_update_user; + + private: + Image *create_test_image(int width, int height) + { + return BKE_image_add_generated(bmain, + width, + height, + "Test Image", + 32, + true, + IMA_GENTYPE_BLANK, + black_color, + false, + false, + false); + } + + protected: + void SetUp() override + { + CLG_init(); + BKE_idtype_init(); + BKE_appdir_init(); + IMB_init(); + + bmain = BKE_main_new(); + /* Creating an image generates a memory-leak during tests. */ + image = create_test_image(1024, 1024); + image_tile = BKE_image_get_tile(image, 0); + image_buffer = BKE_image_acquire_ibuf(image, nullptr, nullptr); + + partial_update_user = BKE_image_partial_update_create(image); + } + + void TearDown() override + { + BKE_image_release_ibuf(image, image_buffer, nullptr); + BKE_image_partial_update_free(partial_update_user); + BKE_main_free(bmain); + + IMB_moviecache_destruct(); + IMB_exit(); + BKE_appdir_exit(); + CLG_exit(); + } +}; + +TEST_F(ImagePartialUpdateTest, mark_full_update) +{ + ePartialUpdateCollectResult result; + /* First tile should always return a full update. */ + result = BKE_image_partial_update_collect_changes(image, partial_update_user); + EXPECT_EQ(result, ePartialUpdateCollectResult::FullUpdateNeeded); + /* Second invoke should now detect no changes. */ + result = BKE_image_partial_update_collect_changes(image, partial_update_user); + EXPECT_EQ(result, ePartialUpdateCollectResult::NoChangesDetected); + + /* Mark full update */ + BKE_image_partial_update_mark_full_update(image); + + /* Validate need full update followed by no changes. */ + result = BKE_image_partial_update_collect_changes(image, partial_update_user); + EXPECT_EQ(result, ePartialUpdateCollectResult::FullUpdateNeeded); + result = BKE_image_partial_update_collect_changes(image, partial_update_user); + EXPECT_EQ(result, ePartialUpdateCollectResult::NoChangesDetected); +} + +TEST_F(ImagePartialUpdateTest, mark_single_tile) +{ + ePartialUpdateCollectResult result; + /* First tile should always return a full update. */ + result = BKE_image_partial_update_collect_changes(image, partial_update_user); + EXPECT_EQ(result, ePartialUpdateCollectResult::FullUpdateNeeded); + /* Second invoke should now detect no changes. */ + result = BKE_image_partial_update_collect_changes(image, partial_update_user); + EXPECT_EQ(result, ePartialUpdateCollectResult::NoChangesDetected); + + /* Mark region. */ + rcti region; + BLI_rcti_init(®ion, 10, 20, 40, 50); + BKE_image_partial_update_mark_region(image, image_tile, image_buffer, ®ion); + + /* Partial Update should be available. */ + result = BKE_image_partial_update_collect_changes(image, partial_update_user); + EXPECT_EQ(result, ePartialUpdateCollectResult::PartialChangesDetected); + + /* Check tiles. */ + PartialUpdateRegion changed_region; + ePartialUpdateIterResult iter_result; + iter_result = BKE_image_partial_update_get_next_change(partial_update_user, &changed_region); + EXPECT_EQ(iter_result, ePartialUpdateIterResult::ChangeAvailable); + EXPECT_EQ(BLI_rcti_inside_rcti(&changed_region.region, ®ion), true); + iter_result = BKE_image_partial_update_get_next_change(partial_update_user, &changed_region); + EXPECT_EQ(iter_result, ePartialUpdateIterResult::Finished); + + result = BKE_image_partial_update_collect_changes(image, partial_update_user); + EXPECT_EQ(result, ePartialUpdateCollectResult::NoChangesDetected); +} + +TEST_F(ImagePartialUpdateTest, mark_unconnected_tiles) +{ + ePartialUpdateCollectResult result; + /* First tile should always return a full update. */ + result = BKE_image_partial_update_collect_changes(image, partial_update_user); + EXPECT_EQ(result, ePartialUpdateCollectResult::FullUpdateNeeded); + /* Second invoke should now detect no changes. */ + result = BKE_image_partial_update_collect_changes(image, partial_update_user); + EXPECT_EQ(result, ePartialUpdateCollectResult::NoChangesDetected); + + /* Mark region. */ + rcti region_a; + BLI_rcti_init(®ion_a, 10, 20, 40, 50); + BKE_image_partial_update_mark_region(image, image_tile, image_buffer, ®ion_a); + rcti region_b; + BLI_rcti_init(®ion_b, 710, 720, 740, 750); + BKE_image_partial_update_mark_region(image, image_tile, image_buffer, ®ion_b); + + /* Partial Update should be available. */ + result = BKE_image_partial_update_collect_changes(image, partial_update_user); + EXPECT_EQ(result, ePartialUpdateCollectResult::PartialChangesDetected); + + /* Check tiles. */ + PartialUpdateRegion changed_region; + ePartialUpdateIterResult iter_result; + iter_result = BKE_image_partial_update_get_next_change(partial_update_user, &changed_region); + EXPECT_EQ(iter_result, ePartialUpdateIterResult::ChangeAvailable); + EXPECT_EQ(BLI_rcti_inside_rcti(&changed_region.region, ®ion_b), true); + iter_result = BKE_image_partial_update_get_next_change(partial_update_user, &changed_region); + EXPECT_EQ(iter_result, ePartialUpdateIterResult::ChangeAvailable); + EXPECT_EQ(BLI_rcti_inside_rcti(&changed_region.region, ®ion_a), true); + iter_result = BKE_image_partial_update_get_next_change(partial_update_user, &changed_region); + EXPECT_EQ(iter_result, ePartialUpdateIterResult::Finished); + + result = BKE_image_partial_update_collect_changes(image, partial_update_user); + EXPECT_EQ(result, ePartialUpdateCollectResult::NoChangesDetected); +} + +TEST_F(ImagePartialUpdateTest, donot_mark_outside_image) +{ + ePartialUpdateCollectResult result; + /* First tile should always return a full update. */ + result = BKE_image_partial_update_collect_changes(image, partial_update_user); + EXPECT_EQ(result, ePartialUpdateCollectResult::FullUpdateNeeded); + /* Second invoke should now detect no changes. */ + result = BKE_image_partial_update_collect_changes(image, partial_update_user); + EXPECT_EQ(result, ePartialUpdateCollectResult::NoChangesDetected); + + /* Mark region. */ + rcti region; + /* Axis. */ + BLI_rcti_init(®ion, -100, 0, 50, 100); + BKE_image_partial_update_mark_region(image, image_tile, image_buffer, ®ion); + result = BKE_image_partial_update_collect_changes(image, partial_update_user); + EXPECT_EQ(result, ePartialUpdateCollectResult::NoChangesDetected); + + BLI_rcti_init(®ion, 1024, 1100, 50, 100); + BKE_image_partial_update_mark_region(image, image_tile, image_buffer, ®ion); + result = BKE_image_partial_update_collect_changes(image, partial_update_user); + EXPECT_EQ(result, ePartialUpdateCollectResult::NoChangesDetected); + + BLI_rcti_init(®ion, 50, 100, -100, 0); + BKE_image_partial_update_mark_region(image, image_tile, image_buffer, ®ion); + result = BKE_image_partial_update_collect_changes(image, partial_update_user); + EXPECT_EQ(result, ePartialUpdateCollectResult::NoChangesDetected); + + BLI_rcti_init(®ion, 50, 100, 1024, 1100); + BKE_image_partial_update_mark_region(image, image_tile, image_buffer, ®ion); + result = BKE_image_partial_update_collect_changes(image, partial_update_user); + EXPECT_EQ(result, ePartialUpdateCollectResult::NoChangesDetected); + + /* Diagonals. */ + BLI_rcti_init(®ion, -100, 0, -100, 0); + BKE_image_partial_update_mark_region(image, image_tile, image_buffer, ®ion); + result = BKE_image_partial_update_collect_changes(image, partial_update_user); + EXPECT_EQ(result, ePartialUpdateCollectResult::NoChangesDetected); + + BLI_rcti_init(®ion, -100, 0, 1024, 1100); + BKE_image_partial_update_mark_region(image, image_tile, image_buffer, ®ion); + result = BKE_image_partial_update_collect_changes(image, partial_update_user); + EXPECT_EQ(result, ePartialUpdateCollectResult::NoChangesDetected); + + BLI_rcti_init(®ion, 1024, 1100, -100, 0); + BKE_image_partial_update_mark_region(image, image_tile, image_buffer, ®ion); + result = BKE_image_partial_update_collect_changes(image, partial_update_user); + EXPECT_EQ(result, ePartialUpdateCollectResult::NoChangesDetected); + + BLI_rcti_init(®ion, 1024, 1100, 1024, 1100); + BKE_image_partial_update_mark_region(image, image_tile, image_buffer, ®ion); + result = BKE_image_partial_update_collect_changes(image, partial_update_user); + EXPECT_EQ(result, ePartialUpdateCollectResult::NoChangesDetected); +} + +TEST_F(ImagePartialUpdateTest, mark_inside_image) +{ + ePartialUpdateCollectResult result; + /* First tile should always return a full update. */ + result = BKE_image_partial_update_collect_changes(image, partial_update_user); + EXPECT_EQ(result, ePartialUpdateCollectResult::FullUpdateNeeded); + /* Second invoke should now detect no changes. */ + result = BKE_image_partial_update_collect_changes(image, partial_update_user); + EXPECT_EQ(result, ePartialUpdateCollectResult::NoChangesDetected); + + /* Mark region. */ + rcti region; + BLI_rcti_init(®ion, 0, 1, 0, 1); + BKE_image_partial_update_mark_region(image, image_tile, image_buffer, ®ion); + result = BKE_image_partial_update_collect_changes(image, partial_update_user); + EXPECT_EQ(result, ePartialUpdateCollectResult::PartialChangesDetected); + + result = BKE_image_partial_update_collect_changes(image, partial_update_user); + EXPECT_EQ(result, ePartialUpdateCollectResult::NoChangesDetected); + BLI_rcti_init(®ion, 1023, 1024, 0, 1); + BKE_image_partial_update_mark_region(image, image_tile, image_buffer, ®ion); + result = BKE_image_partial_update_collect_changes(image, partial_update_user); + EXPECT_EQ(result, ePartialUpdateCollectResult::PartialChangesDetected); + + result = BKE_image_partial_update_collect_changes(image, partial_update_user); + EXPECT_EQ(result, ePartialUpdateCollectResult::NoChangesDetected); + BLI_rcti_init(®ion, 1023, 1024, 1023, 1024); + BKE_image_partial_update_mark_region(image, image_tile, image_buffer, ®ion); + result = BKE_image_partial_update_collect_changes(image, partial_update_user); + EXPECT_EQ(result, ePartialUpdateCollectResult::PartialChangesDetected); + + result = BKE_image_partial_update_collect_changes(image, partial_update_user); + EXPECT_EQ(result, ePartialUpdateCollectResult::NoChangesDetected); + BLI_rcti_init(®ion, 1023, 1024, 0, 1); + BKE_image_partial_update_mark_region(image, image_tile, image_buffer, ®ion); + result = BKE_image_partial_update_collect_changes(image, partial_update_user); + EXPECT_EQ(result, ePartialUpdateCollectResult::PartialChangesDetected); +} + +TEST_F(ImagePartialUpdateTest, sequential_mark_region) +{ + ePartialUpdateCollectResult result; + /* First tile should always return a full update. */ + result = BKE_image_partial_update_collect_changes(image, partial_update_user); + EXPECT_EQ(result, ePartialUpdateCollectResult::FullUpdateNeeded); + /* Second invoke should now detect no changes. */ + result = BKE_image_partial_update_collect_changes(image, partial_update_user); + EXPECT_EQ(result, ePartialUpdateCollectResult::NoChangesDetected); + + { + /* Mark region. */ + rcti region; + BLI_rcti_init(®ion, 10, 20, 40, 50); + BKE_image_partial_update_mark_region(image, image_tile, image_buffer, ®ion); + + /* Partial Update should be available. */ + result = BKE_image_partial_update_collect_changes(image, partial_update_user); + EXPECT_EQ(result, ePartialUpdateCollectResult::PartialChangesDetected); + + /* Check tiles. */ + PartialUpdateRegion changed_region; + ePartialUpdateIterResult iter_result; + iter_result = BKE_image_partial_update_get_next_change(partial_update_user, &changed_region); + EXPECT_EQ(iter_result, ePartialUpdateIterResult::ChangeAvailable); + EXPECT_EQ(BLI_rcti_inside_rcti(&changed_region.region, ®ion), true); + iter_result = BKE_image_partial_update_get_next_change(partial_update_user, &changed_region); + EXPECT_EQ(iter_result, ePartialUpdateIterResult::Finished); + + result = BKE_image_partial_update_collect_changes(image, partial_update_user); + EXPECT_EQ(result, ePartialUpdateCollectResult::NoChangesDetected); + } + + { + /* Mark different region. */ + rcti region; + BLI_rcti_init(®ion, 710, 720, 740, 750); + BKE_image_partial_update_mark_region(image, image_tile, image_buffer, ®ion); + + /* Partial Update should be available. */ + result = BKE_image_partial_update_collect_changes(image, partial_update_user); + EXPECT_EQ(result, ePartialUpdateCollectResult::PartialChangesDetected); + + /* Check tiles. */ + PartialUpdateRegion changed_region; + ePartialUpdateIterResult iter_result; + iter_result = BKE_image_partial_update_get_next_change(partial_update_user, &changed_region); + EXPECT_EQ(iter_result, ePartialUpdateIterResult::ChangeAvailable); + EXPECT_EQ(BLI_rcti_inside_rcti(&changed_region.region, ®ion), true); + iter_result = BKE_image_partial_update_get_next_change(partial_update_user, &changed_region); + EXPECT_EQ(iter_result, ePartialUpdateIterResult::Finished); + + result = BKE_image_partial_update_collect_changes(image, partial_update_user); + EXPECT_EQ(result, ePartialUpdateCollectResult::NoChangesDetected); + } +} + +TEST_F(ImagePartialUpdateTest, mark_multiple_chunks) +{ + ePartialUpdateCollectResult result; + /* First tile should always return a full update. */ + result = BKE_image_partial_update_collect_changes(image, partial_update_user); + EXPECT_EQ(result, ePartialUpdateCollectResult::FullUpdateNeeded); + /* Second invoke should now detect no changes. */ + result = BKE_image_partial_update_collect_changes(image, partial_update_user); + EXPECT_EQ(result, ePartialUpdateCollectResult::NoChangesDetected); + + /* Mark region. */ + rcti region; + BLI_rcti_init(®ion, 300, 700, 300, 700); + BKE_image_partial_update_mark_region(image, image_tile, image_buffer, ®ion); + + /* Partial Update should be available. */ + result = BKE_image_partial_update_collect_changes(image, partial_update_user); + EXPECT_EQ(result, ePartialUpdateCollectResult::PartialChangesDetected); + + /* Check tiles. */ + PartialUpdateRegion changed_region; + int num_chunks_found = 0; + while (BKE_image_partial_update_get_next_change(partial_update_user, &changed_region) == + ePartialUpdateIterResult::ChangeAvailable) { + BLI_rcti_isect(&changed_region.region, ®ion, nullptr); + num_chunks_found++; + } + EXPECT_EQ(num_chunks_found, 4); +} + +TEST_F(ImagePartialUpdateTest, iterator) +{ + PartialUpdateChecker<NoTileData> checker(image, &image_user, partial_update_user); + /* First tile should always return a full update. */ + PartialUpdateChecker<NoTileData>::CollectResult changes = checker.collect_changes(); + EXPECT_EQ(changes.get_result_code(), ePartialUpdateCollectResult::FullUpdateNeeded); + /* Second invoke should now detect no changes. */ + changes = checker.collect_changes(); + EXPECT_EQ(changes.get_result_code(), ePartialUpdateCollectResult::NoChangesDetected); + + /* Mark region. */ + rcti region; + BLI_rcti_init(®ion, 300, 700, 300, 700); + BKE_image_partial_update_mark_region(image, image_tile, image_buffer, ®ion); + + /* Partial Update should be available. */ + changes = checker.collect_changes(); + EXPECT_EQ(changes.get_result_code(), ePartialUpdateCollectResult::PartialChangesDetected); + + /* Check tiles. */ + int num_tiles_found = 0; + while (changes.get_next_change() == ePartialUpdateIterResult::ChangeAvailable) { + BLI_rcti_isect(&changes.changed_region.region, ®ion, nullptr); + num_tiles_found++; + } + EXPECT_EQ(num_tiles_found, 4); +} + +} // namespace blender::bke::image::partial_update diff --git a/source/blender/blenkernel/intern/layer.c b/source/blender/blenkernel/intern/layer.c index 9e3cea40fb8..a59dd6f2e0e 100644 --- a/source/blender/blenkernel/intern/layer.c +++ b/source/blender/blenkernel/intern/layer.c @@ -1196,6 +1196,23 @@ static bool view_layer_objects_base_cache_validate(ViewLayer *UNUSED(view_layer) } #endif +void BKE_layer_collection_doversion_2_80(const Scene *scene, ViewLayer *view_layer) +{ + LayerCollection *first_layer_collection = view_layer->layer_collections.first; + if (BLI_listbase_count_at_most(&view_layer->layer_collections, 2) > 1 || + first_layer_collection->collection != scene->master_collection) { + /* In some cases (from older files) we do have a master collection, but no matching layer, + * instead all the children of the master collection have their layer collections in the + * viewlayer's list. This is not a valid situation, add a layer for the master collection and + * add all existing first-level layers as children of that new master layer. */ + ListBase layer_collections = view_layer->layer_collections; + BLI_listbase_clear(&view_layer->layer_collections); + LayerCollection *master_layer_collection = layer_collection_add(&view_layer->layer_collections, + scene->master_collection); + master_layer_collection->layer_collections = layer_collections; + } +} + void BKE_layer_collection_sync(const Scene *scene, ViewLayer *view_layer) { if (no_resync) { @@ -1208,21 +1225,24 @@ void BKE_layer_collection_sync(const Scene *scene, ViewLayer *view_layer) } if (BLI_listbase_is_empty(&view_layer->layer_collections)) { - /* In some cases (from older files) we do have a master collection, yet no matching layer. - * Create the master one here, so that the rest of the code can work as expected. */ + /* In some cases (from older files, or when creating a new ViewLayer from + * #BKE_view_layer_add), we do have a master collection, yet no matching layer. Create the + * master one here, so that the rest of the code can work as expected. */ layer_collection_add(&view_layer->layer_collections, scene->master_collection); } - else if (BLI_listbase_count_at_most(&view_layer->layer_collections, 2) > 1) { - /* In some cases (from older files) we do have a master collection, but no matching layer, - * instead all the children of the master collection have their layer collections in the - * viewlayer's list. This is not a valid situation, add a layer for the master collection and - * add all existing first-level layers as children of that new master layer. */ - ListBase layer_collections = view_layer->layer_collections; - BLI_listbase_clear(&view_layer->layer_collections); - LayerCollection *master_layer_collection = layer_collection_add(&view_layer->layer_collections, - scene->master_collection); - master_layer_collection->layer_collections = layer_collections; + +#ifndef NDEBUG + { + BLI_assert_msg(BLI_listbase_count_at_most(&view_layer->layer_collections, 2) == 1, + "ViewLayer's first level of children layer collections should always have " + "exactly one item"); + + LayerCollection *first_layer_collection = view_layer->layer_collections.first; + BLI_assert_msg(first_layer_collection->collection == scene->master_collection, + "ViewLayer's first layer collection should always be the one for the scene's " + "master collection"); } +#endif /* Free cache. */ MEM_SAFE_FREE(view_layer->object_bases_array); diff --git a/source/blender/blenkernel/intern/lib_id.c b/source/blender/blenkernel/intern/lib_id.c index 692e27731c5..6c19c7a328b 100644 --- a/source/blender/blenkernel/intern/lib_id.c +++ b/source/blender/blenkernel/intern/lib_id.c @@ -454,38 +454,57 @@ static void lib_id_copy_ensure_local(Main *bmain, const ID *old_id, ID *new_id, } } -void BKE_lib_id_make_local_generic(Main *bmain, ID *id, const int flags) +void BKE_lib_id_make_local_generic_action_define( + struct Main *bmain, struct ID *id, int flags, bool *r_force_local, bool *r_force_copy) { - if (!ID_IS_LINKED(id)) { - return; - } - - const bool lib_local = (flags & LIB_ID_MAKELOCAL_FULL_LIBRARY) != 0; bool force_local = (flags & LIB_ID_MAKELOCAL_FORCE_LOCAL) != 0; bool force_copy = (flags & LIB_ID_MAKELOCAL_FORCE_COPY) != 0; BLI_assert(force_copy == false || force_copy != force_local); + if (force_local || force_copy) { + /* Already set by caller code, nothing to do here. */ + *r_force_local = force_local; + *r_force_copy = force_copy; + return; + } + + const bool lib_local = (flags & LIB_ID_MAKELOCAL_FULL_LIBRARY) != 0; bool is_local = false, is_lib = false; - /* - only lib users: do nothing (unless force_local is set) - * - only local users: set flag + /* - no user (neither lib nor local): make local (happens e.g. with UI-used only data). + * - only lib users: do nothing (unless force_local is set) + * - only local users: make local * - mixed: make copy * In case we make a whole lib's content local, * we always want to localize, and we skip remapping (done later). */ - if (!force_copy && !force_local) { - BKE_library_ID_test_usages(bmain, id, &is_local, &is_lib); - if (lib_local || is_local) { - if (!is_lib) { - force_local = true; - } - else { - force_copy = true; - } + BKE_library_ID_test_usages(bmain, id, &is_local, &is_lib); + if (!lib_local && !is_local && !is_lib) { + force_local = true; + } + else if (lib_local || is_local) { + if (!is_lib) { + force_local = true; + } + else { + force_copy = true; } } + *r_force_local = force_local; + *r_force_copy = force_copy; +} + +void BKE_lib_id_make_local_generic(Main *bmain, ID *id, const int flags) +{ + if (!ID_IS_LINKED(id)) { + return; + } + + bool force_local, force_copy; + BKE_lib_id_make_local_generic_action_define(bmain, id, flags, &force_local, &force_copy); + if (force_local) { BKE_lib_id_clear_library_data(bmain, id, flags); BKE_lib_id_expand_local(bmain, id, flags); @@ -516,6 +535,7 @@ void BKE_lib_id_make_local_generic(Main *bmain, ID *id, const int flags) } } + const bool lib_local = (flags & LIB_ID_MAKELOCAL_FULL_LIBRARY) != 0; if (!lib_local) { BKE_libblock_remap(bmain, id, id_new, ID_REMAP_SKIP_INDIRECT_USAGE); } @@ -1898,7 +1918,6 @@ void BKE_library_make_local(Main *bmain, * but complicates slightly the pre-processing of relations between IDs at step 2... */ else if (!do_skip && id->tag & (LIB_TAG_EXTERN | LIB_TAG_INDIRECT | LIB_TAG_NEW) && ELEM(lib, NULL, id->lib) && - !(GS(id->name) == ID_OB && ((Object *)id)->proxy_from != NULL) && ((untagged_only == false) || !(id->tag & LIB_TAG_PRE_EXISTING))) { BLI_linklist_prepend_arena(&todo_ids, id, linklist_mem); id->tag |= LIB_TAG_DOIT; @@ -1962,12 +1981,8 @@ void BKE_library_make_local(Main *bmain, } } else { - /* In this specific case, we do want to make ID local even if it has no local usage yet... - * Note that for objects, we don't want proxy pointers to be cleared yet. This will happen - * down the road in this function. - */ - BKE_lib_id_make_local( - bmain, id, LIB_ID_MAKELOCAL_FULL_LIBRARY | LIB_ID_MAKELOCAL_OBJECT_NO_PROXY_CLEARING); + /* In this specific case, we do want to make ID local even if it has no local usage yet... */ + BKE_lib_id_make_local(bmain, id, LIB_ID_MAKELOCAL_FULL_LIBRARY); if (id->newid) { if (GS(id->newid->name) == ID_OB) { @@ -2029,62 +2044,6 @@ void BKE_library_make_local(Main *bmain, TIMEIT_VALUE_PRINT(make_local); #endif - /* Step 5: proxy 'remapping' hack. */ - for (LinkNode *it = copied_ids; it; it = it->next) { - ID *id = it->link; - - /* Attempt to re-link copied proxy objects. This allows appending of an entire scene - * from another blend file into this one, even when that blend file contains proxified - * armatures that have local references. Since the proxified object needs to be linked - * (not local), this will only work when the "Localize all" checkbox is disabled. - * TL;DR: this is a dirty hack on top of an already weak feature (proxies). */ - if (GS(id->name) == ID_OB && ((Object *)id)->proxy != NULL) { - Object *ob = (Object *)id; - Object *ob_new = (Object *)id->newid; - bool is_local = false, is_lib = false; - - /* Proxies only work when the proxified object is linked-in from a library. */ - if (!ID_IS_LINKED(ob->proxy)) { - CLOG_WARN(&LOG, - "proxy object %s will lose its link to %s, because the " - "proxified object is local.", - id->newid->name, - ob->proxy->id.name); - continue; - } - - BKE_library_ID_test_usages(bmain, id, &is_local, &is_lib); - - /* We can only switch the proxy'ing to a made-local proxy if it is no longer - * referred to from a library. Not checking for local use; if new local proxy - * was not used locally would be a nasty bug! */ - if (is_local || is_lib) { - CLOG_WARN(&LOG, - "made-local proxy object %s will lose its link to %s, " - "because the linked-in proxy is referenced (is_local=%i, is_lib=%i).", - id->newid->name, - ob->proxy->id.name, - is_local, - is_lib); - } - else { - /* we can switch the proxy'ing from the linked-in to the made-local proxy. - * BKE_object_make_proxy() shouldn't be used here, as it allocates memory that - * was already allocated by object_make_local() (which called BKE_object_copy). */ - ob_new->proxy = ob->proxy; - ob_new->proxy_group = ob->proxy_group; - ob_new->proxy_from = ob->proxy_from; - ob_new->proxy->proxy_from = ob_new; - ob->proxy = ob->proxy_from = ob->proxy_group = NULL; - } - } - } - -#ifdef DEBUG_TIME - printf("Step 5: Proxy 'remapping' hack: Done.\n"); - TIMEIT_VALUE_PRINT(make_local); -#endif - /* This is probably more of a hack than something we should do here, but... * Issue is, the whole copying + remapping done in complex cases above may leave pose-channels * of armatures in complete invalid state (more precisely, the bone pointers of the diff --git a/source/blender/blenkernel/intern/lib_id_delete.c b/source/blender/blenkernel/intern/lib_id_delete.c index 6d2e89187f7..5db70d85e71 100644 --- a/source/blender/blenkernel/intern/lib_id_delete.c +++ b/source/blender/blenkernel/intern/lib_id_delete.c @@ -154,7 +154,10 @@ void BKE_id_free_ex(Main *bmain, void *idv, int flag, const bool use_flag_from_i } if (remap_editor_id_reference_cb) { - remap_editor_id_reference_cb(id, NULL); + struct IDRemapper *remapper = BKE_id_remapper_create(); + BKE_id_remapper_add(remapper, id, NULL); + remap_editor_id_reference_cb(remapper); + BKE_id_remapper_free(remapper); } } @@ -262,8 +265,8 @@ static size_t id_delete(Main *bmain, const bool do_tagged_deletion) } for (id = last_remapped_id->next; id; id = id->next) { /* Will tag 'never NULL' users of this ID too. - * Note that we cannot use BKE_libblock_unlink() here, - * since it would ignore indirect (and proxy!) + * + * NOTE: #BKE_libblock_unlink() cannot be used here, since it would ignore indirect * links, this can lead to nasty crashing here in second, actual deleting loop. * Also, this will also flag users of deleted data that cannot be unlinked * (object using deleted obdata, etc.), so that they also get deleted. */ @@ -292,32 +295,40 @@ static size_t id_delete(Main *bmain, const bool do_tagged_deletion) * Note that we go forward here, since we want to check dependencies before users * (e.g. meshes before objects). * Avoids to have to loop twice. */ + struct IDRemapper *remapper = BKE_id_remapper_create(); for (i = 0; i < base_count; i++) { ListBase *lb = lbarray[i]; ID *id, *id_next; + BKE_id_remapper_clear(remapper); for (id = lb->first; id; id = id_next) { id_next = id->next; /* NOTE: in case we delete a library, we also delete all its datablocks! */ if ((id->tag & tag) || (id->lib != NULL && (id->lib->id.tag & tag))) { id->tag |= tag; - - /* Will tag 'never NULL' users of this ID too. - * Note that we cannot use BKE_libblock_unlink() here, since it would ignore indirect - * (and proxy!) links, this can lead to nasty crashing here in second, - * actual deleting loop. - * Also, this will also flag users of deleted data that cannot be unlinked - * (object using deleted obdata, etc.), so that they also get deleted. */ - BKE_libblock_remap_locked(bmain, - id, - NULL, - (ID_REMAP_FLAG_NEVER_NULL_USAGE | - ID_REMAP_FORCE_NEVER_NULL_USAGE | - ID_REMAP_FORCE_INTERNAL_RUNTIME_POINTERS)); + BKE_id_remapper_add(remapper, id, NULL); } } + + if (BKE_id_remapper_is_empty(remapper)) { + continue; + } + + /* Will tag 'never NULL' users of this ID too. + * + * NOTE: #BKE_libblock_unlink() cannot be used here, since it would ignore indirect + * links, this can lead to nasty crashing here in second, actual deleting loop. + * Also, this will also flag users of deleted data that cannot be unlinked + * (object using deleted obdata, etc.), so that they also get deleted. */ + BKE_libblock_remap_multiple_locked(bmain, + remapper, + (ID_REMAP_FLAG_NEVER_NULL_USAGE | + ID_REMAP_FORCE_NEVER_NULL_USAGE | + ID_REMAP_FORCE_INTERNAL_RUNTIME_POINTERS)); } + BKE_id_remapper_free(remapper); } + BKE_main_unlock(bmain); /* In usual reversed order, such that all usage of a given ID, even 'never NULL' ones, diff --git a/source/blender/blenkernel/intern/lib_id_remapper.cc b/source/blender/blenkernel/intern/lib_id_remapper.cc new file mode 100644 index 00000000000..c1734c9826a --- /dev/null +++ b/source/blender/blenkernel/intern/lib_id_remapper.cc @@ -0,0 +1,175 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2022 by Blender Foundation. + */ + +#include "DNA_ID.h" + +#include "BKE_idtype.h" +#include "BKE_lib_id.h" +#include "BKE_lib_remap.h" + +#include "MEM_guardedalloc.h" + +#include "BLI_map.hh" + +using IDTypeFilter = uint64_t; + +namespace blender::bke::id::remapper { +struct IDRemapper { + private: + Map<ID *, ID *> mappings; + IDTypeFilter source_types = 0; + + public: + void clear() + { + mappings.clear(); + source_types = 0; + } + + bool is_empty() const + { + return mappings.is_empty(); + } + + void add(ID *old_id, ID *new_id) + { + BLI_assert(old_id != nullptr); + BLI_assert(new_id == nullptr || (GS(old_id->name) == GS(new_id->name))); + mappings.add(old_id, new_id); + source_types |= BKE_idtype_idcode_to_idfilter(GS(old_id->name)); + } + + bool contains_mappings_for_any(IDTypeFilter filter) const + { + return (source_types & filter) != 0; + } + + IDRemapperApplyResult apply(ID **r_id_ptr, IDRemapperApplyOptions options) const + { + BLI_assert(r_id_ptr != nullptr); + if (*r_id_ptr == nullptr) { + return ID_REMAP_RESULT_SOURCE_NOT_MAPPABLE; + } + + if (!mappings.contains(*r_id_ptr)) { + return ID_REMAP_RESULT_SOURCE_UNAVAILABLE; + } + + if (options & ID_REMAP_APPLY_UPDATE_REFCOUNT) { + id_us_min(*r_id_ptr); + } + + *r_id_ptr = mappings.lookup(*r_id_ptr); + if (*r_id_ptr == nullptr) { + return ID_REMAP_RESULT_SOURCE_UNASSIGNED; + } + + if (options & ID_REMAP_APPLY_UPDATE_REFCOUNT) { + id_us_plus(*r_id_ptr); + } + + if (options & ID_REMAP_APPLY_ENSURE_REAL) { + id_us_ensure_real(*r_id_ptr); + } + return ID_REMAP_RESULT_SOURCE_REMAPPED; + } + + void iter(IDRemapperIterFunction func, void *user_data) const + { + for (auto item : mappings.items()) { + func(item.key, item.value, user_data); + } + } +}; + +} // namespace blender::bke::id::remapper + +/** \brief wrap CPP IDRemapper to a C handle. */ +static IDRemapper *wrap(blender::bke::id::remapper::IDRemapper *remapper) +{ + return static_cast<IDRemapper *>(static_cast<void *>(remapper)); +} + +/** \brief wrap C handle to a CPP IDRemapper. */ +static blender::bke::id::remapper::IDRemapper *unwrap(IDRemapper *remapper) +{ + return static_cast<blender::bke::id::remapper::IDRemapper *>(static_cast<void *>(remapper)); +} + +/** \brief wrap C handle to a CPP IDRemapper. */ +static const blender::bke::id::remapper::IDRemapper *unwrap(const IDRemapper *remapper) +{ + return static_cast<const blender::bke::id::remapper::IDRemapper *>( + static_cast<const void *>(remapper)); +} + +extern "C" { + +IDRemapper *BKE_id_remapper_create(void) +{ + blender::bke::id::remapper::IDRemapper *remapper = + MEM_new<blender::bke::id::remapper::IDRemapper>(__func__); + return wrap(remapper); +} + +void BKE_id_remapper_free(IDRemapper *id_remapper) +{ + blender::bke::id::remapper::IDRemapper *remapper = unwrap(id_remapper); + MEM_delete<blender::bke::id::remapper::IDRemapper>(remapper); +} + +void BKE_id_remapper_clear(struct IDRemapper *id_remapper) +{ + blender::bke::id::remapper::IDRemapper *remapper = unwrap(id_remapper); + remapper->clear(); +} + +bool BKE_id_remapper_is_empty(const struct IDRemapper *id_remapper) +{ + const blender::bke::id::remapper::IDRemapper *remapper = unwrap(id_remapper); + return remapper->is_empty(); +} + +void BKE_id_remapper_add(IDRemapper *id_remapper, ID *old_id, ID *new_id) +{ + blender::bke::id::remapper::IDRemapper *remapper = unwrap(id_remapper); + remapper->add(old_id, new_id); +} + +bool BKE_id_remapper_has_mapping_for(const struct IDRemapper *id_remapper, uint64_t type_filter) +{ + const blender::bke::id::remapper::IDRemapper *remapper = unwrap(id_remapper); + return remapper->contains_mappings_for_any(type_filter); +} + +IDRemapperApplyResult BKE_id_remapper_apply(const IDRemapper *id_remapper, + ID **r_id_ptr, + const IDRemapperApplyOptions options) +{ + const blender::bke::id::remapper::IDRemapper *remapper = unwrap(id_remapper); + return remapper->apply(r_id_ptr, options); +} + +void BKE_id_remapper_iter(const struct IDRemapper *id_remapper, + IDRemapperIterFunction func, + void *user_data) +{ + const blender::bke::id::remapper::IDRemapper *remapper = unwrap(id_remapper); + remapper->iter(func, user_data); +} +} diff --git a/source/blender/blenkernel/intern/lib_id_remapper_test.cc b/source/blender/blenkernel/intern/lib_id_remapper_test.cc new file mode 100644 index 00000000000..594f64dac73 --- /dev/null +++ b/source/blender/blenkernel/intern/lib_id_remapper_test.cc @@ -0,0 +1,83 @@ +/* + * 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) 2022 by Blender Foundation. + */ + +#include "testing/testing.h" + +#include "BKE_lib_remap.h" + +#include "BLI_string.h" + +#include "DNA_ID.h" + +namespace blender::bke::id::remapper::tests { + +TEST(lib_id_remapper, unavailable) +{ + ID id1; + ID *idp = &id1; + + IDRemapper *remapper = BKE_id_remapper_create(); + IDRemapperApplyResult result = BKE_id_remapper_apply(remapper, &idp, ID_REMAP_APPLY_DEFAULT); + EXPECT_EQ(result, ID_REMAP_RESULT_SOURCE_UNAVAILABLE); + + BKE_id_remapper_free(remapper); +} + +TEST(lib_id_remapper, not_mappable) +{ + ID *idp = nullptr; + + IDRemapper *remapper = BKE_id_remapper_create(); + IDRemapperApplyResult result = BKE_id_remapper_apply(remapper, &idp, ID_REMAP_APPLY_DEFAULT); + EXPECT_EQ(result, ID_REMAP_RESULT_SOURCE_NOT_MAPPABLE); + + BKE_id_remapper_free(remapper); +} + +TEST(lib_id_remapper, mapped) +{ + ID id1; + ID id2; + ID *idp = &id1; + BLI_strncpy(id1.name, "OB1", sizeof(id1.name)); + BLI_strncpy(id2.name, "OB2", sizeof(id2.name)); + + IDRemapper *remapper = BKE_id_remapper_create(); + BKE_id_remapper_add(remapper, &id1, &id2); + IDRemapperApplyResult result = BKE_id_remapper_apply(remapper, &idp, ID_REMAP_APPLY_DEFAULT); + EXPECT_EQ(result, ID_REMAP_RESULT_SOURCE_REMAPPED); + EXPECT_EQ(idp, &id2); + + BKE_id_remapper_free(remapper); +} + +TEST(lib_id_remapper, unassigned) +{ + ID id1; + ID *idp = &id1; + + IDRemapper *remapper = BKE_id_remapper_create(); + BKE_id_remapper_add(remapper, &id1, nullptr); + IDRemapperApplyResult result = BKE_id_remapper_apply(remapper, &idp, ID_REMAP_APPLY_DEFAULT); + EXPECT_EQ(result, ID_REMAP_RESULT_SOURCE_UNASSIGNED); + EXPECT_EQ(idp, nullptr); + + BKE_id_remapper_free(remapper); +} + +} // namespace blender::bke::id::remapper::tests diff --git a/source/blender/blenkernel/intern/lib_override.c b/source/blender/blenkernel/intern/lib_override.c index 38ce8ea88b9..314351e4ad7 100644 --- a/source/blender/blenkernel/intern/lib_override.c +++ b/source/blender/blenkernel/intern/lib_override.c @@ -211,6 +211,7 @@ void BKE_lib_override_library_free(struct IDOverrideLibrary **override, const bo } static ID *lib_override_library_create_from(Main *bmain, + Library *owner_library, ID *reference_id, const int lib_id_copy_flags) { @@ -227,6 +228,12 @@ static ID *lib_override_library_create_from(Main *bmain, } id_us_min(local_id); + /* TODO: Handle this properly in LIB_NO_MAIN case as well (i.e. resync case). Or offload to + * generic ID copy code? */ + if ((lib_id_copy_flags & LIB_ID_CREATE_NO_MAIN) == 0) { + local_id->lib = owner_library; + } + BKE_lib_override_library_init(local_id, reference_id); /* NOTE: From liboverride perspective (and RNA one), shape keys are considered as local embedded @@ -281,7 +288,7 @@ ID *BKE_lib_override_library_create_from_id(Main *bmain, BLI_assert(reference_id != NULL); BLI_assert(ID_IS_LINKED(reference_id)); - ID *local_id = lib_override_library_create_from(bmain, reference_id, 0); + ID *local_id = lib_override_library_create_from(bmain, NULL, reference_id, 0); /* We cannot allow automatic hierarchy resync on this ID, it is highly likely to generate a giant * mess in case there are a lot of hidden, non-instantiated, non-properly organized dependencies. * Ref T94650. */ @@ -320,6 +327,7 @@ ID *BKE_lib_override_library_create_from_id(Main *bmain, } bool BKE_lib_override_library_create_from_tag(Main *bmain, + Library *owner_library, const Library *reference_library, const bool do_no_main) { @@ -351,7 +359,7 @@ bool BKE_lib_override_library_create_from_tag(Main *bmain, * This requires extra care further down the resync process, * see: #BKE_lib_override_library_resync. */ reference_id->newid = lib_override_library_create_from( - bmain, reference_id, do_no_main ? LIB_ID_CREATE_NO_MAIN : 0); + bmain, owner_library, reference_id, do_no_main ? LIB_ID_CREATE_NO_MAIN : 0); if (reference_id->newid == NULL) { success = false; break; @@ -616,6 +624,35 @@ static void lib_override_linked_group_tag_recursive(LibOverrideGroupTagData *dat } } +static bool lib_override_linked_group_tag_collections_keep_tagged_check_recursive( + LibOverrideGroupTagData *data, Collection *collection) +{ + /* NOTE: Collection's object cache (using bases, as returned by #BKE_collection_object_cache_get) + * is not usable here, as it may have become invalid from some previous operation and it should + * not be updated here. So instead only use collections' reliable 'raw' data to check if some + * object in the hierarchy of the given collection is still tagged for override. */ + for (CollectionObject *collection_object = collection->gobject.first; collection_object != NULL; + collection_object = collection_object->next) { + Object *object = collection_object->ob; + if (object == NULL) { + continue; + } + if ((object->id.tag & data->tag) != 0) { + return true; + } + } + + for (CollectionChild *collection_child = collection->children.first; collection_child != NULL; + collection_child = collection_child->next) { + if (lib_override_linked_group_tag_collections_keep_tagged_check_recursive( + data, collection_child->collection)) { + return true; + } + } + + return false; +} + static void lib_override_linked_group_tag_clear_boneshapes_objects(LibOverrideGroupTagData *data) { Main *bmain = data->bmain; @@ -638,15 +675,8 @@ static void lib_override_linked_group_tag_clear_boneshapes_objects(LibOverrideGr if ((collection->id.tag & data->tag) == 0) { continue; } - bool keep_tagged = false; - const ListBase object_bases = BKE_collection_object_cache_get(collection); - LISTBASE_FOREACH (Base *, base, &object_bases) { - if ((base->object->id.tag & data->tag) != 0) { - keep_tagged = true; - break; - } - } - if (!keep_tagged) { + + if (!lib_override_linked_group_tag_collections_keep_tagged_check_recursive(data, collection)) { collection->id.tag &= ~data->tag; } } @@ -813,7 +843,10 @@ static void lib_override_overrides_group_tag(LibOverrideGroupTagData *data) lib_override_overrides_group_tag_recursive(data); } -static bool lib_override_library_create_do(Main *bmain, Scene *scene, ID *id_root) +static bool lib_override_library_create_do(Main *bmain, + Scene *scene, + Library *owner_library, + ID *id_root) { BKE_main_relations_create(bmain, 0); LibOverrideGroupTagData data = {.bmain = bmain, @@ -832,12 +865,13 @@ static bool lib_override_library_create_do(Main *bmain, Scene *scene, ID *id_roo BKE_main_relations_free(bmain); lib_override_group_tag_data_clear(&data); - return BKE_lib_override_library_create_from_tag(bmain, id_root->lib, false); + return BKE_lib_override_library_create_from_tag(bmain, owner_library, id_root->lib, false); } static void lib_override_library_create_post_process(Main *bmain, Scene *scene, ViewLayer *view_layer, + const Library *owner_library, ID *id_root, ID *id_reference, Collection *residual_storage, @@ -859,7 +893,8 @@ static void lib_override_library_create_post_process(Main *bmain, /* Instantiating the root collection or object should never be needed in resync case, since the * old override would be remapped to the new one. */ - if (!is_resync && id_root != NULL && id_root->newid != NULL && !ID_IS_LINKED(id_root->newid)) { + if (!is_resync && id_root != NULL && id_root->newid != NULL && + (!ID_IS_LINKED(id_root->newid) || id_root->newid->lib == owner_library)) { switch (GS(id_root->name)) { case ID_GR: { Object *ob_reference = id_reference != NULL && GS(id_reference->name) == ID_OB ? @@ -904,7 +939,7 @@ static void lib_override_library_create_post_process(Main *bmain, Collection *default_instantiating_collection = residual_storage; LISTBASE_FOREACH (Object *, ob, &bmain->objects) { Object *ob_new = (Object *)ob->id.newid; - if (ob_new == NULL || ID_IS_LINKED(ob_new)) { + if (ob_new == NULL || (ID_IS_LINKED(ob_new) && ob_new->id.lib != owner_library)) { continue; } @@ -967,6 +1002,7 @@ static void lib_override_library_create_post_process(Main *bmain, bool BKE_lib_override_library_create(Main *bmain, Scene *scene, ViewLayer *view_layer, + Library *owner_library, ID *id_root, ID *id_reference, ID **r_id_root_override) @@ -975,7 +1011,7 @@ bool BKE_lib_override_library_create(Main *bmain, *r_id_root_override = NULL; } - const bool success = lib_override_library_create_do(bmain, scene, id_root); + const bool success = lib_override_library_create_do(bmain, scene, owner_library, id_root); if (!success) { return success; @@ -986,7 +1022,7 @@ bool BKE_lib_override_library_create(Main *bmain, } lib_override_library_create_post_process( - bmain, scene, view_layer, id_root, id_reference, NULL, false); + bmain, scene, view_layer, owner_library, id_root, id_reference, NULL, false); /* Cleanup. */ BKE_main_id_newptr_and_tag_clear(bmain); @@ -1011,114 +1047,43 @@ bool BKE_lib_override_library_template_create(struct ID *id) return true; } -bool BKE_lib_override_library_proxy_convert(Main *bmain, - Scene *scene, - ViewLayer *view_layer, - Object *ob_proxy) +static void lib_override_library_remap(Main *bmain, + const ID *id_root_reference, + GHash *linkedref_to_old_override) { - /* `proxy_group`, if defined, is the empty instantiating the collection from which the proxy is - * coming. */ - Object *ob_proxy_group = ob_proxy->proxy_group; - const bool is_override_instancing_object = ob_proxy_group != NULL; - ID *id_root = is_override_instancing_object ? &ob_proxy_group->instance_collection->id : - &ob_proxy->proxy->id; - ID *id_reference = is_override_instancing_object ? &ob_proxy_group->id : &ob_proxy->id; - - /* In some cases the instance collection of a proxy object may be local (see e.g. T83875). Not - * sure this is a valid state, but for now just abort the overriding process. */ - if (!ID_IS_OVERRIDABLE_LIBRARY(id_root)) { - return false; - } - - /* We manually convert the proxy object into a library override, further override handling will - * then be handled by `BKE_lib_override_library_create()` just as for a regular override - * creation. - */ - ob_proxy->proxy->id.tag |= LIB_TAG_DOIT; - ob_proxy->proxy->id.newid = &ob_proxy->id; - BKE_lib_override_library_init(&ob_proxy->id, &ob_proxy->proxy->id); - - ob_proxy->proxy->proxy_from = NULL; - ob_proxy->proxy = ob_proxy->proxy_group = NULL; - - DEG_id_tag_update(&ob_proxy->id, ID_RECALC_COPY_ON_WRITE); - - /* In case of proxy conversion, remap all local ID usages to linked IDs to their newly created - * overrides. - * While this might not be 100% the desired behavior, it is likely to be the case most of the - * time. Ref: T91711. */ - ID *id_iter; - FOREACH_MAIN_ID_BEGIN (bmain, id_iter) { - if (!ID_IS_LINKED(id_iter)) { - id_iter->tag |= LIB_TAG_DOIT; - } - } - FOREACH_MAIN_ID_END; - - return BKE_lib_override_library_create(bmain, scene, view_layer, id_root, id_reference, NULL); -} - -static void lib_override_library_proxy_convert_do(Main *bmain, - Scene *scene, - Object *ob_proxy, - BlendFileReadReport *reports) -{ - Object *ob_proxy_group = ob_proxy->proxy_group; - const bool is_override_instancing_object = ob_proxy_group != NULL; - - const bool success = BKE_lib_override_library_proxy_convert(bmain, scene, NULL, ob_proxy); - - if (success) { - CLOG_INFO(&LOG, - 4, - "Proxy object '%s' successfully converted to library overrides", - ob_proxy->id.name); - /* Remove the instance empty from this scene, the items now have an overridden collection - * instead. */ - if (is_override_instancing_object) { - BKE_scene_collections_object_remove(bmain, scene, ob_proxy_group, true); - } - reports->count.proxies_to_lib_overrides_success++; - } -} + ID *id; + struct IDRemapper *remapper = BKE_id_remapper_create(); + FOREACH_MAIN_ID_BEGIN (bmain, id) { -void BKE_lib_override_library_main_proxy_convert(Main *bmain, BlendFileReadReport *reports) -{ - LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) { - FOREACH_SCENE_OBJECT_BEGIN (scene, object) { - if (object->proxy_group == NULL) { + if (id->tag & LIB_TAG_DOIT && id->newid != NULL && id->lib == id_root_reference->lib) { + ID *id_override_new = id->newid; + ID *id_override_old = BLI_ghash_lookup(linkedref_to_old_override, id); + if (id_override_old == NULL) { continue; } - lib_override_library_proxy_convert_do(bmain, scene, object, reports); - } - FOREACH_SCENE_OBJECT_END; + BKE_id_remapper_add(remapper, id_override_old, id_override_new); + /* Remap no-main override IDs we just created too. */ + GHashIterator linkedref_to_old_override_iter; + GHASH_ITER (linkedref_to_old_override_iter, linkedref_to_old_override) { + ID *id_override_old_iter = BLI_ghashIterator_getValue(&linkedref_to_old_override_iter); + if ((id_override_old_iter->tag & LIB_TAG_NO_MAIN) == 0) { + continue; + } - FOREACH_SCENE_OBJECT_BEGIN (scene, object) { - if (object->proxy == NULL) { - continue; + BKE_libblock_relink_ex(bmain, + id_override_old_iter, + id_override_old, + id_override_new, + ID_REMAP_FORCE_USER_REFCOUNT | ID_REMAP_FORCE_NEVER_NULL_USAGE); } - - lib_override_library_proxy_convert_do(bmain, scene, object, reports); } - FOREACH_SCENE_OBJECT_END; } + FOREACH_MAIN_ID_END; - LISTBASE_FOREACH (Object *, object, &bmain->objects) { - if (ID_IS_LINKED(object)) { - if (object->proxy != NULL) { - CLOG_WARN(&LOG, "Did not try to convert linked proxy object '%s'", object->id.name); - reports->count.linked_proxies++; - } - continue; - } - - if (object->proxy_group != NULL || object->proxy != NULL) { - CLOG_WARN( - &LOG, "Proxy object '%s' failed to be converted to library override", object->id.name); - reports->count.proxies_to_lib_overrides_failures++; - } - } + /* Remap all IDs to use the new override. */ + BKE_libblock_remap_multiple(bmain, remapper, 0); + BKE_id_remapper_free(remapper); } static bool lib_override_library_resync(Main *bmain, @@ -1249,7 +1214,7 @@ static bool lib_override_library_resync(Main *bmain, * override IDs (including within the old overrides themselves, since those are tagged too * above). */ const bool success = BKE_lib_override_library_create_from_tag( - bmain, id_root_reference->lib, true); + bmain, NULL, id_root_reference->lib, true); if (!success) { return success; @@ -1312,32 +1277,9 @@ static bool lib_override_library_resync(Main *bmain, } FOREACH_MAIN_LISTBASE_END; - /* We need to remap old to new override usages in a separate loop, after all new overrides have + /* We remap old to new override usages in a separate loop, after all new overrides have * been added to Main. */ - FOREACH_MAIN_ID_BEGIN (bmain, id) { - if (id->tag & LIB_TAG_DOIT && id->newid != NULL && id->lib == id_root_reference->lib) { - ID *id_override_new = id->newid; - ID *id_override_old = BLI_ghash_lookup(linkedref_to_old_override, id); - - if (id_override_old != NULL) { - /* Remap all IDs to use the new override. */ - BKE_libblock_remap(bmain, id_override_old, id_override_new, 0); - /* Remap no-main override IDs we just created too. */ - GHashIterator linkedref_to_old_override_iter; - GHASH_ITER (linkedref_to_old_override_iter, linkedref_to_old_override) { - ID *id_override_old_iter = BLI_ghashIterator_getValue(&linkedref_to_old_override_iter); - if (id_override_old_iter->tag & LIB_TAG_NO_MAIN) { - BKE_libblock_relink_ex(bmain, - id_override_old_iter, - id_override_old, - id_override_new, - ID_REMAP_FORCE_USER_REFCOUNT | ID_REMAP_FORCE_NEVER_NULL_USAGE); - } - } - } - } - } - FOREACH_MAIN_ID_END; + lib_override_library_remap(bmain, id_root_reference, linkedref_to_old_override); BKE_main_collection_sync(bmain); @@ -1486,6 +1428,7 @@ static bool lib_override_library_resync(Main *bmain, lib_override_library_create_post_process(bmain, scene, view_layer, + NULL, id_root_reference, id_root, override_resync_residual_storage, @@ -1537,11 +1480,13 @@ static void lib_override_resync_tagging_finalize_recurse(Main *bmain, CLOG_ERROR( &LOG, "While processing indirect level %d, ID %s from lib %s of indirect level %d detected " - "as needing resync.", + "as needing resync, skipping.", library_indirect_level, id->name, id->lib->filepath, id->lib->temp_index); + id->tag &= ~LIB_TAG_LIB_OVERRIDE_NEED_RESYNC; + return; } MainIDRelationsEntry *entry = BLI_ghash_lookup(bmain->relations->relations_from_pointers, id); @@ -1908,7 +1853,7 @@ void BKE_lib_override_library_main_resync(Main *bmain, /* Essentially ensures that potentially new overrides of new objects will be instantiated. */ lib_override_library_create_post_process( - bmain, scene, view_layer, NULL, NULL, override_resync_residual_storage, true); + bmain, scene, view_layer, NULL, NULL, NULL, override_resync_residual_storage, true); if (BKE_collection_is_empty(override_resync_residual_storage)) { BKE_collection_delete(bmain, override_resync_residual_storage, true); @@ -2961,10 +2906,6 @@ void BKE_lib_override_library_main_update(Main *bmain) bool BKE_lib_override_library_id_is_user_deletable(struct Main *bmain, struct ID *id) { - if (!(ID_IS_LINKED(id) || ID_IS_OVERRIDE_LIBRARY(id))) { - return true; - } - /* The only strong known case currently are objects used by override collections. */ /* TODO: There are most likely other cases... This may need to be addressed in a better way at * some point. */ diff --git a/source/blender/blenkernel/intern/lib_override_proxy_conversion.c b/source/blender/blenkernel/intern/lib_override_proxy_conversion.c new file mode 100644 index 00000000000..d99a9b57a6e --- /dev/null +++ b/source/blender/blenkernel/intern/lib_override_proxy_conversion.c @@ -0,0 +1,176 @@ +/* + * 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) 2016 by Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup bke + */ + +#include "CLG_log.h" + +#include "MEM_guardedalloc.h" + +#include "BLI_linklist.h" + +/* Required for proxy to liboverrides conversion code. */ +#define DNA_DEPRECATED_ALLOW + +#include "DNA_ID.h" +#include "DNA_collection_types.h" +#include "DNA_object_types.h" +#include "DNA_scene_types.h" + +#include "DEG_depsgraph.h" + +#include "BKE_collection.h" +#include "BKE_idtype.h" +#include "BKE_lib_id.h" +#include "BKE_lib_override.h" +#include "BKE_main.h" + +#include "BLO_readfile.h" + +static CLG_LogRef LOG = {"bke.liboverride_proxy_conversion"}; + +bool BKE_lib_override_library_proxy_convert(Main *bmain, + Scene *scene, + ViewLayer *view_layer, + Object *ob_proxy) +{ + /* `proxy_group`, if defined, is the empty instantiating the collection from which the proxy is + * coming. */ + Object *ob_proxy_group = ob_proxy->proxy_group; + const bool is_override_instancing_object = ob_proxy_group != NULL; + ID *id_root = is_override_instancing_object ? &ob_proxy_group->instance_collection->id : + &ob_proxy->proxy->id; + ID *id_reference = is_override_instancing_object ? &ob_proxy_group->id : &ob_proxy->id; + + /* In some cases the instance collection of a proxy object may be local (see e.g. T83875). Not + * sure this is a valid state, but for now just abort the overriding process. */ + if (!ID_IS_OVERRIDABLE_LIBRARY_HIERARCHY(id_root)) { + if (ob_proxy->proxy != NULL) { + ob_proxy->proxy->proxy_from = NULL; + } + id_us_min((ID *)ob_proxy->proxy); + ob_proxy->proxy = ob_proxy->proxy_group = NULL; + return false; + } + + /* We manually convert the proxy object into a library override, further override handling will + * then be handled by `BKE_lib_override_library_create()` just as for a regular override + * creation. + */ + ob_proxy->proxy->id.tag |= LIB_TAG_DOIT; + ob_proxy->proxy->id.newid = &ob_proxy->id; + BKE_lib_override_library_init(&ob_proxy->id, &ob_proxy->proxy->id); + + ob_proxy->proxy->proxy_from = NULL; + ob_proxy->proxy = ob_proxy->proxy_group = NULL; + + DEG_id_tag_update(&ob_proxy->id, ID_RECALC_COPY_ON_WRITE); + + /* In case of proxy conversion, remap all local ID usages to linked IDs to their newly created + * overrides. Also do that for the IDs from the same lib as the proxy in case it is linked. + * While this might not be 100% the desired behavior, it is likely to be the case most of the + * time. Ref: T91711. */ + ID *id_iter; + FOREACH_MAIN_ID_BEGIN (bmain, id_iter) { + if (!ID_IS_LINKED(id_iter) || id_iter->lib == ob_proxy->id.lib) { + id_iter->tag |= LIB_TAG_DOIT; + } + } + FOREACH_MAIN_ID_END; + + return BKE_lib_override_library_create( + bmain, scene, view_layer, ob_proxy->id.lib, id_root, id_reference, NULL); +} + +static void lib_override_library_proxy_convert_do(Main *bmain, + Scene *scene, + Object *ob_proxy, + BlendFileReadReport *reports) +{ + Object *ob_proxy_group = ob_proxy->proxy_group; + const bool is_override_instancing_object = ob_proxy_group != NULL; + + const bool success = BKE_lib_override_library_proxy_convert(bmain, scene, NULL, ob_proxy); + + if (success) { + CLOG_INFO(&LOG, + 4, + "Proxy object '%s' successfully converted to library overrides", + ob_proxy->id.name); + /* Remove the instance empty from this scene, the items now have an overridden collection + * instead. */ + if (is_override_instancing_object) { + BKE_scene_collections_object_remove(bmain, scene, ob_proxy_group, true); + } + reports->count.proxies_to_lib_overrides_success++; + } +} + +void BKE_lib_override_library_main_proxy_convert(Main *bmain, BlendFileReadReport *reports) +{ + LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) { + LinkNodePair proxy_objects = {NULL}; + + FOREACH_SCENE_OBJECT_BEGIN (scene, object) { + if (object->proxy_group != NULL) { + BLI_linklist_append(&proxy_objects, object); + } + } + FOREACH_SCENE_OBJECT_END; + + FOREACH_SCENE_OBJECT_BEGIN (scene, object) { + if (object->proxy != NULL && object->proxy_group == NULL) { + BLI_linklist_append(&proxy_objects, object); + } + } + FOREACH_SCENE_OBJECT_END; + + for (LinkNode *proxy_object_iter = proxy_objects.list; proxy_object_iter != NULL; + proxy_object_iter = proxy_object_iter->next) { + Object *proxy_object = proxy_object_iter->link; + lib_override_library_proxy_convert_do(bmain, scene, proxy_object, reports); + } + + BLI_linklist_free(proxy_objects.list, NULL); + } + + LISTBASE_FOREACH (Object *, object, &bmain->objects) { + if (object->proxy_group != NULL || object->proxy != NULL) { + if (ID_IS_LINKED(object)) { + CLOG_WARN(&LOG, + "Linked proxy object '%s' from '%s' failed to be converted to library override", + object->id.name + 2, + object->id.lib->filepath); + } + else { + CLOG_WARN(&LOG, + "Proxy object '%s' failed to be converted to library override", + object->id.name + 2); + } + reports->count.proxies_to_lib_overrides_failures++; + if (object->proxy != NULL) { + object->proxy->proxy_from = NULL; + } + id_us_min((ID *)object->proxy); + object->proxy = object->proxy_group = NULL; + } + } +} diff --git a/source/blender/blenkernel/intern/lib_query.c b/source/blender/blenkernel/intern/lib_query.c index 1f20a84098c..f69fba5b540 100644 --- a/source/blender/blenkernel/intern/lib_query.c +++ b/source/blender/blenkernel/intern/lib_query.c @@ -471,7 +471,7 @@ bool BKE_library_id_can_use_idtype(ID *id_owner, const short id_type_used) return ELEM(id_type_used, ID_MA); case ID_WS: return ELEM(id_type_used, ID_SCR, ID_SCE); - case ID_HA: + case ID_CV: return ELEM(id_type_used, ID_MA); case ID_PT: return ELEM(id_type_used, ID_MA); @@ -517,7 +517,7 @@ static int foreach_libblock_id_users_callback(LibraryIDLinkCallbackData *cb_data IDUsersIter *iter = cb_data->user_data; if (*id_p) { - /* 'Loopback' ID pointers (the ugly 'from' ones, Object->proxy_from and Key->from). + /* 'Loopback' ID pointers (the ugly 'from' ones, like Key->from). * Those are not actually ID usage, we can ignore them here. */ if (cb_flag & IDWALK_CB_LOOPBACK) { @@ -768,7 +768,7 @@ static int foreach_libblock_used_linked_data_tag_clear_cb(LibraryIDLinkCallbackD bool *is_changed = cb_data->user_data; if (*id_p) { - /* The infamous 'from' pointers (Key.from, Object.proxy_from, ...). + /* The infamous 'from' pointers (Key.from, ...). * those are not actually ID usage, so we ignore them here. */ if (cb_flag & IDWALK_CB_LOOPBACK) { return IDWALK_RET_NOP; diff --git a/source/blender/blenkernel/intern/lib_remap.c b/source/blender/blenkernel/intern/lib_remap.c index ec97ca83703..a9de5b4a189 100644 --- a/source/blender/blenkernel/intern/lib_remap.c +++ b/source/blender/blenkernel/intern/lib_remap.c @@ -91,26 +91,18 @@ enum { ID_REMAP_IS_USER_ONE_SKIPPED = 1 << 1, /* There was some skipped 'user_one' usages of old_id. */ }; -static void foreach_libblock_remap_callback_skip(const ID *id_owner, - ID **id_ptr, +static void foreach_libblock_remap_callback_skip(const ID *UNUSED(id_owner), + ID **UNUSED(id_ptr), IDRemap *id_remap_data, const int cb_flag, const bool is_indirect, const bool is_reference, const bool is_never_null, - const bool is_obj, + const bool UNUSED(is_obj), const bool is_obj_editmode) { if (is_indirect) { id_remap_data->skipped_indirect++; - if (is_obj) { - Object *ob = (Object *)id_owner; - if (ob->data == *id_ptr && ob->proxy != NULL) { - /* And another 'Proudly brought to you by Proxy Hell' hack! - * This will allow us to avoid clearing 'LIB_EXTERN' flag of obdata of proxies... */ - id_remap_data->skipped_direct++; - } - } } else if (is_never_null || is_obj_editmode || is_reference) { id_remap_data->skipped_direct++; @@ -136,8 +128,7 @@ static void foreach_libblock_remap_callback_apply(ID *id_owner, const int cb_flag, const bool is_indirect, const bool is_never_null, - const bool force_user_refcount, - const bool is_obj_proxy) + const bool force_user_refcount) { if (!is_never_null) { *id_ptr = new_id; @@ -170,16 +161,9 @@ static void foreach_libblock_remap_callback_apply(ID *id_owner, /* We cannot affect old_id->us directly, LIB_TAG_EXTRAUSER(_SET) * are assumed to be set as needed, that extra user is processed in final handling. */ } - if (!is_indirect || is_obj_proxy) { + if (!is_indirect) { id_remap_data->status |= ID_REMAP_IS_LINKED_DIRECT; } - /* We need to remap proxy_from pointer of remapped proxy... sigh. */ - if (is_obj_proxy && new_id != NULL) { - Object *ob = (Object *)id_owner; - if (ob->proxy == (Object *)new_id) { - ob->proxy->proxy_from = ob; - } - } } static int foreach_libblock_remap_callback(LibraryIDLinkCallbackData *cb_data) @@ -221,12 +205,9 @@ static int foreach_libblock_remap_callback(LibraryIDLinkCallbackData *cb_data) const bool is_reference = (cb_flag & IDWALK_CB_OVERRIDE_LIBRARY_REFERENCE) != 0; const bool is_indirect = (cb_flag & IDWALK_CB_INDIRECT_USAGE) != 0; const bool skip_indirect = (id_remap_data->flag & ID_REMAP_SKIP_INDIRECT_USAGE) != 0; - /* NOTE: proxy usage implies LIB_TAG_EXTERN, so on this aspect it is direct, - * on the other hand since they get reset to lib data on file open/reload it is indirect too. - * Edit Mode is also a 'skip direct' case. */ const bool is_obj = (GS(id_owner->name) == ID_OB); - const bool is_obj_proxy = (is_obj && - (((Object *)id_owner)->proxy || ((Object *)id_owner)->proxy_group)); + /* NOTE: Edit Mode is a 'skip direct' case, unless specifically requested, obdata should not be + * remapped in this situation. */ const bool is_obj_editmode = (is_obj && BKE_object_is_in_editmode((Object *)id_owner) && (id_remap_data->flag & ID_REMAP_FORCE_OBDATA_IN_EDITMODE) == 0); const bool is_never_null = ((cb_flag & IDWALK_CB_NEVER_NULL) && (new_id == NULL) && @@ -281,8 +262,7 @@ static int foreach_libblock_remap_callback(LibraryIDLinkCallbackData *cb_data) cb_flag, is_indirect, is_never_null, - force_user_refcount, - is_obj_proxy); + force_user_refcount); } return IDWALK_RET_NOP; @@ -430,10 +410,7 @@ 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) | - ((remap_flags & ID_REMAP_FORCE_INTERNAL_RUNTIME_POINTERS) != 0 ? + const int foreach_id_flags = ((remap_flags & ID_REMAP_FORCE_INTERNAL_RUNTIME_POINTERS) != 0 ? IDWALK_DO_INTERNAL_RUNTIME_POINTERS : IDWALK_NOP); @@ -510,11 +487,18 @@ static void libblock_remap_data( #endif } -void BKE_libblock_remap_locked(Main *bmain, void *old_idv, void *new_idv, const short remap_flags) +typedef struct LibblockRemapMultipleUserData { + Main *bmain; + short remap_flags; +} LibBlockRemapMultipleUserData; + +static void libblock_remap_foreach_idpair_cb(ID *old_id, ID *new_id, void *user_data) { + LibBlockRemapMultipleUserData *data = user_data; + Main *bmain = data->bmain; + const short remap_flags = data->remap_flags; + IDRemap id_remap_data; - ID *old_id = old_idv; - ID *new_id = new_idv; int skipped_direct, skipped_refcounted; BLI_assert(old_id != NULL); @@ -527,13 +511,6 @@ void BKE_libblock_remap_locked(Main *bmain, void *old_idv, void *new_idv, const free_notifier_reference_cb(old_id); } - /* We assume editors do not hold references to their IDs... This is false in some cases - * (Image is especially tricky here), - * editors' code is to handle refcount (id->us) itself then. */ - if (remap_editor_id_reference_cb) { - remap_editor_id_reference_cb(old_id, new_id); - } - skipped_direct = id_remap_data.skipped_direct; skipped_refcounted = id_remap_data.skipped_refcounted; @@ -581,7 +558,7 @@ void BKE_libblock_remap_locked(Main *bmain, void *old_idv, void *new_idv, const case ID_ME: case ID_CU: case ID_MB: - case ID_HA: + case ID_CV: case ID_PT: case ID_VO: if (new_id) { /* Only affects us in case obdata was relinked (changed). */ @@ -606,6 +583,41 @@ void BKE_libblock_remap_locked(Main *bmain, void *old_idv, void *new_idv, const DEG_relations_tag_update(bmain); } +void BKE_libblock_remap_multiple_locked(Main *bmain, + const struct IDRemapper *mappings, + const short remap_flags) +{ + if (BKE_id_remapper_is_empty(mappings)) { + /* Early exit nothing to do. */ + return; + } + + LibBlockRemapMultipleUserData user_data; + user_data.bmain = bmain; + user_data.remap_flags = remap_flags; + BKE_id_remapper_iter(mappings, libblock_remap_foreach_idpair_cb, &user_data); + + /* We assume editors do not hold references to their IDs... This is false in some cases + * (Image is especially tricky here), + * editors' code is to handle refcount (id->us) itself then. */ + if (remap_editor_id_reference_cb) { + remap_editor_id_reference_cb(mappings); + } + + /* Full rebuild of DEG! */ + DEG_relations_tag_update(bmain); +} + +void BKE_libblock_remap_locked(Main *bmain, void *old_idv, void *new_idv, const short remap_flags) +{ + struct IDRemapper *remapper = BKE_id_remapper_create(); + ID *old_id = old_idv; + ID *new_id = new_idv; + BKE_id_remapper_add(remapper, old_id, new_id); + BKE_libblock_remap_multiple_locked(bmain, remapper, remap_flags); + BKE_id_remapper_free(remapper); +} + void BKE_libblock_remap(Main *bmain, void *old_idv, void *new_idv, const short remap_flags) { BKE_main_lock(bmain); @@ -615,6 +627,17 @@ void BKE_libblock_remap(Main *bmain, void *old_idv, void *new_idv, const short r BKE_main_unlock(bmain); } +void BKE_libblock_remap_multiple(Main *bmain, + const struct IDRemapper *mappings, + const short remap_flags) +{ + BKE_main_lock(bmain); + + BKE_libblock_remap_multiple_locked(bmain, mappings, remap_flags); + + BKE_main_unlock(bmain); +} + void BKE_libblock_unlink(Main *bmain, void *idv, const bool do_flag_never_null, diff --git a/source/blender/blenkernel/intern/main.c b/source/blender/blenkernel/intern/main.c index 64731be57ac..53b1a9c9e16 100644 --- a/source/blender/blenkernel/intern/main.c +++ b/source/blender/blenkernel/intern/main.c @@ -643,8 +643,8 @@ ListBase *which_libbase(Main *bmain, short type) return &(bmain->cachefiles); case ID_WS: return &(bmain->workspaces); - case ID_HA: - return &(bmain->hairs); + case ID_CV: + return &(bmain->hair_curves); case ID_PT: return &(bmain->pointclouds); case ID_VO: @@ -688,7 +688,7 @@ int set_listbasepointers(Main *bmain, ListBase *lb[/*INDEX_ID_MAX*/]) lb[INDEX_ID_ME] = &(bmain->meshes); lb[INDEX_ID_CU] = &(bmain->curves); lb[INDEX_ID_MB] = &(bmain->metaballs); - lb[INDEX_ID_HA] = &(bmain->hairs); + lb[INDEX_ID_CV] = &(bmain->hair_curves); lb[INDEX_ID_PT] = &(bmain->pointclouds); lb[INDEX_ID_VO] = &(bmain->volumes); diff --git a/source/blender/blenkernel/intern/material.c b/source/blender/blenkernel/intern/material.c index d6035887790..1c1b2c2cd27 100644 --- a/source/blender/blenkernel/intern/material.c +++ b/source/blender/blenkernel/intern/material.c @@ -36,10 +36,10 @@ #include "DNA_anim_types.h" #include "DNA_collection_types.h" #include "DNA_curve_types.h" +#include "DNA_curves_types.h" #include "DNA_customdata_types.h" #include "DNA_defaults.h" #include "DNA_gpencil_types.h" -#include "DNA_hair_types.h" #include "DNA_material_types.h" #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" @@ -341,9 +341,9 @@ Material ***BKE_object_material_array_p(Object *ob) bGPdata *gpd = ob->data; return &(gpd->mat); } - if (ob->type == OB_HAIR) { - Hair *hair = ob->data; - return &(hair->mat); + if (ob->type == OB_CURVES) { + Curves *curves = ob->data; + return &(curves->mat); } if (ob->type == OB_POINTCLOUD) { PointCloud *pointcloud = ob->data; @@ -374,9 +374,9 @@ short *BKE_object_material_len_p(Object *ob) bGPdata *gpd = ob->data; return &(gpd->totcol); } - if (ob->type == OB_HAIR) { - Hair *hair = ob->data; - return &(hair->totcol); + if (ob->type == OB_CURVES) { + Curves *curves = ob->data; + return &(curves->totcol); } if (ob->type == OB_POINTCLOUD) { PointCloud *pointcloud = ob->data; @@ -403,8 +403,8 @@ Material ***BKE_id_material_array_p(ID *id) return &(((MetaBall *)id)->mat); case ID_GD: return &(((bGPdata *)id)->mat); - case ID_HA: - return &(((Hair *)id)->mat); + case ID_CV: + return &(((Curves *)id)->mat); case ID_PT: return &(((PointCloud *)id)->mat); case ID_VO: @@ -429,8 +429,8 @@ short *BKE_id_material_len_p(ID *id) return &(((MetaBall *)id)->totcol); case ID_GD: return &(((bGPdata *)id)->totcol); - case ID_HA: - return &(((Hair *)id)->totcol); + case ID_CV: + return &(((Curves *)id)->totcol); case ID_PT: return &(((PointCloud *)id)->totcol); case ID_VO: @@ -454,7 +454,7 @@ static void material_data_index_remove_id(ID *id, short index) BKE_curve_material_index_remove((Curve *)id, index); break; case ID_MB: - case ID_HA: + case ID_CV: case ID_PT: case ID_VO: /* No material indices for these object data types. */ @@ -509,7 +509,7 @@ static void material_data_index_clear_id(ID *id) BKE_curve_material_index_clear((Curve *)id); break; case ID_MB: - case ID_HA: + case ID_CV: case ID_PT: case ID_VO: /* No material indices for these object data types. */ @@ -720,8 +720,9 @@ static ID *get_evaluated_object_data_with_materials(Object *ob) /* Meshes in edit mode need special handling. */ if (ob->type == OB_MESH && ob->mode == OB_MODE_EDIT) { Mesh *mesh = ob->data; - if (mesh->edit_mesh && mesh->edit_mesh->mesh_eval_final) { - data = &mesh->edit_mesh->mesh_eval_final->id; + Mesh *editmesh_eval_final = BKE_object_get_editmesh_eval_final(ob); + if (mesh->edit_mesh && editmesh_eval_final) { + data = &editmesh_eval_final->id; } } return data; diff --git a/source/blender/blenkernel/intern/mesh.cc b/source/blender/blenkernel/intern/mesh.cc index 9b0cf17a424..c1b1f62a881 100644 --- a/source/blender/blenkernel/intern/mesh.cc +++ b/source/blender/blenkernel/intern/mesh.cc @@ -173,19 +173,26 @@ static void mesh_copy_data(Main *bmain, ID *id_dst, const ID *id_src, const int BKE_mesh_assert_normals_dirty_or_calculated(mesh_dst); } +void BKE_mesh_free_editmesh(struct Mesh *mesh) +{ + if (mesh->edit_mesh == nullptr) { + return; + } + + if (mesh->edit_mesh->is_shallow_copy == false) { + BKE_editmesh_free_data(mesh->edit_mesh); + } + MEM_freeN(mesh->edit_mesh); + mesh->edit_mesh = nullptr; +} + static void mesh_free_data(ID *id) { Mesh *mesh = (Mesh *)id; BLI_freelistN(&mesh->vertex_group_names); - if (mesh->edit_mesh) { - if (mesh->edit_mesh->is_shallow_copy == false) { - BKE_editmesh_free_data(mesh->edit_mesh); - } - MEM_freeN(mesh->edit_mesh); - mesh->edit_mesh = nullptr; - } + BKE_mesh_free_editmesh(mesh); BKE_mesh_runtime_free_data(mesh); mesh_clear_geometry(mesh); @@ -678,6 +685,17 @@ static int customdata_compare( } break; } + case CD_PROP_INT8: { + const int8_t *l1_data = (int8_t *)l1->data; + const int8_t *l2_data = (int8_t *)l2->data; + + for (int i = 0; i < total_length; i++) { + if (l1_data[i] != l2_data[i]) { + return MESHCMP_ATTRIBUTE_VALUE_MISMATCH; + } + } + break; + } case CD_PROP_BOOL: { const bool *l1_data = (bool *)l1->data; const bool *l2_data = (bool *)l2->data; diff --git a/source/blender/blenkernel/intern/mesh_convert.cc b/source/blender/blenkernel/intern/mesh_convert.cc index cbc772d93a6..7d5f156040d 100644 --- a/source/blender/blenkernel/intern/mesh_convert.cc +++ b/source/blender/blenkernel/intern/mesh_convert.cc @@ -1102,8 +1102,11 @@ static Mesh *mesh_new_from_mesh_object(Depsgraph *depsgraph, Mesh *mesh_input = (Mesh *)object->data; /* If we are in edit mode, use evaluated mesh from edit structure, matching to what * viewport is using for visualization. */ - if (mesh_input->edit_mesh != nullptr && mesh_input->edit_mesh->mesh_eval_final) { - mesh_input = mesh_input->edit_mesh->mesh_eval_final; + if (mesh_input->edit_mesh != nullptr) { + Mesh *editmesh_eval_final = BKE_object_get_editmesh_eval_final(object); + if (editmesh_eval_final != nullptr) { + mesh_input = editmesh_eval_final; + } } return mesh_new_from_mesh(object, mesh_input); } diff --git a/source/blender/blenkernel/intern/mesh_iterators.c b/source/blender/blenkernel/intern/mesh_iterators.c index b77d0e38f0f..4fb03c7b329 100644 --- a/source/blender/blenkernel/intern/mesh_iterators.c +++ b/source/blender/blenkernel/intern/mesh_iterators.c @@ -34,13 +34,23 @@ #include "MEM_guardedalloc.h" +/* General note on iterating verts/loops/edges/polys and end mode. + * + * The edit mesh pointer is set for both final and cage meshes in both cases when there are + * modifiers applied and not. This helps consistency of checks in the draw manager, where the + * existence of the edit mesh pointer does not depend on object configuration. + * + * For the iterating, however, we need to follow the `CD_ORIGINDEX` code paths when there are + * modifiers applied on the cage. In the code terms it means that the check for the edit mode code + * path needs to consist of both edit mesh and edit data checks. */ + void BKE_mesh_foreach_mapped_vert( Mesh *mesh, void (*func)(void *userData, int index, const float co[3], const float no[3]), void *userData, MeshForeachFlag flag) { - if (mesh->edit_mesh != NULL) { + if (mesh->edit_mesh != NULL && mesh->runtime.edit_data != NULL) { BMEditMesh *em = mesh->edit_mesh; BMesh *bm = em->bm; BMIter iter; @@ -100,7 +110,7 @@ void BKE_mesh_foreach_mapped_edge( void (*func)(void *userData, int index, const float v0co[3], const float v1co[3]), void *userData) { - if (mesh->edit_mesh != NULL) { + if (mesh->edit_mesh != NULL && mesh->runtime.edit_data) { BMEditMesh *em = mesh->edit_mesh; BMesh *bm = em->bm; BMIter iter; @@ -158,7 +168,7 @@ void BKE_mesh_foreach_mapped_loop(Mesh *mesh, /* We can't use dm->getLoopDataLayout(dm) here, * we want to always access dm->loopData, EditDerivedBMesh would * return loop data from bmesh itself. */ - if (mesh->edit_mesh != NULL) { + if (mesh->edit_mesh != NULL && mesh->runtime.edit_data) { BMEditMesh *em = mesh->edit_mesh; BMesh *bm = em->bm; BMIter iter; @@ -231,7 +241,7 @@ void BKE_mesh_foreach_mapped_face_center( void *userData, MeshForeachFlag flag) { - if (mesh->edit_mesh != NULL) { + if (mesh->edit_mesh != NULL && mesh->runtime.edit_data != NULL) { BMEditMesh *em = mesh->edit_mesh; BMesh *bm = em->bm; const float(*polyCos)[3]; diff --git a/source/blender/blenkernel/intern/mesh_merge.c b/source/blender/blenkernel/intern/mesh_merge.c index 134a1344f83..3c01d5a4a50 100644 --- a/source/blender/blenkernel/intern/mesh_merge.c +++ b/source/blender/blenkernel/intern/mesh_merge.c @@ -27,6 +27,7 @@ #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" +#include "BLI_bitmap.h" #include "BLI_edgehash.h" #include "BLI_ghash.h" #include "BLI_utildefines.h" @@ -351,6 +352,8 @@ Mesh *BKE_mesh_merge_verts(Mesh *mesh, &poly_map, &poly_map_mem, mesh->mpoly, mesh->mloop, totvert, totpoly, totloop); } /* done preparing for fast poly compare */ + BLI_bitmap *vert_tag = BLI_BITMAP_NEW(mesh->totvert, __func__); + mp = mesh->mpoly; mv = mesh->mvert; for (i = 0; i < totpoly; i++, mp++) { @@ -365,11 +368,11 @@ Mesh *BKE_mesh_merge_verts(Mesh *mesh, if (vtargetmap[ml->v] == -1) { all_vertices_merged = false; /* This will be used to check for poly using several time the same vert. */ - mv[ml->v].flag &= ~ME_VERT_TMP_TAG; + BLI_BITMAP_DISABLE(vert_tag, ml->v); } else { /* This will be used to check for poly using several time the same vert. */ - mv[vtargetmap[ml->v]].flag &= ~ME_VERT_TMP_TAG; + BLI_BITMAP_DISABLE(vert_tag, vtargetmap[ml->v]); } } @@ -457,8 +460,8 @@ Mesh *BKE_mesh_merge_verts(Mesh *mesh, #endif /* A loop is only valid if its matching edge is, * and it's not reusing a vertex already used by this poly. */ - if (LIKELY((newe[ml->e] != -1) && ((mv[mlv].flag & ME_VERT_TMP_TAG) == 0))) { - mv[mlv].flag |= ME_VERT_TMP_TAG; + if (LIKELY((newe[ml->e] != -1) && !BLI_BITMAP_TEST(vert_tag, mlv))) { + BLI_BITMAP_ENABLE(vert_tag, mlv); if (UNLIKELY(last_valid_ml != NULL && need_edge_from_last_valid_ml)) { /* We need to create a new edge between last valid loop and this one! */ @@ -644,6 +647,8 @@ Mesh *BKE_mesh_merge_verts(Mesh *mesh, MEM_freeN(oldl); MEM_freeN(oldp); + MEM_freeN(vert_tag); + BLI_edgehash_free(ehash, NULL); if (poly_map != NULL) { diff --git a/source/blender/blenkernel/intern/mesh_mirror.c b/source/blender/blenkernel/intern/mesh_mirror.c index abc0b518d92..ec3655ac491 100644 --- a/source/blender/blenkernel/intern/mesh_mirror.c +++ b/source/blender/blenkernel/intern/mesh_mirror.c @@ -420,7 +420,7 @@ Mesh *BKE_mesh_mirror_apply_mirror_on_axis_for_modifier(MirrorModifierData *mmd, /* calculate custom normals into loop_normals, then mirror first half into second half */ BKE_mesh_normals_loop_split(result->mvert, - BKE_mesh_vertex_normals_ensure(mesh), + BKE_mesh_vertex_normals_ensure(result), result->totvert, result->medge, result->totedge, @@ -428,7 +428,7 @@ Mesh *BKE_mesh_mirror_apply_mirror_on_axis_for_modifier(MirrorModifierData *mmd, loop_normals, totloop, result->mpoly, - BKE_mesh_poly_normals_ensure(mesh), + BKE_mesh_poly_normals_ensure(result), totpoly, true, mesh->smoothresh, diff --git a/source/blender/blenkernel/intern/mesh_validate.c b/source/blender/blenkernel/intern/mesh_validate.c index 005c916b4e0..a5ba2767301 100644 --- a/source/blender/blenkernel/intern/mesh_validate.c +++ b/source/blender/blenkernel/intern/mesh_validate.c @@ -28,6 +28,7 @@ #include "CLG_log.h" +#include "BLI_bitmap.h" #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" #include "DNA_object_types.h" @@ -560,6 +561,8 @@ bool BKE_mesh_validate_arrays(Mesh *mesh, * so be sure to leave at most one poly per loop! */ { + BLI_bitmap *vert_tag = BLI_BITMAP_NEW(mesh->totvert, __func__); + SortPoly *sort_polys = MEM_callocN(sizeof(SortPoly) * totpoly, "mesh validate's sort_polys"); SortPoly *prev_sp, *sp = sort_polys; int prev_end; @@ -608,7 +611,7 @@ bool BKE_mesh_validate_arrays(Mesh *mesh, * so we have to ensure here all verts of current poly are cleared. */ for (j = 0, ml = &mloops[sp->loopstart]; j < mp->totloop; j++, ml++) { if (ml->v < totvert) { - mverts[ml->v].flag &= ~ME_VERT_TMP_TAG; + BLI_BITMAP_DISABLE(vert_tag, ml->v); } } @@ -619,12 +622,12 @@ bool BKE_mesh_validate_arrays(Mesh *mesh, PRINT_ERR("\tLoop %u has invalid vert reference (%u)", sp->loopstart + j, ml->v); sp->invalid = true; } - else if (mverts[ml->v].flag & ME_VERT_TMP_TAG) { + else if (BLI_BITMAP_TEST(vert_tag, ml->v)) { PRINT_ERR("\tPoly %u has duplicated vert reference at corner (%u)", i, j); sp->invalid = true; } else { - mverts[ml->v].flag |= ME_VERT_TMP_TAG; + BLI_BITMAP_ENABLE(vert_tag, ml->v); } *v = ml->v; } @@ -698,6 +701,8 @@ bool BKE_mesh_validate_arrays(Mesh *mesh, } } + MEM_freeN(vert_tag); + /* Second check pass, testing polys using the same verts. */ qsort(sort_polys, totpoly, sizeof(SortPoly), search_poly_cmp); sp = prev_sp = sort_polys; diff --git a/source/blender/blenkernel/intern/modifier.c b/source/blender/blenkernel/intern/modifier.c index 3f9faa415cb..829ef08a8fb 100644 --- a/source/blender/blenkernel/intern/modifier.c +++ b/source/blender/blenkernel/intern/modifier.c @@ -290,6 +290,16 @@ ModifierData *BKE_modifiers_findby_name(const Object *ob, const char *name) return BLI_findstring(&(ob->modifiers), name, offsetof(ModifierData, name)); } +ModifierData *BKE_modifiers_findby_session_uuid(const Object *ob, const SessionUUID *session_uuid) +{ + LISTBASE_FOREACH (ModifierData *, md, &ob->modifiers) { + if (BLI_session_uuid_is_equal(&md->session_uuid, session_uuid)) { + return md; + } + } + return NULL; +} + void BKE_modifiers_clear_errors(Object *ob) { LISTBASE_FOREACH (ModifierData *, md, &ob->modifiers) { @@ -439,9 +449,7 @@ void BKE_modifier_set_error(const Object *ob, ModifierData *md, const char *_for #ifndef NDEBUG if ((md->mode & eModifierMode_Virtual) == 0) { /* Ensure correct object is passed in. */ - const Object *ob_orig = (Object *)DEG_get_original_id((ID *)&ob->id); - const ModifierData *md_orig = md->orig_modifier_data ? md->orig_modifier_data : md; - BLI_assert(BLI_findindex(&ob_orig->modifiers, md_orig) != -1); + BLI_assert(BKE_modifier_get_original(ob, md) != NULL); } #endif @@ -1036,8 +1044,11 @@ Mesh *BKE_modifier_get_evaluated_mesh_from_evaluated_object(Object *ob_eval, BMEditMesh *em = BKE_editmesh_from_object(ob_eval); /* 'em' might not exist yet in some cases, just after loading a .blend file, see T57878. */ if (em != NULL) { - me = (get_cage_mesh && em->mesh_eval_cage != NULL) ? em->mesh_eval_cage : - em->mesh_eval_final; + Mesh *editmesh_eval_final = BKE_object_get_editmesh_eval_final(ob_eval); + Mesh *editmesh_eval_cage = BKE_object_get_editmesh_eval_cage(ob_eval); + + me = (get_cage_mesh && editmesh_eval_cage != NULL) ? editmesh_eval_cage : + editmesh_eval_final; } } if (me == NULL) { @@ -1049,12 +1060,10 @@ Mesh *BKE_modifier_get_evaluated_mesh_from_evaluated_object(Object *ob_eval, return me; } -ModifierData *BKE_modifier_get_original(ModifierData *md) +ModifierData *BKE_modifier_get_original(const Object *object, ModifierData *md) { - if (md->orig_modifier_data == NULL) { - return md; - } - return md->orig_modifier_data; + const Object *object_orig = DEG_get_original_object((Object *)object); + return BKE_modifiers_findby_session_uuid(object_orig, &md->session_uuid); } struct ModifierData *BKE_modifier_get_evaluated(Depsgraph *depsgraph, @@ -1065,7 +1074,7 @@ struct ModifierData *BKE_modifier_get_evaluated(Depsgraph *depsgraph, if (object_eval == object) { return md; } - return BKE_modifiers_findby_name(object_eval, md->name); + return BKE_modifiers_findby_session_uuid(object_eval, &md->session_uuid); } void BKE_modifier_check_uuids_unique_and_report(const Object *object) @@ -1179,8 +1188,8 @@ void BKE_modifier_blend_write(BlendWriter *writer, ListBase *modbase) #if 0 CollisionModifierData *collmd = (CollisionModifierData *)md; - // TODO: CollisionModifier should use pointcache - // + have proper reset events before enabling this + /* TODO: CollisionModifier should use pointcache + * + have proper reset events before enabling this. */ writestruct(wd, DATA, MVert, collmd->numverts, collmd->x); writestruct(wd, DATA, MVert, collmd->numverts, collmd->xnew); writestruct(wd, DATA, MFace, collmd->numfaces, collmd->mfaces); diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc index 8baffff9ecf..a1c10a733ce 100644 --- a/source/blender/blenkernel/intern/node.cc +++ b/source/blender/blenkernel/intern/node.cc @@ -648,7 +648,6 @@ static void ntree_blend_write(BlendWriter *writer, ID *id, const void *id_addres bNodeTree *ntree = (bNodeTree *)id; /* Clean up, important in undo case to reduce false detection of changed datablocks. */ - ntree->init = 0; /* to set callbacks and force setting types */ ntree->is_updating = false; ntree->typeinfo = nullptr; ntree->interface_type = nullptr; @@ -677,7 +676,6 @@ static void direct_link_node_socket(BlendDataReader *reader, bNodeSocket *sock) void ntreeBlendReadData(BlendDataReader *reader, bNodeTree *ntree) { /* NOTE: writing and reading goes in sync, for speed. */ - ntree->init = 0; /* to set callbacks and force setting types */ ntree->is_updating = false; ntree->typeinfo = nullptr; ntree->interface_type = nullptr; @@ -1145,8 +1143,6 @@ static void ntree_set_typeinfo(bNodeTree *ntree, bNodeTreeType *typeinfo) } else { ntree->typeinfo = &NodeTreeTypeUndefined; - - ntree->init &= ~NTREE_TYPE_INIT; } /* Deprecated integer type. */ @@ -1177,8 +1173,6 @@ static void node_set_typeinfo(const struct bContext *C, } else { node->typeinfo = &NodeTypeUndefined; - - ntree->init &= ~NTREE_TYPE_INIT; } } @@ -1199,8 +1193,6 @@ static void node_socket_set_typeinfo(bNodeTree *ntree, } else { sock->typeinfo = &NodeSocketTypeUndefined; - - ntree->init &= ~NTREE_TYPE_INIT; } BKE_ntree_update_tag_socket_type(ntree, sock); } @@ -1218,8 +1210,6 @@ static void update_typeinfo(Main *bmain, } FOREACH_NODETREE_BEGIN (bmain, ntree, id) { - ntree->init |= NTREE_TYPE_INIT; - if (treetype && STREQ(ntree->idname, treetype->idname)) { ntree_set_typeinfo(ntree, unregister ? nullptr : treetype); } @@ -1260,8 +1250,6 @@ static void update_typeinfo(Main *bmain, void ntreeSetTypes(const struct bContext *C, bNodeTree *ntree) { - ntree->init |= NTREE_TYPE_INIT; - ntree_set_typeinfo(ntree, ntreeTypeFind(ntree->idname)); LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { @@ -2674,11 +2662,6 @@ bNodeTree *ntreeAddTree(Main *bmain, const char *name, const char *idname) ntree->id.flag |= LIB_EMBEDDED_DATA; } - /* Types are fully initialized at this point, - * if an undefined node is added later this will be reset. - */ - ntree->init |= NTREE_TYPE_INIT; - BLI_strncpy(ntree->idname, idname, sizeof(ntree->idname)); ntree_set_typeinfo(ntree, ntreeTypeFind(idname)); @@ -4503,6 +4486,8 @@ static void registerCompositNodes() register_node_type_cmp_sepycca(); register_node_type_cmp_combycca(); register_node_type_cmp_premulkey(); + register_node_type_cmp_separate_xyz(); + register_node_type_cmp_combine_xyz(); register_node_type_cmp_diff_matte(); register_node_type_cmp_distance_matte(); @@ -4668,6 +4653,7 @@ static void registerTextureNodes() register_node_type_sh_tangent(); register_node_type_sh_normal_map(); register_node_type_sh_hair_info(); + register_node_type_sh_point_info(); register_node_type_sh_volume_info(); register_node_type_tex_checker(); @@ -4801,6 +4787,7 @@ static void registerGeometryNodes() register_node_type_geo_join_geometry(); register_node_type_geo_material_replace(); register_node_type_geo_material_selection(); + register_node_type_geo_merge_by_distance(); register_node_type_geo_mesh_primitive_circle(); register_node_type_geo_mesh_primitive_cone(); register_node_type_geo_mesh_primitive_cube(); diff --git a/source/blender/blenkernel/intern/node_tree_update.cc b/source/blender/blenkernel/intern/node_tree_update.cc index be9777281cb..904a0de9a90 100644 --- a/source/blender/blenkernel/intern/node_tree_update.cc +++ b/source/blender/blenkernel/intern/node_tree_update.cc @@ -34,6 +34,7 @@ #include "NOD_node_declaration.hh" #include "NOD_node_tree_ref.hh" +#include "NOD_texture.h" #include "DEG_depsgraph_query.h" @@ -272,6 +273,12 @@ static OutputFieldDependency find_group_output_dependencies( while (!sockets_to_check.is_empty()) { const InputSocketRef *input_socket = sockets_to_check.pop(); + if (!input_socket->is_directly_linked() && + !field_state_by_socket_id[input_socket->id()].is_single) { + /* This socket uses a field as input by default. */ + return OutputFieldDependency::ForFieldSource(); + } + for (const OutputSocketRef *origin_socket : input_socket->directly_linked_sockets()) { const NodeRef &origin_node = origin_socket->node(); const SocketFieldState &origin_state = field_state_by_socket_id[origin_socket->id()]; diff --git a/source/blender/blenkernel/intern/object.cc b/source/blender/blenkernel/intern/object.cc index 5045851d7f9..8faae6efb26 100644 --- a/source/blender/blenkernel/intern/object.cc +++ b/source/blender/blenkernel/intern/object.cc @@ -87,7 +87,9 @@ #include "BKE_camera.h" #include "BKE_collection.h" #include "BKE_constraint.h" +#include "BKE_crazyspace.h" #include "BKE_curve.h" +#include "BKE_curves.h" #include "BKE_deform.h" #include "BKE_displist.h" #include "BKE_duplilist.h" @@ -102,7 +104,6 @@ #include "BKE_gpencil.h" #include "BKE_gpencil_geom.h" #include "BKE_gpencil_modifier.h" -#include "BKE_hair.h" #include "BKE_icons.h" #include "BKE_idprop.h" #include "BKE_idtype.h" @@ -324,66 +325,6 @@ static void object_free_data(ID *id) BKE_previewimg_free(&ob->preview); } -static void object_make_local(Main *bmain, ID *id, const int flags) -{ - if (!ID_IS_LINKED(id)) { - return; - } - - Object *ob = (Object *)id; - const bool lib_local = (flags & LIB_ID_MAKELOCAL_FULL_LIBRARY) != 0; - const bool clear_proxy = (flags & LIB_ID_MAKELOCAL_OBJECT_NO_PROXY_CLEARING) == 0; - bool force_local = (flags & LIB_ID_MAKELOCAL_FORCE_LOCAL) != 0; - bool force_copy = (flags & LIB_ID_MAKELOCAL_FORCE_COPY) != 0; - BLI_assert(force_copy == false || force_copy != force_local); - - bool is_local = false, is_lib = false; - - /* - only lib users: do nothing (unless force_local is set) - * - only local users: set flag - * - mixed: make copy - * In case we make a whole lib's content local, - * we always want to localize, and we skip remapping (done later). - */ - - if (!force_local && !force_copy) { - BKE_library_ID_test_usages(bmain, ob, &is_local, &is_lib); - if (lib_local || is_local) { - if (!is_lib) { - force_local = true; - } - else { - force_copy = true; - } - } - } - - if (force_local) { - BKE_lib_id_clear_library_data(bmain, &ob->id, flags); - BKE_lib_id_expand_local(bmain, &ob->id, flags); - if (clear_proxy) { - if (ob->proxy_from != nullptr) { - ob->proxy_from->proxy = nullptr; - ob->proxy_from->proxy_group = nullptr; - } - ob->proxy = ob->proxy_from = ob->proxy_group = nullptr; - } - } - else if (force_copy) { - Object *ob_new = (Object *)BKE_id_copy(bmain, &ob->id); - id_us_min(&ob_new->id); - - ob_new->proxy = ob_new->proxy_from = ob_new->proxy_group = nullptr; - - /* setting newid is mandatory for complex make_lib_local logic... */ - ID_NEW_SET(ob, ob_new); - - if (!lib_local) { - BKE_libblock_remap(bmain, ob, ob_new, ID_REMAP_SKIP_INDIRECT_USAGE); - } - } -} - static void library_foreach_modifiersForeachIDLink(void *user_data, Object *UNUSED(object), ID **id_pointer, @@ -439,51 +380,25 @@ static void object_foreach_id(ID *id, LibraryForeachIDData *data) { Object *object = (Object *)id; - /* Object is special, proxies make things hard... */ - const int proxy_cb_flag = ((BKE_lib_query_foreachid_process_flags_get(data) & - IDWALK_NO_INDIRECT_PROXY_DATA_USAGE) == 0 && - (object->proxy || object->proxy_group)) ? - IDWALK_CB_INDIRECT_USAGE : - 0; - /* object data special case */ if (object->type == OB_EMPTY) { /* empty can have nullptr or Image */ - BKE_LIB_FOREACHID_PROCESS_ID(data, object->data, proxy_cb_flag | IDWALK_CB_USER); + BKE_LIB_FOREACHID_PROCESS_ID(data, object->data, IDWALK_CB_USER); } else { /* when set, this can't be nullptr */ if (object->data) { - BKE_LIB_FOREACHID_PROCESS_ID( - data, object->data, proxy_cb_flag | IDWALK_CB_USER | IDWALK_CB_NEVER_NULL); + BKE_LIB_FOREACHID_PROCESS_ID(data, object->data, IDWALK_CB_USER | IDWALK_CB_NEVER_NULL); } } BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, object->parent, IDWALK_CB_NEVER_SELF); BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, object->track, IDWALK_CB_NEVER_SELF); - /* object->proxy is refcounted, but not object->proxy_group... *sigh* */ - BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, object->proxy, IDWALK_CB_USER | IDWALK_CB_NEVER_SELF); - BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, object->proxy_group, IDWALK_CB_NOP); - - /* Special case! - * Since this field is set/owned by 'user' of this ID (and not ID itself), - * it is only indirect usage if proxy object is linked... Twisted. */ - { - const int cb_flag_orig = BKE_lib_query_foreachid_process_callback_flag_override( - data, - (object->proxy_from != nullptr && ID_IS_LINKED(object->proxy_from)) ? - IDWALK_CB_INDIRECT_USAGE : - 0, - true); - BKE_LIB_FOREACHID_PROCESS_IDSUPER( - data, object->proxy_from, IDWALK_CB_LOOPBACK | IDWALK_CB_NEVER_SELF); - BKE_lib_query_foreachid_process_callback_flag_override(data, cb_flag_orig, true); - } BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, object->poselib, IDWALK_CB_USER); for (int i = 0; i < object->totcol; i++) { - BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, object->mat[i], proxy_cb_flag | IDWALK_CB_USER); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, object->mat[i], IDWALK_CB_USER); } /* Note that ob->gpd is deprecated, so no need to handle it here. */ @@ -496,8 +411,6 @@ static void object_foreach_id(ID *id, LibraryForeachIDData *data) /* Note that ob->effect is deprecated, so no need to handle it here. */ if (object->pose) { - const int cb_flag_orig = BKE_lib_query_foreachid_process_callback_flag_override( - data, proxy_cb_flag, false); LISTBASE_FOREACH (bPoseChannel *, pchan, &object->pose->chanbase) { BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL( data, @@ -512,7 +425,6 @@ static void object_foreach_id(ID *id, LibraryForeachIDData *data) BKE_constraints_id_loop( &pchan->constraints, library_foreach_constraintObjectLooper, data)); } - BKE_lib_query_foreachid_process_callback_flag_override(data, cb_flag_orig, true); } if (object->rigidbody_constraint) { @@ -647,9 +559,6 @@ static void object_blend_write(BlendWriter *writer, ID *id, const void *id_addre bArmature *arm = nullptr; if (ob->type == OB_ARMATURE) { arm = (bArmature *)ob->data; - if (arm && ob->pose && arm->act_bone) { - BLI_strncpy(ob->pose->proxy_act_bone, arm->act_bone->name, sizeof(ob->pose->proxy_act_bone)); - } } BKE_pose_blend_write(writer, ob->pose, arm); @@ -1325,7 +1234,7 @@ IDTypeInfo IDType_ID_OB = { /* init_data */ object_init_data, /* copy_data */ object_copy_data, /* free_data */ object_free_data, - /* make_local */ object_make_local, + /* make_local */ nullptr, /* foreach_id */ object_foreach_id, /* foreach_cache */ nullptr, /* foreach_path */ object_foreach_path, @@ -1506,10 +1415,7 @@ bool BKE_object_support_modifier_type_check(const Object *ob, int modifier_type) } /* Only geometry objects should be able to get modifiers T25291. */ - if (ob->type == OB_HAIR) { - return (mti->modifyHair != nullptr) || (mti->flags & eModifierTypeFlag_AcceptsVertexCosOnly); - } - if (ELEM(ob->type, OB_POINTCLOUD, OB_VOLUME)) { + if (ELEM(ob->type, OB_POINTCLOUD, OB_VOLUME, OB_CURVES)) { return (mti->modifyGeometrySet != nullptr); } if (ELEM(ob->type, OB_MESH, OB_CURVE, OB_SURF, OB_FONT, OB_LATTICE)) { @@ -1867,6 +1773,12 @@ void BKE_object_free_derived_caches(Object *ob) object_update_from_subsurf_ccg(ob); + if (ob->runtime.editmesh_eval_cage && + ob->runtime.editmesh_eval_cage != reinterpret_cast<Mesh *>(ob->runtime.data_eval)) { + BKE_mesh_eval_delete(ob->runtime.editmesh_eval_cage); + } + ob->runtime.editmesh_eval_cage = nullptr; + if (ob->runtime.data_eval != nullptr) { if (ob->runtime.is_data_eval_owned) { ID *data_eval = ob->runtime.data_eval; @@ -1896,6 +1808,8 @@ void BKE_object_free_derived_caches(Object *ob) BKE_object_to_curve_clear(ob); BKE_object_free_curve_cache(ob); + BKE_crazyspace_api_eval_clear(ob); + /* Clear grease pencil data. */ if (ob->runtime.gpd_eval != nullptr) { BKE_gpencil_eval_delete(ob->runtime.gpd_eval); @@ -1906,6 +1820,8 @@ void BKE_object_free_derived_caches(Object *ob) BKE_geometry_set_free(ob->runtime.geometry_set_eval); ob->runtime.geometry_set_eval = nullptr; } + + MEM_SAFE_FREE(ob->runtime.editmesh_bb_cage); } void BKE_object_free_caches(Object *object) @@ -2182,8 +2098,8 @@ static const char *get_obdata_defname(int type) return DATA_("Armature"); case OB_SPEAKER: return DATA_("Speaker"); - case OB_HAIR: - return DATA_("Hair"); + case OB_CURVES: + return DATA_("HairCurves"); case OB_POINTCLOUD: return DATA_("PointCloud"); case OB_VOLUME: @@ -2257,8 +2173,8 @@ void *BKE_object_obdata_add_from_type(Main *bmain, int type, const char *name) return BKE_lightprobe_add(bmain, name); case OB_GPENCIL: return BKE_gpencil_data_addnew(bmain, name); - case OB_HAIR: - return BKE_hair_add(bmain, name); + case OB_CURVES: + return BKE_curves_add(bmain, name); case OB_POINTCLOUD: return BKE_pointcloud_add_default(bmain, name); case OB_VOLUME: @@ -2295,8 +2211,8 @@ int BKE_object_obdata_to_type(const ID *id) return OB_ARMATURE; case ID_LP: return OB_LIGHTPROBE; - case ID_HA: - return OB_HAIR; + case ID_CV: + return OB_CURVES; case ID_PT: return OB_POINTCLOUD; case ID_VO: @@ -2813,8 +2729,8 @@ Object *BKE_object_duplicate(Main *bmain, Object *ob, uint dupflag, uint duplica id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag, copy_flags); } break; - case OB_HAIR: - if (dupflag & USER_DUP_HAIR) { + case OB_CURVES: + if (dupflag & USER_DUP_CURVES) { id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag, copy_flags); } break; @@ -2886,161 +2802,6 @@ bool BKE_object_obdata_is_libdata(const Object *ob) return (ob && ob->data && ID_IS_LINKED(ob->data)); } -/* -------------------------------------------------------------------- */ -/** \name Object Proxy API - * \{ */ - -/* when you make proxy, ensure the exposed layers are extern */ -static void armature_set_id_extern(Object *ob) -{ - bArmature *arm = (bArmature *)ob->data; - unsigned int lay = arm->layer_protected; - - LISTBASE_FOREACH (bPoseChannel *, pchan, &ob->pose->chanbase) { - if (!(pchan->bone->layer & lay)) { - id_lib_extern((ID *)pchan->custom); - } - } -} - -void BKE_object_copy_proxy_drivers(Object *ob, Object *target) -{ - if ((target->adt) && (target->adt->drivers.first)) { - - /* add new animdata block */ - if (!ob->adt) { - ob->adt = BKE_animdata_ensure_id(&ob->id); - } - - /* make a copy of all the drivers (for now), then correct any links that need fixing */ - BKE_fcurves_free(&ob->adt->drivers); - BKE_fcurves_copy(&ob->adt->drivers, &target->adt->drivers); - - LISTBASE_FOREACH (FCurve *, fcu, &ob->adt->drivers) { - ChannelDriver *driver = fcu->driver; - - LISTBASE_FOREACH (DriverVar *, dvar, &driver->variables) { - /* all drivers */ - DRIVER_TARGETS_LOOPER_BEGIN (dvar) { - if (dtar->id) { - if ((Object *)dtar->id == target) { - dtar->id = (ID *)ob; - } - else { - /* only on local objects because this causes indirect links - * 'a -> b -> c', blend to point directly to a.blend - * when a.blend has a proxy that's linked into `c.blend`. */ - if (!ID_IS_LINKED(ob)) { - id_lib_extern((ID *)dtar->id); - } - } - } - } - DRIVER_TARGETS_LOOPER_END; - } - } - } -} - -void BKE_object_make_proxy(Main *bmain, Object *ob, Object *target, Object *cob) -{ - /* paranoia checks */ - if (ID_IS_LINKED(ob) || !ID_IS_LINKED(target)) { - CLOG_ERROR(&LOG, "cannot make proxy"); - return; - } - - ob->proxy = target; - id_us_plus(&target->id); - ob->proxy_group = cob; - - DEG_id_tag_update(&ob->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY | ID_RECALC_ANIMATION); - DEG_id_tag_update(&target->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY | ID_RECALC_ANIMATION); - - /* copy transform - * - cob means this proxy comes from a collection, just apply the matrix - * so the object won't move from its dupli-transform. - * - * - no cob means this is being made from a linked object, - * this is closer to making a copy of the object - in-place. */ - if (cob) { - ob->rotmode = target->rotmode; - mul_m4_m4m4(ob->obmat, cob->obmat, target->obmat); - if (cob->instance_collection) { /* should always be true */ - float tvec[3]; - mul_v3_mat3_m4v3(tvec, ob->obmat, cob->instance_collection->instance_offset); - sub_v3_v3(ob->obmat[3], tvec); - } - BKE_object_apply_mat4(ob, ob->obmat, false, true); - } - else { - BKE_object_transform_copy(ob, target); - ob->parent = target->parent; /* libdata */ - copy_m4_m4(ob->parentinv, target->parentinv); - } - - /* copy animdata stuff - drivers only for now... */ - BKE_object_copy_proxy_drivers(ob, target); - - /* skip constraints? */ - /* FIXME: this is considered by many as a bug */ - - /* set object type and link to data */ - ob->type = target->type; - ob->data = target->data; - id_us_plus((ID *)ob->data); /* ensures lib data becomes LIB_TAG_EXTERN */ - - /* copy material and index information */ - ob->actcol = ob->totcol = 0; - if (ob->mat) { - MEM_freeN(ob->mat); - } - if (ob->matbits) { - MEM_freeN(ob->matbits); - } - ob->mat = nullptr; - ob->matbits = nullptr; - if ((target->totcol) && (target->mat) && OB_TYPE_SUPPORT_MATERIAL(ob->type)) { - int i; - - ob->actcol = target->actcol; - ob->totcol = target->totcol; - - ob->mat = (Material **)MEM_dupallocN(target->mat); - ob->matbits = (char *)MEM_dupallocN(target->matbits); - for (i = 0; i < target->totcol; i++) { - /* don't need to run BKE_object_materials_test - * since we know this object is new and not used elsewhere */ - id_us_plus((ID *)ob->mat[i]); - } - } - - /* type conversions */ - if (target->type == OB_ARMATURE) { - copy_object_pose(ob, target, 0); /* data copy, object pointers in constraints */ - BKE_pose_rest(ob->pose, false); /* clear all transforms in channels */ - BKE_pose_rebuild(bmain, ob, (bArmature *)ob->data, true); /* set all internal links */ - - armature_set_id_extern(ob); - } - else if (target->type == OB_EMPTY) { - ob->empty_drawtype = target->empty_drawtype; - ob->empty_drawsize = target->empty_drawsize; - } - - /* copy IDProperties */ - if (ob->id.properties) { - IDP_FreeProperty(ob->id.properties); - ob->id.properties = nullptr; - } - if (target->id.properties) { - ob->id.properties = IDP_CopyProperty(target->id.properties); - } - - /* copy drawtype info */ - ob->dt = target->dt; -} - void BKE_object_obdata_size_init(struct Object *ob, const float size) { /* apply radius as a scale to types that support it */ @@ -3082,8 +2843,6 @@ void BKE_object_obdata_size_init(struct Object *ob, const float size) } } -/** \} */ - /* -------------------------------------------------------------------- */ /** \name Object Matrix Get/Set API * \{ */ @@ -3411,7 +3170,8 @@ static void give_parvert(Object *par, int nr, float vec[3]) if (par->type == OB_MESH) { Mesh *me = (Mesh *)par->data; BMEditMesh *em = me->edit_mesh; - Mesh *me_eval = (em) ? em->mesh_eval_final : BKE_object_get_evaluated_mesh(par); + Mesh *me_eval = (em) ? BKE_object_get_editmesh_eval_final(par) : + BKE_object_get_evaluated_mesh(par); if (me_eval) { int count = 0; @@ -3864,8 +3624,8 @@ BoundBox *BKE_object_boundbox_get(Object *ob) case OB_GPENCIL: bb = BKE_gpencil_boundbox_get(ob); break; - case OB_HAIR: - bb = BKE_hair_boundbox_get(ob); + case OB_CURVES: + bb = BKE_curves_boundbox_get(ob); break; case OB_POINTCLOUD: bb = BKE_pointcloud_boundbox_get(ob); @@ -4066,8 +3826,8 @@ void BKE_object_minmax(Object *ob, float r_min[3], float r_max[3], const bool us } break; } - case OB_HAIR: { - BoundBox bb = *BKE_hair_boundbox_get(ob); + case OB_CURVES: { + BoundBox bb = *BKE_curves_boundbox_get(ob); BKE_boundbox_minmax(&bb, ob->obmat, r_min, r_max); changed = true; break; @@ -4198,7 +3958,11 @@ bool BKE_object_minmax_dupli(Depsgraph *depsgraph, /* pass */ } else { - BoundBox *bb = BKE_object_boundbox_get(dob->ob); + Object temp_ob = *dob->ob; + /* Do not modify the original boundbox. */ + temp_ob.runtime.bb = nullptr; + BKE_object_replace_data_on_shallow_copy(&temp_ob, dob->ob_data); + BoundBox *bb = BKE_object_boundbox_get(&temp_ob); if (bb) { int i; @@ -4210,6 +3974,8 @@ bool BKE_object_minmax_dupli(Depsgraph *depsgraph, ok = true; } + + MEM_SAFE_FREE(temp_ob.runtime.bb); } } free_object_duplilist(lb); /* does restore */ @@ -4368,33 +4134,10 @@ void BKE_object_tfm_restore(Object *ob, void *obtfm_pt) /** \name Object Evaluation/Update API * \{ */ -static void object_handle_update_proxy(Depsgraph *depsgraph, - Scene *scene, - Object *object, - const bool do_proxy_update) -{ - /* The case when this is a collection proxy, object_update is called in collection.c */ - if (object->proxy == nullptr) { - return; - } - /* set pointer in library proxy target, for copying, but restore it */ - object->proxy->proxy_from = object; - // printf("set proxy pointer for later collection stuff %s\n", ob->id.name); - - /* the no-group proxy case, we call update */ - if (object->proxy_group == nullptr) { - if (do_proxy_update) { - // printf("call update, lib ob %s proxy %s\n", ob->proxy->id.name, ob->id.name); - BKE_object_handle_update(depsgraph, scene, object->proxy); - } - } -} - void BKE_object_handle_update_ex(Depsgraph *depsgraph, Scene *scene, Object *ob, - RigidBodyWorld *rbw, - const bool do_proxy_update) + RigidBodyWorld *rbw) { const ID *object_data = (ID *)ob->data; const bool recalc_object = (ob->id.recalc & ID_RECALC_ALL) != 0; @@ -4402,7 +4145,6 @@ void BKE_object_handle_update_ex(Depsgraph *depsgraph, ((object_data->recalc & ID_RECALC_ALL) != 0) : false; if (!recalc_object && !recalc_data) { - object_handle_update_proxy(depsgraph, scene, ob, do_proxy_update); return; } /* Speed optimization for animation lookups. */ @@ -4431,22 +4173,17 @@ void BKE_object_handle_update_ex(Depsgraph *depsgraph, if (G.debug & G_DEBUG_DEPSGRAPH_EVAL) { printf("recalcob %s\n", ob->id.name + 2); } - /* Handle proxy copy for target. */ - if (!BKE_object_eval_proxy_copy(depsgraph, ob)) { - BKE_object_where_is_calc_ex(depsgraph, scene, rbw, ob, nullptr); - } + BKE_object_where_is_calc_ex(depsgraph, scene, rbw, ob, nullptr); } if (recalc_data) { BKE_object_handle_data_update(depsgraph, scene, ob); } - - object_handle_update_proxy(depsgraph, scene, ob, do_proxy_update); } void BKE_object_handle_update(Depsgraph *depsgraph, Scene *scene, Object *ob) { - BKE_object_handle_update_ex(depsgraph, scene, ob, nullptr, true); + BKE_object_handle_update_ex(depsgraph, scene, ob, nullptr); } void BKE_object_sculpt_data_create(Object *ob) @@ -4574,6 +4311,33 @@ Mesh *BKE_object_get_original_mesh(const Object *object) return result; } +Mesh *BKE_object_get_editmesh_eval_final(const Object *object) +{ + BLI_assert(!DEG_is_original_id(&object->id)); + BLI_assert(object->type == OB_MESH); + + const Mesh *mesh = static_cast<const Mesh *>(object->data); + if (mesh->edit_mesh == nullptr) { + /* Happens when requesting material of evaluated 3d font object: the evaluated object get + * converted to mesh, and it does not have edit mesh. */ + return nullptr; + } + + return reinterpret_cast<Mesh *>(object->runtime.data_eval); +} + +Mesh *BKE_object_get_editmesh_eval_cage(const Object *object) +{ + BLI_assert(!DEG_is_original_id(&object->id)); + BLI_assert(object->type == OB_MESH); + + const Mesh *mesh = static_cast<const Mesh *>(object->data); + BLI_assert(mesh->edit_mesh != nullptr); + UNUSED_VARS_NDEBUG(mesh); + + return object->runtime.editmesh_eval_cage; +} + Lattice *BKE_object_get_lattice(const Object *object) { ID *data = (ID *)object->data; @@ -5177,7 +4941,7 @@ bool BKE_object_supports_material_slots(struct Object *ob) OB_SURF, OB_FONT, OB_MBALL, - OB_HAIR, + OB_CURVES, OB_POINTCLOUD, OB_VOLUME, OB_GPENCIL); @@ -5204,6 +4968,9 @@ void BKE_object_runtime_reset_on_copy(Object *object, const int UNUSED(flag)) runtime->object_as_temp_mesh = nullptr; runtime->object_as_temp_curve = nullptr; runtime->geometry_set_eval = nullptr; + + runtime->crazyspace_deform_imats = nullptr; + runtime->crazyspace_deform_cos = nullptr; } void BKE_object_runtime_free_data(Object *object) diff --git a/source/blender/blenkernel/intern/object_dupli.cc b/source/blender/blenkernel/intern/object_dupli.cc index 5dcb753abf4..3082d6f25f3 100644 --- a/source/blender/blenkernel/intern/object_dupli.cc +++ b/source/blender/blenkernel/intern/object_dupli.cc @@ -371,7 +371,7 @@ static const Mesh *mesh_data_from_duplicator_object(Object *ob, if (em != nullptr) { /* Note that this will only show deformation if #eModifierMode_OnCage is enabled. * We could change this but it matches 2.7x behavior. */ - me_eval = em->mesh_eval_cage; + me_eval = BKE_object_get_editmesh_eval_cage(ob); if ((me_eval == nullptr) || (me_eval->runtime.wrapper_type == ME_WRAPPER_TYPE_BMESH)) { EditMeshData *emd = me_eval ? me_eval->runtime.edit_data : nullptr; diff --git a/source/blender/blenkernel/intern/object_update.c b/source/blender/blenkernel/intern/object_update.c index 904fe9be51f..803dde50d96 100644 --- a/source/blender/blenkernel/intern/object_update.c +++ b/source/blender/blenkernel/intern/object_update.c @@ -41,12 +41,12 @@ #include "BKE_armature.h" #include "BKE_constraint.h" #include "BKE_curve.h" +#include "BKE_curves.h" #include "BKE_displist.h" #include "BKE_editmesh.h" #include "BKE_effect.h" #include "BKE_gpencil.h" #include "BKE_gpencil_modifier.h" -#include "BKE_hair.h" #include "BKE_image.h" #include "BKE_key.h" #include "BKE_lattice.h" @@ -160,12 +160,6 @@ void BKE_object_handle_data_update(Depsgraph *depsgraph, Scene *scene, Object *o /* includes all keys and modifiers */ switch (ob->type) { case OB_MESH: { -#if 0 - BMEditMesh *em = (ob->mode & OB_MODE_EDIT) ? BKE_editmesh_from_object(ob) : NULL; -#else - BMEditMesh *em = (ob->mode & OB_MODE_EDIT) ? ((Mesh *)ob->data)->edit_mesh : NULL; -#endif - CustomData_MeshMasks cddata_masks = scene->customdata_mask; CustomData_MeshMasks_update(&cddata_masks, &CD_MASK_BAREMESH); /* Custom attributes should not be removed automatically. They might be used by the render @@ -192,25 +186,11 @@ void BKE_object_handle_data_update(Depsgraph *depsgraph, Scene *scene, Object *o cddata_masks.lmask |= CD_MASK_MLOOPUV | CD_MASK_MLOOPCOL; cddata_masks.vmask |= CD_MASK_ORCO | CD_MASK_PROP_COLOR; } - if (em) { - makeDerivedMesh(depsgraph, scene, ob, em, &cddata_masks); /* was CD_MASK_BAREMESH */ - } - else { - makeDerivedMesh(depsgraph, scene, ob, NULL, &cddata_masks); - } + makeDerivedMesh(depsgraph, scene, ob, &cddata_masks); /* was CD_MASK_BAREMESH */ break; } case OB_ARMATURE: - if (ID_IS_LINKED(ob) && ob->proxy_from) { - if (BKE_pose_copy_result(ob->pose, ob->proxy_from->pose) == false) { - printf("Proxy copy error, lib Object: %s proxy Object: %s\n", - ob->id.name + 2, - ob->proxy_from->id.name + 2); - } - } - else { - BKE_pose_where_is(depsgraph, scene, ob); - } + BKE_pose_where_is(depsgraph, scene, ob); break; case OB_MBALL: @@ -234,8 +214,8 @@ void BKE_object_handle_data_update(Depsgraph *depsgraph, Scene *scene, Object *o BKE_gpencil_update_layer_transforms(depsgraph, ob); break; } - case OB_HAIR: - BKE_hair_data_update(depsgraph, scene, ob); + case OB_CURVES: + BKE_curves_data_update(depsgraph, scene, ob); break; case OB_POINTCLOUD: BKE_pointcloud_data_update(depsgraph, scene, ob); @@ -322,33 +302,9 @@ void BKE_object_sync_to_original(Depsgraph *depsgraph, Object *object) object_sync_boundbox_to_original(object_orig, object); } -bool BKE_object_eval_proxy_copy(Depsgraph *depsgraph, Object *object) -{ - /* Handle proxy copy for target, */ - if (ID_IS_LINKED(object) && object->proxy_from) { - DEG_debug_print_eval(depsgraph, __func__, object->id.name, object); - if (object->proxy_from->proxy_group) { - /* Transform proxy into group space. */ - Object *obg = object->proxy_from->proxy_group; - float imat[4][4]; - invert_m4_m4(imat, obg->obmat); - mul_m4_m4m4(object->obmat, imat, object->proxy_from->obmat); - /* Should always be true. */ - if (obg->instance_collection) { - add_v3_v3(object->obmat[3], obg->instance_collection->instance_offset); - } - } - else { - copy_m4_m4(object->obmat, object->proxy_from->obmat); - } - return true; - } - return false; -} - -void BKE_object_eval_uber_transform(Depsgraph *depsgraph, Object *object) +void BKE_object_eval_uber_transform(Depsgraph *UNUSED(depsgraph), Object *UNUSED(object)) { - BKE_object_eval_proxy_copy(depsgraph, object); + return; } void BKE_object_data_batch_cache_dirty_tag(ID *object_data) @@ -370,8 +326,8 @@ void BKE_object_data_batch_cache_dirty_tag(ID *object_data) case ID_GD: BKE_gpencil_batch_cache_dirty_tag((struct bGPdata *)object_data); break; - case ID_HA: - BKE_hair_batch_cache_dirty_tag((struct Hair *)object_data, BKE_HAIR_BATCH_DIRTY_ALL); + case ID_CV: + BKE_curves_batch_cache_dirty_tag((struct Curves *)object_data, BKE_CURVES_BATCH_DIRTY_ALL); break; case ID_PT: BKE_pointcloud_batch_cache_dirty_tag((struct PointCloud *)object_data, diff --git a/source/blender/blenkernel/intern/paint.c b/source/blender/blenkernel/intern/paint.c index 407375c4d22..72210eea71d 100644 --- a/source/blender/blenkernel/intern/paint.c +++ b/source/blender/blenkernel/intern/paint.c @@ -1648,7 +1648,6 @@ static void sculpt_update_object(Depsgraph *depsgraph, ss->totvert = me->totvert; ss->totpoly = me->totpoly; ss->totfaces = me->totpoly; - ss->vert_normals = BKE_mesh_vertex_normals_ensure(me); ss->mvert = me->mvert; ss->mpoly = me->mpoly; ss->mloop = me->mloop; diff --git a/source/blender/blenkernel/intern/particle.c b/source/blender/blenkernel/intern/particle.c index 4dba13ce4c2..aa08a6494d1 100644 --- a/source/blender/blenkernel/intern/particle.c +++ b/source/blender/blenkernel/intern/particle.c @@ -4640,7 +4640,7 @@ void psys_get_particle_on_path(ParticleSimulationData *sim, * account when subdividing for instance. */ pind.mesh = psys_in_edit_mode(sim->depsgraph, psys) ? NULL : - psys->hair_out_mesh; /* XXX(@sybren) EEK. */ + psys->hair_out_mesh; /* XXX(@sybren): EEK. */ init_particle_interpolation(sim->ob, psys, pa, &pind); do_particle_interpolation(psys, p, pa, t, &pind, state); diff --git a/source/blender/blenkernel/intern/pbvh.c b/source/blender/blenkernel/intern/pbvh.c index 1926bbcda02..bfedd4d3f49 100644 --- a/source/blender/blenkernel/intern/pbvh.c +++ b/source/blender/blenkernel/intern/pbvh.c @@ -1007,6 +1007,28 @@ typedef struct PBVHUpdateData { bool show_sculpt_face_sets; } PBVHUpdateData; +static void pbvh_update_normals_clear_task_cb(void *__restrict userdata, + const int n, + const TaskParallelTLS *__restrict UNUSED(tls)) +{ + PBVHUpdateData *data = userdata; + PBVH *pbvh = data->pbvh; + PBVHNode *node = data->nodes[n]; + float(*vnors)[3] = data->vnors; + + if (node->flag & PBVH_UpdateNormals) { + const int *verts = node->vert_indices; + const int totvert = node->uniq_verts; + for (int i = 0; i < totvert; i++) { + const int v = verts[i]; + const MVert *mvert = &pbvh->verts[v]; + if (mvert->flag & ME_VERT_PBVH_UPDATE) { + zero_v3(vnors[v]); + } + } + } +} + static void pbvh_update_normals_accum_task_cb(void *__restrict userdata, const int n, const TaskParallelTLS *__restrict UNUSED(tls)) @@ -1107,6 +1129,8 @@ static void pbvh_faces_update_normals(PBVH *pbvh, PBVHNode **nodes, int totnode) TaskParallelSettings settings; BKE_pbvh_parallel_range_settings(&settings, true, totnode); + /* Zero normals before accumulation. */ + BLI_task_parallel_range(0, totnode, &data, pbvh_update_normals_clear_task_cb, &settings); BLI_task_parallel_range(0, totnode, &data, pbvh_update_normals_accum_task_cb, &settings); BLI_task_parallel_range(0, totnode, &data, pbvh_update_normals_store_task_cb, &settings); } diff --git a/source/blender/blenkernel/intern/scene.c b/source/blender/blenkernel/intern/scene.c index 916a2786a98..ed9f10aaa91 100644 --- a/source/blender/blenkernel/intern/scene.c +++ b/source/blender/blenkernel/intern/scene.c @@ -2522,7 +2522,7 @@ void BKE_scene_update_sound(Depsgraph *depsgraph, Main *bmain) Scene *scene = DEG_get_evaluated_scene(depsgraph); const int recalc = scene->id.recalc; BKE_sound_ensure_scene(scene); - if (recalc & ID_RECALC_AUDIO_SEEK) { + if (recalc & ID_RECALC_FRAME_CHANGE) { BKE_sound_seek_scene(bmain, scene); } if (recalc & ID_RECALC_AUDIO_FPS) { diff --git a/source/blender/blenkernel/intern/subdiv_modifier.c b/source/blender/blenkernel/intern/subdiv_modifier.c index 65809782f8f..525c4837bc4 100644 --- a/source/blender/blenkernel/intern/subdiv_modifier.c +++ b/source/blender/blenkernel/intern/subdiv_modifier.c @@ -92,7 +92,7 @@ bool BKE_subsurf_modifier_can_do_gpu_subdiv_ex(const Scene *scene, return false; } - if (!GPU_compute_shader_support()) { + if (!(GPU_compute_shader_support() && GPU_shader_storage_buffer_objects_support())) { return false; } diff --git a/source/blender/blenkernel/intern/subsurf_ccg.c b/source/blender/blenkernel/intern/subsurf_ccg.c index 9d66c354b54..2f63edebb67 100644 --- a/source/blender/blenkernel/intern/subsurf_ccg.c +++ b/source/blender/blenkernel/intern/subsurf_ccg.c @@ -580,7 +580,6 @@ static void ss_sync_ccg_from_derivedmesh(CCGSubSurf *ss, #endif MVert *mvert = dm->getVertArray(dm); MEdge *medge = dm->getEdgeArray(dm); - // MFace *mface = dm->getTessFaceArray(dm); /* UNUSED */ MVert *mv; MEdge *me; MLoop *mloop = dm->getLoopArray(dm), *ml; @@ -1129,44 +1128,6 @@ static void ccgDM_copyFinalEdgeArray(DerivedMesh *dm, MEdge *medge) } } -static void ccgDM_copyFinalFaceArray(DerivedMesh *dm, MFace *mface) -{ - CCGDerivedMesh *ccgdm = (CCGDerivedMesh *)dm; - CCGSubSurf *ss = ccgdm->ss; - int index; - int totface; - int gridSize = ccgSubSurf_getGridSize(ss); - int edgeSize = ccgSubSurf_getEdgeSize(ss); - int i = 0; - DMFlagMat *faceFlags = ccgdm->faceFlags; - - totface = dm->getNumTessFaces(dm); - for (index = 0; index < totface; index++) { - CCGFace *f = ccgdm->faceMap[index].face; - int x, y, S, numVerts = ccgSubSurf_getFaceNumVerts(f); - /* keep types in sync with MFace, avoid many conversions */ - char flag = (faceFlags) ? faceFlags[index].flag : ME_SMOOTH; - short mat_nr = (faceFlags) ? faceFlags[index].mat_nr : 0; - - for (S = 0; S < numVerts; S++) { - for (y = 0; y < gridSize - 1; y++) { - for (x = 0; x < gridSize - 1; x++) { - MFace *mf = &mface[i]; - mf->v1 = getFaceIndex(ss, f, S, x + 0, y + 0, edgeSize, gridSize); - mf->v2 = getFaceIndex(ss, f, S, x + 0, y + 1, edgeSize, gridSize); - mf->v3 = getFaceIndex(ss, f, S, x + 1, y + 1, edgeSize, gridSize); - mf->v4 = getFaceIndex(ss, f, S, x + 1, y + 0, edgeSize, gridSize); - mf->mat_nr = mat_nr; - mf->flag = flag; - mf->edcode = 0; - - i++; - } - } - } - } -} - typedef struct CopyFinalLoopArrayData { CCGDerivedMesh *ccgdm; MLoop *mloop; @@ -1457,63 +1418,6 @@ static void *ccgDM_get_edge_data_layer(DerivedMesh *dm, int type) return DM_get_edge_data_layer(dm, type); } -static void *ccgDM_get_tessface_data_layer(DerivedMesh *dm, int type) -{ - if (type == CD_ORIGINDEX) { - /* create origindex on demand to save memory */ - int *origindex; - - /* Avoid re-creation if the layer exists already */ - origindex = DM_get_tessface_data_layer(dm, CD_ORIGINDEX); - if (origindex) { - return origindex; - } - - DM_add_tessface_layer(dm, CD_ORIGINDEX, CD_CALLOC, NULL); - origindex = DM_get_tessface_data_layer(dm, CD_ORIGINDEX); - - /* silly loop counting up */ - range_vn_i(origindex, dm->getNumTessFaces(dm), 0); - - return origindex; - } - - if (type == CD_TESSLOOPNORMAL) { - /* Create tessloopnormal on demand to save memory. */ - /* Note that since tessellated face corners are the same a loops in CCGDM, - * and since all faces have four loops/corners, we can simplify the code - * here by converting tessloopnormals from 'short (*)[4][3]' to 'short (*)[3]'. */ - short(*tlnors)[3]; - - /* Avoid re-creation if the layer exists already */ - tlnors = DM_get_tessface_data_layer(dm, CD_TESSLOOPNORMAL); - if (!tlnors) { - float(*lnors)[3]; - short(*tlnors_it)[3]; - const int numLoops = ccgDM_getNumLoops(dm); - int i; - - lnors = dm->getLoopDataArray(dm, CD_NORMAL); - if (!lnors) { - return NULL; - } - - DM_add_tessface_layer(dm, CD_TESSLOOPNORMAL, CD_CALLOC, NULL); - tlnors = tlnors_it = (short(*)[3])DM_get_tessface_data_layer(dm, CD_TESSLOOPNORMAL); - - /* With ccgdm, we have a simple one to one mapping between loops - * and tessellated face corners. */ - for (i = 0; i < numLoops; i++, tlnors_it++, lnors++) { - normal_float_to_short_v3(*tlnors_it, *lnors); - } - } - - return tlnors; - } - - return DM_get_tessface_data_layer(dm, type); -} - static void *ccgDM_get_poly_data_layer(DerivedMesh *dm, int type) { if (type == CD_ORIGINDEX) { @@ -1551,46 +1455,6 @@ static void *ccgDM_get_poly_data_layer(DerivedMesh *dm, int type) return DM_get_poly_data_layer(dm, type); } -static void *ccgDM_get_vert_data(DerivedMesh *dm, int index, int type) -{ - if (type == CD_ORIGINDEX) { - /* ensure creation of CD_ORIGINDEX layer */ - ccgDM_get_vert_data_layer(dm, type); - } - - return DM_get_vert_data(dm, index, type); -} - -static void *ccgDM_get_edge_data(DerivedMesh *dm, int index, int type) -{ - if (type == CD_ORIGINDEX) { - /* ensure creation of CD_ORIGINDEX layer */ - ccgDM_get_edge_data_layer(dm, type); - } - - return DM_get_edge_data(dm, index, type); -} - -static void *ccgDM_get_tessface_data(DerivedMesh *dm, int index, int type) -{ - if (ELEM(type, CD_ORIGINDEX, CD_TESSLOOPNORMAL)) { - /* ensure creation of CD_ORIGINDEX/CD_TESSLOOPNORMAL layers */ - ccgDM_get_tessface_data_layer(dm, type); - } - - return DM_get_tessface_data(dm, index, type); -} - -static void *ccgDM_get_poly_data(DerivedMesh *dm, int index, int type) -{ - if (type == CD_ORIGINDEX) { - /* ensure creation of CD_ORIGINDEX layer */ - ccgDM_get_tessface_data_layer(dm, type); - } - - return DM_get_poly_data(dm, index, type); -} - static int ccgDM_getNumGrids(DerivedMesh *dm) { CCGDerivedMesh *ccgdm = (CCGDerivedMesh *)dm; @@ -1705,25 +1569,6 @@ static BLI_bitmap **ccgDM_getGridHidden(DerivedMesh *dm) return ccgdm->gridHidden; } -static const MeshElemMap *ccgDM_getPolyMap(Object *ob, DerivedMesh *dm) -{ - CCGDerivedMesh *ccgdm = (CCGDerivedMesh *)dm; - - if (!ccgdm->multires.mmd && !ccgdm->pmap && ob->type == OB_MESH) { - Mesh *me = ob->data; - - BKE_mesh_vert_poly_map_create(&ccgdm->pmap, - &ccgdm->pmap_mem, - me->mpoly, - me->mloop, - me->totvert, - me->totpoly, - me->totloop); - } - - return ccgdm->pmap; -} - /* WARNING! *MUST* be called in an 'loops_cache_rwlock' protected thread context! */ static void ccgDM_recalcLoopTri(DerivedMesh *dm) { @@ -1773,17 +1618,11 @@ static void set_default_ccgdm_callbacks(CCGDerivedMesh *ccgdm) ccgdm->dm.copyVertArray = ccgDM_copyFinalVertArray; ccgdm->dm.copyEdgeArray = ccgDM_copyFinalEdgeArray; - ccgdm->dm.copyTessFaceArray = ccgDM_copyFinalFaceArray; ccgdm->dm.copyLoopArray = ccgDM_copyFinalLoopArray; ccgdm->dm.copyPolyArray = ccgDM_copyFinalPolyArray; - ccgdm->dm.getVertData = ccgDM_get_vert_data; - ccgdm->dm.getEdgeData = ccgDM_get_edge_data; - ccgdm->dm.getTessFaceData = ccgDM_get_tessface_data; - ccgdm->dm.getPolyData = ccgDM_get_poly_data; ccgdm->dm.getVertDataArray = ccgDM_get_vert_data_layer; ccgdm->dm.getEdgeDataArray = ccgDM_get_edge_data_layer; - ccgdm->dm.getTessFaceDataArray = ccgDM_get_tessface_data_layer; ccgdm->dm.getPolyDataArray = ccgDM_get_poly_data_layer; ccgdm->dm.getNumGrids = ccgDM_getNumGrids; ccgdm->dm.getGridSize = ccgDM_getGridSize; @@ -1792,7 +1631,6 @@ static void set_default_ccgdm_callbacks(CCGDerivedMesh *ccgdm) ccgdm->dm.getGridKey = ccgDM_getGridKey; ccgdm->dm.getGridFlagMats = ccgDM_getGridFlagMats; ccgdm->dm.getGridHidden = ccgDM_getGridHidden; - ccgdm->dm.getPolyMap = ccgDM_getPolyMap; ccgdm->dm.recalcLoopTri = ccgDM_recalcLoopTri; @@ -1848,7 +1686,7 @@ static void set_ccgdm_all_geometry(CCGDerivedMesh *ccgdm, int index; int i; int vertNum = 0, edgeNum = 0, faceNum = 0; - int *vertOrigIndex, *faceOrigIndex, *polyOrigIndex, *base_polyOrigIndex, *edgeOrigIndex; + int *vertOrigIndex, *polyOrigIndex, *base_polyOrigIndex, *edgeOrigIndex; short *edgeFlags = ccgdm->edgeFlags; DMFlagMat *faceFlags = ccgdm->faceFlags; int *polyidx = NULL; @@ -1884,7 +1722,6 @@ static void set_ccgdm_all_geometry(CCGDerivedMesh *ccgdm, vertOrigIndex = DM_get_vert_data_layer(&ccgdm->dm, CD_ORIGINDEX); edgeOrigIndex = DM_get_edge_data_layer(&ccgdm->dm, CD_ORIGINDEX); - faceOrigIndex = DM_get_tessface_data_layer(&ccgdm->dm, CD_ORIGINDEX); polyOrigIndex = DM_get_poly_data_layer(&ccgdm->dm, CD_ORIGINDEX); has_edge_cd = ((ccgdm->dm.edgeData.totlayer - (edgeOrigIndex ? 1 : 0)) != 0); @@ -2006,12 +1843,6 @@ static void set_ccgdm_all_geometry(CCGDerivedMesh *ccgdm, /* Copy over poly data, e.g. #CD_FACEMAP. */ CustomData_copy_data(&dm->polyData, &ccgdm->dm.polyData, origIndex, faceNum, 1); - /* Set original index data. */ - if (faceOrigIndex) { - /* reference the index in 'polyOrigIndex' */ - *faceOrigIndex = faceNum; - faceOrigIndex++; - } if (polyOrigIndex) { *polyOrigIndex = base_polyOrigIndex ? base_polyOrigIndex[origIndex] : origIndex; polyOrigIndex++; diff --git a/source/blender/blenkernel/intern/texture.c b/source/blender/blenkernel/intern/texture.c index ee9247e6e60..c37e2fb6144 100644 --- a/source/blender/blenkernel/intern/texture.c +++ b/source/blender/blenkernel/intern/texture.c @@ -67,6 +67,8 @@ #include "BKE_scene.h" #include "BKE_texture.h" +#include "NOD_texture.h" + #include "RE_texture.h" #include "BLO_read_write.h" @@ -721,10 +723,10 @@ void BKE_texture_get_value_ex(const Scene *scene, * if the texture didn't give an RGB value, copy the intensity across */ if (result_type & TEX_RGB) { - texres->tin = (1.0f / 3.0f) * (texres->tr + texres->tg + texres->tb); + texres->tin = (1.0f / 3.0f) * (texres->trgba[0] + texres->trgba[1] + texres->trgba[2]); } else { - copy_v3_fl(&texres->tr, texres->tin); + copy_v3_fl(texres->trgba, texres->tin); } } diff --git a/source/blender/blenkernel/intern/type_conversions.cc b/source/blender/blenkernel/intern/type_conversions.cc index cb05337ef2a..b2011d2baf7 100644 --- a/source/blender/blenkernel/intern/type_conversions.cc +++ b/source/blender/blenkernel/intern/type_conversions.cc @@ -62,6 +62,11 @@ static bool float_to_bool(const float &a) { return a > 0.0f; } +static int8_t float_to_int8(const float &a) +{ + return std::clamp( + a, float(std::numeric_limits<int8_t>::min()), float(std::numeric_limits<int8_t>::max())); +} static ColorGeometry4f float_to_color(const float &a) { return ColorGeometry4f(a, a, a, 1.0f); @@ -83,6 +88,10 @@ static bool float2_to_bool(const float2 &a) { return !is_zero_v2(a); } +static int8_t float2_to_int8(const float2 &a) +{ + return float_to_int8((a.x + a.y) / 2.0f); +} static ColorGeometry4f float2_to_color(const float2 &a) { return ColorGeometry4f(a.x, a.y, 0.0f, 1.0f); @@ -92,6 +101,10 @@ static bool float3_to_bool(const float3 &a) { return !is_zero_v3(a); } +static int8_t float3_to_int8(const float3 &a) +{ + return float_to_int8((a.x + a.y + a.z) / 3.0f); +} static float float3_to_float(const float3 &a) { return (a.x + a.y + a.z) / 3.0f; @@ -113,6 +126,11 @@ static bool int_to_bool(const int32_t &a) { return a > 0; } +static int8_t int_to_int8(const int32_t &a) +{ + return std::clamp( + a, int(std::numeric_limits<int8_t>::min()), int(std::numeric_limits<int8_t>::max())); +} static float int_to_float(const int32_t &a) { return (float)a; @@ -130,10 +148,39 @@ static ColorGeometry4f int_to_color(const int32_t &a) return ColorGeometry4f((float)a, (float)a, (float)a, 1.0f); } +static bool int8_to_bool(const int8_t &a) +{ + return a > 0; +} +static int int8_to_int(const int8_t &a) +{ + return static_cast<int>(a); +} +static float int8_to_float(const int8_t &a) +{ + return (float)a; +} +static float2 int8_to_float2(const int8_t &a) +{ + return float2((float)a); +} +static float3 int8_to_float3(const int8_t &a) +{ + return float3((float)a); +} +static ColorGeometry4f int8_to_color(const int8_t &a) +{ + return ColorGeometry4f((float)a, (float)a, (float)a, 1.0f); +} + static float bool_to_float(const bool &a) { return (bool)a; } +static int8_t bool_to_int8(const bool &a) +{ + return static_cast<int8_t>(a); +} static int32_t bool_to_int(const bool &a) { return (int32_t)a; @@ -163,6 +210,10 @@ static int32_t color_to_int(const ColorGeometry4f &a) { return (int)rgb_to_grayscale(a); } +static int8_t color_to_int8(const ColorGeometry4f &a) +{ + return int_to_int8(color_to_int(a)); +} static float2 color_to_float2(const ColorGeometry4f &a) { return float2(a.r, a.g); @@ -180,33 +231,46 @@ static DataTypeConversions create_implicit_conversions() add_implicit_conversion<float, float3, float_to_float3>(conversions); add_implicit_conversion<float, int32_t, float_to_int>(conversions); add_implicit_conversion<float, bool, float_to_bool>(conversions); + add_implicit_conversion<float, int8_t, float_to_int8>(conversions); add_implicit_conversion<float, ColorGeometry4f, float_to_color>(conversions); add_implicit_conversion<float2, float3, float2_to_float3>(conversions); add_implicit_conversion<float2, float, float2_to_float>(conversions); add_implicit_conversion<float2, int32_t, float2_to_int>(conversions); add_implicit_conversion<float2, bool, float2_to_bool>(conversions); + add_implicit_conversion<float2, int8_t, float2_to_int8>(conversions); add_implicit_conversion<float2, ColorGeometry4f, float2_to_color>(conversions); add_implicit_conversion<float3, bool, float3_to_bool>(conversions); + add_implicit_conversion<float3, int8_t, float3_to_int8>(conversions); add_implicit_conversion<float3, float, float3_to_float>(conversions); add_implicit_conversion<float3, int32_t, float3_to_int>(conversions); add_implicit_conversion<float3, float2, float3_to_float2>(conversions); add_implicit_conversion<float3, ColorGeometry4f, float3_to_color>(conversions); add_implicit_conversion<int32_t, bool, int_to_bool>(conversions); + add_implicit_conversion<int32_t, int8_t, int_to_int8>(conversions); add_implicit_conversion<int32_t, float, int_to_float>(conversions); add_implicit_conversion<int32_t, float2, int_to_float2>(conversions); add_implicit_conversion<int32_t, float3, int_to_float3>(conversions); add_implicit_conversion<int32_t, ColorGeometry4f, int_to_color>(conversions); + add_implicit_conversion<int8_t, bool, int8_to_bool>(conversions); + add_implicit_conversion<int8_t, int32_t, int8_to_int>(conversions); + add_implicit_conversion<int8_t, float, int8_to_float>(conversions); + add_implicit_conversion<int8_t, float2, int8_to_float2>(conversions); + add_implicit_conversion<int8_t, float3, int8_to_float3>(conversions); + add_implicit_conversion<int8_t, ColorGeometry4f, int8_to_color>(conversions); + add_implicit_conversion<bool, float, bool_to_float>(conversions); + add_implicit_conversion<bool, int8_t, bool_to_int8>(conversions); add_implicit_conversion<bool, int32_t, bool_to_int>(conversions); add_implicit_conversion<bool, float2, bool_to_float2>(conversions); add_implicit_conversion<bool, float3, bool_to_float3>(conversions); add_implicit_conversion<bool, ColorGeometry4f, bool_to_color>(conversions); add_implicit_conversion<ColorGeometry4f, bool, color_to_bool>(conversions); + add_implicit_conversion<ColorGeometry4f, int8_t, color_to_int8>(conversions); add_implicit_conversion<ColorGeometry4f, float, color_to_float>(conversions); add_implicit_conversion<ColorGeometry4f, int32_t, color_to_int>(conversions); add_implicit_conversion<ColorGeometry4f, float2, color_to_float2>(conversions); diff --git a/source/blender/blenkernel/intern/writeffmpeg.c b/source/blender/blenkernel/intern/writeffmpeg.c index 4d94132e6fd..9effeb831b6 100644 --- a/source/blender/blenkernel/intern/writeffmpeg.c +++ b/source/blender/blenkernel/intern/writeffmpeg.c @@ -726,14 +726,12 @@ static AVStream *alloc_video_stream(FFMpegContext *context, } } - if (codec_id == AV_CODEC_ID_VP9) { - if (rd->im_format.planes == R_IMF_PLANES_RGBA) { - c->pix_fmt = AV_PIX_FMT_YUVA420P; - } + if (codec_id == AV_CODEC_ID_VP9 && rd->im_format.planes == R_IMF_PLANES_RGBA) { + c->pix_fmt = AV_PIX_FMT_YUVA420P; } - - /* Use 4:4:4 instead of 4:2:0 pixel format for lossless rendering. */ - if ((codec_id == AV_CODEC_ID_H264 || codec_id == AV_CODEC_ID_VP9) && context->ffmpeg_crf == 0) { + else if ((codec_id == AV_CODEC_ID_H264 || codec_id == AV_CODEC_ID_VP9) && + context->ffmpeg_crf == 0) { + /* Use 4:4:4 instead of 4:2:0 pixel format for lossless rendering. */ c->pix_fmt = AV_PIX_FMT_YUV444P; } |