Add Chromium-only Blender WebEngine parity work

This commit is contained in:
mes123456
2026-08-12 04:47:48 -04:00
commit 9fd26010f6
18225 changed files with 11622124 additions and 0 deletions

View File

@@ -0,0 +1,346 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
/** \file
* \ingroup bke
* \brief Blender kernel action and pose functionality.
*/
#include "BLI_compiler_attrs.h"
#include "BLI_function_ref.hh"
#include "BLI_span.hh"
#include "BKE_pose.hh"
namespace blender {
struct BlendDataReader;
struct BlendLibReader;
struct BlendWriter;
struct bArmature;
struct BoneParentTransform;
/* The following structures are defined in DNA_action_types.h, and DNA_anim_types.h */
struct AnimationEvalContext;
struct BoneColor;
struct FCurve;
struct ID;
struct Main;
struct Object;
struct bAction;
struct bActionGroup;
struct bItasc;
struct bPose;
struct bPoseChannel;
struct bPoseChannel_Runtime;
namespace animrig {
/**
* Action slot handle type.
*
* An identifier of slots within an action that is guaranteed to be unique
* within that action and is guaranteed not to change for a slot.
*
* NOTE: keep this type in sync with `ActionSlot::handle` in the action DNA
* types. We redefine it here rather than making a type alias to avoid bringing
* in the entirety of DNA_action_types.h for everything that includes this
* header.
*
* \see `ActionSlot::handle`
*/
using slot_handle_t = int32_t;
} // namespace animrig
/* Action Lib Stuff ----------------- */
/* Allocate a new bAction with the given name */
bAction *BKE_action_add(Main *bmain, const char name[]);
/* Action Groups API ----------------- */
/**
* Sync colors used for action/bone group with theme settings.
* This has to be called when the color theme index on the group changes so the actual color can be
* copied from the theme.
*
* \note Only meaningful on objects since for bones the group color is defined by the bone color.
*/
void action_group_colors_sync(bActionGroup *grp);
/**
* Set colors used on this action group.
*/
void action_group_colors_set(bActionGroup *grp, const BoneColor *color);
/**
* Set colors used on this action group, using the color of the pose bone.
*
* If `pchan->color` is set to a non-default color, that is used. Otherwise the
* armature bone color is used.
*
* Note that if the posechan's armature bone is nullptr, this function silently does nothing.
*/
void action_group_colors_set_from_posebone(bActionGroup *grp, bke::PChanBoneConst pchanbone);
/* Pose API ----------------- */
void BKE_pose_channel_free(bPoseChannel *pchan) ATTR_NONNULL(1);
/**
* Deallocates a pose channel.
* Does not free the pose channel itself.
*/
void BKE_pose_channel_free_ex(bPoseChannel *pchan, bool do_id_user) ATTR_NONNULL(1);
/**
* Clears the runtime cache of a pose channel without free.
*/
void BKE_pose_channel_runtime_reset(bPoseChannel_Runtime *runtime) ATTR_NONNULL(1);
/**
* Reset all non-persistent fields.
*/
void BKE_pose_channel_runtime_reset_on_copy(bPoseChannel_Runtime *runtime) ATTR_NONNULL(1);
/**
* Deallocates runtime cache of a pose channel
*/
void BKE_pose_channel_runtime_free(bPoseChannel_Runtime *runtime) ATTR_NONNULL(1);
/**
* Deallocates runtime cache of a pose channel's B-Bone shape.
*/
void BKE_pose_channel_free_bbone_cache(bPoseChannel_Runtime *runtime) ATTR_NONNULL(1);
void BKE_pose_channels_free(bPose *pose) ATTR_NONNULL(1);
/**
* Removes and deallocates all channels from a pose.
* Does not free the pose itself.
*/
void BKE_pose_channels_free_ex(bPose *pose, bool do_id_user) ATTR_NONNULL(1);
/**
* Removes the hash for quick lookup of channels, must be done when adding/removing channels.
*/
void BKE_pose_channels_hash_ensure(bPose *pose) ATTR_NONNULL(1);
void BKE_pose_channels_hash_free(bPose *pose) ATTR_NONNULL(1);
/**
* Selectively remove pose channels.
*/
void BKE_pose_channels_remove(Object *ob,
bool (*filter_fn)(const char *bone_name, void *user_data),
void *user_data) ATTR_NONNULL(1, 2);
void BKE_pose_free_data_ex(bPose *pose, bool do_id_user) ATTR_NONNULL(1);
void BKE_pose_free_data(bPose *pose) ATTR_NONNULL(1);
void BKE_pose_free(bPose *pose);
/**
* Removes and deallocates all data from a pose, and also frees the pose.
*/
void BKE_pose_free_ex(bPose *pose, bool do_id_user);
/**
* Allocate a new pose on the heap, and copy the src pose and its channels
* into the new pose. *dst is set to the newly allocated structure, and assumed to be NULL.
*
* \param dst: Should be freed already, makes entire duplicate.
*/
void BKE_pose_copy_data_ex(bPose **dst, const bPose *src, int flag, bool copy_constraints);
void BKE_pose_copy_data(bPose **dst, const bPose *src, bool copy_constraints);
/**
* Copy the internal members of each pose channel including constraints
* and ID-Props, used when duplicating bones in edit-mode.
* (unlike copy_pose_channel_data which only does posing-related stuff).
*
* \note use when copying bones in edit-mode (on returned value from #BKE_pose_channel_ensure)
*/
void BKE_pose_channel_copy_data(bPoseChannel *pchan, const bPoseChannel *pchan_from);
void BKE_pose_channel_session_uid_generate(bPoseChannel *pchan);
/**
* Return a pointer to the pose channel of the given name
* from this pose.
*/
bPoseChannel *BKE_pose_channel_find_name(const bPose *pose, const char *name);
/**
* Checks if the bone is on a visible bone collection
*
* \return true if on a visible layer, false otherwise.
*/
bool BKE_pose_is_bonecoll_visible(const bArmature *arm,
const bPoseChannel *pchan) ATTR_WARN_UNUSED_RESULT;
/**
* Find the active pose-channel for an object
*
* \param check_bonecoll: checks if the bone is on a visible bone collection
* (this might be skipped: e.g. for "Show Active" from the Outliner).
* \return #bPoseChannel if found or NULL.
* \note #Object, not #bPose is used here, as we need info (collection/active bone) from Armature.
*/
bPoseChannel *BKE_pose_channel_active(Object *ob, bool check_bonecoll);
/**
* Find the active pose-channel for an object if it is on a visible bone collection
* (calls #BKE_pose_channel_active with check_bonecoll set to true)
*
* \return #bPoseChannel if found or NULL.
* \note #Object, not #bPose is used here, as we need info (collection/active bone) from Armature.
*/
bPoseChannel *BKE_pose_channel_active_if_bonecoll_visible(Object *ob) ATTR_WARN_UNUSED_RESULT;
/**
* Use this when detecting the "other selected bone",
* when we have multiple armatures in pose mode.
*
* In this case the active-selected is an obvious choice when finding the target for a
* constraint for eg. however from the users perspective the active pose bone of the
* active object is the _real_ active bone, so any other non-active selected bone
* is a candidate for being the other selected bone, see: #58447.
*/
bPoseChannel *BKE_pose_channel_active_or_first_selected(Object *ob) ATTR_WARN_UNUSED_RESULT;
/**
* Looks to see if the channel with the given name already exists
* in this pose - if not a new one is allocated and initialized.
*
* \note Use with care, not on Armature poses but for temporal ones.
* \note (currently used for action constraints and in rebuild_pose).
*/
bPoseChannel *BKE_pose_channel_ensure(bPose *pose, const char *name) ATTR_NONNULL(2);
/**
* \see #ED_armature_ebone_get_mirrored (edit-mode, matching function)
*/
bPoseChannel *BKE_pose_channel_get_mirrored(const bPose *pose,
const char *name) ATTR_WARN_UNUSED_RESULT;
void BKE_pose_check_uids_unique_and_report(const bPose *pose);
#ifndef NDEBUG
bool BKE_pose_channels_is_valid(const bPose *pose) ATTR_WARN_UNUSED_RESULT;
#endif
/**
* Checks for IK constraint, Spline IK, and also for Follow-Path constraint.
* can do more constraints flags later. pose should be entirely OK.
*/
void BKE_pose_update_constraint_flags(Object &pose_ob);
/**
* Tag constraint flags for update.
*/
void BKE_pose_tag_update_constraint_flags(bPose *pose) ATTR_NONNULL(1);
/**
* Return the name of structure pointed by `pose->ikparam`.
*/
const char *BKE_pose_ikparam_get_name(bPose *pose) ATTR_WARN_UNUSED_RESULT;
/**
* Allocate and initialize `pose->ikparam` according to `pose->iksolver`.
*/
void BKE_pose_ikparam_init(bPose *pose) ATTR_NONNULL(1);
/**
* Initialize a #bItasc structure with default value.
*/
void BKE_pose_itasc_init(bItasc *itasc);
/**
* Checks if a bone is part of an IK chain or not.
*/
bool BKE_pose_channel_in_IK_chain(Object *ob, bPoseChannel *pchan);
/**
* Get the transform location, accounting for POSE_TRANSFORM_AT_CUSTOM_TX.
*/
void BKE_pose_channel_transform_location(const bArmature *arm,
const bPoseChannel *pose_bone,
float r_pose_space_pivot[3]);
/**
* Get the transform pose orientation, accounting for
* POSE_TRANSFORM_AT_CUSTOM_TX.
*/
void BKE_pose_channel_transform_orientation(const bArmature *arm,
const bPoseChannel *pose_bone,
float r_pose_orientation[3][3]);
/* Bone Groups API --------------------- */
/**
* Adds a new bone-group (name may be NULL).
*/
bActionGroup *BKE_pose_add_group(bPose *pose, const char *name) ATTR_NONNULL(1);
/**
* Remove the given bone-group (expects 'virtual' index (+1 one, used by active_group etc.))
* index might be invalid ( < 1), in which case it will be find from grp.
*/
void BKE_pose_remove_group(bPose *pose, bActionGroup *grp, int index) ATTR_NONNULL(1);
/**
* Remove the indexed bone-group (expects 'virtual' index (+1 one, used by active_group etc.)).
*/
void BKE_pose_remove_group_index(bPose *pose, int index) ATTR_NONNULL(1);
/* Assorted Evaluation ----------------- */
/**
* For the calculation of the effects of an Action at the given frame on an object
* This is currently only used for the Action Constraint
*/
void what_does_obaction(Object *ob,
Object *workob,
bPose *pose,
bAction *act,
int32_t action_slot_handle,
char groupname[],
const AnimationEvalContext *anim_eval_context) ATTR_NONNULL(1, 2);
void BKE_pose_copy_pchan_result(bPoseChannel *pchanto, const bPoseChannel *pchanfrom)
ATTR_NONNULL(1, 2);
/**
* Both poses should be in sync.
*/
bool BKE_pose_copy_result(bPose *to, bPose *from);
/**
* Zero the pose transforms for the entire pose or only for selected bones.
*/
void BKE_pose_rest(Object &pose_ob, bool selected_bones_only);
/**
* Tag pose for recalculation. Also tag all related data to be recalculated.
*/
void BKE_pose_tag_recalc(Main *bmain, bPose *pose) ATTR_NONNULL(1, 2);
void BKE_pose_blend_write(BlendWriter *writer, bPose *pose) ATTR_NONNULL(1, 2);
void BKE_pose_blend_read_data(BlendDataReader *reader, ID *id_owner, bPose *pose)
ATTR_NONNULL(1, 2);
void BKE_pose_blend_read_after_liblink(BlendLibReader *reader, Object *ob, bPose *pose)
ATTR_NONNULL(1, 2);
/* `action_mirror.cc` */
/**
* Flip the action so it can be applied as a mirror. Only data of slots that are related to the
* given objects is mirrored.
*/
void BKE_action_flip_with_pose(bAction *act, Span<Object *> objects) ATTR_NONNULL(1);
namespace bke {
using FoundFCurveCallback = FunctionRef<void(FCurve *fcurve, const char *bone_name)>;
using FoundFCurveCallbackConst = FunctionRef<void(const FCurve *fcurve, const char *bone_name)>;
/**
* Calls `callback` for every fcurve in an action slot that targets any bone.
*
* \param slot_handle: only FCurves from the given action slot are visited.
*/
void BKE_action_find_fcurves_with_bones(bAction *action,
animrig::slot_handle_t slot_handle,
FoundFCurveCallback callback);
void BKE_action_find_fcurves_with_bones(const bAction *action,
animrig::slot_handle_t slot_handle,
FoundFCurveCallbackConst callback);
}; // namespace bke
} // namespace blender

View File

@@ -0,0 +1,42 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
/** \file
* \ingroup bke
*/
#include "DNA_listBase.h"
namespace blender {
struct bAddon;
#ifdef __RNA_TYPES_H__
struct bAddonPrefType {
/** Type info, match #bAddon::module. */
char idname[128];
/* RNA integration */
ExtensionRNA rna_ext;
};
#else
struct bAddonPrefType;
#endif
bAddonPrefType *BKE_addon_pref_type_find(const char *idname, bool quiet);
void BKE_addon_pref_type_add(bAddonPrefType *apt);
void BKE_addon_pref_type_remove(const bAddonPrefType *apt);
void BKE_addon_pref_type_init();
void BKE_addon_pref_type_free();
struct bAddon *BKE_addon_new();
struct bAddon *BKE_addon_find(const ListBaseT<bAddon> *addon_list, const char *module);
struct bAddon *BKE_addon_ensure(ListBaseT<bAddon> *addon_list, const char *module);
bool BKE_addon_remove_safe(ListBaseT<bAddon> *addon_list, const char *module);
void BKE_addon_free(struct bAddon *addon);
} // namespace blender

View File

@@ -0,0 +1,220 @@
/* SPDX-FileCopyrightText: 2009 Blender Authors, Joshua Leung. All rights reserved.
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
/** \file
* \ingroup bke
*/
#include <optional>
#include "BLI_function_ref.hh"
#include "BLI_string_ref.hh"
namespace blender {
struct AnimData;
struct BlendDataReader;
struct BlendWriter;
struct FCurve;
struct ID;
struct Library;
struct LibraryForeachIDData;
struct Main;
struct ReportList;
struct bAction;
/* ************************************* */
/* AnimData API */
/**
* Container for the owning ID and its AnimData struct (`adt`).
*
* Fetching the `adt` pointer from an ID via BKE_animdata_from_id() involves finding the ID type,
* and checking whether that's animatable. This is why the `adt` is often obtained once, and then
* passed to other functions.
*
* However, Action Slot (un)assignment needs to keep track of which ID is being animated. Also
* Slots have to be checked whether they are suitable for the given ID, before they can be
* assigned. This means that not just the `adt` needs to be known, but also the animated ID itself.
*
* This struct is here to avoid passing two references all the time.
*/
struct OwnedAnimData {
ID &owner_id;
AnimData &adt;
};
/**
* Check if the given ID-block can have AnimData.
*/
bool id_type_can_have_animdata(short id_type);
bool id_can_have_animdata(const ID *id);
/**
* Get #AnimData from the given ID-block.
*/
AnimData *BKE_animdata_from_id(const ID *id);
/**
* Ensure #AnimData exists in the given ID-block (when supported).
*/
AnimData *BKE_animdata_ensure_id(ID *id);
/**
* Set active action used by AnimData from the given ID-block.
*
* Called when user tries to change the active action of an #AnimData block
* (via RNA, Outliner, etc.)
*
* \param reports: Can be NULL.
* \param id: The owner of the animation data
* \param act: The Action to set, or NULL to clear.
*
* \return true when the action was successfully updated, false otherwise.
*/
bool BKE_animdata_set_action(ReportList *reports, ID *id, bAction *act);
bool BKE_animdata_action_editable(const AnimData *adt);
/**
* Free AnimData used by the nominated ID-block, and clear ID-block's AnimData pointer.
*/
void BKE_animdata_free(ID *id, bool do_id_user);
/**
* Return true if the ID-block has non-empty AnimData.
*
* \see bke::animdata::prop_is_animated().
*/
bool BKE_animdata_id_is_animated(const ID *id);
/**
* Callback used by lib_query to walk over all ID usages
* (mimics `foreach_id` callback of #IDTypeInfo structure).
*/
void BKE_animdata_foreach_id(AnimData *adt, LibraryForeachIDData *data);
/**
* Make a copy of the given AnimData - to be used when copying data-blocks.
*
* \note Regarding handling of IDs managed by the #AnimData struct, this function follows the
* behaviors of the generic #BKE_id_copy_ex, please see its documentation for more details.
*
* \param flag: Control ID pointers management, see LIB_ID_CREATE_.../LIB_ID_COPY_... flags in
* `BKE_lib_id.hh`.
*
* \return The copied animdata.
*/
AnimData *BKE_animdata_copy(Main *bmain, AnimData *adt, int flag);
/**
* Same as #BKE_animdata_copy, but allows to duplicate Action IDs into a library.
*
* \param owner_library: the Library to 'assign' the newly created ID to. Use `nullptr` to make ID
* not use any library (i.e. become a local ID). Use `std::nullopt` for default behavior (i.e.
* behavior of the #BKE_animdata_copy function).
*/
AnimData *BKE_animdata_copy_in_lib(Main *bmain,
std::optional<Library *> owner_library,
AnimData *adt,
int flag);
/**
* \param flag: Control ID pointers management,
* see LIB_ID_CREATE_.../LIB_ID_COPY_... flags in BKE_lib_id.hh
* \return true is successfully copied.
*/
bool BKE_animdata_copy_id(Main *bmain, ID *id_to, ID *id_from, int flag);
/**
* Copy AnimData Actions.
*/
void BKE_animdata_copy_id_action(Main *bmain, ID *id);
void BKE_animdata_duplicate_id_action(Main *bmain, ID *id, uint duplicate_flags);
/* Merge copies of data from source AnimData block */
enum eAnimData_MergeCopy_Modes {
/* Keep destination action */
ADT_MERGECOPY_KEEP_DST = 0,
/* Use src action (make a new copy) */
ADT_MERGECOPY_SRC_COPY = 1,
/* Use src action (but just reference the existing version) */
ADT_MERGECOPY_SRC_REF = 2,
};
/**
* Merge copies of the data from the src AnimData into the destination AnimData.
*/
void BKE_animdata_merge_copy(
Main *bmain, ID *dst_id, ID *src_id, eAnimData_MergeCopy_Modes action_mode, bool fix_drivers);
void BKE_animdata_blend_write(BlendWriter *writer, ID *id);
void BKE_animdata_blend_read_data(BlendDataReader *reader, ID *id);
/**
* Process the AnimData struct after all library overrides have been applied.
*
* This is necessary as an extra step to fix the NLA, as that requires multiple pointers & various
* sets of flags to all be consistent. It's much easier to do that once all overrides have been
* applied.
*/
void BKE_animdata_liboverride_post_process(ID *id);
/* ************************************* */
/* Batch AnimData API */
/* Loop over all datablocks applying callback */
void BKE_animdata_main_cb(struct Main *bmain, FunctionRef<void(ID *, AnimData *)> func);
/** Apply the given callback function on all F-Curves attached to data in `main` database. */
void BKE_fcurves_main_cb(struct Main *bmain, FunctionRef<void(ID *, FCurve *)> func);
/**
* Loop over all f-curves of a given ID.
*
* \note Only use this function in versioning code after the conversion to layered
* actions happened which is at the start of `do_versions_after_linking_440`.
*/
void BKE_fcurves_id_cb(struct ID *id, FunctionRef<void(ID *, FCurve *)> func);
/* ************************************* */
/* TODO: overrides, remapping, and path-finding API's. */
namespace bke::animdata {
/**
* Action Slots keep a runtime list of the IDs animated by them.
*
* This function mark this 'user cache' as 'dirty', triggering a full rebuild
* next time it is accessed.
*
* This is typically only necessary after remapping IDs, swapping them, etc,
* and only called from low-level code.
*
* \note This function invalidates all user caches of all Action Slots of all
* Actions in this `bmain`.
*
* \see #animrig::Slot::users_invalidate()
* \see #animrig::internal::rebuild_slot_user_cache()
*/
void action_slots_user_cache_invalidate(Main &bmain);
/**
* Return whether there is any animation on the given property.
*
* This covers animation by direct Action assignment, the NLA, and drivers.
*
* \note This performs a full scan of all Actions (direct assignment and each
* NLA Action strip), as well as all drivers.
*
* \param adt: can be nullptr, in which case the function will return false.
*/
bool prop_is_animated(const AnimData *adt, StringRefNull rna_path, int array_index);
} // namespace bke::animdata
} // namespace blender

View File

@@ -0,0 +1,44 @@
/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
/** \file
* \ingroup bke
*/
namespace blender {
struct CurveCache;
struct Object;
/* ---------------------------------------------------- */
/* Curve Paths */
int BKE_anim_path_get_array_size(const struct CurveCache *curve_cache);
float BKE_anim_path_get_length(const struct CurveCache *curve_cache);
/**
* This function populates the `ob->runtime->curve_cache->anim_path_accum_length` data.
* You should never have to call this manually as it should already have been called by
* #BKE_displist_make_curveTypes. Do not call this manually unless you know what you are doing.
*/
void BKE_anim_path_calc_data(struct Object *ob);
/**
* Calculate the deformation implied by the curve path at a given parametric position,
* and returns whether this operation succeeded.
*
* \param ctime: Time is normalized range <0-1>.
*
* \return success.
*/
bool BKE_where_on_path(const struct Object *ob,
float ctime,
float r_vec[4],
float r_dir[3],
float r_quat[4],
float *r_radius,
float *r_weight);
} // namespace blender

View File

@@ -0,0 +1,61 @@
/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
/** \file
* \ingroup bke
*/
namespace blender {
struct BlendDataReader;
struct BlendWriter;
struct Object;
struct ReportList;
struct Scene;
struct bAnimVizSettings;
struct bMotionPath;
struct bPoseChannel;
/* ---------------------------------------------------- */
/* Animation Visualization */
/**
* Initialize the default settings for animation visualization.
*/
void animviz_settings_init(struct bAnimVizSettings *avs);
/**
* Make a copy of motion-path data, so that viewing with copy on write works.
*/
struct bMotionPath *animviz_copy_motionpath(const struct bMotionPath *mpath_src);
/**
* Free the given motion path's cache.
*/
void animviz_free_motionpath_cache(struct bMotionPath *mpath);
/**
* Free the given motion path instance and its data.
* \note this frees the motion path given!
*/
void animviz_free_motionpath(struct bMotionPath *mpath);
/**
* Setup motion paths for the given data.
* \note Only used when explicitly calculating paths on bones which may/may not be consider already
*
* \param scene: Current scene (for frame ranges, etc.)
* \param ob: Object to add paths for (must be provided)
* \param pchan: Pose-channel to add paths for
* (optional; if not provided, object-paths are assumed).
*/
struct bMotionPath *animviz_verify_motionpaths(struct ReportList *reports,
struct Scene *scene,
struct Object *ob,
struct bPoseChannel *pchan);
void animviz_motionpath_blend_write(struct BlendWriter *writer, struct bMotionPath *mpath);
void animviz_motionpath_blend_read_data(struct BlendDataReader *reader, struct bMotionPath *mpath);
} // namespace blender

View File

@@ -0,0 +1,415 @@
/* SPDX-FileCopyrightText: 2009 Blender Authors, Joshua Leung. All rights reserved.
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
/** \file
* \ingroup bke
*/
#include "DNA_listBase.h"
#include "BLI_bit_vector.hh"
#include "BLI_span.hh"
#include "BLI_sys_types.h" /* for bool */
#include <string>
namespace blender {
struct AnimData;
struct BlendDataReader;
struct BlendWriter;
struct Depsgraph;
struct FCurve;
struct ID;
struct KS_Path;
struct KeyingSet;
struct LibraryForeachIDData;
struct Main;
struct NlaKeyframingContext;
struct PathResolvedRNA;
struct PointerRNA;
struct PropertyRNA;
struct TimeMarker;
struct bAction;
struct bActionGroup;
enum eInsertKeyFlags : short;
enum eKSP_Grouping : short;
enum eKSP_Settings : short;
enum eKS_Settings : short;
/** Container for data required to do FCurve and Driver evaluation. */
struct AnimationEvalContext {
/* For drivers, so that they have access to the dependency graph and the current view layer. See
* #77086. */
struct Depsgraph *depsgraph;
/* FCurves and Drivers can be evaluated at a different time than the current scene time, for
* example when evaluating NLA strips. This means that, even though the current time is stored in
* the dependency graph, we need an explicit evaluation time. */
float eval_time;
};
AnimationEvalContext BKE_animsys_eval_context_construct(struct Depsgraph *depsgraph,
float eval_time) ATTR_WARN_UNUSED_RESULT;
AnimationEvalContext BKE_animsys_eval_context_construct_at(
const AnimationEvalContext *anim_eval_context, float eval_time) ATTR_WARN_UNUSED_RESULT;
/* ************************************* */
/* KeyingSets API */
/**
* Used to create a new 'custom' KeyingSet for the user,
* that will be automatically added to the stack.
*/
struct KeyingSet *BKE_keyingset_add(ListBaseT<KeyingSet> *list,
const char idname[],
const char name[],
eKS_Settings flag,
eInsertKeyFlags keyingflag);
/**
* Add a path to a KeyingSet. Nothing is returned for now.
* Checks are performed to ensure that destination is appropriate for the KeyingSet in question
*/
struct KS_Path *BKE_keyingset_add_path(struct KeyingSet *ks,
struct ID *id,
const char group_name[],
const char rna_path[],
int array_index,
eKSP_Settings flag,
eKSP_Grouping groupmode);
/**
* Find the destination matching the criteria given.
* TODO: do we want some method to perform partial matches too?
*/
struct KS_Path *BKE_keyingset_find_path(struct KeyingSet *ks,
struct ID *id,
const char group_name[],
const char rna_path[],
int array_index,
int group_mode);
/** Copy all KeyingSets in the given list. */
void BKE_keyingsets_copy(ListBaseT<KeyingSet> *newlist, const ListBaseT<KeyingSet> *list);
/**
* Process the ID pointers inside a scene's keying-sets, in.
* see `BKE_lib_query.hh` for details.
*/
void BKE_keyingsets_foreach_id(struct LibraryForeachIDData *data,
const ListBaseT<KeyingSet> *keyingsets);
/** Free the given Keying Set path. */
void BKE_keyingset_free_path(struct KeyingSet *ks, struct KS_Path *ksp);
/** Free data for KeyingSet but not set itself. */
void BKE_keyingset_free_paths(struct KeyingSet *ks);
/** Free all the KeyingSets in the given list. */
void BKE_keyingsets_free(ListBaseT<KeyingSet> *list);
void BKE_keyingsets_blend_write(struct BlendWriter *writer, ListBaseT<KeyingSet> *list);
void BKE_keyingsets_blend_read_data(struct BlendDataReader *reader, ListBaseT<KeyingSet> *list);
/* ************************************* */
/* Path Fixing API */
/**
* Get a "fixed" version of the given path `old_path`.
*
* This is just an external wrapper for the RNA-Path fixing function,
* with input validity checks on top of the basic method.
*
* \note it is assumed that the structure we're replacing is `<prefix><["><name><"]>`
* i.e. `pose.bones["Bone"]`.
*/
char *BKE_animsys_fix_rna_path_rename(struct ID *owner_id,
char *old_path,
const char *prefix,
const char *oldName,
const char *newName,
int oldSubscript,
int newSubscript,
bool verify_paths);
/**
* Fix all the paths for the given ID + Action.
*
* This is just an external wrapper for the F-Curve fixing function,
* with input validity checks on top of the basic method.
*
* \note it is assumed that the structure we're replacing is `<prefix><["><name><"]>`
* i.e. `pose.bones["Bone"]`.
*/
void BKE_action_fix_paths_rename(struct ID *owner_id,
struct bAction *act,
int32_t /*slot_handle_t*/ slot_handle,
const char *prefix,
const char *oldName,
const char *newName,
int oldSubscript,
int newSubscript,
bool verify_paths);
/**
* Fix all the paths for the given ID+AnimData
*
* \param old_infix, new_infix: The path section immediately following the `prefix`. If
* `infix_is_name` is true, this is processed as a name..
*
* \param infix_is_name: If true, old_infix and new_infix are treated as names and padded with
* [""] so that only exact matches are made. For example, the structure we're replacing is
* `<prefix><["><name><"]>` i.e. `pose.bones["Bone"]`.
*/
void BKE_animdata_fix_paths_rename(struct ID *owner_id,
struct AnimData *adt,
struct ID *ref_id,
const char *prefix,
const char *old_infix,
const char *new_infix,
int oldSubscript,
int newSubscript,
bool verify_paths,
bool infix_is_name);
/**
* Fix all RNA-Paths throughout the database (directly access the #Global.main version).
*
* \param old_infix, new_infix: The path section immediately following the `prefix`. If
* `infix_is_name` is true, this is processed as a name.
*
* \param infix_is_name: If true, old_infix and new_infix are treated as names and padded with
* [""] so that only exact matches are made. For example, the structure we're replacing is
* `<prefix><["><name><"]>` i.e. `pose.bones["Bone"]`
*/
void BKE_animdata_fix_paths_rename_all_ex(struct Main *bmain,
struct ID *ref_id,
const char *prefix,
const char *old_infix,
const char *new_infix,
int oldSubscript,
int newSubscript,
bool verify_paths,
bool infix_is_name);
/** See #BKE_animdata_fix_paths_rename_all_ex */
void BKE_animdata_fix_paths_rename_all(struct ID *ref_id,
const char *prefix,
const char *oldName,
const char *newName);
/**
* Remove any animation data (F-Curves from Actions, and drivers) that have an
* RNA path starting with `prefix`.
*
* Return true if any animation data was affected.
*/
bool BKE_animdata_fix_paths_remove(struct ID *id, const char *prefix);
/**
* Remove drivers that have an RNA path starting with `prefix`.
*
* \return true if any driver was removed.
*/
bool BKE_animdata_driver_path_remove(struct ID *id, const char *prefix);
/**
* Remove all drivers from the given struct.
*
* \param type: needs to be a struct owned by the given ID.
* \param data: the actual struct data, needs to be the data for the StructRNA.
*
* \return true if any driver was removed.
*/
bool BKE_animdata_drivers_remove_for_rna_struct(struct ID &owner_id,
struct StructRNA &type,
void *data);
/* -------------------------------------- */
struct AnimationBasePathChange {
std::string src_basepath;
std::string dst_basepath;
};
/**
* Copy any animation data under the base paths from the #src_id animation data to the #dst_id
* animation data. Animation data in #dst_id is created if necessary. If #dst_id has an assigned
* action it may be modified or an empty action is assigned if none exists. F-Curves are copied to
* the action assigned to #dst_id and drivers are copied to the animation data.
*
* \param basepaths: List of base path pairs to transfer.
*/
void BKE_animdata_copy_by_basepath(Main &bmain,
const ID &src_id,
ID &dst_id,
Span<AnimationBasePathChange> basepaths);
/* ------------ NLA Keyframing --------------- */
struct NlaKeyframingContext;
/**
* Prepare data necessary to compute correct keyframe values for NLA strips
* with non-Replace mode or influence different from 1.
*
* \param cache: List used to cache contexts for reuse when keying
* multiple channels in one operation.
* \param ptr: RNA pointer to the ID with the animation.
* \return Keyframing context, or NULL if not necessary.
*/
struct NlaKeyframingContext *BKE_animsys_get_nla_keyframing_context(
ListBaseT<NlaKeyframingContext> *cache,
struct PointerRNA *ptr,
struct AnimData *adt,
const struct AnimationEvalContext *anim_eval_context);
/**
* Apply correction from the NLA context to the values about to be keyframed.
*
* \param context: Context to use (may be NULL).
* \param prop_ptr: Property about to be keyframed.
* \param[in,out] values: Span of property values to adjust.
* \param index: Index of the element about to be updated, or -1.
* \param[out] r_force_all: For array properties, set to true if the property
* should be treated as all-or-nothing (i.e. where either all elements get keyed
* or none do). Irrelevant for non-array properties. May be NULL.
* \param[out] r_values_mask: A mask for the elements of `values`, where bits
* are set to true for the elements that were both indicated by `index` and for
* which valid keying values were successfully computed. In short, this is a
* mask for the indices that can get keyed.
*/
void BKE_animsys_nla_remap_keyframe_values(struct NlaKeyframingContext *context,
struct PointerRNA *prop_ptr,
struct PropertyRNA *prop,
const MutableSpan<float> values,
int index,
const struct AnimationEvalContext *anim_eval_context,
bool *r_force_all,
BitVector<> &r_values_mask);
/**
* Free all cached contexts from the list.
*/
void BKE_animsys_free_nla_keyframing_context_cache(ListBaseT<NlaKeyframingContext> *cache);
/* ************************************* */
/* Evaluation API */
/* ------------- Main API -------------------- */
/* In general, these ones should be called to do all animation evaluation */
/* Flags for recalc parameter, indicating which part to recalculate. */
enum eAnimData_Recalc {
ADT_RECALC_DRIVERS = (1 << 0),
ADT_RECALC_ANIM = (1 << 1),
ADT_RECALC_ALL = (ADT_RECALC_DRIVERS | ADT_RECALC_ANIM),
};
bool BKE_animsys_rna_path_resolve(struct PointerRNA *ptr,
const char *rna_path,
int array_index,
struct PathResolvedRNA *r_result);
bool BKE_animsys_read_from_rna_path(struct PathResolvedRNA *anim_rna, float *r_value);
/**
* Write the given value to a setting using RNA, and return success.
*
* \param force_write: When false, this function will only call the RNA setter when `value` is
* different from the property's current value. When true, this function will skip that check and
* always call the RNA setter.
*/
bool BKE_animsys_write_to_rna_path(struct PathResolvedRNA *anim_rna,
float value,
bool force_write = false);
/**
* Evaluation loop for evaluation animation data
*
* This assumes that the animation-data provided belongs to the ID block in question,
* and that the flags for which parts of the animation-data settings need to be recalculated
* have been set already by the depsgraph. Now, we use the recalculate.
*/
void BKE_animsys_evaluate_animdata(struct ID *id,
struct AnimData *adt,
const struct AnimationEvalContext *anim_eval_context,
eAnimData_Recalc recalc,
bool flush_to_original);
/**
* Evaluation of all ID-blocks with Animation Data blocks - Animation Data Only
*
* This will evaluate only the animation info available in the animation data-blocks
* encountered. In order to enforce the system by which some settings controlled by a
* 'local' (i.e. belonging in the nearest ID-block that setting is related to, not a
* standard 'root') block are overridden by a larger 'user'
*/
void BKE_animsys_evaluate_all_animation(struct Main *main,
struct Depsgraph *depsgraph,
float ctime);
/* ------------ Specialized API --------------- */
/* There are a few special tools which require these following functions. They are NOT to be used
* for standard animation evaluation UNDER ANY CIRCUMSTANCES!
*
* i.e. Pose Library (PoseLib) uses some of these for selectively applying poses, but
* Particles/Sequencer performing funky time manipulation is not ok.
*/
/**
* Evaluate Action (F-Curve Bag).
*/
void animsys_evaluate_action(struct PointerRNA *ptr,
struct bAction *act,
int32_t action_slot_handle,
const struct AnimationEvalContext *anim_eval_context,
bool flush_to_original);
/**
* Evaluate action, and blend the result into the current values (instead of overwriting fully).
*/
void animsys_blend_in_action(struct PointerRNA *ptr,
struct bAction *act,
int32_t action_slot_handle,
const AnimationEvalContext *anim_eval_context,
float blend_factor);
/** Evaluate Action Group. */
void animsys_evaluate_action_group(struct PointerRNA *ptr,
struct bAction *act,
struct bActionGroup *agrp,
const struct AnimationEvalContext *anim_eval_context);
/* ************************************* */
/* ------------ Evaluation API --------------- */
void BKE_animsys_eval_animdata(struct Depsgraph *depsgraph, struct ID *id);
void BKE_animsys_eval_driver_unshare(Depsgraph *depsgraph, ID *id);
void BKE_animsys_eval_driver(struct Depsgraph *depsgraph,
struct ID *id,
int driver_index,
struct FCurve *fcu_orig);
void BKE_animsys_update_driver_array(struct ID *id);
/* ************************************* */
void BKE_time_markers_blend_write(BlendWriter *writer, ListBaseT<TimeMarker> &markers);
void BKE_time_markers_blend_read(BlendDataReader *reader, ListBaseT<TimeMarker> &markers);
/**
* Copy a list of time markers.
*
* Note: this is meant to be called in the context of duplicating an ID.
*
* \param flag: ID copy flags. Corresponds to the `flag` parameter of `BKE_id_copy_ex()`.
*/
void BKE_copy_time_markers(ListBaseT<TimeMarker> &markers_dst,
const ListBaseT<TimeMarker> &markers_src,
int flag);
} // namespace blender

View File

@@ -0,0 +1,40 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup bke
*/
#pragma once
#include "BLI_string_ref.hh"
#include "BKE_attribute_filter.hh"
namespace blender::bke {
/**
* Checks if the attribute name has the `.a_` prefix which indicates that it is an anonymous
* attribute. I.e. it is just internally used by Blender and the name should not be exposed to the
* user.
*
* Use #hash_to_anonymous_attribute_name to generate names for anonymous attributes.
*/
inline bool attribute_name_is_anonymous(const StringRef name)
{
return name.startswith(".a_");
}
class ProcessAllAttributeExceptAnonymous : public AttributeFilter {
public:
Result filter(const StringRef name) const override
{
if (attribute_name_is_anonymous(name)) {
return AttributeFilter::Result::AllowSkip;
}
return AttributeFilter::Result::Process;
}
};
} // namespace blender::bke

View File

@@ -0,0 +1,24 @@
/* SPDX-FileCopyrightText: 2024 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup bke
*/
#include <fmt/format.h>
#include <sstream>
#include <xxhash.h>
namespace blender::bke {
template<typename... Args> inline std::string hash_to_anonymous_attribute_name(Args &&...args)
{
std::stringstream ss;
((ss << args), ...);
const std::string long_name = ss.str();
const XXH128_hash_t hash = XXH3_128bits(long_name.c_str(), long_name.size());
return fmt::format(".a_{:x}{:x}", hash.low64, hash.high64);
}
} // namespace blender::bke

View File

@@ -0,0 +1,204 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
/** \file
* \ingroup bke
*
* \note on naming: typical _get() suffix is omitted here,
* since its the main purpose of the API.
*/
#include <cstddef>
#include <optional>
#include <string>
#include "BLI_compiler_attrs.h"
#include "BLI_string_ref.hh"
#include "DNA_listBase.h"
namespace blender {
/**
* Sanity check to ensure correct API use in debug mode.
*
* Run this once the first level of arguments has been passed so we can be sure
* `--env-system-datafiles`, and other `--env-*` arguments has been passed.
*
* Without this any callers to this module that run early on,
* will miss out on changes from parsing arguments.
*/
void BKE_appdir_init();
void BKE_appdir_exit();
/**
* Get the folder that's the "natural" starting point for browsing files on an OS.
* - Unix: `$HOME`
* - Windows: `%userprofile%/Documents`
*
* \note On Windows `Users/{MyUserName}/Documents` is used as it's the default location to save
* documents.
*/
const char *BKE_appdir_folder_default() ATTR_WARN_UNUSED_RESULT;
const char *BKE_appdir_folder_root() ATTR_WARN_UNUSED_RESULT ATTR_RETURNS_NONNULL;
const char *BKE_appdir_folder_default_or_root() ATTR_WARN_UNUSED_RESULT ATTR_RETURNS_NONNULL;
/**
* Get the user's document directory, i.e.
* - Linux: `$HOME/Documents`
* - Windows: `%userprofile%/Documents`
*
* If this can't be found using OS queries (via Ghost), try manually finding it.
*
* \returns True if the path is valid and points to an existing directory.
*/
bool BKE_appdir_folder_documents(char *dir) ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT;
/**
* Get the user's cache directory, i.e.
* - Linux: `$HOME/.cache/blender/`
* - Windows: `%USERPROFILE%\AppData\Local\Blender Foundation\Blender\Cache\`
* - MacOS: `$HOME/Library/Caches/Blender/`
*
* \note In rare cases when the cache directory is inaccessible,
* the temporary session directory is used with a `.cache/` subdirectory.
*
* \note The value may be set without the directory existing.
* The caller is responsible for creating the directory.
*/
void BKE_appdir_folder_caches(char *path, size_t path_maxncpy) ATTR_NONNULL(1);
/**
* Get a folder out of the \a folder_id presets for paths.
*
* \param subfolder: The name of a directory to check for,
* this may contain path separators but must resolve to a directory, checked with #BLI_is_dir.
* \return The path if found, NULL string if not.
*/
bool BKE_appdir_folder_id_ex(int folder_id,
const char *subfolder,
char *path,
size_t path_maxncpy);
std::optional<std::string> BKE_appdir_folder_id(int folder_id,
const char *subfolder) ATTR_WARN_UNUSED_RESULT;
/**
* Returns the path to a folder in the user area, creating it if it doesn't exist.
*/
std::optional<std::string> BKE_appdir_folder_id_create(int folder_id, const char *subfolder)
ATTR_WARN_UNUSED_RESULT;
/**
* Returns the path to a folder in the user area without checking that it actually exists first.
*/
std::optional<std::string> BKE_appdir_folder_id_user_notest(int folder_id, const char *subfolder)
ATTR_WARN_UNUSED_RESULT;
/**
* Returns the path of the top-level version-specific local, user or system directory.
* If check_is_dir, then the result will be NULL if the directory doesn't exist.
*/
std::optional<std::string> BKE_appdir_resource_path_id_with_version(int folder_id,
bool check_is_dir,
int version);
std::optional<std::string> BKE_appdir_resource_path_id(int folder_id, bool check_is_dir);
/**
* Return true if templates exist
*/
bool BKE_appdir_app_template_any();
bool BKE_appdir_app_template_id_search(const char *app_template, char *path, size_t path_maxncpy)
ATTR_NONNULL(1);
bool BKE_appdir_app_template_has_userpref(const char *app_template) ATTR_NONNULL(1);
void BKE_appdir_app_templates(ListBaseT<LinkData> *templates) ATTR_NONNULL(1);
/**
* Initialize path to program executable.
*/
void BKE_appdir_program_path_init(const char *argv0) ATTR_NONNULL(1);
/**
* Path to executable
*/
const char *BKE_appdir_program_path() ATTR_WARN_UNUSED_RESULT ATTR_RETURNS_NONNULL;
/**
* Path to directory of executable
*/
const char *BKE_appdir_program_dir() ATTR_WARN_UNUSED_RESULT ATTR_RETURNS_NONNULL;
/**
* Gets a good default directory for fonts.
*/
bool BKE_appdir_font_folder_default(char *dir, size_t dir_maxncpy);
/**
* Find Python executable.
*/
bool BKE_appdir_program_python_search(char *program_filepath,
size_t program_filepath_maxncpy,
int version_major,
int version_minor) ATTR_NONNULL(1);
/**
* Initialize path to temporary directory.
*/
void BKE_tempdir_init(const char *userdir);
/**
* Path to persistent temporary directory (with trailing slash)
*/
const char *BKE_tempdir_base() ATTR_WARN_UNUSED_RESULT ATTR_RETURNS_NONNULL;
/**
* Path to temporary directory (with trailing slash)
*/
const char *BKE_tempdir_session() ATTR_WARN_UNUSED_RESULT ATTR_RETURNS_NONNULL;
/**
* Delete content of this instance's temp dir.
*/
void BKE_tempdir_session_purge();
/**
* The `folder_id` for #BKE_appdir_folder_id and related functions.
*
* Run-time only so existing values may change.
*/
enum {
/* general, will find based on user/local/system priority */
BLENDER_DATAFILES = 2,
/* user-specific */
BLENDER_USER_CONFIG = 31,
BLENDER_USER_DATAFILES = 32,
BLENDER_USER_SCRIPTS = 33,
BLENDER_USER_EXTENSIONS = 34,
/* system */
BLENDER_SYSTEM_DATAFILES = 52,
BLENDER_SYSTEM_SCRIPTS = 53,
BLENDER_SYSTEM_EXTENSIONS = 54,
BLENDER_SYSTEM_PYTHON = 55,
};
/** For #BKE_appdir_folder_id_version only. */
enum {
BLENDER_RESOURCE_PATH_USER = 0,
BLENDER_RESOURCE_PATH_LOCAL = 1,
BLENDER_RESOURCE_PATH_SYSTEM = 2,
/**
* Architecture-dependent libraries, mirroring #BLENDER_RESOURCE_PATH_SYSTEM.
* Typically a path under `/usr/lib/...` instead of `/usr/share/...`,
* although exact paths are configurable at build-time.
*
* For run-time library checks, first check if this path is defined,
* then fallback to #BLENDER_RESOURCE_PATH_SYSTEM.
*/
BLENDER_RESOURCE_PATH_SYSTEM_LIBS = 3,
};
#define BLENDER_STARTUP_FILE "startup.blend"
#define BLENDER_USERPREF_FILE "userpref.blend"
#define BLENDER_QUIT_FILE "quit.blend"
#define BLENDER_BOOKMARK_FILE "bookmarks.txt"
#define BLENDER_HISTORY_FILE "recent-files.txt"
#define BLENDER_RECENT_SEARCHES_FILE "recent-searches.txt"
#define BLENDER_PLATFORM_SUPPORT_FILE "platform_support.txt"
} // namespace blender

View File

@@ -0,0 +1,789 @@
/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
/** \file
* \ingroup bke
*/
#include <optional>
#include "BLI_bounds_types.hh"
#include "BLI_function_ref.hh"
#include "BLI_math_matrix_types.hh"
#include "BLI_math_vector_types.hh"
#include "BLI_mutex.hh"
#include "BLI_set.hh"
#include "DNA_armature_types.h"
#include "DNA_listBase.h"
#include "BKE_pose.hh"
namespace blender {
struct bDeformGroup;
struct BMEditMesh;
struct Bone;
struct Depsgraph;
struct IDProperty;
struct Main;
struct Mesh;
struct Object;
struct PoseTree;
struct Scene;
struct bArmature;
struct bConstraint;
struct bPose;
struct bPoseChannel;
struct MDeformVert;
enum eRotationModes : short;
struct EditBone {
EditBone *next = nullptr, *prev = nullptr;
/** User-Defined Properties on this Bone */
IDProperty *prop = nullptr;
/** System-Defined Properties storage. */
IDProperty *system_properties = nullptr;
/**
* Edit-bones have a one-way link (i.e. children refer to parents).
* This is converted to a two-way link for normal bones when leaving edit-mode.
*/
EditBone *parent = nullptr;
char name[/*MAXBONENAME*/ 64] = "";
/**
* Roll along axis. We'll ultimately use the axis/angle method
* for determining the transformation matrix of the bone. The axis
* is tail-head while roll provides the angle. Refer to Graphics
* Gems 1 p. 466 (section IX.6) if it's not already in here somewhere.
*/
float roll = 0.0f;
/** Orientation and length is implicit during editing */
float head[3] = {};
float tail[3] = {};
/**
* All joints are considered to have zero rotation with respect to
* their parents. Therefore any rotations specified during the
* animation are automatically relative to the bones' rest positions.
*/
eBone_Flag flag = {};
int layer = 0;
int drawtype = 0; /* eArmature_Drawtype */
eBone_InheritScaleMode inherit_scale_mode = BONE_INHERIT_SCALE_FULL;
/* Envelope distance & weight */
float dist = 0, weight = 0;
/** put them in order! transform uses this as scale */
float xwidth = 0, length = 0, zwidth = 0;
float rad_head = 0, rad_tail = 0;
/* Bendy-Bone parameters */
short segments = 0;
float roll1 = 0, roll2 = 0;
float curve_in_x = 0, curve_in_z = 0;
float curve_out_x = 0, curve_out_z = 0;
float ease1 = 0, ease2 = 0;
float scale_in[3] = {}, scale_out[3] = {};
/** for envelope scaling */
float oldlength = 0;
/** Mapping of vertices to segments. */
eBone_BBoneMappingMode bbone_mapping_mode = BBONE_MAPPING_STRAIGHT;
/** Type of next/prev bone handles */
eBone_BBoneHandleType bbone_prev_type = BBONE_HANDLE_AUTO;
eBone_BBoneHandleType bbone_next_type = BBONE_HANDLE_AUTO;
/** B-Bone flags. */
eBone_BBoneFlag bbone_flag = {};
eBone_BBoneHandleFlag bbone_prev_flag = {};
eBone_BBoneHandleFlag bbone_next_flag = {};
/** Next/prev bones to use as handle references when calculating bbones (optional) */
EditBone *bbone_prev = nullptr;
EditBone *bbone_next = nullptr;
/* Used for display */
/** in Armature space, rest pos matrix */
float disp_mat[4][4] = {};
/** in Armature space, rest pos matrix */
float disp_tail_mat[4][4] = {};
/** in Armature space, rest pos matrix. */
float disp_bbone_mat[/*MAX_BBONE_SUBDIV*/ 32][4][4] = {};
/** connected child temporary during drawing */
EditBone *bbone_child = nullptr;
BoneColor color; /* MUST be named the same as in bPoseChannel and Bone structs. */
ListBaseT<BoneCollectionReference> bone_collections = {};
/* Used to store temporary data */
union {
EditBone *ebone = nullptr;
Bone *bone;
void *p;
int i;
} temp;
};
struct PoseTarget {
PoseTarget *next, *prev;
bConstraint *con; /* the constraint of this target */
int tip; /* index of tip pchan in PoseTree */
};
struct PoseTree {
PoseTree *next, *prev;
int type; /* type of IK that this serves (CONSTRAINT_TYPE_KINEMATIC or ..._SPLINEIK) */
int totchannel; /* number of pose channels */
ListBaseT<PoseTarget> targets; /* list of targets of the tree */
bPoseChannel **pchan; /* array of pose channels */
int *parent; /* and their parents */
float (*basis_change)[3][3]; /* basis change result from solver */
int iterations; /* iterations from the constraint */
int stretch; /* disable stretching */
};
/* Core armature functionality. */
bArmature *BKE_armature_add(Main *bmain, const char *name);
bArmature *BKE_armature_from_object(Object *ob);
int BKE_armature_bonelist_count(const ListBaseT<Bone> *lb);
void BKE_armature_bonelist_free(ListBaseT<Bone> *lb, bool do_id_user);
void BKE_armature_editbonelist_free(ListBaseT<EditBone> *lb, bool do_id_user);
void BKE_armature_copy_bone_transforms(bArmature *armature_dst, const bArmature *armature_src);
void BKE_armature_transform(bArmature *arm, const float mat[4][4], bool do_props);
/**
* Return the posed Armature bounding box in object-local coordinate space.
*/
std::optional<Bounds<float3>> BKE_armature_min_max(const Object *ob);
using ForeachBoneFn = FunctionRef<void(int index, const Bone &bone)>;
/**
* Visit each bone in the armature, in depth-first order.
*/
void BKE_armature_foreach_bone(const bArmature &armature, ForeachBoneFn callback);
/**
* Calculate the axis-aligned bounds of `pchan` in object-space,
* taking into account custom transform when set.
*
* `r_min` and `r_max` are expanded to fit `pchan` so the caller must initialize them
* (typically using #INIT_MINMAX).
*
* \note The bounds are calculated based on the head & tail of the bone
* or the custom object's bounds (if the bone uses a custom object).
* Visual elements such as the envelopes radius & bendy-bone spline segments are *not* included,
* making this not so useful for viewport culling.
*
* \param use_empty_drawtype: When enabled, the draw type of empty custom-objects is taken into
* account when calculating the bounds.
*/
void BKE_pchan_minmax(const Object *ob,
bke::PChanBoneConst pchanbone,
bool use_empty_drawtype,
float3 &r_min,
float3 &r_max);
/**
* Calculate the axis aligned bounds of the pose of `ob` in object-space.
*
* This only considers visible bones. When they are either directly (via a flag on the bone) or
* indirectly (via bone collections) hidden, they are not part of the bounds calculation. When a
* bone has a custom bone shape, that is included in the bounding box.
*
* \note This uses #BKE_pchan_minmax, see its documentation for details on bounds calculation.
*
* \param use_select: When true, only consider selected bones. When false, selection state is
* ignored and all bones are included in the bounds.
*/
std::optional<Bounds<float3>> BKE_pose_minmax(const Object *ob, bool use_select);
/**
* Finds the best possible extension to the name on a particular axis.
* (For renaming, check for unique names afterwards)
* \param strip_number: removes number extensions (TODO: not used).
* \param axis: The axis to name on.
* \param head: The head coordinate of the bone on the specified axis.
* \param tail: The tail coordinate of the bone on the specified axis.
*/
bool bone_autoside_name(char name[64], int strip_number, short axis, float head, float tail);
/**
* Find the bone with the given name.
*
* When doing multiple subsequent calls to this function, consider calling
* #BKE_armature_bone_hash_make first to hash the bone names and speed up
* queries.
*/
Bone *BKE_armature_find_bone_name(bArmature *arm, const char *name);
void BKE_armature_bone_hash_make(bArmature *arm);
void BKE_armature_bone_hash_free(bArmature *arm);
bool BKE_armature_bone_flag_test_recursive(const Bone *bone, int flag);
/**
* Bone influence factor from envelope distance.
*/
float distfactor_to_bone(const float3 &position,
const float3 &head,
const float3 &tail,
float radius_head,
float radius_tail,
float falloff_distance);
/**
* Updates vectors and matrices on rest-position level, only needed
* after editing armature itself, now only on reading file.
*/
void BKE_armature_where_is(bArmature *arm);
/**
* Recursive part, calculates rest-position of entire tree of children.
* \note Used when exiting edit-mode too.
*/
void BKE_armature_where_is_bone(Bone *bone, const Bone *bone_parent, bool use_recursion);
/**
* Clear pointers of object's pose
* (needed in remap case, since we cannot always wait for a complete pose rebuild).
*/
void BKE_pose_clear_pointers(bPose *pose);
/**
* Update the links for the B-Bone handles from Bone data.
*/
void BKE_pchan_rebuild_bbone_handles(bPose *pose, bke::PChanBone pchanbone);
void BKE_pose_channels_clear_with_null_bone(Object *armature_ob, bool do_id_user);
/**
* Only after leave edit-mode, duplicating, validating older files, library syncing.
*
* \note pose->flag is set for it.
*
* \param bmain: May be NULL, only used to tag depsgraph as being dirty.
*/
void BKE_pose_rebuild(Main *bmain, Object *ob, bArmature *arm, bool do_id_user);
/**
* Ensures object's pose is rebuilt if needed.
*
* \param bmain: May be NULL, only used to tag depsgraph as being dirty.
*/
void BKE_pose_ensure(Main *bmain, Object *ob, bArmature *arm, bool do_id_user);
/**
* Ensure the object's pose bone indices are up to date.
*
* This is only necessary when calling `pchan->bone_get(armature)`, as that cannot verify the
* up-to-dateness of the pose bone indices.
*
* When calling `pchan->bone_get(object)` the check is performed automatically. However, calling
* that in a hot loop will cost some performance; passing the armature is preferred in that case.
*
* This takes a const Object, because it does not modify anything except its pose channels' runtime
* field `bone_index`. There is no conceptual change to the object, it's just the bone lookup info
* that's being refreshed. Ideally the field would be marked `mutable`, but currently the
* bPoseChannel_runtime struct is part of DNA, which doesn't support that keyword.
*/
void BKE_pose_ensure_bone_indices(const Object &pose_object);
/**
* \note This is the only function adding poses.
* \note This only reads anim data from channels, and writes to channels.
*/
void BKE_pose_where_is(Depsgraph *depsgraph, Scene *scene, Object *ob);
/**
* The main armature solver, does all constraints excluding IK.
*
* \param pchan: pose-channel - validated, as having bone and parent pointer.
* \param do_extra: when zero skips loc/size/rot, constraints and strip modifiers.
*/
void BKE_pose_where_is_bone(Depsgraph *depsgraph,
Scene *scene,
Object *ob,
bPoseChannel *pchan,
float ctime,
bool do_extra);
/**
* Calculate tail of pose-channel.
*/
void BKE_pose_where_is_bone_tail(bke::PChanBone pchanbone);
void vec_roll_to_mat3(const float vec[3], float roll, float r_mat[3][3]);
/**
* Calculates the rest matrix of a bone based on its vector and a roll around that vector.
*/
void vec_roll_to_mat3_normalized(const float nor[3], float roll, float r_mat[3][3]);
/**
* Computes vector and roll based on a rotation.
* "mat" must contain only a rotation, and no scaling.
*/
void mat3_to_vec_roll(const float mat[3][3], float r_vec[3], float *r_roll);
/**
* Computes roll around the vector that best approximates the matrix.
* If `vec` is the Y vector from purely rotational `mat`, result should be exact.
*/
void mat3_vec_to_roll(const float mat[3][3], const float vec[3], float *r_roll);
/* Common Conversions Between Coordinate Spaces */
/**
* Convert World-Space Matrix to Pose-Space Matrix.
*/
void BKE_armature_mat_world_to_pose(Object *ob, const float inmat[4][4], float outmat[4][4]);
/**
* Convert World-Space Location to Pose-Space Location
* \note this cannot be used to convert to pose-space location of the supplied
* pose-channel into its local space (i.e. *visual*-keyframing).
*/
void BKE_armature_loc_world_to_pose(Object *ob, const float inloc[3], float outloc[3]);
/**
* Convert Pose-Space Matrix to Bone-Space Matrix.
* \note this cannot be used to convert to pose-space transforms of the supplied
* pose-channel into its local space (i.e. *visual*-keyframing).
*/
void BKE_armature_mat_pose_to_bone(bke::PChanBoneConst pchanbone,
const float inmat[4][4],
float outmat[4][4]);
/**
* Convert Pose-Space Location to Bone-Space Location
* \note this cannot be used to convert to pose-space location of the supplied
* pose-channel into its local space (i.e. *visual*-keyframing).
*/
void BKE_armature_loc_pose_to_bone(bke::PChanBoneConst pchanbone,
const float inloc[3],
float outloc[3]);
/**
* Convert Bone-Space Matrix to Pose-Space Matrix.
*/
void BKE_armature_mat_bone_to_pose(bke::PChanBoneConst pchanbone,
const float inmat[4][4],
float outmat[4][4]);
/**
* Remove rest-position effects from pose-transform for obtaining
* 'visual' transformation of pose-channel.
* (used by the Visual-Keyframing stuff).
*/
void BKE_armature_mat_pose_to_delta(float delta_mat[4][4],
float pose_mat[4][4],
float arm_mat[4][4]);
void BKE_armature_mat_pose_to_bone_ex(Depsgraph *depsgraph,
Object *ob,
const bPoseChannel *pchan,
const float inmat[4][4],
float outmat[4][4]);
/**
* Same as #BKE_object_mat3_to_rot().
*
* \param use_compat: only applies when the `pchan` is in euler rotation mode. It then picks the
* closest euler values relative to what the `pchan` already has.
*/
void BKE_pchan_mat3_to_rot(bPoseChannel *pchan, const float mat[3][3], bool use_compat);
/**
* Same as #BKE_object_rot_to_mat3().
*/
void BKE_pchan_rot_to_mat3(const bPoseChannel *pchan, float r_mat[3][3]);
/**
* Returns a quaternion representation of the current rotation of the bone.
* Euler and Axis Angle will be converted to Quaternion and then returned.
*/
float4 BKE_pchan_rot_to_quat(const bPoseChannel &pchan);
/**
* Applies the quaternion rotation to the current rotation of the bone.
* Depending on the `pchan.rotmode` this modifies either Euler, Axis Angle or Quaternion values.
*/
void BKE_pchan_quat_to_rot(bPoseChannel &pchan, const float4 &quat);
/**
* Apply a 4x4 matrix to the pose bone,
* similar to #BKE_object_apply_mat4().
*/
void BKE_pchan_apply_mat4(bPoseChannel *pchan, const float mat[4][4], bool use_compat);
/**
* Convert the loc/rot/size to \a r_chanmat (typically #bPoseChannel.chan_mat).
*/
void BKE_pchan_to_mat4(bke::PChanBoneConst pchanbone, float r_chanmat[4][4]);
/**
* Convert the loc/rot/size to mat4 (`pchan.chan_mat`),
* used in `constraint.cc` too.
*/
void BKE_pchan_calc_mat(bke::PChanBone pchanbone);
/**
* Simple helper, computes the offset bone matrix:
* `offs_bone = yoffs(b-1) + root(b) + bonemat(b)`.
*/
void BKE_bone_offset_matrix_get(const Bone *bone, float offs_bone[4][4]);
/* Transformation inherited from the parent bone. These matrices apply the effects of
* HINGE/NO_SCALE/NO_LOCAL_LOCATION options over the pchan loc/rot/scale transformations. */
struct BoneParentTransform {
float rotscale_mat[4][4]; /* parent effect on rotation & scale pose channels */
float loc_mat[4][4]; /* parent effect on location pose channel */
float post_scale[3]; /* additional scale to apply with post-multiply */
};
/* Matrix-like algebra operations on the transform */
void BKE_bone_parent_transform_clear(BoneParentTransform *bpt);
void BKE_bone_parent_transform_invert(BoneParentTransform *bpt);
void BKE_bone_parent_transform_combine(const BoneParentTransform *in1,
const BoneParentTransform *in2,
BoneParentTransform *result);
void BKE_bone_parent_transform_apply(const BoneParentTransform *bpt,
const float inmat[4][4],
float outmat[4][4]);
/**
* Get the current parent transformation for the given pose bone.
*
* Construct the matrices (rot/scale and loc)
* to apply the PoseChannels into the armature (object) space.
* I.e. (roughly) the `pose_mat(b-1) * yoffs(b-1) * d_root(b) * bone_mat(b)` in the
* `pose_mat(b)= pose_mat(b-1) * yoffs(b-1) * d_root(b) * bone_mat(b) * chan_mat(b)`
* ...function.
*
* This allows to get the transformations of a bone in its object space,
* *before* constraints (and IK) get applied (used by pose evaluation code).
* And reverse: to find pchan transformations needed to place a bone at a given loc/rot/scale
* in object space (used by interactive transform, and snapping code).
*
* Note that, with the HINGE/NO_SCALE/NO_LOCAL_LOCATION options, the location matrix
* will differ from the rotation/scale matrix...
*
* \note This cannot be used to convert to pose-space transforms of the supplied
* pose-channel into its local space (i.e. *visual*-key-framing).
* (NOTE(@mont29): I don't understand that, so I keep it :p).
*/
void BKE_bone_parent_transform_calc_from_pchan(bke::PChanBoneConst pchanbone,
BoneParentTransform *r_bpt);
/**
* Compute the parent transform using data decoupled from specific data structures.
*
* \param bone_flag: #Bone.flag containing settings.
* \param offs_bone: delta from parent to current arm_mat (or just arm_mat if no parent).
* \param parent_arm_mat: arm_mat of parent, or NULL.
* \param parent_pose_mat: pose_mat of parent, or NULL.
* \param r_bpt: OUTPUT parent transform.
*/
void BKE_bone_parent_transform_calc_from_matrices(int bone_flag,
int inherit_scale_mode,
const float offs_bone[4][4],
const float parent_arm_mat[4][4],
const float parent_pose_mat[4][4],
BoneParentTransform *r_bpt);
/**
* Rotation Mode Conversions - Used for Pose-Channels + Objects.
*
* Called from RNA when rotation mode changes
* - the result should be that the rotations given in the provided pointers have had conversions
* applied (as appropriate), such that the rotation of the element hasn't 'visually' changed.
*/
void BKE_rotMode_change_values(float quat[4],
float eul[3],
float axis[3],
float *angle,
eRotationModes oldMode,
eRotationModes newMode);
/* B-Bone support */
#define MAX_BBONE_SUBDIV 32
struct Mat4 {
float mat[4][4];
};
struct BBoneSplineParameters {
int segments;
float length;
/* Non-uniform scale correction. */
bool do_scale;
float scale[3];
/* Handle control bone data. */
bool use_prev, prev_bbone;
bool use_next, next_bbone;
float prev_h[3], next_h[3];
float prev_mat[4][4], next_mat[4][4];
/* Control values. */
float ease1, ease2;
float roll1, roll2;
float scale_in[3], scale_out[3];
float curve_in_x, curve_in_z, curve_out_x, curve_out_z;
};
/** Sets the location of the pose channel, respecting #bPoseChannel::protectflag. */
void BKE_pchan_protected_location_set(bPoseChannel *pchan, const float location[3]);
/** Sets the rotation of the pose channel, respecting the #bPoseChannel::protectflag. Depending on
* the current rotation mode, this either modifies quaternion, euler or axis angle. */
void BKE_pchan_protected_rotation_set(bPoseChannel *pchan, const float mat[3][3]);
/** Sets the location of the pose channel, respecting #bPoseChannel::protectflag. */
void BKE_pchan_protected_scale_set(bPoseChannel *pchan, const float scale[3]);
/** Sets the quaternion rotation of the pose channel, respecting #bPoseChannel::protectflag. */
void BKE_pchan_protected_rotation_quaternion_set(bPoseChannel *pchan, const float quat[4]);
/** Sets the euler rotation of the pose channel, respecting #bPoseChannel::protectflag. */
void BKE_pchan_protected_rotation_euler_set(bPoseChannel *pchan, const float rotation_euler[3]);
/** Sets the axis-angle rotation of the pose channel, respecting #bPoseChannel::protectflag. */
void BKE_pchan_protected_rotation_axisangle_set(bPoseChannel *pchan,
const float axis[3],
float angle);
/**
* Get "next" and "prev" bones - these are used for handle calculations.
*/
void BKE_pchan_bbone_handles_get(bke::PChanBoneConst pchanbone,
bPoseChannel **r_prev,
bPoseChannel **r_next);
/**
* Compute B-Bone spline parameters for the given channel.
*/
void BKE_pchan_bbone_spline_params_get(bke::PChanBoneConst pchanbone,
const bArmature &armature,
bool rest,
BBoneSplineParameters *param);
/**
* Fills the array with the desired amount of bone->segments elements.
* This calculation is done within unit bone space.
*/
void BKE_pchan_bbone_spline_setup(bke::PChanBone pchanbone,
const bArmature &armature,
bool rest,
bool for_deform,
Mat4 *result_array);
/**
* Computes the bezier handle vectors and rolls coming from custom handles.
*/
void BKE_pchan_bbone_handles_compute(const BBoneSplineParameters *param,
float h1[3],
float *r_roll1,
float h2[3],
float *r_roll2,
bool ease,
bool offsets);
/**
* Fills the array with the desired amount of `bone->segments` elements.
* This calculation is done within unit bone space.
*/
int BKE_pchan_bbone_spline_compute(BBoneSplineParameters *param,
bool for_deform,
Mat4 *result_array);
/**
* Compute and cache the B-Bone shape in the channel runtime struct.
*/
void BKE_pchan_bbone_segments_cache_compute(bke::PChanBone pchanbone, const bArmature &armature);
/**
* Copy cached B-Bone segments from one channel to another.
*/
void BKE_pchan_bbone_segments_cache_copy(bPoseChannel *pchan, bPoseChannel *pchan_from);
/**
* Calculate index and blend factor for the two B-Bone segment nodes
* affecting the specified point along the bone.
*
* \param bone: Pose channel's armature bone.
* \param head_tail: head-tail position along the bone (auto-clamped between 0 and 1).
* \param r_index: OUTPUT index of the first segment joint affecting the point.
* \param r_blend_next: OUTPUT blend factor between the first and the second segment in [0..1]
*/
void BKE_pchan_bbone_deform_clamp_segment_index(const Bone &bone,
float head_tail,
int *r_index,
float *r_blend_next);
/**
* Calculate index and blend factor for the two B-Bone segment nodes
* affecting the specified point in object (pose) space.
*
* \param pchanbone: Pose channel.
* \param co: Pose space coordinates of the point being deformed.
* \param r_index: OUTPUT index of the first segment joint affecting the point.
* \param r_blend_next: OUTPUT blend factor between the first and the second segment in [0..1]
*/
void BKE_pchan_bbone_deform_segment_index(bke::PChanBoneConst pchanbone,
const float *co,
int *r_index,
float *r_blend_next);
/* context.selected_pose_bones */
#define FOREACH_PCHAN_SELECTED_IN_OBJECT_BEGIN(_ob, _pchan) \
for (bPoseChannel *_pchan = (bPoseChannel *)(_ob)->pose->chanbase.first; _pchan; \
_pchan = _pchan->next) \
{ \
if (animrig::bone_is_visible(((bArmature *)(_ob)->data), {_pchan, _pchan->bone_get(*_ob)}) && \
((_pchan)->flag & POSE_SELECTED)) \
{
#define FOREACH_PCHAN_SELECTED_IN_OBJECT_END \
} \
} \
((void)0)
/* context.visible_pose_bones */
#define FOREACH_PCHAN_VISIBLE_IN_OBJECT_BEGIN(_ob, _pchan) \
for (bPoseChannel *_pchan = (bPoseChannel *)(_ob)->pose->chanbase.first; _pchan; \
_pchan = _pchan->next) \
{ \
if (animrig::bone_is_visible(((bArmature *)(_ob)->data), {_pchan, pchan->bone_get(*_ob)})) {
#define FOREACH_PCHAN_VISIBLE_IN_OBJECT_END \
} \
} \
((void)0)
/* Evaluation helpers */
struct bKinematicConstraint;
struct bPose;
struct bSplineIKConstraint;
bPoseChannel *BKE_armature_ik_solver_find_root(bPoseChannel *pchan, bKinematicConstraint *data);
bPoseChannel *BKE_armature_splineik_solver_find_root(bPoseChannel *pchan,
bSplineIKConstraint *data);
void BKE_pose_splineik_init_tree(Scene *scene, Object *ob, float ctime);
void BKE_splineik_execute_tree(
Depsgraph *depsgraph, Scene *scene, Object *ob, bPoseChannel *pchan_root, float ctime);
void BKE_pose_pchan_index_rebuild(bPose *pose);
void BKE_pose_eval_init(Depsgraph *depsgraph, Scene *scene, Object *object);
void BKE_pose_eval_init_ik(Depsgraph *depsgraph, Scene *scene, Object *object);
void BKE_pose_eval_bone(Depsgraph *depsgraph, Scene *scene, Object *object, int pchan_index);
void BKE_pose_constraints_evaluate(Depsgraph *depsgraph,
Scene *scene,
Object *object,
int pchan_index);
void BKE_pose_bone_done(Depsgraph *depsgraph, Object *object, int pchan_index);
void BKE_pose_eval_bbone_segments(Depsgraph *depsgraph, Object *object, int pchan_index);
void BKE_pose_iktree_evaluate(Depsgraph *depsgraph,
Scene *scene,
Object *object,
int rootchan_index);
void BKE_pose_splineik_evaluate(Depsgraph *depsgraph,
Scene *scene,
Object *object,
int rootchan_index);
void BKE_pose_eval_done(Depsgraph *depsgraph, Object *object);
void BKE_pose_eval_cleanup(Depsgraph *depsgraph, Scene *scene, Object *object);
/* -------------------------------------------------------------------- */
/** \name Deform 3D Coordinates by Armature (`armature_deform.cc`)
* \{ */
/* Note that we could have a #BKE_armature_deform_coords that doesn't take object data
* currently there are no callers for this though. */
void BKE_armature_deform_coords_with_curves(const Object &ob_arm,
const Object &ob_target,
const ListBaseT<bDeformGroup> *defbase,
MutableSpan<float3> vert_coords,
std::optional<Span<float3>> vert_coords_prev,
std::optional<MutableSpan<float3x3>> vert_deform_mats,
Span<MDeformVert> dverts,
int deformflag,
StringRefNull defgrp_name);
void BKE_armature_deform_coords_with_mesh(const Object &ob_arm,
const Object &ob_target,
MutableSpan<float3> vert_coords,
std::optional<Span<float3>> vert_coords_prev,
std::optional<MutableSpan<float3x3>> vert_deform_mats,
int deformflag,
StringRefNull defgrp_name,
const Mesh *me_target);
void BKE_armature_deform_coords_with_editmesh(
const Object &ob_arm,
const Object &ob_target,
MutableSpan<float3> vert_coords,
std::optional<Span<float3>> vert_coords_prev,
std::optional<MutableSpan<float3x3>> vert_deform_mats,
int deformflag,
StringRefNull defgrp_name,
const BMEditMesh &em_target);
/** \} */
namespace bke {
struct bArmature_Runtime {
/**
* Index of the active collection, -1 if there is no collection active.
*
* For UIList support in the user interface. Assigning here does nothing, use
* `ANIM_armature_bonecoll_active_set` to set the active bone collection.
*/
int active_collection_index = 0;
BoneCollection *active_collection = nullptr;
/**
* Indexable storage for bones. The bone hierarchy is stored depth-first, so a bone is followed
* by its children.
*
* Used by bPoseChan::bone_get() to obtain a bone pointer by index.
*/
Array<Bone *> bones;
uint64_t bones_generation_count = 0;
/**
* Mutex to protect the `bones` and `bones_generation_count` fields.
*
* This is used when regenerating the bones array, to ensure only a single thread does this. Read
* access to the bones array is not protected. So far this has worked well; it's not guaranteed
* that there won't be a race condition, though. */
Mutex bones_mutex;
/** Clear the `bones` array, ensuring it is rebuilt on its next use. */
void bones_tag_rebuild();
/**
* Return whether the `bones` array has bones (true), or whether it needs rebuilding (false).
*
* Note that this returns 'invalid' when the Armature has no bones. This is because the bones
* array is only used to obtain a bone pointer by index, which means it's only valid to be used
* when there actually are bones.
*/
bool is_bones_array_valid() const;
};
struct SelectedBonesResult {
bool all_bones_selected = true;
bool no_bones_selected = true;
};
using SelectedBoneCallback = FunctionRef<void(Bone *bone)>;
SelectedBonesResult BKE_armature_find_selected_bones(const bArmature *armature,
SelectedBoneCallback callback);
using BoneNameSet = Set<std::string>;
/**
* Return a set of names of the selected bones.
*/
BoneNameSet BKE_armature_find_selected_bone_names(const bArmature *armature);
BoneNameSet BKE_pose_channel_find_selected_names(const Object *object);
}; // namespace bke
} // namespace blender

View File

@@ -0,0 +1,101 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup bke
*/
#pragma once
#include "BLI_compiler_attrs.h"
#include "DNA_asset_types.h"
namespace blender {
struct AssetLibraryReference;
struct AssetMetaData;
struct AssetTag;
struct BlendDataReader;
struct BlendWriter;
struct ID;
struct IDProperty;
struct PreviewImage;
using PreSaveFn = void (*)(void *asset_ptr, AssetMetaData *asset_data);
using OnMarkAssetFn = void (*)(void *asset_ptr, AssetMetaData *asset_data);
using OnClearAssetDataFn = void (*)(void *asset_ptr, AssetMetaData *asset_data);
struct AssetTypeInfo {
/**
* For local assets (assets in the current .blend file), a callback to execute before the file is
* saved.
*/
PreSaveFn pre_save_fn;
OnMarkAssetFn on_mark_asset_fn;
/**
* Should be called whenever a local asset gets cleared of its asset data but stays available
* otherwise, i.e. when an asset data-block is turned back into a normal data-block.
*/
OnClearAssetDataFn on_clear_asset_fn;
};
AssetMetaData *BKE_asset_metadata_create();
void BKE_asset_metadata_free(AssetMetaData **asset_data);
/**
* Create a copy of the #AssetMetaData so that it can be assigned to another asset.
*
* The caller becomes the owner of the returned pointer.
*/
AssetMetaData *BKE_asset_metadata_copy(const AssetMetaData *source);
struct AssetTagEnsureResult {
AssetTag *tag;
/* Set to false if a tag of this name was already present. */
bool is_new;
};
AssetTag *BKE_asset_metadata_tag_add(AssetMetaData *asset_data, const char *name)
ATTR_NONNULL(1, 2);
/**
* Make sure there is a tag with name \a name, create one if needed.
*/
AssetTagEnsureResult BKE_asset_metadata_tag_ensure(AssetMetaData *asset_data, const char *name);
void BKE_asset_metadata_tag_remove(AssetMetaData *asset_data, AssetTag *tag);
/** Clean up the catalog ID (white-spaces removed, length reduced, etc.) and assign it. */
void BKE_asset_metadata_catalog_id_clear(AssetMetaData *asset_data);
void BKE_asset_metadata_catalog_id_set(AssetMetaData *asset_data,
bUUID catalog_id,
const char *catalog_simple_name);
void BKE_asset_library_reference_init_default(AssetLibraryReference *library_ref);
void BKE_asset_metadata_idprop_ensure(AssetMetaData *asset_data, IDProperty *prop);
IDProperty *BKE_asset_metadata_idprop_find(const AssetMetaData *asset_data,
const char *name) ATTR_WARN_UNUSED_RESULT;
PreviewImage *BKE_asset_metadata_preview_get_from_id(const AssetMetaData *asset_data,
const ID *owner_id);
void BKE_asset_metadata_write(BlendWriter *writer, AssetMetaData *asset_data);
void BKE_asset_metadata_read(BlendDataReader *reader, AssetMetaData *asset_data);
void BKE_asset_weak_reference_write(BlendWriter *writer, const AssetWeakReference *weak_ref);
void BKE_asset_weak_reference_read(BlendDataReader *reader, AssetWeakReference *weak_ref);
void BKE_asset_catalog_path_list_free(ListBaseT<AssetCatalogPathLink> &catalog_path_list);
ListBaseT<AssetCatalogPathLink> BKE_asset_catalog_path_list_duplicate(
const ListBaseT<AssetCatalogPathLink> &catalog_path_list);
void BKE_asset_catalog_path_list_blend_write(
BlendWriter *writer, const ListBaseT<AssetCatalogPathLink> &catalog_path_list);
void BKE_asset_catalog_path_list_blend_read_data(
BlendDataReader *reader, ListBaseT<AssetCatalogPathLink> &catalog_path_list);
bool BKE_asset_catalog_path_list_has_path(const ListBaseT<AssetCatalogPathLink> &catalog_path_list,
const char *catalog_path);
void BKE_asset_catalog_path_list_add_path(ListBaseT<AssetCatalogPathLink> &catalog_path_list,
const char *catalog_path);
} // namespace blender

View File

@@ -0,0 +1,82 @@
/* SPDX-FileCopyrightText: 2024 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup bke
*/
#pragma once
/**
* Editing of datablocks from asset libraries.
*
* Asset blend files are linked into the global main database, with the asset
* datablock itself and its dependencies. These datablocks remain linked but
* are marked as editable.
*
* User edited asset datablocks are written to individual blend files per
* asset. These blend files include any datablock dependencies and packaged
* image files.
*
* This way the blend file can be easily saved, reloaded and deleted.
*
* This mechanism is currently only used for brush assets.
*/
#include <optional>
#include <string>
#include "BLI_string_ref.hh"
#include "DNA_ID_enums.h"
namespace blender {
struct bUserAssetLibrary;
struct AssetWeakReference;
struct ID;
struct Main;
struct ReportList;
namespace bke {
/** Get datablock from weak reference, loading the blend file as needed. */
ID *asset_edit_id_from_weak_reference(Main &global_main,
ID_Type id_type,
const AssetWeakReference &weak_ref);
/** Get asset weak reference from ID. */
std::optional<AssetWeakReference> asset_edit_weak_reference_from_id(const ID &id);
/** Asset editing operations. */
bool asset_edit_id_is_editable(const ID &id);
bool asset_edit_id_is_writable(const ID &id);
std::optional<std::string> asset_edit_id_save_as(Main &global_main,
const ID &id,
StringRefNull name,
const bUserAssetLibrary &user_library,
AssetWeakReference &r_weak_ref,
ReportList &reports);
bool asset_edit_id_save(Main &global_main, const ID &id, ReportList &reports);
/**
* Relink the asset from the library. This causes the ID to be re-allocated, so its address
* changes. Even in case of failure to reload the asset, \a id will be deleted.
* \return the new address of the reloaded \a id.
*/
ID *asset_edit_id_revert(Main &global_main, ID &id, ReportList &reports);
bool asset_edit_id_delete(Main &global_main, ID &id, ReportList &reports);
/** Find a local copy of the asset. */
ID *asset_edit_id_find_local(Main &global_main, ID &id);
/**
* Ensure a local copy of the asset and its direct and indirect dependencies exists. Dependencies
* where #ID_TYPE_SUPPORTS_ASSET_EDITABLE() fails will not be made local and will be cleared.
*/
ID *asset_edit_id_ensure_local(Main &global_main, ID &id);
} // namespace bke
} // namespace blender

View File

@@ -0,0 +1,152 @@
/* SPDX-FileCopyrightText: 2006 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup bke
* \brief Generic geometry attributes built on CustomData.
*/
#pragma once
#include <optional>
#include <string>
#include "BLI_enum_flags.hh"
#include "BLI_string_ref.hh"
#include "BLI_sys_types.h"
#include "DNA_customdata_types.h"
namespace blender {
namespace bke {
enum class AttrDomain : int8_t;
class AttributeAccessor;
class AttributeStorage;
class MutableAttributeAccessor;
} // namespace bke
struct BMesh;
struct CustomData;
struct CustomDataLayer;
struct ID;
struct ReportList;
struct Mesh;
struct PointCloud;
struct Curves;
struct GreasePencil;
struct GreasePencilDrawing;
enum AttrDomainMask : uint8_t {
ATTR_DOMAIN_MASK_POINT = (1 << 0),
ATTR_DOMAIN_MASK_EDGE = (1 << 1),
ATTR_DOMAIN_MASK_FACE = (1 << 2),
ATTR_DOMAIN_MASK_CORNER = (1 << 3),
ATTR_DOMAIN_MASK_CURVE = (1 << 4),
ATTR_DOMAIN_MASK_GREASE_PENCIL_LAYER = (1 << 6),
ATTR_DOMAIN_MASK_ALL = (1 << 7) - 1
};
ENUM_OPERATORS(AttrDomainMask);
enum class AttributeOwnerType {
Mesh,
PointCloud,
Curves,
GreasePencil,
GreasePencilDrawing,
};
class AttributeOwner {
AttributeOwnerType type_;
void *ptr_ = nullptr;
public:
AttributeOwner() {};
AttributeOwner(AttributeOwnerType type, void *ptr) : type_(type), ptr_(ptr) {};
static AttributeOwner from_id(ID *id);
AttributeOwnerType type() const;
bool is_valid() const;
bke::AttributeStorage *get_storage() const;
std::optional<bke::MutableAttributeAccessor> get_accessor() const;
Mesh *get_mesh() const;
PointCloud *get_pointcloud() const;
Curves *get_curves() const;
GreasePencil *get_grease_pencil() const;
GreasePencilDrawing *get_grease_pencil_drawing() const;
};
#define ATTR_DOMAIN_AS_MASK(domain) ((AttrDomainMask)((1 << int(domain))))
/* All domains that support color attributes. */
#define ATTR_DOMAIN_MASK_COLOR \
((AttrDomainMask)((ATTR_DOMAIN_MASK_POINT | ATTR_DOMAIN_MASK_CORNER)))
/* Attributes. */
/**
* Create a new attribute layer.
*/
struct CustomDataLayer *BKE_attribute_new(Mesh &mesh,
BMesh &bm,
StringRef name,
eCustomDataType type,
bke::AttrDomain domain,
struct ReportList *reports);
bool BKE_attribute_remove(AttributeOwner &owner, StringRef name, struct ReportList *reports);
bke::AttrDomain BKE_attribute_domain(const Mesh &mesh,
const BMesh &bm,
const struct CustomDataLayer *layer);
int BKE_attribute_domain_size(const AttributeOwner &owner, int domain);
bool BKE_attribute_required(const AttributeOwner &owner, StringRef name);
bool BKE_attribute_rename(AttributeOwner &owner,
StringRef old_name,
StringRef new_name,
struct ReportList *reports);
int BKE_attributes_length(const AttributeOwner &owner,
AttrDomainMask domain_mask,
eCustomDataMask mask,
bool include_anonymous = true);
std::optional<StringRefNull> BKE_attributes_active_name_get(AttributeOwner &owner);
void BKE_attributes_active_set(AttributeOwner &owner, StringRef name);
void BKE_attributes_active_clear(AttributeOwner &owner);
int *BKE_attributes_active_index_p(AttributeOwner &owner);
/**
* After changing the active attribute index (e.g after removing an attribute) make sure that index
* is valid (is pointing to a non-internal attribute), f it is not, then set it to the first
* non-internal attribute.
*/
void BKE_attributes_active_index_validate(AttributeOwner &owner);
std::optional<StringRef> BKE_attribute_from_index(AttributeOwner &owner,
int lookup_index,
AttrDomainMask domain_mask,
eCustomDataMask layer_mask,
bool include_anonymous = true);
/** Layer is allowed to be nullptr; if so -1 (layer not found) will be returned. */
int BKE_attribute_to_index(const AttributeOwner &owner,
const StringRef name,
AttrDomainMask domain_mask,
eCustomDataMask layer_mask,
bool include_anonymous = true);
std::optional<StringRef> BKE_id_attributes_active_color_name(const struct ID *id);
std::optional<StringRef> BKE_id_attributes_default_color_name(const struct ID *id);
void BKE_id_attributes_active_color_set(struct ID *id, std::optional<StringRef> name);
void BKE_id_attributes_active_color_clear(struct ID *id);
void BKE_id_attributes_default_color_set(struct ID *id, std::optional<StringRef> name);
bool BKE_id_attributes_color_find(const struct ID *id, StringRef name);
std::string BKE_attribute_calc_unique_name(const AttributeOwner &owner, StringRef name);
[[nodiscard]] StringRef BKE_uv_map_pin_name_get(StringRef uv_map_name, char *buffer);
} // namespace blender

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,60 @@
/* SPDX-FileCopyrightText: 2026 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup bke
*/
#pragma once
#include <cstdint>
namespace blender::bke {
/** Some storage types are only relevant for certain attribute types. */
enum class AttrStorageType : int8_t {
/** #AttributeDataArray. */
Array = 0,
/** A single value for the whole attribute. */
Single = 1,
};
enum class AttrType : int16_t {
Bool = 0,
Int8 = 1,
Int16_2D = 2,
Int32 = 3,
Int32_2D = 4,
Float = 5,
Float2 = 6,
Float3 = 7,
Float4x4 = 8,
ColorByte = 9,
ColorFloat = 10,
Quaternion = 11,
String = 12,
Float4 = 13,
};
enum class AttrDomain : int8_t {
/* Used to choose automatically based on other data. */
Auto = -1,
/* Mesh, Curve or Point Cloud Point. */
Point = 0,
/* Mesh Edge. */
Edge = 1,
/* Mesh Face. */
Face = 2,
/* Mesh Corner. */
Corner = 3,
/* A single curve in a larger curve data-block. */
Curve = 4,
/* Instance. */
Instance = 5,
/* A layer in a grease pencil data-block. */
Layer = 6,
};
#define ATTR_DOMAIN_NUM 7
} // namespace blender::bke

View File

@@ -0,0 +1,61 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup bke
*/
#pragma once
#include "BLI_string_ref.hh"
namespace blender::bke {
/**
* Many geometry algorithms need to deal with attributes. For example, the Subdivide Curves code
* has to properly interpolate attributes to the intermediate points. However, sometimes certain
* attributes are not necessary after the operation anymore, so they can just be skipped for
* optimization purposes. This is where #AttributeFilter comes in. It allows the caller to specify
* which attributes should be processed and which should be ignored.
*
* \note It depends on the algorithm whether the output of the filter is followed exactly. For
* example, some algorithm might not be able to propagate attributes on some domain, even if the
* filter says that the attribute should be propagated.
*/
struct AttributeFilter {
public:
enum class Result {
/** The algorithm is allowed to skip processing the attribute. */
AllowSkip,
/** The attribute should be processed/propagated if at all possible. */
Process,
};
virtual ~AttributeFilter() = default;
/**
* This function has different implementations in each derived class. By default, all attributes
* should be processed.
*/
virtual Result filter(const StringRef /*name*/) const
{
return Result::Process;
}
/**
* Utility to simplify the check for whether some attribute can be skipped.
*/
bool allow_skip(const StringRef name) const
{
return this->filter(name) == Result::AllowSkip;
}
static const AttributeFilter &default_filter()
{
static AttributeFilter filter;
return filter;
}
};
} // namespace blender::bke

View File

@@ -0,0 +1,86 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup bke
*/
#pragma once
#include "BKE_attribute_filter.hh"
#include "BLI_set.hh"
namespace blender::bke {
/**
* Utility to create an #AttributeFilter from a lambda.
*/
template<typename Fn> struct AttributeFilterFromFunc : public AttributeFilter {
private:
Fn fn_;
static_assert(std::is_invocable_r_v<Result, Fn, StringRef>);
public:
constexpr AttributeFilterFromFunc(Fn fn) : fn_(std::move(fn)) {}
Result filter(const StringRef name) const override
{
return fn_(name);
}
};
/**
* Combines an existing #AttributeFilter and tags a few additional attributes that can/should be
* skipped.
*/
inline auto attribute_filter_with_skip_ref(AttributeFilter filter, const Span<StringRef> skip)
{
return AttributeFilterFromFunc([filter, skip](const StringRef name) {
if (skip.contains(name)) {
return AttributeFilter::Result::AllowSkip;
}
return filter.filter(name);
});
}
/** Same as above but with a #Set. */
template<typename StringT>
inline auto attribute_filter_with_skip_ref(AttributeFilter filter, const Set<StringT> &skip)
{
return AttributeFilterFromFunc([filter, &skip](const StringRef name) {
if (skip.contains_as(name)) {
return AttributeFilter::Result::AllowSkip;
}
return filter.filter(name);
});
}
/**
* Creates a simple #AttributeFilter that skips allows the given attributes to be skipped, while
* all others should be processed.
*/
inline auto attribute_filter_from_skip_ref(const Span<StringRef> skip)
{
return AttributeFilterFromFunc([skip](const StringRef name) {
if (skip.contains(name)) {
return AttributeFilter::Result::AllowSkip;
}
return AttributeFilter::Result::Process;
});
}
/** Same as above but with a #Set. */
template<typename StringT> inline auto attribute_filter_from_skip_ref(const Set<StringT> &skip)
{
return AttributeFilterFromFunc([&skip](const StringRef name) {
if (skip.contains_as(name)) {
return AttributeFilter::Result::AllowSkip;
}
return AttributeFilter::Result::Process;
});
}
} // namespace blender::bke

View File

@@ -0,0 +1,73 @@
/* SPDX-FileCopyrightText: 2025 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
#include "BLI_vector.hh"
#include "DNA_attribute_types.h"
#include "BKE_attribute.h"
#include "BKE_attribute.hh"
#include "BKE_attribute_storage.hh"
namespace blender {
struct CustomData;
namespace bke {
class CurvesGeometry;
}
struct PointCloud;
struct GreasePencil;
struct Mesh;
namespace bke {
const CPPType *custom_data_type_to_cpp_type(eCustomDataType type);
std::optional<eCustomDataType> cpp_type_to_custom_data_type(const CPPType &type);
/**
* Convert a custom data type to an attribute type. May return `std::nullopt` if the custom data
* type isn't used at runtime, is not a generic type that can be stored as an attribute, or is only
* used for #BMesh.
*/
std::optional<AttrType> custom_data_type_to_attr_type(eCustomDataType data_type);
/**
* Convert an attribute type to a legacy custom data type.
*/
std::optional<eCustomDataType> attr_type_to_custom_data_type(AttrType attr_type);
/**
* Move generic attributes from #CustomData to #AttributeStorage (not including non-generic layer
* types). Use for versioning old files when the newer #AttributeStorage format is used at runtime.
*/
void mesh_convert_customdata_to_storage(Mesh &mesh);
/** See #mesh_convert_customdata_to_storage. */
void curves_convert_customdata_to_storage(CurvesGeometry &curves);
/** See #mesh_convert_customdata_to_storage. */
void pointcloud_convert_customdata_to_storage(PointCloud &pointcloud);
/** See #mesh_convert_customdata_to_storage. */
void grease_pencil_convert_customdata_to_storage(GreasePencil &grease_pencil);
/** Abstraction for copying #CustomData layers and #AttributeStorage attributes. */
class LegacyMeshInterpolator {
Vector<GVArraySpan> attrs_src_;
Vector<GMutableSpan> attrs_dst_;
const CustomData &cd_src_;
CustomData &cd_dst_;
public:
LegacyMeshInterpolator(const Mesh &src, Mesh &dst, AttrDomain domain);
void copy(int src_index, int dst_index, int count) const;
void mix(Span<int> src_indices, std::optional<Span<float>> weights, int dst_index) const;
};
} // namespace bke
} // namespace blender

View File

@@ -0,0 +1,951 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup bke
*/
#pragma once
#include "BLI_array.hh"
#include "BLI_color_types.hh"
#include "BLI_cpp_type.hh"
#include "BLI_generic_span.hh"
#include "BLI_math_axis_angle.hh"
#include "BLI_math_color.hh"
#include "BLI_math_quaternion.hh"
#include "BLI_math_vector.h"
#include "BLI_math_vector.hh"
#include "BLI_offset_indices.hh"
#include "PRF_profile.hh"
#include "BKE_attribute.hh"
namespace blender {
class GVArray;
namespace bke::attribute_math {
/**
* Utility function that simplifies calling a templated function based on a run-time data type.
*/
template<typename Fn> inline void to_static_type(const CPPType &cpp_type, Fn &&fn)
{
cpp_type.to_static_type<float,
float2,
float3,
float4,
int,
int2,
bool,
int8_t,
short2,
ColorGeometry4f,
ColorGeometry4b,
math::Quaternion,
float4x4>([&]<typename T>() { fn.template operator()<T>(); });
}
template<typename Fn> inline void to_static_type(const bke::AttrType data_type, Fn &&fn)
{
const CPPType &cpp_type = bke::attribute_type_to_cpp_type(data_type);
to_static_type(cpp_type, std::forward<Fn>(fn));
}
/* -------------------------------------------------------------------- */
/** \name Mix two values of the same type.
*
* This is just basic linear interpolation.
* \{ */
template<typename T> T mix2(float factor, const T &a, const T &b);
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 int8_t(std::round((1.0f - factor) * a + factor * b));
}
template<> inline int mix2(const float factor, const int &a, const int &b)
{
return int(std::round((1.0f - factor) * a + factor * b));
}
template<> inline short2 mix2(const float factor, const short2 &a, const short2 &b)
{
return math::interpolate(a, b, factor);
}
template<> inline int2 mix2(const float factor, const int2 &a, const int2 &b)
{
return math::interpolate(a, b, factor);
}
template<> inline float mix2(const float factor, const float &a, const float &b)
{
return (1.0f - factor) * a + factor * b;
}
template<> inline float2 mix2(const float factor, const float2 &a, const float2 &b)
{
return math::interpolate(a, b, factor);
}
template<> inline float3 mix2(const float factor, const float3 &a, const float3 &b)
{
return math::interpolate(a, b, factor);
}
template<> inline float4 mix2(const float factor, const float4 &a, const float4 &b)
{
return math::interpolate(a, b, factor);
}
template<>
inline ColorGeometry4f mix2(const float factor, const ColorGeometry4f &a, const ColorGeometry4f &b)
{
return math::interpolate(a, b, factor);
}
template<>
inline ColorGeometry4b mix2(const float factor, const ColorGeometry4b &a, const ColorGeometry4b &b)
{
return math::interpolate(a, b, factor);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Mix three values of the same type.
*
* This is typically used to interpolate values within a triangle.
* \{ */
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 int8_t(std::round(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;
}
template<> inline int mix3(const float3 &weights, const int &v0, const int &v1, const int &v2)
{
return int(std::round(weights.x * v0 + weights.y * v1 + weights.z * v2));
}
template<>
inline short2 mix3(const float3 &weights, const short2 &v0, const short2 &v1, const short2 &v2)
{
return short2(weights.x * float2(v0) + weights.y * float2(v1) + weights.z * float2(v2));
}
template<> inline int2 mix3(const float3 &weights, const int2 &v0, const int2 &v1, const int2 &v2)
{
return int2(weights.x * float2(v0) + weights.y * float2(v1) + weights.z * float2(v2));
}
template<>
inline float mix3(const float3 &weights, const float &v0, const float &v1, const float &v2)
{
return weights.x * v0 + weights.y * v1 + weights.z * v2;
}
template<>
inline float2 mix3(const float3 &weights, const float2 &v0, const float2 &v1, const float2 &v2)
{
return weights.x * v0 + weights.y * v1 + weights.z * v2;
}
template<>
inline float3 mix3(const float3 &weights, const float3 &v0, const float3 &v1, const float3 &v2)
{
return weights.x * v0 + weights.y * v1 + weights.z * v2;
}
template<>
inline float4 mix3(const float3 &weights, const float4 &v0, const float4 &v1, const float4 &v2)
{
return weights.x * v0 + weights.y * v1 + weights.z * v2;
}
template<>
inline ColorGeometry4f mix3(const float3 &weights,
const ColorGeometry4f &v0,
const ColorGeometry4f &v1,
const ColorGeometry4f &v2)
{
ColorGeometry4f result;
interp_v4_v4v4v4(result, v0, v1, v2, weights);
return result;
}
template<>
inline ColorGeometry4b mix3(const float3 &weights,
const ColorGeometry4b &v0,
const ColorGeometry4b &v1,
const ColorGeometry4b &v2)
{
const float4 v0_f{&v0.r};
const float4 v1_f{&v1.r};
const float4 v2_f{&v2.r};
const float4 mixed = v0_f * weights[0] + v1_f * weights[1] + v2_f * weights[2];
return ColorGeometry4b{
uint8_t(mixed[0]), uint8_t(mixed[1]), uint8_t(mixed[2]), uint8_t(mixed[3])};
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Mix four values of the same type.
*
* \{ */
template<typename T>
T mix4(const float4 &weights, const T &v0, const T &v1, const T &v2, const T &v3);
template<>
inline int8_t mix4(
const float4 &weights, const int8_t &v0, const int8_t &v1, const int8_t &v2, const int8_t &v3)
{
return int8_t(std::round(weights.x * v0 + weights.y * v1 + weights.z * v2 + weights.w * v3));
}
template<>
inline bool mix4(
const float4 &weights, const bool &v0, const bool &v1, const bool &v2, const bool &v3)
{
return (weights.x * v0 + weights.y * v1 + weights.z * v2 + weights.w * v3) >= 0.5f;
}
template<>
inline int mix4(const float4 &weights, const int &v0, const int &v1, const int &v2, const int &v3)
{
return int(std::round(weights.x * v0 + weights.y * v1 + weights.z * v2 + weights.w * v3));
}
template<>
inline short2 mix4(
const float4 &weights, const short2 &v0, const short2 &v1, const short2 &v2, const short2 &v3)
{
return short2(weights.x * float2(v0) + weights.y * float2(v1) + weights.z * float2(v2) +
weights.w * float2(v3));
}
template<>
inline int2 mix4(
const float4 &weights, const int2 &v0, const int2 &v1, const int2 &v2, const int2 &v3)
{
return int2(weights.x * float2(v0) + weights.y * float2(v1) + weights.z * float2(v2) +
weights.w * float2(v3));
}
template<>
inline float mix4(
const float4 &weights, const float &v0, const float &v1, const float &v2, const float &v3)
{
return weights.x * v0 + weights.y * v1 + weights.z * v2 + weights.w * v3;
}
template<>
inline float2 mix4(
const float4 &weights, const float2 &v0, const float2 &v1, const float2 &v2, const float2 &v3)
{
return weights.x * v0 + weights.y * v1 + weights.z * v2 + weights.w * v3;
}
template<>
inline float3 mix4(
const float4 &weights, const float3 &v0, const float3 &v1, const float3 &v2, const float3 &v3)
{
return weights.x * v0 + weights.y * v1 + weights.z * v2 + weights.w * v3;
}
template<>
inline float4 mix4(
const float4 &weights, const float4 &v0, const float4 &v1, const float4 &v2, const float4 &v3)
{
return weights.x * v0 + weights.y * v1 + weights.z * v2 + weights.w * v3;
}
template<>
inline ColorGeometry4f mix4(const float4 &weights,
const ColorGeometry4f &v0,
const ColorGeometry4f &v1,
const ColorGeometry4f &v2,
const ColorGeometry4f &v3)
{
ColorGeometry4f result;
interp_v4_v4v4v4v4(result, v0, v1, v2, v3, weights);
return result;
}
template<>
inline ColorGeometry4b mix4(const float4 &weights,
const ColorGeometry4b &v0,
const ColorGeometry4b &v1,
const ColorGeometry4b &v2,
const ColorGeometry4b &v3)
{
const float4 v0_f{&v0.r};
const float4 v1_f{&v1.r};
const float4 v2_f{&v2.r};
const float4 v3_f{&v3.r};
float4 mixed;
interp_v4_v4v4v4v4(mixed, v0_f, v1_f, v2_f, v3_f, weights);
return ColorGeometry4b{
uint8_t(mixed[0]), uint8_t(mixed[1]), uint8_t(mixed[2]), uint8_t(mixed[3])};
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Mix a dynamic amount of values with weights for many elements.
*
* This section provides an abstraction for "mixers". The abstraction encapsulates details about
* how different types should be mixed. Usually #DefaultMixer<T> should be used to get a mixer for
* a specific type.
* \{ */
namespace detail {
static double int_to_double(const int &value)
{
return double(value);
}
static int double_to_int(const double &value)
{
return int(std::round(value));
}
static double2 int2_to_double2(const int2 &value)
{
return double2(value);
}
static int2 double2_to_int2(const double2 &value)
{
return int2(math::round(value));
}
static float int8_t_to_float(const int8_t &value)
{
return float(value);
}
static int8_t float_to_int8_t(const float &value)
{
return int8_t(std::round(value));
}
static float2 short2_to_float2(const short2 &value)
{
return float2(value);
}
static short2 float2_to_short2(const float2 &value)
{
return short2(math::round(value));
}
static float3 quat_to_expmap(const math::Quaternion &value)
{
return value.expmap();
}
static math::Quaternion expmap_to_quat(const float3 &value)
{
return math::Quaternion::expmap(value);
}
static float4 byte_color_to_float4(const ColorGeometry4b &value)
{
return float4(value.r, value.g, value.b, value.a);
}
static ColorGeometry4b float4_to_byte_color(const float4 &value)
{
return ColorGeometry4b(value.x, value.y, value.z, value.w);
}
} // namespace detail
template<typename T> inline T mix_indices(const Span<T> src, const Span<int> indices)
{
T accum(0);
const float weight = math::rcp(float(indices.size()));
for (const int i : indices) {
accum += src[i] * weight;
}
return accum;
}
template<typename T, typename ToAccumFn, typename ToFinalFn>
inline T mix_indices(const Span<T> src,
const Span<int> indices,
const ToAccumFn &to_accum_fn,
const ToFinalFn &to_final_fn)
{
using AccumT = std::invoke_result_t<ToAccumFn, T>;
static_assert(std::is_same_v<std::invoke_result_t<ToFinalFn, AccumT>, T>);
const float weight = math::rcp(float(indices.size()));
AccumT accum(0);
for (const int i : indices) {
accum += to_accum_fn(src[i]) * weight;
}
return to_final_fn(accum);
}
inline bool mix_indices(const Span<bool> src, const Span<int> indices)
{
return std::ranges::any_of(indices, [&](const int i) { return src[i]; });
}
inline int mix_indices(const Span<int> src, const Span<int> indices)
{
return mix_indices(src, indices, detail::int_to_double, detail::double_to_int);
}
inline int2 mix_indices(const Span<int2> src, const Span<int> indices)
{
return mix_indices(src, indices, detail::int2_to_double2, detail::double2_to_int2);
}
inline int8_t mix_indices(const Span<int8_t> src, const Span<int> indices)
{
return mix_indices(src, indices, detail::int8_t_to_float, detail::float_to_int8_t);
}
inline short2 mix_indices(const Span<short2> src, const Span<int> indices)
{
return mix_indices(src, indices, detail::short2_to_float2, detail::float2_to_short2);
}
inline math::Quaternion mix_indices(const Span<math::Quaternion> src, const Span<int> indices)
{
return mix_indices(src, indices, detail::quat_to_expmap, detail::expmap_to_quat);
}
inline ColorGeometry4b mix_indices(const Span<ColorGeometry4b> src, const Span<int> indices)
{
return mix_indices(src, indices, detail::byte_color_to_float4, detail::float4_to_byte_color);
}
inline ColorGeometry4f mix_indices(const Span<ColorGeometry4f> src, const Span<int> indices)
{
return ColorGeometry4f(mix_indices(src.cast<float4>(), indices));
}
float4x4 mix_indices(Span<float4x4> src, Span<int> indices);
template<typename T>
inline T mix_indices(const Span<T> src, const Span<int> indices, const Span<float> weights)
{
T accum(0);
float weight_accum = 0.0f;
for (const int i : indices.index_range()) {
accum += src[indices[i]] * weights[i];
weight_accum += weights[i];
}
return accum * math::safe_rcp(weight_accum);
}
template<typename T, typename ToAccumFn, typename ToFinalFn>
inline T mix_indices(const Span<T> src,
const Span<int> indices,
const Span<float> weights,
const ToAccumFn &to_accum_fn,
const ToFinalFn &to_final_fn)
{
using AccumT = std::invoke_result_t<ToAccumFn, T>;
static_assert(std::is_same_v<std::invoke_result_t<ToFinalFn, AccumT>, T>);
AccumT accum(0);
float weight_accum = 0.0f;
for (const int i : indices.index_range()) {
accum += to_accum_fn(src[indices[i]]) * weights[i];
weight_accum += weights[i];
}
return to_final_fn(accum * math::safe_rcp(weight_accum));
}
inline bool mix_indices(const Span<bool> src, const Span<int> indices, const Span<float> weights)
{
for (const int i : indices.index_range()) {
if (src[indices[i]] && weights[i] > 0.0f) {
return true;
}
}
return false;
}
inline int mix_indices(const Span<int> src, const Span<int> indices, const Span<float> weights)
{
return mix_indices(src, indices, weights, detail::int_to_double, detail::double_to_int);
}
inline int2 mix_indices(const Span<int2> src, const Span<int> indices, const Span<float> weights)
{
return mix_indices(src, indices, weights, detail::int2_to_double2, detail::double2_to_int2);
}
inline int8_t mix_indices(const Span<int8_t> src,
const Span<int> indices,
const Span<float> weights)
{
return mix_indices(src, indices, weights, detail::int8_t_to_float, detail::float_to_int8_t);
}
inline short2 mix_indices(const Span<short2> src,
const Span<int> indices,
const Span<float> weights)
{
return mix_indices(src, indices, weights, detail::short2_to_float2, detail::float2_to_short2);
}
inline math::Quaternion mix_indices(const Span<math::Quaternion> src,
const Span<int> indices,
const Span<float> weights)
{
return mix_indices(src, indices, weights, detail::quat_to_expmap, detail::expmap_to_quat);
}
inline ColorGeometry4b mix_indices(const Span<ColorGeometry4b> src,
const Span<int> indices,
const Span<float> weights)
{
return mix_indices(
src, indices, weights, detail::byte_color_to_float4, detail::float4_to_byte_color);
}
inline ColorGeometry4f mix_indices(const Span<ColorGeometry4f> src,
const Span<int> indices,
const Span<float> weights)
{
return ColorGeometry4f(mix_indices(src.cast<float4>(), indices, weights));
}
float4x4 mix_indices(Span<float4x4> src, Span<int> indices, Span<float> weights);
template<typename T> class SimpleMixer {
private:
MutableSpan<T> buffer_;
T default_value_;
Array<float> total_weights_;
public:
/**
* \param buffer: Span where the interpolated values should be stored.
* \param default_value: Output value for an element that has not been affected by a #mix_in.
*/
SimpleMixer(MutableSpan<T> buffer, T default_value = {})
: SimpleMixer(buffer, buffer.index_range(), default_value)
{
}
/**
* \param mask: Only initialize these indices. Other indices in the buffer will be invalid.
*/
SimpleMixer(MutableSpan<T> buffer, const IndexMask &mask, T default_value = {})
: buffer_(buffer), default_value_(default_value), total_weights_(buffer.size(), 0.0f)
{
BLI_STATIC_ASSERT(std::is_trivial_v<T>, "");
index_mask::masked_fill(buffer_, default_value_, mask);
}
/**
* Set a #value into the element with the given #index.
*/
void set(const int64_t index, const T &value, const float weight = 1.0f)
{
buffer_[index] = value * weight;
total_weights_[index] = weight;
}
/**
* Mix a #value into the element with the given #index.
*/
void mix_in(const int64_t index, const T &value, const float weight = 1.0f)
{
buffer_[index] += value * weight;
total_weights_[index] += weight;
}
/**
* Has to be called before the buffer provided in the constructor is used.
*/
void finalize()
{
this->finalize(IndexMask(buffer_.size()));
}
void finalize(const IndexMask &mask)
{
PRF_scope_with_name("SimpleMixer::finalize", ProfileCategory::Default);
mask.foreach_index([&](const int64_t i) {
const float weight = total_weights_[i];
if (weight > 0.0f) {
buffer_[i] *= 1.0f / weight;
}
else {
buffer_[i] = default_value_;
}
});
}
};
/**
* Mixes together booleans with "or" while fitting the same interface as the other
* mixers in order to be simpler to use. This mixing method has a few benefits:
* - An "average" for selections is relatively meaningless.
* - Predictable selection propagation is very super important.
* - It's generally easier to remove an element from a selection that is slightly too large than
* the opposite.
*/
class BooleanPropagationMixer {
private:
MutableSpan<bool> buffer_;
public:
/**
* \param buffer: Span where the interpolated values should be stored.
*/
BooleanPropagationMixer(MutableSpan<bool> buffer)
: BooleanPropagationMixer(buffer, buffer.index_range())
{
}
/**
* \param mask: Only initialize these indices. Other indices in the buffer will be invalid.
*/
BooleanPropagationMixer(MutableSpan<bool> buffer, const IndexMask &mask) : buffer_(buffer)
{
index_mask::masked_fill(buffer_, false, mask);
}
/**
* Set a #value into the element with the given #index.
*/
void set(const int64_t index, const bool value, [[maybe_unused]] const float weight = 1.0f)
{
buffer_[index] = value;
}
/**
* Mix a #value into the element with the given #index.
*/
void mix_in(const int64_t index, const bool value, [[maybe_unused]] const float weight = 1.0f)
{
buffer_[index] |= value;
}
/**
* Does not do anything, since the mixing is trivial.
*/
void finalize() {}
void finalize(const IndexMask & /*mask*/) {}
};
/**
* This mixer accumulates values in a type that is different from the one that is mixed.
* Some types cannot encode the floating point weights in their values (e.g. int and bool).
*/
template<typename T,
typename AccumulationT,
AccumulationT (*ValueToAccumulate)(const T &value),
T (*AccumulateToValue)(const AccumulationT &value)>
class SimpleMixerWithAccumulationType {
private:
struct Item {
/* Store both values together, because they are accessed together. */
AccumulationT value = AccumulationT(0);
float weight = 0.0f;
};
MutableSpan<T> buffer_;
T default_value_;
Array<Item> accumulation_buffer_;
public:
SimpleMixerWithAccumulationType(MutableSpan<T> buffer, T default_value = {})
: SimpleMixerWithAccumulationType(buffer, buffer.index_range(), default_value)
{
}
/**
* \param mask: Only initialize these indices. Other indices in the buffer will be invalid.
*/
SimpleMixerWithAccumulationType(MutableSpan<T> buffer,
const IndexMask &mask,
T default_value = {})
: buffer_(buffer), default_value_(default_value), accumulation_buffer_(buffer.size())
{
index_mask::masked_fill(buffer_, default_value_, mask);
}
void set(const int64_t index, const T &value, const float weight = 1.0f)
{
const AccumulationT converted_value = ValueToAccumulate(value);
Item &item = accumulation_buffer_[index];
item.value = converted_value * weight;
item.weight = weight;
}
void mix_in(const int64_t index, const T &value, const float weight = 1.0f)
{
const AccumulationT converted_value = ValueToAccumulate(value);
Item &item = accumulation_buffer_[index];
item.value += converted_value * weight;
item.weight += weight;
}
void finalize()
{
this->finalize(buffer_.index_range());
}
void finalize(const IndexMask &mask)
{
PRF_scope_with_name("SimpleMixerWithAccumulationType::finalize", ProfileCategory::Default);
mask.foreach_index([&](const int64_t i) {
const Item &item = accumulation_buffer_[i];
if (item.weight > 0.0f) {
const float weight_inv = 1.0f / item.weight;
const T converted_value = AccumulateToValue(item.value * weight_inv);
buffer_[i] = converted_value;
}
else {
buffer_[i] = default_value_;
}
});
}
};
class ColorGeometry4fMixer {
private:
MutableSpan<ColorGeometry4f> buffer_;
ColorGeometry4f default_color_;
Array<float> total_weights_;
public:
ColorGeometry4fMixer(MutableSpan<ColorGeometry4f> buffer,
ColorGeometry4f default_color = ColorGeometry4f(0.0f, 0.0f, 0.0f, 1.0f));
/**
* \param mask: Only initialize these indices. Other indices in the buffer will be invalid.
*/
ColorGeometry4fMixer(MutableSpan<ColorGeometry4f> buffer,
const IndexMask &mask,
ColorGeometry4f default_color = ColorGeometry4f(0.0f, 0.0f, 0.0f, 1.0f));
void set(int64_t index, const ColorGeometry4f &color, float weight = 1.0f);
void mix_in(int64_t index, const ColorGeometry4f &color, float weight = 1.0f);
void finalize();
void finalize(const IndexMask &mask);
};
class ColorGeometry4bMixer {
private:
MutableSpan<ColorGeometry4b> buffer_;
ColorGeometry4b default_color_;
Array<float> total_weights_;
Array<float4> accumulation_buffer_;
public:
ColorGeometry4bMixer(MutableSpan<ColorGeometry4b> buffer,
ColorGeometry4b default_color = ColorGeometry4b(0, 0, 0, 255));
/**
* \param mask: Only initialize these indices. Other indices in the buffer will be invalid.
*/
ColorGeometry4bMixer(MutableSpan<ColorGeometry4b> buffer,
const IndexMask &mask,
ColorGeometry4b default_color = ColorGeometry4b(0, 0, 0, 255));
void set(int64_t index, const ColorGeometry4b &color, float weight = 1.0f);
void mix_in(int64_t index, const ColorGeometry4b &color, float weight = 1.0f);
void finalize();
void finalize(const IndexMask &mask);
};
class float4x4Mixer {
private:
MutableSpan<float4x4> buffer_;
Array<float> total_weights_;
Array<float3> location_buffer_;
Array<float3> expmap_buffer_;
Array<float3> scale_buffer_;
public:
float4x4Mixer(MutableSpan<float4x4> buffer);
/**
* \param mask: Only initialize these indices. Other indices in the buffer will be invalid.
*/
float4x4Mixer(MutableSpan<float4x4> buffer, const IndexMask &mask);
void set(int64_t index, const float4x4 &value, float weight = 1.0f);
void mix_in(int64_t index, const float4x4 &value, float weight = 1.0f);
void finalize();
void finalize(const IndexMask &mask);
};
template<typename T> struct DefaultMixerStruct {
/* Use void by default. This can be checked for in `if constexpr` statements. */
using type = void;
};
template<> struct DefaultMixerStruct<float> {
using type = SimpleMixer<float>;
};
template<> struct DefaultMixerStruct<float2> {
using type = SimpleMixer<float2>;
};
template<> struct DefaultMixerStruct<float3> {
using type = SimpleMixer<float3>;
};
template<> struct DefaultMixerStruct<float4> {
using type = SimpleMixer<float4>;
};
template<> struct DefaultMixerStruct<ColorGeometry4f> {
/* Use a special mixer for colors. ColorGeometry4f can't be added/multiplied, because this is not
* something one should usually do with colors. */
using type = ColorGeometry4fMixer;
};
template<> struct DefaultMixerStruct<ColorGeometry4b> {
using type = ColorGeometry4bMixer;
};
template<> struct DefaultMixerStruct<float4x4> {
using type = float4x4Mixer;
};
template<> struct DefaultMixerStruct<int> {
/* Store interpolated ints in a double temporarily, so that weights are handled correctly. It
* uses double instead of float so that it is accurate for all 32 bit integers. */
using type =
SimpleMixerWithAccumulationType<int, double, detail::int_to_double, detail::double_to_int>;
};
template<> struct DefaultMixerStruct<short2> {
using type = SimpleMixerWithAccumulationType<short2,
float2,
detail::short2_to_float2,
detail::float2_to_short2>;
};
template<> struct DefaultMixerStruct<int2> {
/* Store interpolated ints in a double temporarily, so that weights are handled correctly. It
* uses double instead of float so that it is accurate for all 32 bit integers. */
using type = SimpleMixerWithAccumulationType<int2,
double2,
detail::int2_to_double2,
detail::double2_to_int2>;
};
template<> struct DefaultMixerStruct<bool> {
static float bool_to_float(const bool &value)
{
return value ? 1.0f : 0.0f;
}
static bool float_to_bool(const float &value)
{
return value >= 0.5f;
}
/* Store interpolated booleans in a float temporary.
* Otherwise information provided by weights is easily rounded away. */
using type = SimpleMixerWithAccumulationType<bool, float, bool_to_float, float_to_bool>;
};
template<> struct DefaultMixerStruct<int8_t> {
/* Store interpolated 8 bit integers in a float temporarily to increase accuracy. */
using type = SimpleMixerWithAccumulationType<int8_t,
float,
detail::int8_t_to_float,
detail::float_to_int8_t>;
};
template<> struct DefaultMixerStruct<math::Quaternion> {
using type = SimpleMixerWithAccumulationType<math::Quaternion,
float3,
detail::quat_to_expmap,
detail::expmap_to_quat>;
};
template<typename T> struct DefaultPropagationMixerStruct {
/* Use void by default. This can be checked for in `if constexpr` statements. */
using type = typename DefaultMixerStruct<T>::type;
};
template<> struct DefaultPropagationMixerStruct<bool> {
using type = BooleanPropagationMixer;
};
/**
* This mixer is meant for propagating attributes when creating new geometry. A key difference
* with the default mixer is that booleans are mixed with "or" instead of "at least half"
* (the default mixing for booleans).
*/
template<typename T>
using DefaultPropagationMixer = typename DefaultPropagationMixerStruct<T>::type;
/* Utility to get a good default mixer for a given type. This is `void` when there is no default
* mixer for the given type. */
template<typename T> using DefaultMixer = typename DefaultMixerStruct<T>::type;
void mix_groups(GSpan src,
OffsetIndices<int> groups,
Span<int> all_indices,
std::optional<Span<float>> all_weights,
GMutableSpan dst);
inline void mix_groups(GSpan src,
OffsetIndices<int> groups,
Span<int> all_indices,
GMutableSpan dst)
{
mix_groups(src, groups, all_indices, std::nullopt, dst);
}
inline void mix_groups(GSpan src, GroupedSpan<int> indices, GMutableSpan dst)
{
mix_groups(src, indices.offsets, indices.data, dst);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Generic Array Utils Implementations
*
* Extra implementations of functions from #BLI_array_utils.hh for all attribute types,
* used to avoid templating the same logic for each type in many places.
* \{ */
/**
* Move elements from [src_begin, src_end) to dest_begin.
* dest_begin must be <= src_begin.
*/
void shift_left(GMutableSpan data, int src_begin, int src_end, int dst_begin);
/**
* Move elements from [src_begin, src_end) to a range ending at dst_end.
* dst_end must be >= src_end.
*/
void shift_right(GMutableSpan data, int src_begin, int src_end, int dst_begin);
void gather(GSpan src, Span<int> map, GMutableSpan dst);
void gather(const GVArray &src, Span<int> map, GMutableSpan dst);
void gather(GSpan src, Span<int> map, const IndexMask &dst_mask, GMutableSpan dst);
void gather(const GVArray &src, Span<int> map, const IndexMask &dst_mask, GMutableSpan dst);
void gather_group_to_group(OffsetIndices<int> src_offsets,
OffsetIndices<int> dst_offsets,
const IndexMask &selection,
GSpan src,
GMutableSpan dst);
void gather_to_groups(OffsetIndices<int> dst_offsets,
const IndexMask &src_selection,
GSpan src,
GMutableSpan dst);
void gather_ranges_to_groups(Span<IndexRange> src_ranges,
OffsetIndices<int> dst_offsets,
GSpan src,
GMutableSpan dst);
/** \} */
} // namespace bke::attribute_math
} // namespace blender

View File

@@ -0,0 +1,348 @@
/* SPDX-FileCopyrightText: 2025 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
#include <variant>
#include "BLI_implicit_sharing_ptr.hh"
#include "BLI_map.hh"
#include "BLI_memory_counter_fwd.hh"
#include "BLI_random_access_iterator_mixin.hh"
#include "BLI_set.hh"
#include "BLI_string_ref.hh"
#include "BLI_vector_set.hh"
#include "BKE_attribute_enums.hh"
#include "DNA_attribute_types.h"
namespace blender {
struct Attribute;
struct BlendDataReader;
struct BlendWriter;
struct IDTypeForeachColorFunctionCallback;
class GPointer;
class CPPType;
class ResourceScope;
namespace bke {
enum class AttrDomain : int8_t;
enum class AttrType : int16_t;
enum class AttrStorageType : int8_t;
/** Data and metadata for a single geometry attribute. */
class Attribute {
public:
/**
* Data for an attribute stored as a full contiguous array with a data type exactly matching the
* attribute's type. The array referenced must match the size of the domain and the data type.
*/
struct ArrayData {
/* NOTE: Since the shared data pointed to by `sharing_info` knows how to free itself, it often
* stores the size and type itself. It may be possible to make use of that fact to avoid
* storing it here, or even vice versa. */
void *data;
/** The number of elements in the array. */
int64_t size;
ImplicitSharingPtr<> sharing_info;
static ArrayData from_value(const GPointer &value, int64_t domain_size);
static ArrayData from_default_value(const CPPType &type, int64_t domain_size);
static ArrayData from_uninitialized(const CPPType &type, int64_t domain_size);
static ArrayData from_constructed(const CPPType &type, int64_t domain_size);
};
/** Data for an attribute stored as a single value for the entire domain. */
struct SingleData {
/* NOTE: For simplicity and to avoid a bit of redundancy, the domain size isn't stored here.
* It's not necessary to manage a single value. */
void *value;
ImplicitSharingPtr<> sharing_info;
static SingleData from_value(const GPointer &value);
static SingleData from_default_value(const CPPType &type);
};
using DataVariant = std::variant<ArrayData, SingleData>;
friend AttributeStorage;
private:
/**
* Because it's used as the custom ID for the attributes vector set, the name cannot be changed
* without removing and adding the attribute.
*/
std::string name_;
AttrDomain domain_;
AttrType type_;
DataVariant data_;
public:
/**
* Unique name across all domains.
* \note Compared to #CustomData, which doesn't enforce uniqueness, across domains on its own,
* this is enforced by asserts when adding attributes. See #unique_name_calc() (which is also
* called during the conversion process).
*/
StringRefNull name() const;
/** Which part of a geometry the attribute corresponds to. */
AttrDomain domain() const;
/**
* The data type exposed to the user. Depending on the storage type, the actual internal values
* may not be the same type.
*/
AttrType data_type() const;
/**
* The method used to store the data. This gives flexibility to optimize the internal storage
* even though conceptually the attribute is an array of values.
*/
AttrStorageType storage_type() const;
/**
* Low level access to the data stored for the attribute. The variant's type will correspond to
* the storage type.
*/
const DataVariant &data() const;
/**
* The same as #data(), but if the attribute data is shared initially, it will be unshared and
* made mutable.
*/
DataVariant &data_for_write();
/** Replace the attribute's data without first making the existing data mutable. */
void assign_data(DataVariant &&data);
};
class AttributeStorageRuntime {
friend AttributeStorage;
struct AttributeNameGetter {
StringRef operator()(const std::unique_ptr<Attribute> &value) const
{
return value->name();
}
};
/**
* For quick access, the attributes are stored in a vector set, keyed by their name. Attributes
* can still be reordered by rebuilding the vector set from scratch. Each attribute is allocated
* to give pointer stability across additions and removals.
*/
CustomIDVectorSet<std::unique_ptr<Attribute>, AttributeNameGetter> attributes;
};
class AttributeStorage : public blender::AttributeStorage {
public:
AttributeStorage();
AttributeStorage(const AttributeStorage &other);
AttributeStorage(AttributeStorage &&other);
AttributeStorage &operator=(const AttributeStorage &other);
AttributeStorage &operator=(AttributeStorage &&other);
~AttributeStorage();
/** Return the number of attributes. */
int count() const;
/** Return the attribute at the given index. */
Attribute &at_index(int index);
const Attribute &at_index(int index) const;
/** Return the index of the attribute with the given name, or -1 if not found. */
int index_of(StringRef name) const;
/**
* Try to find the attribute with a given name. The non-const overload does not make the
* attribute data itself mutable.
*/
Attribute *lookup(StringRef name);
const Attribute *lookup(StringRef name) const;
/**
* Attempt to remove the attribute with the given name, returning `true` if successful. Should
* not be called while iterating over attributes.
*/
bool remove(StringRef name);
bool remove(const Set<StringRef> &names);
bool remove(const Set<const Attribute *> &attributes);
/**
* Add an attribute with the given name, which must not already be used by an existing attribute
* or this will invoke undefined behavior.
*/
Attribute &add(std::string name,
bke::AttrDomain domain,
bke::AttrType data_type,
Attribute::DataVariant data);
/** Return a possibly changed version of the input name that is unique within existing names. */
std::string unique_name_calc(StringRef name) const;
/** Change the name of a single existing attribute. */
void rename(StringRef old_name, std::string new_name);
void rename(Attribute &attr, std::string new_name);
void rename(const Map<Attribute *, StringRef> &renames);
/**
* Resize the data for a given domain. New values will be default initialized (meaning no zero
* initialization for trivial types).
*/
void resize(AttrDomain domain, int64_t new_size);
/**
* Read data owned by the #AttributeStorage struct. This works by converting the DNA-specific
* types stored in the files to the runtime data structures.
*/
void blend_read(BlendDataReader &reader);
/**
* Temporary data used to write a #AttributeStorage struct embedded in another struct. See
* #attribute_storage_blend_write_prepare for more information.
*/
struct BlendWriteData {
BlendWriter *writer;
ResourceScope &scope;
Vector<blender::Attribute, 16> &attributes;
explicit BlendWriteData(BlendWriter *writer, ResourceScope &scope);
};
/**
* Write the prepared data and the data stored in the DNA fields in
* the #AttributeStorage struct.
*/
void blend_write(BlendWriter &writer, const BlendWriteData &write_data);
/**
* Iterate over every color to change it to another color-space.
*/
void foreach_working_space_color(const IDTypeForeachColorFunctionCallback &fn);
void count_memory(MemoryCounter &memory) const;
class Iterator : public iterator::RandomAccessIteratorMixin<Iterator> {
private:
using It = const std::unique_ptr<Attribute> *;
It it_;
public:
using value_type = Attribute;
using pointer = const Attribute *;
using reference = const Attribute &;
explicit Iterator(It it) : it_(it) {}
const Attribute &operator*() const
{
return **it_;
}
const It &iter_prop() const
{
return it_;
}
};
class MutableIterator : public iterator::RandomAccessIteratorMixin<MutableIterator> {
private:
using It = std::unique_ptr<Attribute> *;
It it_;
public:
using value_type = Attribute;
using pointer = Attribute *;
using reference = Attribute &;
explicit MutableIterator(It it) : it_(it) {}
Attribute &operator*() const
{
return **it_;
}
const It &iter_prop() const
{
return it_;
}
};
Iterator begin() const;
Iterator end() const;
MutableIterator begin();
MutableIterator end();
};
/** The C++ wrapper needs to be the same size as the DNA struct. */
static_assert(sizeof(AttributeStorage) == sizeof(AttributeStorage));
inline StringRefNull Attribute::name() const
{
return name_;
}
inline AttrDomain Attribute::domain() const
{
return domain_;
}
inline AttrType Attribute::data_type() const
{
return type_;
}
inline const Attribute::DataVariant &Attribute::data() const
{
return data_;
}
inline AttrStorageType Attribute::storage_type() const
{
static_assert(std::is_same_v<
std::variant_alternative_t<int(AttrStorageType::Array), Attribute::DataVariant>,
Attribute::ArrayData>);
static_assert(std::is_same_v<
std::variant_alternative_t<int(AttrStorageType::Single), Attribute::DataVariant>,
Attribute::SingleData>);
return AttrStorageType(data_.index());
}
inline void Attribute::assign_data(DataVariant &&data)
{
data_ = std::move(data);
}
inline AttributeStorage::Iterator AttributeStorage::begin() const
{
return Iterator(this->runtime->attributes.begin());
}
inline AttributeStorage::Iterator AttributeStorage::end() const
{
return Iterator(this->runtime->attributes.end());
}
inline AttributeStorage::MutableIterator AttributeStorage::begin()
{
/* Removing const is fine as long as the name of the attribute is not changed while iterating
* over the attributes. Renaming goes through #AttributeStorage::rename anyway. */
return MutableIterator(
const_cast<std::unique_ptr<Attribute> *>(this->runtime->attributes.begin()));
}
inline AttributeStorage::MutableIterator AttributeStorage::end()
{
return MutableIterator(
const_cast<std::unique_ptr<Attribute> *>(this->runtime->attributes.end()));
}
} // namespace bke
inline bke::AttributeStorage &AttributeStorage::wrap()
{
return *reinterpret_cast<bke::AttributeStorage *>(this);
}
inline const bke::AttributeStorage &AttributeStorage::wrap() const
{
return *reinterpret_cast<const bke::AttributeStorage *>(this);
}
} // namespace blender

View File

@@ -0,0 +1,25 @@
/* SPDX-FileCopyrightText: 2025 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
#include "BKE_attribute_storage.hh"
namespace blender::bke {
/**
* Prepare an #AttributeStorage struct embedded in another struct to be written. This is necessary
* because the #AttributeStorage implementation doesn't use the DNA structs at runtime, they are
* created just for the writing process. Creating them mutates the struct, which must be done
* before writing the struct that embeds it.
*
* \param use_5_0_compatibility: If true, the attribute storage will be written in a way that is
* compatible with Blender 5.0.
*/
void attribute_storage_blend_write_prepare(AttributeStorage &data,
bool use_5_0_compatibility,
FunctionRef<int(AttrDomain)> get_domain_size,
AttributeStorage::BlendWriteData &write_data);
} // namespace blender::bke

View File

@@ -0,0 +1,18 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
/** \file
* \ingroup bke
*/
namespace blender {
/**
* \param path: The path to check against.
* \return Success
*/
bool BKE_autoexec_match(const char *path);
} // namespace blender

View File

@@ -0,0 +1,34 @@
/* SPDX-FileCopyrightText: 2026 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
#include "FN_field.hh"
namespace blender::bke::bake {
/**
* When reading an attribute field from a bake, the type of the field may not be known immediately
* because it used to not be stored. However, it can be derived later when all actually existing
* attributes are known.
*/
class DeferredTypeAttributeFieldInput : public fn::FieldInput {
public:
std::string attribute_name;
using DummyT = float;
DeferredTypeAttributeFieldInput(std::string attribute_name)
: fn::FieldInput(CPPType::get<DummyT>()), attribute_name(std::move(attribute_name))
{
}
GVArray get_varray_for_context(const fn::FieldContext & /*context*/,
const IndexMask &mask,
ResourceScope & /*scope*/) const override
{
return VArray<DummyT>::from_single({}, mask.min_array_size());
}
};
} // namespace blender::bke::bake

View File

@@ -0,0 +1,60 @@
/* SPDX-FileCopyrightText: 2024 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
/** \file
* \ingroup bke
*/
#include <optional>
#include <string>
#include "BLI_vector.hh"
#include "DNA_ID_enums.h"
namespace blender {
struct ID;
struct NodesModifierDataBlock;
namespace bke::bake {
/**
* Unique weak reference to a data block within a #Main. It's used when caching/baking data-block
* references. Data-block pointers can't be used directly, because they are not stable over time
* and between Blender sessions.
*/
struct BakeDataBlockID {
ID_Type type;
/**
* Name of the data-block, without the type prefix.
*/
std::string id_name;
/**
* Name of the library data-block that the data-block is in. This refers to `Library.id.name` and
* not the file path. The type prefix of the name is omitted. If this is empty, the data-block is
* expected to be local and not linked.
*/
std::string lib_name;
BakeDataBlockID(ID_Type type, std::string id_name, std::string lib_name);
BakeDataBlockID(const ID &id);
BakeDataBlockID(const NodesModifierDataBlock &data_block);
uint64_t hash() const;
friend std::ostream &operator<<(std::ostream &stream, const BakeDataBlockID &id);
friend bool operator==(const BakeDataBlockID &a, const BakeDataBlockID &b) = default;
};
/**
* A list of weak data-block references for material slots.
*/
struct BakeMaterialsList : public Vector<std::optional<BakeDataBlockID>> {};
} // namespace bke::bake
} // namespace blender

View File

@@ -0,0 +1,36 @@
/* SPDX-FileCopyrightText: 2024 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
/** \file
* \ingroup bke
*/
#include "BKE_bake_data_block_id.hh"
namespace blender::bke::bake {
/**
* Maps #BakeDataBlockID to the corresponding data-blocks. This is used during depsgraph evaluation
* to remap weak data-block references stored in baked data to the actual data-blocks at run-time.
*
* Also it keeps track of missing data-blocks, so that they can be added later.
*/
struct BakeDataBlockMap {
public:
/**
* Tries to retrieve the data block for the given key. If it's not explicitly mapped, it might be
* added to the mapping. If it's still not found, null is returned.
*/
virtual ID *lookup_or_remember_missing(const BakeDataBlockID &key) = 0;
/**
* Tries to add the data block to the map. This may not succeed in all cases, e.g. if the
* implementation does not allow inserting new mapping items.
*/
virtual void try_add(ID &id) = 0;
};
} // namespace blender::bke::bake

View File

@@ -0,0 +1,156 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup bke
*/
#pragma once
#include <variant>
#include "BLI_mutex.hh"
#include "BLI_set.hh"
#include "BLI_sub_frame.hh"
#include "BKE_bake_items_paths.hh"
#include "BKE_bake_items_serialize.hh"
#include "BKE_bake_values.hh"
#include "DNA_modifier_types.h"
namespace blender {
struct NodesModifierData;
struct Main;
struct Object;
struct Scene;
namespace bke::bake {
enum class CacheStatus {
/** The cache is up-to-date with the inputs. */
Valid,
/**
* Nodes or input values have changed since the cache was created, i.e. the output would be
* different if the simulation was run again.
*/
Invalid,
/** The cache has been baked and will not be invalidated by changing inputs. */
Baked,
};
/**
* Stores the state for a specific frame.
*/
struct FrameCache {
SubFrame frame;
BakeValues values;
/**
* Used when the baked data is loaded lazily. The meta data either has to be loaded from a file
* or from an in-memory buffer.
*/
std::optional<std::variant<std::string, Span<std::byte>>> meta_data_source;
};
/**
* Stores the state after the previous simulation step. This is only used, when the frame-cache is
* not used.
*/
struct PrevCache {
BakeValues values;
SubFrame frame;
};
/**
* Baked data that corresponds to either a Simulation Output or Bake node.
*/
struct NodeBakeCache {
/** All cached frames sorted by frame. */
Vector<std::unique_ptr<FrameCache>> frames;
/** Loads blob data from memory when the bake is packed. */
std::unique_ptr<MemoryBlobReader> memory_blob_reader;
/** Where to load blobs from disk when loading the baked data lazily from disk. */
std::optional<std::string> blobs_dir;
/** Used to avoid reading blobs multiple times for different frames. */
std::unique_ptr<BlobReadSharing> blob_sharing;
/** Used to avoid checking if a bake exists many times. */
bool failed_finding_bake = false;
/** Range spanning from the first to the last baked frame. */
IndexRange frame_range() const;
void reset();
};
struct SimulationNodeCache {
NodeBakeCache bake;
CacheStatus cache_status = CacheStatus::Valid;
/** Previous simulation state when only that is stored (instead of the state for every frame). */
std::optional<PrevCache> prev_cache;
void reset();
};
struct BakeNodeCache {
NodeBakeCache bake;
void reset();
};
struct ModifierCache {
mutable Mutex mutex;
/**
* Set of nested node IDs (see #bNestedNodeRef) that is expected to be baked in the next
* evaluation. This is filled and cleared by the bake operator.
*/
Set<int> requested_bakes;
Map<int, std::unique_ptr<SimulationNodeCache>> simulation_cache_by_id;
Map<int, std::unique_ptr<BakeNodeCache>> bake_cache_by_id;
SimulationNodeCache *get_simulation_node_cache(const int id);
BakeNodeCache *get_bake_node_cache(const int id);
NodeBakeCache *get_node_bake_cache(const int id);
void reset_cache(int id);
};
/**
* Reset all simulation caches in the scene, for use when some fundamental change made them
* impossible to reuse.
*/
void scene_simulation_states_reset(Scene &scene);
std::optional<NodesModifierBakeTarget> get_node_bake_target(const Object &object,
const NodesModifierData &nmd,
int node_id);
std::optional<BakePath> get_node_bake_path(const Main &bmain,
const Object &object,
const NodesModifierData &nmd,
int node_id);
std::optional<IndexRange> get_node_bake_frame_range(const Scene &scene,
const Object &object,
const NodesModifierData &nmd,
int node_id);
std::optional<std::string> get_modifier_bake_path(const Main &bmain,
const Object &object,
const NodesModifierData &nmd);
/**
* Get default directory for baking modifier to disk.
*/
std::string get_default_modifier_bake_directory(const Main &bmain,
const Object &object,
const NodesModifierData &nmd);
std::string get_default_node_bake_directory(const Main &bmain,
const Object &object,
const NodesModifierData &nmd,
int node_id);
} // namespace bke::bake
} // namespace blender

View File

@@ -0,0 +1,56 @@
/* SPDX-FileCopyrightText: 2024 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup bke
*/
#pragma once
#include "DNA_modifier_types.h"
#include "BKE_bake_items_paths.hh"
#include "BKE_packedFile.hh"
namespace blender {
struct ReportList;
struct Main;
namespace bke::bake {
NodesModifierPackedBake *pack_bake_from_disk(const BakePath &bake_path, ReportList *reports);
[[nodiscard]] bool unpack_bake_to_disk(const NodesModifierPackedBake &packed_bake,
const BakePath &bake_path,
ReportList *reports);
enum class PackGeometryNodesBakeResult {
NoDataFound,
PackedAlready,
Success,
};
PackGeometryNodesBakeResult pack_geometry_nodes_bake(Main &bmain,
ReportList *reports,
Object &object,
NodesModifierData &nmd,
NodesModifierBake &bake);
enum class UnpackGeometryNodesBakeResult {
BlendFileNotSaved,
NoPackedData,
Error,
Success,
};
UnpackGeometryNodesBakeResult unpack_geometry_nodes_bake(Main &bmain,
ReportList *reports,
Object &object,
NodesModifierData &nmd,
NodesModifierBake &bake,
ePF_FileStatus how);
} // namespace bke::bake
} // namespace blender

View File

@@ -0,0 +1,47 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup bke
*/
#pragma once
#include <optional>
#include <string>
#include "BLI_string_ref.hh"
#include "BLI_sub_frame.hh"
#include "BLI_vector.hh"
namespace blender::bke::bake {
struct MetaFile {
SubFrame frame;
std::string path;
};
struct BakePath {
/** Path to the directory containing the meta data per frame. */
std::string meta_dir;
/**
* Path to the directory that contains the binary data. Could be shared between multiple bakes
* to reduce memory consumption.
*/
std::string blobs_dir;
/**
* Folder that is allowed to be deleted when the bake is deleted and it doesn't contain anything
* else. Typically, this contains the meta and blob directories.
*/
std::optional<std::string> bake_dir;
static BakePath from_single_root(StringRefNull root_dir);
};
std::string frame_to_file_name(const SubFrame &frame);
std::optional<SubFrame> file_name_to_frame(StringRef file_name);
Vector<MetaFile> find_sorted_meta_files(StringRefNull meta_dir);
} // namespace blender::bke::bake

View File

@@ -0,0 +1,257 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup bke
*/
#pragma once
#include "BLI_fileops.hh"
#include "BLI_function_ref.hh"
#include "BLI_implicit_sharing.hh"
#include "BLI_mutex.hh"
#include "BLI_serialize.hh"
#include "BKE_bake_values.hh"
namespace blender::bke::bake {
/**
* Reference to a slice of memory typically stored on disk.
* A blob is a "binary large object".
*/
struct BlobSlice {
std::string name;
IndexRange range;
std::shared_ptr<io::serialize::DictionaryValue> serialize() const;
static std::optional<BlobSlice> deserialize(const io::serialize::DictionaryValue &io_slice);
};
/**
* Abstract base class for loading binary data.
*/
class BlobReader {
public:
virtual ~BlobReader() = default;
/**
* Read the data from the given slice into the provided memory buffer.
* \return True on success, otherwise false.
*/
[[nodiscard]] virtual bool read(const BlobSlice &slice, void *r_data) const = 0;
/**
* Provides an #istream that can be used to read the data from the given slice.
* \return True on success, otherwise false.
*/
[[nodiscard]] virtual bool read_as_stream(const BlobSlice &slice,
FunctionRef<bool(std::istream &)> fn) const;
};
/**
* Abstract base class for writing binary data.
*/
class BlobWriter {
protected:
int64_t total_written_size_ = 0;
public:
virtual ~BlobWriter() = default;
/**
* Write the provided binary data.
* \return Slice where the data has been written to.
*/
virtual BlobSlice write(const void *data, int64_t size) = 0;
/**
* Provides an #ostream that can be used to write the blob.
* \param file_extension: May be used if the data is written to an independent file. Based on the
* implementation, this may be ignored.
* \return Slice where the data has been written to.
*/
virtual BlobSlice write_as_stream(StringRef file_extension,
FunctionRef<void(std::ostream &)> fn);
int64_t written_size() const
{
return total_written_size_;
}
};
/**
* Allows deduplicating data before it's written.
*/
class BlobWriteSharing : NonCopyable, NonMovable {
private:
struct StoredByRuntimeValue {
/**
* Version of the shared data that was written before. This is needed because the data might
* be changed later without changing the #ImplicitSharingInfo pointer.
*/
int64_t sharing_info_version;
/**
* Identifier of the stored data. This includes information for where the data is stored (a
* #BlobSlice) and optionally information for how it is loaded (e.g. endian information).
*/
std::shared_ptr<io::serialize::DictionaryValue> io_data;
};
/**
* Map used to detect when some data has already been written. It keeps a weak reference to
* #ImplicitSharingInfo, allowing it to check for equality of two arrays just by comparing the
* sharing info's pointer and version.
*/
Map<const ImplicitSharingInfo *, StoredByRuntimeValue> stored_by_runtime_;
/**
* Remembers where data was stored based on the hash of the data. This allows us to skip writing
* the same array again if it has the same hash.
*/
Map<uint64_t, BlobSlice> slice_by_content_hash_;
public:
~BlobWriteSharing();
/**
* Check if the data referenced by `sharing_info` has been written before. If yes, return the
* identifier for the previously written data. Otherwise, write the data now and store the
* identifier for later use.
* \return Identifier that indicates from where the data has been written.
*/
[[nodiscard]] std::shared_ptr<io::serialize::DictionaryValue> write_implicitly_shared(
const ImplicitSharingInfo *sharing_info,
FunctionRef<std::shared_ptr<io::serialize::DictionaryValue>()> write_fn);
/**
* Checks if the given data was written before. If it was, it's not written again, but a
* reference to the previously written data is returned. If the data is new, it's written now.
* Its hash is remembered so that the same data won't be written again.
*/
[[nodiscard]] std::shared_ptr<io::serialize::DictionaryValue> write_deduplicated(
BlobWriter &writer, const void *data, int64_t size_in_bytes);
};
/**
* Avoids loading the same data multiple times by caching and sharing previously read buffers.
*/
class BlobReadSharing : NonCopyable, NonMovable {
private:
/**
* Use a mutex so that #read_shared can be implemented in a thread-safe way.
*/
mutable Mutex mutex_;
/**
* Map used to detect when some data has been previously loaded. This keeps strong
* references to #ImplicitSharingInfo.
*/
mutable Map<std::string, ImplicitSharingInfoAndData> runtime_by_stored_;
public:
~BlobReadSharing();
/**
* Check if the data identified by `io_data` has been read before or load it now.
* \return Shared ownership to the read data, or none if there was an error.
*/
[[nodiscard]] std::optional<ImplicitSharingInfoAndData> read_shared(
const io::serialize::DictionaryValue &io_data,
FunctionRef<std::optional<ImplicitSharingInfoAndData>()> read_fn) const;
};
/**
* A specific #BlobReader that reads from disk.
*/
class DiskBlobReader : public BlobReader {
private:
const std::string blobs_dir_;
mutable Mutex mutex_;
mutable Map<std::string, std::unique_ptr<fstream>> open_input_streams_;
public:
DiskBlobReader(std::string blobs_dir);
[[nodiscard]] bool read(const BlobSlice &slice, void *r_data) const override;
};
/**
* A specific #BlobWriter that writes to a file on disk.
*/
class DiskBlobWriter : public BlobWriter {
private:
/** Directory path that contains all blob files. */
std::string blob_dir_;
/** Name of the file that data is written to. */
std::string base_name_;
std::string blob_name_;
/** File handle. The file is opened when the first data is written. */
std::fstream blob_stream_;
/** Current position in the file. */
int64_t current_offset_ = 0;
/** Used to generate file names for bake data that is stored in independent files. */
int independent_file_count_ = 0;
public:
DiskBlobWriter(std::string blob_dir, std::string base_name);
BlobSlice write(const void *data, int64_t size) override;
BlobSlice write_as_stream(StringRef file_extension,
FunctionRef<void(std::ostream &)> fn) override;
};
/**
* A specific #BlobWriter that keeps all data in memory.
*/
class MemoryBlobWriter : public BlobWriter {
public:
struct OutputStream {
std::unique_ptr<std::ostringstream> stream;
int64_t offset = 0;
};
private:
std::string base_name_;
std::string blob_name_;
Map<std::string, OutputStream> stream_by_name_;
int independent_file_count_ = 0;
public:
MemoryBlobWriter(std::string base_name);
BlobSlice write(const void *data, int64_t size) override;
BlobSlice write_as_stream(StringRef file_extension,
FunctionRef<void(std::ostream &)> fn) override;
const Map<std::string, OutputStream> &get_stream_by_name() const
{
return stream_by_name_;
}
};
/**
* A specific #BlobReader that reads data from in-memory buffers.
*/
class MemoryBlobReader : public BlobReader {
private:
Map<std::string, Span<std::byte>> blob_by_name_;
public:
void add(StringRef name, Span<std::byte> blob);
[[nodiscard]] bool read(const BlobSlice &slice, void *r_data) const override;
};
void serialize_bake(const BakeValues &bake_values,
BlobWriter &blob_writer,
BlobWriteSharing &blob_sharing,
std::ostream &r_stream);
std::optional<BakeValues> deserialize_bake(std::istream &stream,
const BlobReader &blob_reader,
const BlobReadSharing &blob_sharing);
} // namespace blender::bke::bake

View File

@@ -0,0 +1,87 @@
/* SPDX-FileCopyrightText: 2026 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
#include "BKE_attribute_enums.hh"
#include "BKE_bake_data_block_map.hh"
#include "BKE_node_socket_value.hh"
#include "BLI_compute_context.hh"
#include "BLI_map.hh"
namespace blender::bke::bake {
/**
* Intermediate storage for values that are read from a bake or are about to written to a bake.
* Values stored in this intermediate form have some constraints. For example, they can't store
* arbitrary fields (only attribute fields currently since those can be serialized). Additionally,
* data-block references to e.g. materials are weak and will only be restored in
* #to_runtime_values.
*/
class BakeValues {
public:
struct Item {
SocketValueVariant value;
std::optional<std::string> name;
};
private:
Map<int, Item> values_by_id_;
public:
struct InputValue {
int id;
std::string name;
SocketValueVariant value;
/**
* If a domain is given and the value is a field (that is not just an attribute), the field is
* captured on the previous geometry. This mainly exists to preserve legacy behavior.
*/
std::optional<AttrDomain> field_domain;
};
struct OutputKey {
int id;
eNodeSocketDatatype type;
};
BakeValues() = default;
explicit BakeValues(Map<int, Item> values_by_id) : values_by_id_(std::move(values_by_id)) {}
/**
* Creates new bake values from the given values. This makes sure that the values follow the
* restrictions for bake data.
*
* \param is_for_cache: If false, some run-time data can be kept on the data which would
* otherwise need to be removed because it could cause dangling pointers.
*/
static BakeValues from_runtime_values(Vector<InputValue> runtime_values,
BakeDataBlockMap *data_block_map,
bool is_for_cache = true);
/**
* Create fully valid run-time data again from the bake data. This also restores potential
* data-block references.
*/
Vector<SocketValueVariant> to_runtime_values(const Span<OutputKey> keys,
const ComputeContext &compute_context,
BakeDataBlockMap *data_block_map) const;
bool is_empty() const
{
return values_by_id_.is_empty();
}
void clear()
{
values_by_id_.clear();
}
const Map<int, Item> &values_by_id() const
{
return values_by_id_;
}
};
} // namespace blender::bke::bake

View File

@@ -0,0 +1,60 @@
/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
/** \file
* \ingroup bke
* \brief Blender util stuff
*/
namespace blender {
struct Main;
struct UserDef;
/**
* Only to be called on exit Blender.
*/
void BKE_blender_free();
void BKE_blender_globals_init();
void BKE_blender_globals_clear();
/** Replace current global Main by the given one, freeing existing one. */
void BKE_blender_globals_main_replace(Main *bmain);
/**
* Replace current global Main by the given one, returning the old one.
*
* \warning Advanced, risky workaround addressing the issue that current RNA is not able to process
* correctly non-G_MAIN data, use with (a lot of) care.
*/
Main *BKE_blender_globals_main_swap(Main *new_gmain);
void BKE_blender_globals_crash_path_get(char *filepath);
void BKE_blender_userdef_data_swap(UserDef *userdef_a, UserDef *userdef_b);
void BKE_blender_userdef_data_set(UserDef *userdef);
void BKE_blender_userdef_data_set_and_free(UserDef *userdef);
/**
* This function defines which settings a template will override for the user preferences.
*
* \note the order of `userdef_a` & `userdef_b` isn't important as values are simply swapped.
*/
void BKE_blender_userdef_app_template_data_swap(UserDef *userdef_a, UserDef *userdef_b);
void BKE_blender_userdef_app_template_data_set(UserDef *userdef);
void BKE_blender_userdef_app_template_data_set_and_free(UserDef *userdef);
/**
* When loading a new userdef from file,
* or when exiting Blender.
*/
void BKE_blender_userdef_data_free(UserDef *userdef, bool clear_fonts);
/* Blenders' own atexit (avoids leaking) */
void BKE_blender_atexit_register(void (*func)(void *user_data), void *user_data);
void BKE_blender_atexit_unregister(void (*func)(void *user_data), const void *user_data);
void BKE_blender_atexit();
} // namespace blender

View File

@@ -0,0 +1,71 @@
/* SPDX-FileCopyrightText: 2024 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
/** \file
* \ingroup bke
* \brief Blender CLI Generic `--command` Support.
*
* \note all registered commands must print help to the STDOUT & exit with a zero exit-code
* when `--help` is passed in as the first argument to a command.
*/
#include "BLI_utility_mixins.hh"
#include <memory>
#include <string>
namespace blender {
/**
* Each instance of this class can run the command with an argument list.
* The arguments begin at the first argument after the command identifier.
*/
class CommandHandler : NonCopyable, NonMovable {
public:
CommandHandler(const std::string &id) : id(id) {}
virtual ~CommandHandler() = default;
/** Matched against `--command {id}`. */
const std::string id;
/**
* The main execution function.
* The return value is used as the commands exit-code.
*/
virtual int exec(struct bContext *C, int argc, const char **argv) = 0;
/** True when one or more registered commands share an ID. */
bool is_duplicate = false;
};
/**
* \param cmd: The memory for a command type (ownership is transferred).
*/
void BKE_blender_cli_command_register(std::unique_ptr<CommandHandler> cmd);
/**
* Unregister a previously registered command.
*/
bool BKE_blender_cli_command_unregister(CommandHandler *cmd);
/**
* Run the command by `id`, passing in the argument list & context.
* The argument list must begin after the command identifier.
*/
int BKE_blender_cli_command_exec(struct bContext *C,
const char *id,
const int argc,
const char **argv);
/**
* Print all known commands (used for passing `--command help` in the command-line).
*/
void BKE_blender_cli_command_print_help();
/**
* Frees all commands (using their #CommandFreeFn call-backs).
*/
void BKE_blender_cli_command_free_all();
} // namespace blender

View File

@@ -0,0 +1,58 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
/** \file
* \ingroup bke
*/
#include "BLI_sys_types.h"
namespace blender {
struct Main;
struct ReportList;
struct bContext;
/** Paste-buffer helper API. For copy, use directly the #PartialWriteContext API. */
/**
* Import data-blocks from the given .blend filepath (i.e. append them) into the given Main
* data-base.
*
* Unlike #BKE_copybuffer_paste, it does not perform any instantiation of collections/objects/etc.,
* and does not clear the potential #ID_FLAG_CLIPBOARD_MARK flag from read IDs.
*
* Typically used for complex copy/pasting, using a temporary Main instead of the global one.
*
* \param libname: Full path to the .blend file used as copy/paste buffer.
* \param id_types_mask: Only directly link IDs of those types from the given .blend file buffer.
*
* \return true on success, false otherwise.
*/
bool BKE_copybuffer_read(Main *bmain_dst,
const char *libname,
ReportList *reports,
uint64_t id_types_mask);
/**
* Paste data-blocks from the given .blend filepath 'buffer' (i.e. append them) into the context's
* bmain.
*
* Similar to #BKE_copybuffer_read, but also handles instantiation of collections/objects/etc., and
* does clear the #ID_FLAG_CLIPBOARD_MARK from pasted data.
*
* \param libname: Full path to the .blend file used as copy/paste buffer.
* \param flag: A combination of #eBLOLibLinkFlags and ##eFileSel_Params_Flag to control
* link/append behavior.
* \note Ignores #FILE_LINK flag, since it always appends IDs.
* \param id_types_mask: Only directly link IDs of those types from the given .blend file buffer.
*
* \return Number of IDs directly pasted from the buffer (does not includes indirectly linked
* ones).
*/
int BKE_copybuffer_paste(
bContext *C, const char *libname, int flag, ReportList *reports, uint64_t id_types_mask);
} // namespace blender

View File

@@ -0,0 +1,27 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
/** \file
* \ingroup bke
*/
#include "BKE_undo_system.hh"
namespace blender {
struct Main;
struct MemFileUndoData;
struct bContext;
#define BKE_UNDO_STR_MAX 64
MemFileUndoData *BKE_memfile_undo_encode(Main *bmain, MemFileUndoData *mfu_prev);
bool BKE_memfile_undo_decode(MemFileUndoData *mfu,
eUndoStepDir undo_direction,
bool use_old_bmain_data,
bContext *C);
void BKE_memfile_undo_free(MemFileUndoData *mfu);
} // namespace blender

View File

@@ -0,0 +1,31 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
/** \file
* \ingroup bke
*/
#include "DNA_listBase.h"
namespace blender {
struct bUserMenu;
struct bUserMenuItem;
enum eUserMenu_Type : char;
bUserMenu *BKE_blender_user_menu_find(ListBaseT<bUserMenu> *lb,
char space_type,
const char *context);
bUserMenu *BKE_blender_user_menu_ensure(ListBaseT<bUserMenu> *lb,
char space_type,
const char *context);
bUserMenuItem *BKE_blender_user_menu_item_add(ListBaseT<bUserMenuItem> *lb, eUserMenu_Type type);
void BKE_blender_user_menu_item_free(bUserMenuItem *umi);
void BKE_blender_user_menu_item_free_list(ListBaseT<bUserMenuItem> *lb);
} // namespace blender

View File

@@ -0,0 +1,72 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
#include <stdbool.h>
#include <stddef.h>
namespace blender {
/** \file
* \ingroup bke
*/
/**
* The lines below use regex from scripts to extract their values,
* Keep this in mind when modifying this file and keep this comment above the defines.
*
* \note Use #STRINGIFY() rather than defining with quotes.
*/
/** Blender major and minor version. */
#define BLENDER_VERSION 502
/** Blender patch version for bug-fix releases. */
#define BLENDER_VERSION_PATCH 0
/** Blender release cycle stage: alpha/beta/rc/release. */
#define BLENDER_VERSION_CYCLE release
/** Blender release type suffix. LTS or blank. */
#define BLENDER_VERSION_SUFFIX LTS
/* Blender file format version. */
#define BLENDER_FILE_VERSION BLENDER_VERSION
#define BLENDER_FILE_SUBVERSION 44
/* Minimum Blender version that supports reading file written with the current
* version. Older Blender versions will test this and cancel loading the file, showing a warning to
* the user.
*
* See
* https://developer.blender.org/docs/handbook/guidelines/compatibility_handling_for_blend_files/
* for details. */
#define BLENDER_FILE_MIN_VERSION 405
#define BLENDER_FILE_MIN_SUBVERSION 85
/** User readable version string. */
const char *BKE_blender_version_string();
/** As above but does not show patch version. */
const char *BKE_blender_version_string_compact();
/** Returns true when version cycle is alpha, otherwise (beta, rc) returns false. */
bool BKE_blender_version_is_alpha();
/** Returns true when version suffix is LTS, otherwise returns false. */
bool BKE_blender_version_is_lts();
/**
* Fill in given string buffer with user-readable formatted file version and subversion (if
* provided).
*
* \param str_buff: a char buffer where the formatted string is written,
* minimal recommended size is 8, or 16 if subversion is provided.
*
* \param file_subversion: the file subversion, if given value < 0, it is ignored, and only the
* `file_version` is used.
*/
void BKE_blender_version_blendfile_string_from_values(char *str_buff,
const size_t str_buff_maxncpy,
const short file_version,
const short file_subversion);
} // namespace blender

View File

@@ -0,0 +1,450 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
/** \file
* \ingroup bke
*/
#include "BKE_main.hh"
#include "BLI_enum_flags.hh"
#include "BLI_function_ref.hh"
#include "BLI_map.hh"
#include "BLI_utility_mixins.hh"
#include <string>
namespace blender {
struct bContext;
struct BlendFileData;
struct BlendFileReadParams;
struct BlendFileReadReport;
struct BlendFileReadWMSetupData;
struct BlendFileWriteParams;
struct ID;
struct IDNameLib_Map;
struct Library;
struct LibraryIDLinkCallbackData;
struct MemFile;
struct ReportList;
struct UserDef;
struct WorkspaceConfigFileData;
/**
* The suffix used for blend-files managed by the asset system.
*/
#define BLENDER_ASSET_FILE_SUFFIX ".asset.blend"
/**
* Check whether given path ends with a blend file compatible extension
* (`.blend`, `.ble` or `.blend.gz`).
*
* \param str: The path to check.
* \return true is this path ends with a blender file extension.
*/
bool BKE_blendfile_extension_check(const char *str);
/**
* Try to explode given path into its 'library components'
* (i.e. a .blend file, id type/group, and data-block itself).
*
* \param path: the full path to explode.
* \param r_dir: the string that'll contain path up to blend file itself ('library' path).
* WARNING! Must be at least #FILE_MAX_LIBEXTRA long (it also stores group and name strings)!
* \param r_group: a pointer within `r_dir` to the 'group' part of the path, if any ('\0'
* terminated). May be NULL.
* \param r_name: a pointer within `r_dir` to the data-block name, if any ('\0' terminated). May be
* NULL.
* \return true if path contains a blend file.
*/
bool BKE_blendfile_library_path_explode(const char *path,
char *r_dir,
char **r_group,
char **r_name);
/**
* Check whether a given path is actually a Blender-readable, valid .blend file.
*
* \note Currently does attempt to open and read (part of) the given file.
*/
bool BKE_blendfile_is_readable(const char *path, ReportList *reports);
/**
* Shared setup function that makes the data from `bfd` into the current blend file,
* replacing the contents of #G.main.
* This uses the bfd returned by #BKE_blendfile_read and similarly named functions.
*
* This is done in a separate step so the caller may perform actions after it is known the file
* loaded correctly but before the file replaces the existing blend file contents.
*/
void BKE_blendfile_read_setup_readfile(bContext *C,
BlendFileData *bfd,
const BlendFileReadParams *params,
BlendFileReadWMSetupData *wm_setup_data,
BlendFileReadReport *reports,
bool startup_update_defaults,
const char *startup_app_template);
/**
* Simpler version of #BKE_blendfile_read_setup_readfile used when reading undo steps from
* memfile.
*/
void BKE_blendfile_read_setup_undo(bContext *C,
BlendFileData *bfd,
const BlendFileReadParams *params,
BlendFileReadReport *reports);
/**
* \return Blend file data, this must be passed to
* #BKE_blendfile_read_setup_readfile/#BKE_blendfile_read_setup_undo when non-NULL.
*/
BlendFileData *BKE_blendfile_read(const char *filepath,
const BlendFileReadParams *params,
BlendFileReadReport *reports);
/**
* \return Blend file data, this must be passed to
* #BKE_blendfile_read_setup_readfile/#BKE_blendfile_read_setup_undo when non-NULL.
*/
BlendFileData *BKE_blendfile_read_from_memory(const void *file_buf,
int file_buf_size,
const BlendFileReadParams *params,
ReportList *reports);
/**
* \return Blend file data, this must be passed to
* #BKE_blendfile_read_setup_readfile/#BKE_blendfile_read_setup_undo when non-NULL.
*
* \note `memfile` is the undo buffer.
*/
BlendFileData *BKE_blendfile_read_from_memfile(Main *bmain,
MemFile *memfile,
const BlendFileReadParams *params,
ReportList *reports);
/**
* Utility to make a file 'empty' used for startup to optionally give an empty file.
* Handy for tests.
*/
void BKE_blendfile_read_make_empty(bContext *C);
/**
* Only read the #UserDef from a .blend.
*/
UserDef *BKE_blendfile_userdef_read(const char *filepath, ReportList *reports);
UserDef *BKE_blendfile_userdef_read_from_memory(const void *file_buf,
int file_buf_size,
ReportList *reports);
UserDef *BKE_blendfile_userdef_from_defaults();
/**
* Only write the #UserDef in a `.blend`.
* \return success.
*/
bool BKE_blendfile_userdef_write(const char *filepath, ReportList *reports);
/**
* Only write the #UserDef in a `.blend`, merging with the existing blend file.
* \return success.
*
* \note In the future we should re-evaluate user preferences,
* possibly splitting out system/hardware specific preferences.
*/
bool BKE_blendfile_userdef_write_app_template(const char *filepath, ReportList *reports);
bool BKE_blendfile_userdef_write_all(ReportList *reports);
WorkspaceConfigFileData *BKE_blendfile_workspace_config_read(const char *filepath,
const void *file_buf,
int file_buf_size,
ReportList *reports);
void BKE_blendfile_workspace_config_data_free(WorkspaceConfigFileData *workspace_config);
namespace bke::blendfile {
/**
* Partial blendfile writing.
*
* This wrapper around the Main struct is designed to have a very short life span, during which it
* will contain independent copies of the IDs that are added to it.
*
* In general, the #G_MAIN data should not change while such a context exists, otherwise mapping
* info between the context content and the G_MAIN content cannot be kept up-to-date.
*
* The context can then be written to disk, and destroyed.
*
* It also has advanced ways to handle ID dependencies (and libraries for linked IDs), by allowing
* specific handling for each dependency individually. By using the `dependencies_filter_cb`
* optional parameter of #id_add, it is possible to skip (ignore) certain dependencies, or make
* linked ones local in the context, etc.
*
* Design task: #122061
*/
class PartialWriteContext : NonCopyable, NonMovable {
public:
/** The temp Main itself, storing all IDs copied into this partial write context. */
Main bmain = {};
private:
/**
* The filepath that should be used as root for IDs _added_ to the context, when handling
* remapping of their relative filepaths.
*
* Typically, the current G_MAIN's filepath.
*
* \note Currently always also copied into the temp `bmain.filepath`,
* as this simplifies remapping of relative file-paths.
* This may change in the future, if context can be loaded from external blend-files.
*/
std::string reference_root_filepath_;
/**
* This mapping only contains entries for IDs in the context which have a known matching ID in
* current G_MAIN.
*
* It is used to avoid adding several time a same ID (e.g. as a dependency of several other added
* IDs).
*/
IDNameLib_Map *matching_uid_map_;
/** A mapping from the absolute library paths to the #Library IDs in the context. */
Map<std::string, Library *> libraries_map_;
public:
/* Passing a reference root filepath is mandatory, for remapping of relative paths to work as
* expected. */
PartialWriteContext() = delete;
PartialWriteContext(Main &reference_main);
~PartialWriteContext();
/**
* Control how to handle IDs and their dependencies when they are added to this context.
*
* \note For linked IDs, if #MAKE_LOCAL is not used, the library ID pointer is _not_ considered
* nor handled as a regular dependency. Instead, the library is _always_ added to the context
* data, and never duplicated. Also, library matching always happens based on absolute filepath.
*
* \warning Heterogeneous usages of these operations flags during a same PartialWriteContext
* session may not generate expected results. Typically, once an ID has been added to the context
* as 'matching' counterpart of the source Main (i.e. sharing the same session UID), it will not
* be re-processed further if found again as dependency of another ID, or added explicitly as
* root ID.
* So e.g. if an ID is added (explicitly or implicitly) but none of its dependencies are (using
* `CLEAR_DEPENDENCIES`), re-adding the same ID (explicitly or implicitly) with e.g.
* `ADD_DEPENDENCIES` set will __not__ add its dependencies.
* This is not expected to be an issue in current use-cases.
*/
enum IDAddOperations {
NOP = 0,
/**
* Do not keep linked info (library and/or liboverride references).
*
* \warning By default, when #ADD_DEPENDENCIES is defined, this will also apply to all
* dependencies as well.
*
* \note Often required when only a small subset of the ID dependencies are also added to the
* context (i.e. many of the added data's ID pointers are set to `nullptr`). Otherwise, some
* areas not expecting nullptr (like LibOverride data) may assert or error on load of the
* partial written blendfile.
*/
MAKE_LOCAL = 1 << 0,
/**
* Set the 'fake user' flag to the added ID. Ensures that it is never auto-removed from the
* context, and always written to disk.
*/
SET_FAKE_USER = 1 << 1,
/**
* Set the 'clipboard' flag to the added ID. Ensures that it is treated as potential source
* data for a 'paste ID' operation.
*/
SET_CLIPBOARD_MARK = 1 << 4,
/**
* Clear all dependency IDs that are not in the partial write context. Mutually exclusive with
* #ADD_DEPENDENCIES.
*
* WARNING: This also means that dependencies like obdata, shape-keys or actions are not
* duplicated either.
*
* NOTE: Either #CLEAR_DEPENDENCIES or #ADD_DEPENDENCIES must be specified in the final
* operation flags for all ID dependencies. This can be achieved by
*/
CLEAR_DEPENDENCIES = 1 << 8,
/**
* Also add (or reuse if already there) dependency IDs into the partial write context. Mutually
* exclusive with #CLEAR_DEPENDENCIES.
*/
ADD_DEPENDENCIES = 1 << 9,
/**
* For each explicitly added IDs (i.e. these with a fake user), ensure all of their
* dependencies are independent copies, instead of being shared with other explicitly added
* IDs. Only relevant with #ADD_DEPENDENCIES.
*
* \warning Implies that the `session_uid` of these duplicated dependencies will be different
* than their source data.
*/
DUPLICATE_DEPENDENCIES = 1 << 10,
/**
* Operation flags that are (by default) inherited by all dependencies.
*
* \note This will be (partially) superseded by masked-out values from #MASK_PER_ID_USAGES
* below.
*/
MASK_INHERITED = (MAKE_LOCAL | CLEAR_DEPENDENCIES | ADD_DEPENDENCIES | DUPLICATE_DEPENDENCIES),
/**
* Operation flags that are defined by the #dependencies_filter_cb callback, if given.
*
* \note This mask is applied on top of the filter from #MASK_INHERITED, for ID dependencies
* of explicitly added data.
*/
MASK_PER_ID_USAGE = (MAKE_LOCAL | SET_FAKE_USER | SET_CLIPBOARD_MARK | CLEAR_DEPENDENCIES |
ADD_DEPENDENCIES),
};
/**
* Options passed to the #id_add method.
*/
struct IDAddOptions {
IDAddOperations operations;
};
/**
* Add a copy of the given ID to the partial write context.
*
* \note The duplicated ID will have the same session_uid as its source. In case a matching ID
* already exists in the context, it is returned instead of duplicating it again.
*
* \param options: Control how the added ID (and its dependencies) are handled. See
* #IDAddOptions and #IDAddOperations above for details.
* If no #dependencies_filter_cb callback is specified, #options.operations must contain
* either #CLEAR_DEPENDENCIES or #ADD_DEPENDENCIES.
* \param dependencies_filter_cb: Optional, a callback called for each ID usages, which returns
* specific operations flags for each ID usage.
* Currently, only accepted return values are the ones included in #MASK_PER_ID_USAGE.
* Returned flags must always contain either #CLEAR_DEPENDENCIES or #ADD_DEPENDENCIES.
*
* \return The pointer to the duplicated ID in the partial write context.
*/
ID *id_add(const ID *id,
IDAddOptions options,
FunctionRef<IDAddOperations(LibraryIDLinkCallbackData *cb_data, IDAddOptions options)>
dependencies_filter_cb = nullptr);
/**
* Add and return a new ID into the partial write context.
*
* NOTE: Since this ID is _created_ in the partial write buffer, by definition it has no matching
* counterpart in the current G_MAIN. Therefore, there is no need to add it to
* #matching_uid_map_, and its `session_uid` is not guaranteed to be constant (as it may be
* preempted later by another ID added from the current G_MAIN).
*
* \param options: Control how the created ID is handled. See #IDAddOptions and #IDAddOperations
* above for details, note that the only relevant operation flags currently are the
* #SET_FAKE_USER and #SET_CLIPBOARD_MARK ones.
*/
ID *id_create(short id_type, StringRefNull id_name, Library *library, IDAddOptions options);
/**
* Delete the copy of the given ID from the partial write context.
*
* \note The search is based on the #ID.session_uid of the given ID. This means that if
* `duplicate_depencies` option was used when adding the ID, these independent dependencies
* duplicates cannot be removed directly from the context. Use #remove_unused for this.
*
* \note No dependencies will be removed. Use #remove_unused to remove all unused IDs from the
* current context.
*/
void id_delete(const ID *id);
/**
* Remove all unused IDs from the current context.
*
* \param clear_extra_user: If `true`, the runtime tag ensuring that IDs are written on disk will
* be cleared. In other words, only IDs flagged with 'fake user' and their dependencies
* will be kept. Allows to also remove IDs that were added to this context during the same
* editing session, and were not flagged as 'fake user'.
*/
void remove_unused(bool clear_extra_user = false);
/**
* Fully empty the partial write context.
*/
void clear();
/**
* Debug: Check if the current partial write context is fully valid.
*
* Currently, check if any ID in the context still has relations to IDs not in the context.
*
* \return false if the context is invalid.
*/
bool is_valid();
/**
* Write the content of the current context as a blendfile on disk.
*
* \return `true` on success.
*/
bool write(const char *write_filepath, int write_flags, int remap_mode, ReportList &reports);
bool write(const char *write_filepath, ReportList &reports);
/**
* Write the content of the current context as a copy/paste buffer blendfile on disk.
*
* \return `true` on success.
*/
bool write_as_copypaste_buffer(const char *write_filepath, ReportList &reports);
/* TODO: To allow editing an existing external blendfile:
* - API to load a context from a blendfile.
* - API to 'match' a context's content with another Main database's content (based on ID
* names and libraries).
* - API to replace the matching context IDs by a 'new version' (similar to 'add_id', but
* ensuring that the context ID, if it already exists, is a pristine copy of the given source
* one).
* - Rework the remapping of relative filepaths, since data already existing in the
* loaded-from-disk temp context will have different root-path than the data from current
* G_MAIN.
*/
private:
/**
* In case an explicitly added ID has the same session_uid as an existing one in current
* context, the added one should be able to 'steal' that session_uid in the context, and
* re-assign a new one to the other ID.
*/
void preempt_session_uid(ID *ctx_id, unsigned int session_uid);
/**
* Ensures that given ID will be written on disk (within current context), by either setting the
* 'fake user' flag, or the (runtime-only, cleared on next file load) 'extra user' tag, depending
* on whether #SET_FAKE_USER is set or not.
*
* Also handles the setting of the #ID_FLAG_CLIPBOARD_MARK flag if #SET_CLIPBOARD_MARK is set.
*/
void process_added_id(ID *ctx_id, const IDAddOperations operations);
/**
* Utils for #PartialWriteContext::id_add, only adds (duplicate) the given source ID into
* current context.
*/
ID *id_add_copy(const ID *id, bool regenerate_session_uid);
/** Make given context ID local to the context. */
void make_local(ID *ctx_id, int make_local_flags);
/**
* Ensure that the given ID's library has a matching Library ID in the context, copying the
* current `ctx_id->lib` one if needed.
*/
Library *ensure_library(ID *ctx_id);
/**
* Ensure that the given library path has a matching Library ID in the context, creating a new
* one if needed.
*/
Library *ensure_library(StringRefNull library_absolute_path);
/** Actual writing code, hidden behind the public simpler APIs. */
bool write_impl(const char *write_filepath,
int write_flags,
const BlendFileWriteParams &blend_file_write_params,
ReportList &reports);
};
ENUM_OPERATORS(PartialWriteContext::IDAddOperations);
} // namespace bke::blendfile
} // namespace blender

View File

@@ -0,0 +1,443 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
/** \file
* \ingroup bke
*/
#include <list>
#include <string>
#include "BLI_bit_vector.hh"
#include "BLI_function_ref.hh"
#include "BLI_map.hh"
#include "BLO_readfile.hh"
struct BlendHandle;
namespace blender {
struct ID;
struct Library;
struct LibraryLink_Params;
struct MainLibraryWeakReferenceMap;
struct ReportList;
/* TODO: Rename file to `BKE_blendfile_import.hh`. */
/* TODO: Replace `BlendfileLinkAppend` prefix by `bke::blendfile::import` namespace. */
/* TODO: Move these enums to scoped enum classes. */
/** Actions to apply to an item (i.e. linked ID). */
enum {
LINK_APPEND_ACT_UNSET = 0,
LINK_APPEND_ACT_KEEP_LINKED,
LINK_APPEND_ACT_REUSE_LOCAL,
LINK_APPEND_ACT_MAKE_LOCAL,
LINK_APPEND_ACT_COPY_LOCAL,
};
/** Various status info about an item (i.e. linked ID). */
enum {
/** An indirectly linked ID. */
LINK_APPEND_TAG_INDIRECT = 1 << 0,
/**
* An ID also used as liboverride dependency (either directly, as a liboverride reference, or
* indirectly, as data used by a liboverride reference). It should never be directly made local.
*
* Mutually exclusive with #LINK_APPEND_TAG_LIBOVERRIDE_DEPENDENCY_ONLY.
*/
LINK_APPEND_TAG_LIBOVERRIDE_DEPENDENCY = 1 << 1,
/**
* An ID only used as liboverride dependency (either directly or indirectly, see
* #LINK_APPEND_TAG_LIBOVERRIDE_DEPENDENCY for precisions). It should not be considered during
* the 'make local' process, and remain purely linked data.
*
* Mutually exclusive with #LINK_APPEND_TAG_LIBOVERRIDE_DEPENDENCY.
*/
LINK_APPEND_TAG_LIBOVERRIDE_DEPENDENCY_ONLY = 1 << 2,
};
/* NOTE: These three structs are currently exposed in header to allow for their usage in RNA.
* Regular C++ code should not access their content directly.
*
* TODO: Refactor these three structs into classes, and integrated the whole API into them. */
struct BlendfileLinkAppendContext;
/** A data-block (ID) entry in the `items` list from #BlendfileLinkAppendContext. */
struct BlendfileLinkAppendContextItem {
/**
* Link/Append context owner of this item. Used in RNA API, could be removed once RNA paths are
* functional.
*/
BlendfileLinkAppendContext *lapp_context;
/** Name of the ID (without the heading two-chars IDcode). */
std::string name;
/** All libraries (from #BlendfileLinkAppendContext.libraries) to try to load this ID from. */
BitVector<> libraries;
/** ID type. */
short idcode;
/**
* Type of action to perform on this item, and general status tag information.
* NOTE: Mostly used by append post-linking processing.
*/
char action;
char tag;
/** Newly linked ID (nullptr until it has been successfully linked). */
ID *new_id;
/**
* Library ID from which the #new_id has been linked
* (nullptr until it has been successfully linked).
*/
Library *source_library;
/**
* Liboverride of the linked ID
* (nullptr until it has been successfully created or an existing one has been found).
*/
ID *liboverride_id;
/**
* Whether the item has a matching local ID that was already appended from the same source
* before, and has not been modified. In 'Append & Reuse' case, this local ID _may_ be reused
* instead of making linked data local again.
*/
ID *reusable_local_id;
/** Opaque user data pointer. */
void *userdata;
};
/** A blend-file library entry in the `libraries` vector from #BlendfileLinkAppendContext. */
struct BlendfileLinkAppendContextLibrary {
/** Absolute .blend file path. */
std::string path;
/** Blend file handle, if any. */
BlendHandle *blo_handle;
/** Whether the blend file handle is owned, or borrowed. */
bool blo_handle_is_owned;
/** The blend-file report associated with the `blo_handle`, if owned. */
BlendFileReadReport bf_reports;
};
/**
* General container for all relevant data for a library/linked-data related operation (linking,
* appending, library relocating, etc.).
*/
struct BlendfileLinkAppendContext {
/** List of library paths to search IDs in. */
Vector<BlendfileLinkAppendContextLibrary> libraries;
/**
* List of all ID to try to link from #libraries. This is a linked list because iterators must
* not be invalidated when adding more items.
*/
std::list<BlendfileLinkAppendContextItem> items;
using items_iterator_t = std::list<BlendfileLinkAppendContextItem>::iterator;
/** Linking/appending parameters. Including `bmain`, `scene`, `viewlayer` and `view3d`. */
LibraryLink_Params *params = nullptr;
/**
* What is the current stage of the link/append process. Used mainly by the RNA wrappers for the
* pre/post handlers currently.
*/
enum class ProcessStage {
/**
* The context data is being filled with data (Libraries and IDs) to process. Nothing has been
* linked yet.
*/
Init = 0,
/** The context data is being used to linked IDs. */
Linking,
/**
* The context data is being used to append IDs (i.e. make local linked ones, or re-use already
* existing local ones).
*/
Appending,
/**
* The context data is being used to instantiate (loose) IDs (i.e. ensure that Collections,
* Objects and/or ObjectData IDs are added to the current scene).
*/
Instantiating,
/**
* All data has been linked or appended. The context state represents the final result of the
* process.
*/
Done,
/* NOTE: For the time being, liboverride step is not considered here (#BKE_blendfile_override).
* Mainly because it is only available through the BPY API currently. */
};
ProcessStage process_stage;
/** Allows to easily find an existing items from an ID pointer. */
Map<ID *, BlendfileLinkAppendContextItem *> new_id_to_item;
/** Runtime info used by append code to manage re-use of already appended matching IDs. */
MainLibraryWeakReferenceMap *library_weak_reference_mapping = nullptr;
/** Embedded blendfile and its size, if needed. */
const void *blendfile_mem = nullptr;
size_t blendfile_memsize = 0;
};
/**
* Allocate and initialize a new context to link/append data-blocks.
*/
BlendfileLinkAppendContext *BKE_blendfile_link_append_context_new(LibraryLink_Params *params);
/**
* Free a link/append context.
*/
void BKE_blendfile_link_append_context_free(BlendfileLinkAppendContext *lapp_context);
/**
* Set or clear flags in given \a lapp_context.
*
* \param flag: A combination of:
* - #eFileSel_Params_Flag from `DNA_space_types.h` &
* - #eBLOLibLinkFlags * from `BLO_readfile.hh`.
* \param do_set: Set the given \a flag if true, clear it otherwise.
*/
void BKE_blendfile_link_append_context_flag_set(BlendfileLinkAppendContext *lapp_context,
int flag,
bool do_set);
/**
* Store reference to a Blender's embedded memfile into the context.
*
* \note This is required since embedded startup blender file is handled in `ED` module, which
* cannot be linked in BKE code.
*/
void BKE_blendfile_link_append_context_embedded_blendfile_set(
BlendfileLinkAppendContext *lapp_context, const void *blendfile_mem, int blendfile_memsize);
/** Clear reference to Blender's embedded startup file into the context. */
void BKE_blendfile_link_append_context_embedded_blendfile_clear(
BlendfileLinkAppendContext *lapp_context);
/**
* Add a new source library to search for items to be linked to the given link/append context.
*
* \param libname: the absolute path to the library blend file.
* \param blo_handle: the blend file handle of the library, `nullptr` if not available. Note that
* the ownership of this handle is always stolen, because readfile code may
* forcefully clear this handle after reading in some cases (endianness
* conversion, see usages of the #FD_FLAGS_SWITCH_ENDIAN flag).
*
* \note *Never* call #BKE_blendfile_link_append_context_library_add()
* after having added some items.
*/
void BKE_blendfile_link_append_context_library_add(BlendfileLinkAppendContext *lapp_context,
const char *libname,
BlendHandle *blo_handle);
/**
* Add a new item (data-block name and `idcode`) to be searched and linked/appended from libraries
* associated to the given context.
*
* \param userdata: an opaque user-data pointer stored in generated link/append item.
*
* TODO: Add a more friendly version of this function that combines it with the call to
* #BKE_blendfile_link_append_context_item_library_index_enable to enable the added item for all
* added library sources.
*/
BlendfileLinkAppendContextItem *BKE_blendfile_link_append_context_item_add(
BlendfileLinkAppendContext *lapp_context, const char *idname, short idcode, void *userdata);
#define BLENDFILE_LINK_APPEND_INVALID -1
/**
* Search for all ID matching given `id_types_filter` in given `library_index`, and add them to
* the list of items to process.
*
* \note #BKE_blendfile_link_append_context_library_add should never be called on the same
* `lapp_context` after this function.
*
* \param id_types_filter: A set of `FILTER_ID` bit-flags, the types of IDs to add to the items
* list.
* \param library_index: The index of the library to look into, in given `lapp_context`.
*
* \return The number of items found and added to the list, or `BLENDFILE_LINK_APPEND_INVALID` if
* it could not open the .blend file.
*/
int BKE_blendfile_link_append_context_item_idtypes_from_library_add(
BlendfileLinkAppendContext *lapp_context,
ReportList *reports,
uint64_t id_types_filter,
int library_index);
/**
* Enable search of the given \a item into the library stored at given index in the link/append
* context.
*/
void BKE_blendfile_link_append_context_item_library_index_enable(
BlendfileLinkAppendContext *lapp_context,
BlendfileLinkAppendContextItem *item,
int library_index);
/**
* Check if given link/append context is empty (has no items to process) or not.
*/
bool BKE_blendfile_link_append_context_is_empty(BlendfileLinkAppendContext *lapp_context);
void *BKE_blendfile_link_append_context_item_userdata_get(BlendfileLinkAppendContext *lapp_context,
BlendfileLinkAppendContextItem *item);
ID *BKE_blendfile_link_append_context_item_newid_get(BlendfileLinkAppendContext *lapp_context,
BlendfileLinkAppendContextItem *item);
/**
* Replace the newly linked ID by another from the same library. Rarely used, necessary e.g. in
* some complex 'do version after setup' code when an ID is replaced by another one.
*/
void BKE_blendfile_link_append_context_item_newid_set(BlendfileLinkAppendContext *lapp_context,
BlendfileLinkAppendContextItem *item,
ID *new_id);
ID *BKE_blendfile_link_append_context_item_liboverrideid_get(
BlendfileLinkAppendContext *lapp_context, BlendfileLinkAppendContextItem *item);
short BKE_blendfile_link_append_context_item_idcode_get(BlendfileLinkAppendContext *lapp_context,
BlendfileLinkAppendContextItem *item);
enum eBlendfileLinkAppendForeachItemFlag {
/** Loop over directly linked items (i.e. those explicitly defined by user code). */
BKE_BLENDFILE_LINK_APPEND_FOREACH_ITEM_FLAG_DO_DIRECT = 1 << 0,
/**
* Loop over indirectly linked items (i.e. those defined by internal code, as dependencies of
* direct ones).
*
* IMPORTANT: Those 'indirect' items currently may not cover **all** indirectly linked data.
* See comments in #foreach_libblock_link_append_callback.
*/
BKE_BLENDFILE_LINK_APPEND_FOREACH_ITEM_FLAG_DO_INDIRECT = 1 << 1,
};
/**
* Iterate over all (or a subset) of the items listed in given #BlendfileLinkAppendContext,
* and call the `callback_function` on them.
*
* \param flag: Control which type of items to process (see
* #eBlendfileLinkAppendForeachItemFlag enum flags).
*/
void BKE_blendfile_link_append_context_item_foreach(
BlendfileLinkAppendContext *lapp_context,
/**
* Called over each (or a subset of each) of the items in given #BlendfileLinkAppendContext.
*
* \return `true` if iteration should continue, `false` otherwise.
*/
FunctionRef<bool(BlendfileLinkAppendContext *lapp_context,
BlendfileLinkAppendContextItem *item)> callback_function,
eBlendfileLinkAppendForeachItemFlag flag);
/**
* Called once the link/append process has been fully initialized (all of its data has been set).
*
* NOTE: Currently only used to call the matching handler.
*/
void BKE_blendfile_link_append_context_init_done(BlendfileLinkAppendContext *lapp_context);
/**
* Perform linking operation on all items added to given `lapp_context`.
*/
void BKE_blendfile_link(BlendfileLinkAppendContext *lapp_context, ReportList *reports);
/**
* Perform packing operation.
*
* The IDs processed by this functions are the one that have been linked by a previous call to
* #BKE_blendfile_link on the same `lapp_context`.
*/
void BKE_blendfile_link_pack(BlendfileLinkAppendContext *lapp_context, ReportList *reports);
/**
* Perform append operation, using modern ID usage looper to detect which ID should be kept
* linked, made local, duplicated as local, re-used from local etc.
*
* The IDs processed by this functions are the one that have been linked by a previous call to
* #BKE_blendfile_link on the same `lapp_context`.
*/
void BKE_blendfile_append(BlendfileLinkAppendContext *lapp_context, ReportList *reports);
/**
* Instantiate loose data in the scene (e.g. add object to the active collection).
*/
void BKE_blendfile_link_append_instantiate_loose(BlendfileLinkAppendContext *lapp_context,
ReportList *reports);
/**
* Finalize the link/append process.
*
* NOTE: Currently only used to call the matching handler..
*/
void BKE_blendfile_link_append_context_finalize(BlendfileLinkAppendContext *lapp_context);
/**
* Options controlling the behavior of liboverrides creation.
*/
enum eBKELibLinkOverride {
BKE_LIBLINK_OVERRIDE_INIT = 0,
/**
* Try to find a matching existing liboverride first, instead of always creating a new one.
*
* \note Takes into account the #BKE_LIBLINK_CREATE_RUNTIME flag too (i.e. only checks for
* runtime liboverrides if that flag is set, and vice-versa).
*/
BKE_LIBLINK_OVERRIDE_USE_EXISTING_LIBOVERRIDES = 1 << 0,
/**
* Create (or return an existing) runtime liboverride, instead of a regular saved-in-blend-files
* one. See also the #ID_TAG_RUNTIME tag of IDs in DNA_ID.h.
*
* \note Typically, usage of this flag implies that no linked IDs are instantiated, such that
* their usages remain indirect.
*/
BKE_LIBLINK_OVERRIDE_CREATE_RUNTIME = 1 << 1,
};
/**
* Create (or find existing) liboverrides from linked data.
*
* The IDs processed by this functions are the one that have been linked by a previous call to
* #BKE_blendfile_link on the same `lapp_context`.
*
* Control over how liboverrides are created is done through the extra #eBKELibLinkOverride flags.
*
* \warning Currently this function only performs very (very!) basic liboverrides, with no handling
* of dependencies or hierarchies. It is not expected to be directly exposed to users in its
* current state, but rather as a helper for specific use-cases like 'presets assets' handling.
*/
void BKE_blendfile_override(BlendfileLinkAppendContext *lapp_context,
const eBKELibLinkOverride flags,
ReportList *reports);
/**
* Try to relocate all linked IDs added to `lapp_context`, belonging to the given `library`.
*
* This function searches for matching IDs (type and name) in all libraries added to the given
* `lapp_context`.
*
* Typical usages include:
* - Relocating a library:
* - Add the new target library path to `lapp_context`.
* - Add all IDs from the library to relocate to `lapp_context`
* - Mark the new target library to be considered for each ID.
* - Call this function.
*
* - Searching for (e.g.missing) linked IDs in a set or sub-set of libraries:
* - Add all potential library sources paths to `lapp_context`.
* - Add all IDs to search for to `lapp_context`.
* - Mark which libraries should be considered for each ID.
* - Call this function.
*
* NOTE: content of `lapp_context` after execution of that function should not be assumed valid
* anymore, and should immediately be freed.
*/
void BKE_blendfile_library_relocate(BlendfileLinkAppendContext *lapp_context,
ReportList *reports,
Library *library,
bool do_reload);
/**
* Relocate a single linked ID.
*
* NOTE: content of `lapp_context` after execution of that function should not be assumed valid
* anymore, and should immediately be freed.
*/
void BKE_blendfile_id_relocate(BlendfileLinkAppendContext &lapp_context, ReportList *reports);
} // namespace blender

View File

@@ -0,0 +1,55 @@
/* SPDX-FileCopyrightText: 2009 by Janne Karhu. All rights reserved.
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
/** \file
* \ingroup bke
*/
namespace blender {
struct BoidSettings;
struct BoidState;
struct Object;
struct ParticleData;
struct ParticleSettings;
struct ParticleSimulationData;
struct RNG;
enum eBoidRuleType : int;
struct BoidBrainData {
struct ParticleSimulationData *sim;
struct ParticleSettings *part;
float timestep, cfra, dfra;
float wanted_co[3], wanted_speed;
/* Goal stuff */
struct Object *goal_ob;
float goal_co[3];
float goal_nor[3];
float goal_priority;
struct RNG *rng;
};
void boids_precalc_rules(struct ParticleSettings *part, float cfra);
/**
* Determines the velocity the boid wants to have.
*/
void boid_brain(BoidBrainData *bbd, int p, struct ParticleData *pa);
/**
* Tries to realize the wanted velocity taking all constraints into account.
*/
void boid_body(BoidBrainData *bbd, struct ParticleData *pa);
void boid_default_settings(struct BoidSettings *boids);
struct BoidRule *boid_new_rule(eBoidRuleType type);
struct BoidState *boid_new_state(struct BoidSettings *boids);
struct BoidState *boid_duplicate_state(struct BoidSettings *boids, struct BoidState *state);
void boid_free_settings(struct BoidSettings *boids);
struct BoidSettings *boid_copy_settings(const struct BoidSettings *boids);
struct BoidState *boid_get_current_state(struct BoidSettings *boids);
} // namespace blender

View File

@@ -0,0 +1,307 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup bke
*
* \warning All paths manipulated by this API are assumed to be either constant char buffers of
* `FILE_MAX` size, or allocated char buffers not bigger than `FILE_MAX`.
*/
/* TODO: Make this module handle a bit more safely string length, instead of assuming buffers are
* FILE_MAX length etc. */
#pragma once
#include <cstddef>
#include "BLI_enum_flags.hh"
#include "BLI_function_ref.hh"
#include "BLI_string_ref.hh"
namespace blender {
struct ID;
struct Main;
struct ReportList;
/** \name Core `foreach_path` API.
* \{ */
/**
* Flags controlling the behavior of the generic BPath API.
*
* Note: these are referred to by `rna_enum_file_path_foreach_flag_items`, so make sure that any
* new enum items are added there too.
*/
enum eBPathForeachFlag {
/**
* Ensures the `absolute_base_path` member of #BPathForeachPathData is initialized properly with
* the path of the current .blend file. This can be used by the callbacks to convert relative
* paths to absolute ones.
*/
BKE_BPATH_FOREACH_PATH_ABSOLUTE = (1 << 0),
/** Skip paths of linked IDs. */
BKE_BPATH_FOREACH_PATH_SKIP_LINKED = (1 << 1),
/** Skip paths when their matching data is packed. */
BKE_BPATH_FOREACH_PATH_SKIP_PACKED = (1 << 2),
/**
* Resolve tokens within a virtual filepath to a single, concrete, filepath. Currently only used
* for UDIM tiles.
*/
BKE_BPATH_FOREACH_PATH_RESOLVE_TOKEN = (1 << 3),
/**
* Skip weak reference paths. Those paths are typically 'nice to have' extra information, but are
* not used as actual source of data by the current .blend file.
*
* NOTE: Currently this only concerns the weak reference to a library file stored in
* `ID::library_weak_reference`.
*/
BKE_BPATH_TRAVERSE_SKIP_WEAK_REFERENCES = (1 << 5),
/**
* Flags not affecting the generic BPath API. Those may be used by specific IDTypeInfo
* `foreach_path` implementations and/or callbacks to implement specific behaviors.
*/
/**
* Skip paths where a single dir is used with an array of files, eg. sequence strip images or
* point-caches. In this case only the first file path is processed.
*
* This is needed for directory manipulation callbacks which might otherwise modify the same
* directory multiple times.
*/
BKE_BPATH_FOREACH_PATH_SKIP_MULTIFILE = (1 << 8),
/**
* Reload data (when the path is edited).
* \note Only used by Image #IDType currently.
*/
BKE_BPATH_FOREACH_PATH_RELOAD_EDITED = (1 << 9),
/**
* Expand template tokens in virtual file paths to all matching concrete paths, invoking the
* callback once per expanded path. Currently only used for UDIM tiles. These paths can not
* be edited.
*/
BKE_BPATH_FOREACH_PATH_EXPAND_TOKENS = (1 << 10),
/**
* Expand image sequences and similar multi-file resources to all individual file paths on disk,
* invoking the callback once per file. These paths can not be edited.
*/
BKE_BPATH_FOREACH_PATH_EXPAND_SEQUENCES = (1 << 11),
/**
* Visit cache files, for example texture cache files associated with images. These paths can
* not be edited.
*/
BKE_BPATH_FOREACH_PATH_EXPAND_CACHES = (1 << 12),
};
ENUM_OPERATORS(eBPathForeachFlag)
struct BPathForeachPathData;
/**
* Callback used to iterate over an ID's file paths.
* \param path_dst: Optionally write to the path (for callbacks that manipulate the path).
* \note When #BKE_BPATH_FOREACH_PATH_ABSOLUTE us used, `path_src` will be absolute and `path_dst`
* can be used to access the original path.
* \param path_dst_maxncpy: The buffer size of `path_dst` including the null byte.
* \warning Actions such as #BLI_path_abs & #BLI_path_rel must not be called directly
* on `path_dst` as they assume #FILE_MAX size which may not be the case.
*
* \return `true` if the path has been changed, and in that case,
* result must be written to `path_dst`.
*/
using BPathForeachPathFunctionCallback = bool (*)(BPathForeachPathData *bpath_data,
char *path_dst,
size_t path_dst_maxncpy,
const char *path_src);
/** Storage for common data needed across the BPath 'foreach_path' code. */
struct BPathForeachPathData {
Main *bmain;
BPathForeachPathFunctionCallback callback_function;
eBPathForeachFlag flag;
void *user_data;
/* 'Private' data, caller don't need to set those. */
/**
* The root to use as base for relative paths.
* Only set if #BKE_BPATH_FOREACH_PATH_ABSOLUTE flag is set, NULL otherwise.
*/
const char *absolute_base_path;
/** ID owning the path being processed. */
ID *owner_id;
/**
* IDTypeInfo callbacks are responsible to set this boolean if they modified one or more paths.
*/
bool is_path_modified;
/**
* Set while visiting a path expanded from a UDIM tile or sequence frame.
* These paths can not be edited.
*/
bool is_expanded;
/**
* Set while visiting a cache file path, like a texture cache file.
* These paths can not be edited.
*/
bool is_cache;
/**
* Set while visiting a read-only path that callbacks can not edit.
*/
bool is_readonly;
};
/** Run `bpath_data.callback_function` on all paths contained in `id`. */
void BKE_bpath_foreach_path_id(BPathForeachPathData *bpath_data, ID *id);
/** Run `bpath_data.callback_function` on all paths of all IDs in `bmain`. */
void BKE_bpath_foreach_path_main(BPathForeachPathData *bpath_data);
/** \} */
/** \name Helpers to handle common cases from `IDTypeInfo`'s `foreach_path` functions.
* \{ */
/* TODO: Investigate using macros around those calls to check a bit better about actual
* strings/buffers length (e,g, with static asserts). */
/**
* Run the callback on a path, replacing the content of the string as needed.
*
* \param path: A fixed, FILE_MAX-sized char buffer.
*
* \return true is \a path was modified, false otherwise.
*/
bool BKE_bpath_foreach_path_fixed_process(BPathForeachPathData *bpath_data,
char *path,
size_t path_maxncpy);
/**
* Run the callback on a read-only path, any edits will be discarded.
*
* \param path: A fixed, FILE_MAX-sized char buffer.
*/
void BKE_bpath_foreach_path_readonly_process(BPathForeachPathData *bpath_data, const char *path);
/**
* Run the callback on every existing file on disk matching a `<head><digits><tail>`
* numbered-sequence pattern derived from `abs_filepath`. If `abs_filepath` itself is not a
* numbered sequence, the callback is invoked once with `abs_filepath` if it exists.
*/
void BKE_bpath_sequence_filepaths_foreach(
const char *abs_filepath,
blender::FunctionRef<void(blender::StringRef frame_filepath)> callback);
/**
* Run the callback on a (directory + file) path, replacing the content of the two strings as
* needed.
*
* \param path_dir: A fixed, FILE_MAXDIR-sized char buffer.
* \param path_file: A fixed, FILE_MAXFILE-sized char buffer.
*
* \return true is \a path_dir and/or \a path_file were modified, false otherwise.
*/
bool BKE_bpath_foreach_path_dirfile_fixed_process(BPathForeachPathData *bpath_data,
char *path_dir,
size_t path_dir_maxncpy,
char *path_file,
size_t path_file_maxncpy);
/**
* Run the callback on a path, replacing the content of the string as needed.
*
* \param path: A pointer to a MEM-allocated string. If modified, it will be freed and replaced by
* a new allocated string.
* \note path is expected to be FILE_MAX size or smaller.
*
* \return true is \a path was modified and re-allocated, false otherwise.
*/
bool BKE_bpath_foreach_path_allocated_process(BPathForeachPathData *bpath_data, char **path);
/** \} */
/** \name High level features.
* \{ */
/** Check for missing files. */
void BKE_bpath_missing_files_check(Main *bmain, ReportList *reports);
/** A summary of operating on many paths. */
struct BPathSummary {
int count_total = 0;
int count_changed = 0;
int count_failed = 0;
};
void BKE_bpath_summary_report(const BPathSummary &summary, ReportList *reports);
/**
* Recursively search into given search directory, for all file paths of all IDs in given
* \a bmain, and replace existing paths as needed.
*
* \note The search will happen into the whole search directory tree recursively (with a limit of
* MAX_DIR_RECURSE), if several files are found matching a searched filename, the biggest one will
* be used. This is so that things like thumbnails don't get selected instead of the actual image
* e.g.
*
* \param searchpath: The root directory in which the new filepaths should be searched for.
* \param find_all: If `true`, also search for files which current path is still valid, if `false`
* skip those still valid paths.
*/
void BKE_bpath_missing_files_find(Main *bmain,
const char *searchpath,
ReportList *reports,
bool find_all);
/** Rebase all relative file paths in given \a bmain from \a basedir_src to \a basedir_dst. */
void BKE_bpath_relative_rebase(Main *bmain,
const char *basedir_src,
const char *basedir_dst,
ReportList *reports,
BPathSummary *r_summary = nullptr);
/** Make all absolute file paths in given \a bmain relative to given \a basedir. */
void BKE_bpath_relative_convert(Main *bmain,
const char *basedir,
ReportList *reports,
BPathSummary *r_summary = nullptr);
/** Make all relative file paths in given \a bmain absolute, using given \a basedir as root. */
void BKE_bpath_absolute_convert(Main *bmain,
const char *basedir,
ReportList *reports,
BPathSummary *r_summary = nullptr);
/**
* Temp backup of paths from all IDs in given \a bmain.
*
* \return An opaque handle to pass to #BKE_bpath_list_restore and #BKE_bpath_list_free.
*/
void *BKE_bpath_list_backup(Main *bmain, eBPathForeachFlag flag);
/**
* Restore the temp backup of paths from \a path_list_handle into all IDs in given \a bmain.
*
* \note This function assumes that the data in given Main did not change (no
* addition/deletion/re-ordering of IDs, or their file paths) since the call to
* #BKE_bpath_list_backup that generated the given \a path_list_handle.
*/
void BKE_bpath_list_restore(Main *bmain, eBPathForeachFlag flag, void *path_list_handle);
/**
* Free the temp backup of paths in \a path_list_handle.
*
* \note This function assumes that the path list has already been restored with a call to
* #BKE_bpath_list_restore, and is therefore empty.
*/
void BKE_bpath_list_free(void *path_list_handle);
/** \} */
} // namespace blender

View File

@@ -0,0 +1,280 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
/** \file
* \ingroup bke
*
* General operations for brushes.
*/
#include <optional>
#include "BLI_math_vector_types.hh"
#include "BLI_span.hh"
#include "DNA_brush_enums.h"
#include "DNA_color_types.h"
#include "DNA_object_enums.h"
#include "DNA_userdef_enums.h"
namespace blender {
enum class PaintMode : int8_t;
struct Brush;
struct ImBuf;
struct ImagePool;
struct Main;
struct MTex;
struct Paint;
struct Scene;
struct UnifiedPaintSettings;
/* Globals for brush execution. */
void BKE_brush_system_init();
void BKE_brush_system_exit();
/* Data-block functions. */
/**
* \note Resulting brush will have two users: one as a fake user,
* another is assumed to be used by the caller.
*/
Brush *BKE_brush_add(Main *bmain, const char *name, eObjectMode ob_mode);
/**
* Delete a Brush.
*/
bool BKE_brush_delete(Main *bmain, Brush *brush);
/**
* Perform deep-copy of a Brush and its 'children' data-blocks.
*
* \param dupflag: Controls which sub-data are also duplicated
* (see #eDupli_ID_Flags in DNA_userdef_types.h).
* \param duplicate_options: Additional context information about current duplicate call (e.g. if
* it's part of a higher-level duplication or not, etc.). (see #eLibIDDuplicateFlags in
* BKE_lib_id.hh).
*
* \warning By default, this functions will clear all \a bmain #ID.idnew pointers
* (#BKE_main_id_newptr_and_tag_clear), and take care of post-duplication updates like remapping to
* new IDs (#BKE_libblock_relink_to_newid).
* If \a #LIB_ID_DUPLICATE_IS_SUBPROCESS duplicate option is passed on (typically when duplication
* is called recursively from another parent duplication operation), the caller is responsible to
* handle all of these operations.
*
* \note Caller MUST handle updates of the depsgraph (#DAG_relations_tag_update).
*/
Brush *BKE_brush_duplicate(Main *bmain,
Brush *brush,
eDupli_ID_Flags dupflag,
/*eLibIDDuplicateFlags*/ uint duplicate_options);
/**
* Add grease pencil settings.
*/
void BKE_brush_init_gpencil_settings(Brush *brush);
void BKE_brush_init_mesh_automasking_settings(Brush *brush);
void BKE_brush_init_curves_sculpt_settings(Brush *brush);
/**
* Tag a linked brush as having changed settings so an indicator can be displayed to the user,
* showing that the brush settings differ from the state of the imported brush asset. Call
* every time a user visible change to the brush is done.
*
* Since this is meant to indicate brushes that are known to differ from the linked source file,
* tagging is only performed for linked brushes. File local brushes are normal data-blocks that get
* saved with the file, and don't need special attention by the user.
*
* For convenience, null may be passed for \a brush.
*/
void BKE_brush_tag_unsaved_changes(Brush *brush);
float2 BKE_brush_jitter_pos(const Paint &paint, const Brush &brush, const float2 &pos);
void BKE_brush_randomize_texture_coords(Paint *paint, bool mask);
/* Brush curve. */
/**
* Library Operations
*/
void BKE_brush_curve_preset(Brush *b, eCurveMappingPreset preset);
namespace bke::brush {
void common_pressure_curves_init(Brush &brush);
}
/**
* Combine the brush strength based on the distances and brush settings with the existing factors.
*/
void BKE_brush_calc_curve_factors(eBrushCurvePreset preset,
const CurveMapping *cumap,
Span<float> distances,
float brush_radius,
MutableSpan<float> factors);
/**
* Uses the brush curve control to find a strength value between 0 and 1.
*/
float BKE_brush_curve_strength_clamped(const Brush *br, float p, float len);
/**
* Uses the brush curve control to find a strength value.
*/
float BKE_brush_curve_strength(eBrushCurvePreset preset,
const CurveMapping *cumap,
float distance,
float brush_radius);
float BKE_brush_curve_strength(const Brush *br, float p, float len);
/* Sampling. */
/**
* Generic texture sampler for 3D painting systems.
* point has to be either in region space mouse coordinates,
* or 3d world coordinates for 3D mapping.
*
* RGBA outputs straight alpha.
*/
float BKE_brush_sample_tex_3d(const Paint *paint,
const Brush *br,
const MTex *mtex,
const float3 &point,
float4 &rgba,
int thread,
ImagePool *pool);
float BKE_brush_sample_masktex(
const Paint *paint, Brush *br, const float2 &point, int thread, ImagePool *pool);
/**
* Get the mask texture for this given object mode.
*
* This is preferred above using mtex/mask_mtex attributes directly as due to legacy these
* attributes got switched in sculpt mode.
*/
const MTex *BKE_brush_mask_texture_get(const Brush *brush, eObjectMode object_mode);
/**
* Get the color texture for this given object mode.
*
* This is preferred above using mtex/mask_mtex attributes directly as due to legacy these
* attributes got switched in sculpt mode.
*/
const MTex *BKE_brush_color_texture_get(const Brush *brush, eObjectMode object_mode);
/**
* Radial control.
*/
ImBuf *BKE_brush_gen_radial_control_imbuf(Brush *br, bool secondary, bool display_gradient);
/* Unified strength size and color. */
struct BrushColorJitterSettings {
int flag;
/** Jitter amounts */
float hue;
float saturation;
float value;
/** Jitter pressure curves. */
CurveMapping *curve_hue_jitter;
CurveMapping *curve_sat_jitter;
CurveMapping *curve_val_jitter;
};
float3 BKE_brush_color_get(const Paint *paint, const Brush *brush);
std::optional<BrushColorJitterSettings> BKE_brush_color_jitter_get_settings(const Paint *paint,
const Brush *brush);
float3 BKE_brush_secondary_color_get(const Paint *paint, const Brush *brush);
void BKE_brush_color_set(Paint *paint, Brush *brush, const float3 &color);
void BKE_brush_color_sync_legacy(Brush *brush);
void BKE_brush_color_sync_legacy(UnifiedPaintSettings *ups);
int BKE_brush_size_get(const Paint *paint, const Brush *brush);
void BKE_brush_size_set(Paint *paint, Brush *brush, int size);
float BKE_brush_radius_get(const Paint *paint, const Brush *brush);
float BKE_brush_unprojected_size_get(const Paint *paint, const Brush *brush);
void BKE_brush_unprojected_size_set(Paint *paint, Brush *brush, float unprojected_size);
float BKE_brush_unprojected_radius_get(const Paint *paint, const Brush *brush);
float BKE_brush_alpha_get(const Paint *paint, const Brush *brush);
void BKE_brush_alpha_set(Paint *paint, Brush *brush, float alpha);
float BKE_brush_weight_get(const Paint *paint, const Brush *brush);
void BKE_brush_weight_set(Paint *paint, Brush *brush, float value);
int BKE_brush_input_samples_get(const Paint *paint, const Brush *brush);
void BKE_brush_input_samples_set(Paint *paint, Brush *brush, int value);
bool BKE_brush_use_locked_size(const Paint *paint, const Brush *brush);
bool BKE_brush_use_alpha_pressure(const Brush *brush);
bool BKE_brush_use_size_pressure(const Brush *brush);
/**
* Scale unprojected radius to reflect a change in the brush's 2D size.
*/
void BKE_brush_scale_unprojected_size(float *unprojected_size,
int new_brush_size,
int old_brush_size);
/**
* Scale brush size to reflect a change in the brush's unprojected radius.
*/
void BKE_brush_scale_size(int *r_brush_size,
float new_unprojected_size,
float old_unprojected_size);
/* Returns true if a brush requires a cube
* (often presented to the user as a square) tip inside a specific paint mode.
*/
bool BKE_brush_has_cube_tip(const Brush *brush, PaintMode paint_mode);
namespace bke::brush {
float normal_weight_get(const Brush &brush, bool invert);
}
/* debugging only */
void BKE_brush_debug_print_state(Brush *br);
/* -------------------------------------------------------------------- */
/** \name Brush Capabilities
* Common boolean checks used during both brush evaluation and in UI drawing
* via BrushCapabilities inside rna_brush.cc.
* \{ */
namespace bke::brush {
bool supports_dyntopo(const Brush &brush);
bool supports_accumulate(const Brush &brush);
bool supports_topology_rake(const Brush &brush);
bool supports_auto_smooth(const Brush &brush);
bool supports_normal_radius(const Brush &brush);
bool supports_hardness(const Brush &brush);
bool supports_height(const Brush &brush);
bool supports_plane_height(const Brush &brush);
bool supports_plane_depth(const Brush &brush);
bool supports_jitter(const Brush &brush);
bool supports_normal_weight(const Brush &brush);
bool supports_rake_factor(const Brush &brush);
bool supports_persistence(const Brush &brush);
bool supports_pinch_factor(const Brush &brush);
bool supports_plane_offset(const Brush &brush);
bool supports_random_texture_angle(const Brush &brush);
bool supports_sculpt_plane(const Brush &brush);
bool supports_color(const Brush &brush);
bool supports_secondary_cursor_color(const Brush &brush);
bool supports_smooth_stroke(const Brush &brush);
bool supports_space_attenuation(const Brush &brush);
bool supports_strength_pressure(const Brush &brush);
bool supports_size_pressure(const Brush &brush);
bool supports_auto_smooth_pressure(const Brush &brush);
bool supports_hardness_pressure(const Brush &brush);
bool supports_inverted_direction(const Brush &brush);
bool supports_gravity(const Brush &brush);
bool supports_tilt(const Brush &brush);
} // namespace bke::brush
/** \} */
} // namespace blender

View File

@@ -0,0 +1,111 @@
/* SPDX-FileCopyrightText: 2006 NaN Holding BV. All rights reserved.
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
/** \file
* \ingroup bke
*
* This header encapsulates necessary code to build a BVH.
*/
#include "BLI_index_mask_fwd.hh"
#include "BLI_kdopbvh.hh"
#include "BLI_math_vector_types.hh"
#include "BLI_offset_indices.hh"
#include "BLI_span.hh"
namespace blender {
struct BVHTree;
struct MFace;
struct Mesh;
struct PointCloud;
namespace bke {
/**
* Struct that stores basic information about a #BVHTree built from a mesh.
*/
struct BVHTreeFromMesh {
const BVHTree *tree = nullptr;
/** Default callbacks to BVH nearest and ray-cast. */
BVHTree_NearestPointCallback nearest_callback;
BVHTree_RayCastCallback raycast_callback;
/* Vertex array, so that callbacks have instant access to data. */
Span<float3> vert_positions;
Span<int2> edges;
Span<int> corner_verts;
Span<int3> corner_tris;
const MFace *face = nullptr;
std::unique_ptr<BVHTree, BVHTreeDeleter> owned_tree;
};
/**
* Builds a BVH-tree where nodes are the given vertices.
*/
BVHTreeFromMesh bvhtree_from_mesh_verts_ex(Span<float3> vert_positions,
const IndexMask &verts_mask);
/**
* Builds a BVH-tree where nodes are the given edges.
*/
BVHTreeFromMesh bvhtree_from_mesh_edges_ex(Span<float3> vert_positions,
Span<int2> edges,
const IndexMask &edges_mask);
/**
* Builds a BVH-tree where nodes are the triangle faces (#Mesh::corner_tris()) of the given mesh.
*/
BVHTreeFromMesh bvhtree_from_mesh_corner_tris_ex(Span<float3> vert_positions,
OffsetIndices<int> faces,
Span<int> corner_verts,
Span<int3> corner_tris,
const IndexMask &faces_mask);
/**
* Build a BVH-tree from the triangles in the mesh that correspond to the faces in the given mask.
*/
BVHTreeFromMesh bvhtree_from_mesh_tris_init(const Mesh &mesh, const IndexMask &faces_mask);
/**
* Build a BVH-tree containing the given edges.
*/
BVHTreeFromMesh bvhtree_from_mesh_edges_init(const Mesh &mesh, const IndexMask &edges_mask);
/**
* Build a BVH-tree containing the given vertices.
*/
BVHTreeFromMesh bvhtree_from_mesh_verts_init(const Mesh &mesh, const IndexMask &verts_mask);
/**
* Math functions used by callbacks
*/
float bvhtree_ray_tri_intersection(
const BVHTreeRay *ray, float m_dist, const float v0[3], const float v1[3], const float v2[3]);
float bvhtree_sphereray_tri_intersection(const BVHTreeRay *ray,
float radius,
float m_dist,
const float v0[3],
const float v1[3],
const float v2[3]);
struct BVHTreeFromPointCloud {
const BVHTree *tree = nullptr;
BVHTree_NearestPointCallback nearest_callback;
Span<float3> positions;
std::unique_ptr<BVHTree, BVHTreeDeleter> owned_tree;
};
BVHTreeFromPointCloud bvhtree_from_pointcloud_get(const PointCloud &pointcloud,
const IndexMask &points_mask);
} // namespace bke
} // namespace blender

View File

@@ -0,0 +1,53 @@
/* SPDX-FileCopyrightText: 2016 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
/** \file
* \ingroup bke
*/
namespace blender {
struct CacheFile;
struct CacheFileLayer;
struct CacheReader;
struct Depsgraph;
struct Main;
struct Object;
struct Scene;
void *BKE_cachefile_add(Main *bmain, const char *name);
void BKE_cachefile_reload(Depsgraph *depsgraph, CacheFile *cache_file);
void BKE_cachefile_eval(Main *bmain, Depsgraph *depsgraph, CacheFile *cache_file);
bool BKE_cachefile_filepath_get(const Main *bmain,
const Depsgraph *depsgraph,
const CacheFile *cache_file,
char r_filepath[/*FILE_MAX*/ 1024]);
double BKE_cachefile_time_offset(const CacheFile *cache_file, double time, double fps);
double BKE_cachefile_frame_offset(const CacheFile *cache_file, double time);
/* Modifiers and constraints open and free readers through these. */
void BKE_cachefile_reader_open(CacheFile *cache_file,
CacheReader **reader,
Object *object,
const char *object_path);
void BKE_cachefile_reader_free(CacheFile *cache_file, CacheReader **reader);
/**
* Add a layer to the cache_file. Return NULL if the `filepath` is already that of an existing
* layer or if the number of layers exceeds the maximum allowed layer count.
*/
CacheFileLayer *BKE_cachefile_add_layer(CacheFile *cache_file,
const char filepath[/*FILE_MAX*/ 1024]);
CacheFileLayer *BKE_cachefile_get_active_layer(CacheFile *cache_file);
void BKE_cachefile_remove_layer(CacheFile *cache_file, CacheFileLayer *layer);
} // namespace blender

View File

@@ -0,0 +1,143 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup bke
*/
#pragma once
namespace blender {
struct Depsgraph;
struct ID;
struct Main;
struct PointerRNA;
/**
* Callbacks for One Off Actions
* =============================
*
* - `{ACTION}` use in cases where only a single callback is required,
* `VERSION_UPDATE` and `RENDER_STATS` for example.
*
* \note avoid single callbacks if there is a chance `PRE/POST` are useful to differentiate
* since renaming callbacks may break Python scripts.
*
* Callbacks for Common Actions
* ============================
*
* - `{ACTION}_PRE` run before the action.
* - `{ACTION}_POST` run after the action.
*
* Optional Additional Callbacks
* -----------------------------
*
* - `{ACTION}_INIT` when the handler may manipulate the context used to run the action.
*
* Examples where `INIT` functions may be useful are:
*
* - When rendering, an `INIT` function may change the camera or render settings,
* things which a `PRE` function can't support as this information has already been used.
* - When saving an `INIT` function could temporarily change the preferences.
*
* - `{ACTION}_POST_FAIL` should be included if the action may fail.
*
* Use this so a call to the `PRE` callback always has a matching call to `POST` or `POST_FAIL`.
*
* \note in most cases only `PRE/POST` are required.
*
* Callbacks for Background/Modal Tasks
* ====================================
*
* - `{ACTION}_INIT`
* - `{ACTION}_COMPLETE` when a background job has finished.
* - `{ACTION}_CANCEL` When a background job is canceled partway through.
*
* While cancellation may be caused by any number of reasons, common causes may include:
*
* - Explicit user cancellation.
* - Exiting Blender.
* - Failure to acquire resources (such as disk-full, out of memory ... etc).
*
* \note `PRE/POST` handlers may be used along side modal task handlers
* as is the case for rendering, where rendering an animation uses modal task handlers,
* rendering a single frame has `PRE/POST` handlers.
*
* Python Access
* =============
*
* All callbacks here must be exposed via the Python module `bpy.app.handlers`,
* see `bpy_app_handlers.cc`.
*/
enum eCbEvent {
BKE_CB_EVT_FRAME_CHANGE_PRE,
BKE_CB_EVT_FRAME_CHANGE_POST,
BKE_CB_EVT_RENDER_PRE,
BKE_CB_EVT_RENDER_POST,
BKE_CB_EVT_RENDER_WRITE,
BKE_CB_EVT_RENDER_STATS,
BKE_CB_EVT_RENDER_INIT,
BKE_CB_EVT_RENDER_COMPLETE,
BKE_CB_EVT_RENDER_CANCEL,
BKE_CB_EVT_LOAD_PRE,
BKE_CB_EVT_LOAD_POST,
BKE_CB_EVT_LOAD_POST_FAIL,
BKE_CB_EVT_SAVE_PRE,
BKE_CB_EVT_SAVE_POST,
BKE_CB_EVT_SAVE_POST_FAIL,
BKE_CB_EVT_UNDO_PRE,
BKE_CB_EVT_UNDO_POST,
BKE_CB_EVT_REDO_PRE,
BKE_CB_EVT_REDO_POST,
BKE_CB_EVT_DEPSGRAPH_UPDATE_PRE,
BKE_CB_EVT_DEPSGRAPH_UPDATE_POST,
BKE_CB_EVT_VERSION_UPDATE,
BKE_CB_EVT_LOAD_FACTORY_USERDEF_POST,
BKE_CB_EVT_LOAD_FACTORY_STARTUP_POST,
BKE_CB_EVT_XR_SESSION_START_PRE,
BKE_CB_EVT_ANNOTATION_PRE,
BKE_CB_EVT_ANNOTATION_POST,
BKE_CB_EVT_OBJECT_BAKE_PRE,
BKE_CB_EVT_OBJECT_BAKE_COMPLETE,
BKE_CB_EVT_OBJECT_BAKE_CANCEL,
BKE_CB_EVT_COMPOSITE_PRE,
BKE_CB_EVT_COMPOSITE_POST,
BKE_CB_EVT_COMPOSITE_CANCEL,
BKE_CB_EVT_ANIMATION_PLAYBACK_PRE,
BKE_CB_EVT_ANIMATION_PLAYBACK_POST,
BKE_CB_EVT_TRANSLATION_UPDATE_POST,
BKE_CB_EVT_EXTENSION_REPOS_UPDATE_PRE,
BKE_CB_EVT_EXTENSION_REPOS_UPDATE_POST,
BKE_CB_EVT_EXTENSION_REPOS_SYNC,
BKE_CB_EVT_EXTENSION_REPOS_FILES_CLEAR,
BKE_CB_EVT_BLENDIMPORT_PRE,
BKE_CB_EVT_BLENDIMPORT_POST,
BKE_CB_EVT_EXIT_PRE,
BKE_CB_EVT_TOT,
};
struct bCallbackFuncStore {
bCallbackFuncStore *next, *prev;
void (*func)(Main *, PointerRNA **, int pointers_num, void *arg);
void *arg;
short alloc;
};
void BKE_callback_exec(Main *bmain, PointerRNA **pointers, int pointers_num, eCbEvent evt);
void BKE_callback_exec_null(Main *bmain, eCbEvent evt);
void BKE_callback_exec_id(Main *bmain, ID *id, eCbEvent evt);
void BKE_callback_exec_id_depsgraph(Main *bmain, ID *id, Depsgraph *depsgraph, eCbEvent evt);
void BKE_callback_exec_boolean(Main *bmain, bool value, eCbEvent evt);
void BKE_callback_exec_string(Main *bmain, const char *str, eCbEvent evt);
void BKE_callback_add(bCallbackFuncStore *funcstore, eCbEvent evt);
void BKE_callback_remove(bCallbackFuncStore *funcstore, eCbEvent evt);
void BKE_callback_global_init();
/**
* Call on application exit.
*/
void BKE_callback_global_finalize();
} // namespace blender

View File

@@ -0,0 +1,185 @@
/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
#include "DNA_vec_types.h"
namespace blender {
/** \file
* \ingroup bke
* \brief Camera data-block and utility functions.
*/
struct Camera;
struct Depsgraph;
struct Main;
struct Object;
struct RegionView3D;
struct RenderData;
struct Scene;
struct View3D;
/* Camera Data-block */
struct Camera *BKE_camera_add(struct Main *bmain, const char *name);
/* Camera Usage */
/**
* Get the camera's DOF value, takes the DOF object into account.
*/
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);
/**
* Camera Parameters:
*
* Intermediate struct for storing camera parameters from various sources,
* to unify computation of view-plane, window matrix, ... etc.
*/
struct CameraParams {
/* lens */
bool is_ortho = false;
float lens = 0.0f;
float ortho_scale = 1.0f;
float zoom = 1.0f;
float shiftx = 0.0f;
float shifty = 0.0f;
float offsetx = 0.0f;
float offsety = 0.0f;
/* sensor */
float sensor_x = 0.0f;
float sensor_y = 0.0f;
int sensor_fit = 0;
/* clipping */
float clip_start = 0.1f;
float clip_end = 100.0f;
/* computed viewplane */
float ycor = 0.0f;
float viewdx = 0.0f;
float viewdy = 0.0f;
rctf viewplane;
/* computed matrix */
float winmat[4][4] = {};
};
/* Values for CameraParams.zoom, need to be taken into account for some operations. */
#define CAMERA_PARAM_ZOOM_INIT_CAMOB 1.0f
#define CAMERA_PARAM_ZOOM_INIT_PERSP 2.0f
void BKE_camera_params_init(CameraParams *params);
void BKE_camera_params_from_object(CameraParams *params, const struct Object *cam_ob);
void BKE_camera_params_from_view3d(CameraParams *params,
const struct Depsgraph *depsgraph,
const struct View3D *v3d,
const struct RegionView3D *rv3d);
void BKE_camera_params_compute_viewplane(
CameraParams *params, int winx, int winy, float aspx, float aspy);
/**
* Crop `viewplane` given the current resolution and a pixel region inside the view plane.
*/
void BKE_camera_params_crop_viewplane(rctf *viewplane, int winx, int winy, const rcti *region);
/**
* View-plane is assumed to be already computed.
*/
void BKE_camera_params_compute_matrix(CameraParams *params);
/* Camera View Frame */
void BKE_camera_view_frame_ex(const struct Scene *scene,
const struct Camera *camera,
float drawsize,
bool do_clip,
const float scale[3],
float r_asp[2],
float r_shift[2],
float *r_drawsize,
float r_vec[4][3]);
void BKE_camera_view_frame(const struct Scene *scene,
const struct Camera *camera,
float r_vec[4][3]);
/**
* \param r_scale: only valid/useful for orthographic cameras.
*
* \note Don't move the camera, just yield the fit location.
*/
bool BKE_camera_view_frame_fit_to_scene(struct Depsgraph *depsgraph,
const struct Scene *scene,
struct Object *camera_ob,
float r_co[3],
float *r_scale,
float *r_clip_start,
float *r_clip_end);
bool BKE_camera_view_frame_fit_to_coords(const struct Depsgraph *depsgraph,
const float (*cos)[3],
int num_cos,
struct Object *camera_ob,
float r_co[3],
float *r_scale);
/* Camera multi-view API */
/**
* Returns the camera to be used for render.
*/
struct Object *BKE_camera_multiview_render(const Main &bmain,
const struct Scene *scene,
struct Object *camera,
const char *viewname);
/**
* The view matrix is used by the viewport drawing, it is basically the inverted model matrix.
*/
void BKE_camera_multiview_view_matrix(const struct RenderData *rd,
const struct Object *camera,
bool is_left,
float r_viewmat[4][4]);
void BKE_camera_multiview_model_matrix(const struct RenderData *rd,
const struct Object *camera,
const char *viewname,
float r_modelmat[4][4]);
void BKE_camera_multiview_model_matrix_scaled(const struct RenderData *rd,
const struct Object *camera,
const char *viewname,
float r_modelmat[4][4]);
void BKE_camera_multiview_window_matrix(const struct RenderData *rd,
const struct Object *camera,
const char *viewname,
float r_winmat[4][4]);
float BKE_camera_multiview_shift_x(const struct RenderData *rd,
const struct Object *camera,
const char *viewname);
void BKE_camera_multiview_params(const struct RenderData *rd,
struct CameraParams *params,
const struct Object *camera,
const char *viewname);
bool BKE_camera_multiview_spherical_stereo(const struct RenderData *rd,
const struct Object *camera);
/* Camera background image API */
struct CameraBGImage *BKE_camera_background_image_new(struct Camera *cam);
/**
* Duplicate a background image, in a ID management compatible way.
*
* \param flag: The usual ID copying flags, see `LIB_ID_CREATE_`/`LIB_ID_COPY_` enums in
* `BKE_lib_id.hh`.
*/
struct CameraBGImage *BKE_camera_background_image_copy(const struct CameraBGImage *bgpic_src,
int flag);
void BKE_camera_background_image_remove(struct Camera *cam, struct CameraBGImage *bgpic);
void BKE_camera_background_image_clear(struct Camera *cam);
} // namespace blender

View File

@@ -0,0 +1,119 @@
/* SPDX-FileCopyrightText: 2012 by Nicholas Bishop. All rights reserved.
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
/** \file
* \ingroup bke
*/
#include "BLI_assert.h"
#include "BLI_math_vector_types.hh"
struct CCGSubSurf;
/* Each CCGElem is CCGSubSurf's representation of a subdivided
* vertex. All CCGElems in a particular CCGSubSurf have the same
* layout, but the layout can vary from one CCGSubSurf to another. For
* this reason, CCGElem is presented as an opaque pointer, and
* elements should always be accompanied by a CCGKey, which provides
* the necessary offsets to access components of a CCGElem.
*/
struct CCGElem;
namespace blender {
struct CCGKey {
int level;
/* number of bytes in each element (one float per layer, plus
* three floats for normals if enabled) */
int elem_size;
/* number of elements along each side of grid */
int grid_size;
/* number of elements in the grid (grid size squared) */
int grid_area;
/* number of bytes in each grid (grid_area * elem_size) */
int grid_bytes;
/* currently always the last three floats, unless normals are
* disabled */
int normal_offset;
/* offset in bytes of mask value; only valid if 'has_mask' is
* true */
int mask_offset;
int has_normals;
int has_mask;
};
inline float3 &CCG_elem_co(const CCGKey & /*key*/, CCGElem *elem)
{
return *reinterpret_cast<float3 *>(elem);
}
inline float3 &CCG_elem_no(const CCGKey &key, CCGElem *elem)
{
BLI_assert(key.has_normals);
return *reinterpret_cast<float3 *>(reinterpret_cast<char *>(elem) + key.normal_offset);
}
inline float &CCG_elem_mask(const CCGKey &key, CCGElem *elem)
{
BLI_assert(key.has_mask);
return *reinterpret_cast<float *>(reinterpret_cast<char *>(elem) + (key.mask_offset));
}
inline CCGElem *CCG_elem_offset(const CCGKey &key, CCGElem *elem, int offset)
{
return reinterpret_cast<CCGElem *>((reinterpret_cast<char *>(elem)) + key.elem_size * offset);
}
inline int CCG_grid_xy_to_index(const int grid_size, const int x, const int y)
{
return y * grid_size + x;
}
inline CCGElem *CCG_grid_elem(const CCGKey &key, CCGElem *elem, int x, int y)
{
// BLI_assert(x < key.grid_size && y < key.grid_size);
return CCG_elem_offset(key, elem, CCG_grid_xy_to_index(key.grid_size, x, y));
}
inline float3 &CCG_grid_elem_co(const CCGKey &key, CCGElem *elem, int x, int y)
{
return CCG_elem_co(key, CCG_grid_elem(key, elem, x, y));
}
inline float3 &CCG_grid_elem_no(const CCGKey &key, CCGElem *elem, int x, int y)
{
return CCG_elem_no(key, CCG_grid_elem(key, elem, x, y));
}
inline float &CCG_grid_elem_mask(const CCGKey &key, CCGElem *elem, int x, int y)
{
return CCG_elem_mask(key, CCG_grid_elem(key, elem, x, y));
}
inline float3 &CCG_elem_offset_co(const CCGKey &key, CCGElem *elem, int offset)
{
return CCG_elem_co(key, CCG_elem_offset(key, elem, offset));
}
inline int CCG_grid_size(const int level)
{
BLI_assert(level > 0);
return (1 << (level - 1)) + 1;
}
inline int CCG_grid_factor(int low_level, int high_level)
{
BLI_assert(low_level > 0 && high_level > 0);
BLI_assert(low_level <= high_level);
return 1 << (high_level - low_level);
}
} // namespace blender

View File

@@ -0,0 +1,247 @@
/* SPDX-FileCopyrightText: Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
/** \file
* \ingroup bke
*/
#include "BLI_math_vector_types.hh"
#include "BLI_ordered_edge.hh"
#include "BLI_set.hh"
#include <cfloat>
namespace blender {
struct BVHTree;
struct ClothVertex;
struct ClothModifierData;
struct CollisionModifierData;
struct Depsgraph;
struct Implicit_Data;
struct LinkNode;
struct Mesh;
struct Object;
struct Scene;
#define DO_INLINE MALWAYS_INLINE
/* Goal defines. */
#define SOFTGOALSNAP 0.999f
/* This is approximately the smallest number that can be
* represented by a float, given its precision. */
#define ALMOST_ZERO FLT_EPSILON
/* Bits to or into the #ClothVertex.flags. */
enum eClothVertexFlag {
CLOTH_VERT_FLAG_PINNED = (1 << 0),
CLOTH_VERT_FLAG_NOSELFCOLL = (1 << 1), /* vertex NOT used for self collisions */
CLOTH_VERT_FLAG_NOOBJCOLL = (1 << 2), /* vertex NOT used for object collisions */
};
struct ClothHairData {
float loc[3];
float rot[3][3];
float rest_target[3]; /* rest target direction for each segment */
float radius;
float bending_stiffness;
};
struct ClothSolverResult {
int status;
int max_iterations, min_iterations;
float avg_iterations;
float max_error, min_error, avg_error;
};
/**
* This structure describes a cloth object against which the
* simulation can run.
*
* The m and n members of this structure represent the assumed
* rectangular ordered grid for which the original paper is written.
* At some point they need to disappear and we need to determine our
* own connectivity of the mesh based on the actual edges in the mesh.
*/
struct Cloth {
ClothVertex *verts; /* The vertices that represent this cloth. */
LinkNode *springs; /* The springs connecting the mesh. */
unsigned int numsprings; /* The count of springs. */
unsigned int mvert_num; /* The number of verts == m * n. */
unsigned int primitive_num; /* Number of triangles for cloth and edges for hair. */
unsigned char old_solver_type; /* unused, only 1 solver here */
unsigned char pad2;
short pad3;
BVHTree *bvhtree; /* collision tree for this cloth object */
BVHTree *bvhselftree; /* Collision tree for this cloth object (may be same as BVH-tree). */
int3 *vert_tris;
Implicit_Data *implicit; /* our implicit solver connects to this pointer */
Set<OrderedEdge> edgeset; /* Used for self-collisions. */
int last_frame;
float initial_mesh_volume; /* Initial volume of the mesh. Used for pressure */
float average_acceleration[3]; /* Moving average of overall acceleration. */
const int2 *edges; /* Used for hair collisions. */
Set<OrderedEdge> sew_edge_graph; /* Sewing edges. */
};
/**
* The definition of a cloth vertex.
*/
struct ClothVertex {
int flags; /* General flags per vertex. */
float v[3]; /* The velocity of the point. */
float xconst[3]; /* constrained position */
float x[3]; /* The current position of this vertex. */
float xold[3]; /* The previous position of this vertex. */
float tx[3]; /* temporary position */
float txold[3]; /* temporary old position */
float tv[3]; /* temporary "velocity", mostly used as tv = tx-txold */
float mass; /* mass / weight of the vertex */
float goal; /* goal, from SB */
float impulse[3]; /* used in collision.cc */
float xrest[3]; /* rest position of the vertex */
float dcvel[3]; /* delta velocities to be applied by collision response */
unsigned int impulse_count; /* same as above */
float avg_spring_len; /* average length of connected springs */
float struct_stiff;
float bend_stiff;
float shear_stiff;
int spring_count; /* how many springs attached? */
float shrink_factor; /* how much to shrink this cloth */
float internal_stiff; /* internal spring stiffness scaling */
float pressure_factor; /* how much pressure should affect this vertex */
};
/**
* The definition of a spring.
*/
struct ClothSpring {
int ij; /* `Pij` from the paper, one end of the spring. */
int kl; /* `Pkl` from the paper, one end of the spring. */
int mn; /* For hair springs: third vertex index; For bending springs: edge index. */
int *pa; /* Array of vert indices for poly a (for bending springs). */
int *pb; /* Array of vert indices for poly b (for bending springs). */
int la; /* Length of `*pa`. */
int lb; /* Length of `*pb`. */
float restlen; /* The original length of the spring. */
float restang; /* The original angle of the bending springs. */
int type; /* Types defined in BKE_cloth.hh ("springType"). */
int flags; /* Defined in BKE_cloth.hh, e.g. deactivated due to tearing. */
float lin_stiffness; /* Linear stiffness factor from the vertex groups. */
float ang_stiffness; /* Angular stiffness factor from the vertex groups. */
float editrestlen;
/* angular bending spring target and derivatives */
float target[3];
};
/* Some macro enhancements for vector treatment. */
#define VECSUBADDSS(v1, v2, aS, v3, bS) \
{ \
*(v1) -= *(v2) * aS + *(v3) * bS; \
*(v1 + 1) -= *(v2 + 1) * aS + *(v3 + 1) * bS; \
*(v1 + 2) -= *(v2 + 2) * aS + *(v3 + 2) * bS; \
} \
((void)0)
#define VECADDSS(v1, v2, aS, v3, bS) \
{ \
*(v1) = *(v2) * aS + *(v3) * bS; \
*(v1 + 1) = *(v2 + 1) * aS + *(v3 + 1) * bS; \
*(v1 + 2) = *(v2 + 2) * aS + *(v3 + 2) * bS; \
} \
((void)0)
#define VECADDS(v1, v2, v3, bS) \
{ \
*(v1) = *(v2) + *(v3) * bS; \
*(v1 + 1) = *(v2 + 1) + *(v3 + 1) * bS; \
*(v1 + 2) = *(v2 + 2) + *(v3 + 2) * bS; \
} \
((void)0)
#define VECSUBMUL(v1, v2, aS) \
{ \
*(v1) -= *(v2) * aS; \
*(v1 + 1) -= *(v2 + 1) * aS; \
*(v1 + 2) -= *(v2 + 2) * aS; \
} \
((void)0)
#define VECSUBS(v1, v2, v3, bS) \
{ \
*(v1) = *(v2) - *(v3) * bS; \
*(v1 + 1) = *(v2 + 1) - *(v3 + 1) * bS; \
*(v1 + 2) = *(v2 + 2) - *(v3 + 2) * bS; \
} \
((void)0)
#define VECADDMUL(v1, v2, aS) \
{ \
*(v1) += *(v2) * aS; \
*(v1 + 1) += *(v2 + 1) * aS; \
*(v1 + 2) += *(v2 + 2) * aS; \
} \
((void)0)
/* Spring types as defined in the paper. */
enum CLOTH_SPRING_TYPES {
CLOTH_SPRING_TYPE_STRUCTURAL = (1 << 1),
CLOTH_SPRING_TYPE_SHEAR = (1 << 2),
CLOTH_SPRING_TYPE_BENDING = (1 << 3),
CLOTH_SPRING_TYPE_GOAL = (1 << 4),
CLOTH_SPRING_TYPE_SEWING = (1 << 5),
CLOTH_SPRING_TYPE_BENDING_HAIR = (1 << 6),
CLOTH_SPRING_TYPE_INTERNAL = (1 << 7),
};
/* SPRING FLAGS */
enum CLOTH_SPRINGS_FLAGS {
CLOTH_SPRING_FLAG_DEACTIVATE = (1 << 1),
CLOTH_SPRING_FLAG_NEEDED = (1 << 2), /* Springs has values to be applied. */
};
/* -------------------------------------------------------------------- */
/* collision.cc */
struct CollPair;
struct ColliderContacts {
Object *ob;
CollisionModifierData *collmd;
CollPair *collisions;
int totcollisions;
};
/* needed for implicit.c */
int cloth_bvh_collision(
Depsgraph *depsgraph, Object *ob, ClothModifierData *clmd, float step, float dt);
/* -------------------------------------------------------------------- */
/* cloth.cc */
/* Needed for modifier.cc */
/** Frees all. */
void cloth_free_modifier_extern(ClothModifierData *clmd);
/** Frees all. */
void cloth_free_modifier(ClothModifierData *clmd);
void clothModifier_do(ClothModifierData *clmd,
Depsgraph *depsgraph,
Scene *scene,
Object *ob,
const Mesh *mesh,
float (*vertexCos)[3]);
int cloth_uses_vgroup(ClothModifierData *clmd);
/* Needed for collision.cc */
void bvhtree_update_from_cloth(ClothModifierData *clmd, bool moving, bool self);
/* Needed for button_object.c */
void cloth_clear_cache(Object *ob, ClothModifierData *clmd, float framenr);
void cloth_parallel_transport_hair_frame(float mat[3][3],
const float dir_old[3],
const float dir_new[3]);
} // namespace blender

View File

@@ -0,0 +1,554 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
/** \file
* \ingroup bke
*/
#include <string>
#include "BLI_iterator.h"
#include "BLI_map.hh"
#include "BLI_set.hh"
#include "BLI_sys_types.h"
#include "DNA_collection_types.h"
#include "DNA_listBase.h"
#include "DNA_userdef_enums.h"
namespace blender {
/* Structs */
struct BLI_Iterator;
struct Base;
struct BlendDataReader;
struct BlendWriter;
struct Collection;
struct ID;
struct CollectionChild;
struct CollectionImport;
struct CollectionExport;
struct Main;
struct Object;
struct Scene;
struct ViewLayer;
/** #CollectionRuntime.tag */
enum {
/**
* That code (#BKE_main_collections_parent_relations_rebuild and the like)
* is called from very low-level places, like e.g ID remapping...
* Using a generic tag like #ID_TAG_DOIT for this is just impossible, we need our very own.
*/
COLLECTION_TAG_RELATION_REBUILD = (1 << 0),
/**
* Mark the `gobject` list and/or its `runtime.gobject_hash` mapping as dirty, i.e. that their
* data is not reliable and should be cleaned-up or updated.
*
* This should typically only be set by ID remapping code.
*/
COLLECTION_TAG_COLLECTION_OBJECT_DIRTY = (1 << 1),
};
using CollectionObjectMap = Map<const Object *, CollectionObject *>;
struct CollectionParent {
struct CollectionParent *next, *prev;
struct Collection *collection;
};
namespace bke {
struct CollectionRuntime {
/**
* Cache of objects in this collection and all its children.
* This is created on demand when e.g. some physics simulation needs it,
* we don't want to have it for every collections due to memory usage reasons.
*/
ListBaseT<Base> object_cache = {};
/** Need this for line art sub-collection selections. */
ListBaseT<Base> object_cache_instanced = {};
/** List of collections that are a parent of this data-block. */
ListBaseT<CollectionParent> parents = {};
/** An optional map for faster lookups on #Collection.gobject */
CollectionObjectMap *gobject_hash = nullptr;
uint8_t tag = 0;
};
} // namespace bke
/* Collections */
/**
* Add a collection to a collection ListBaseT and synchronize all render layers
* The ListBaseT is NULL when the collection is to be added to the master collection
*/
Collection *BKE_collection_add(Main *bmain,
Collection *collection_parent,
const char *name_custom);
/**
* Add \a collection_dst to all scene collections that reference object \a ob_src is in.
* Used to replace an instance object with a collection (library override operator).
*
* Logic is very similar to #BKE_collection_object_add_from().
*/
void BKE_collection_add_from_object(Main *bmain,
Scene *scene,
const Object *ob_src,
Collection *collection_dst);
/**
* Add \a collection_dst to all scene collections that reference collection \a collection_src is
* in.
*
* Logic is very similar to #BKE_collection_object_add_from().
*/
void BKE_collection_add_from_collection(Main *bmain,
Scene *scene,
Collection *collection_src,
Collection *collection_dst);
/**
* Free (or release) any data used by this collection (does not free the collection itself).
*/
void BKE_collection_free_data(Collection *collection);
/**
* Can the collection contents be modified. Returns an optional reason if the content is not
* editable.
*/
bool BKE_collection_is_content_editable(const Collection *collection,
std::string *reason = nullptr);
/**
* Add a new collection importer to the collection.
*/
CollectionImport *BKE_collection_importer_add(Collection *collection, const char *idname);
/**
* Add a new collection exporter to the collection.
*/
CollectionExport *BKE_collection_exporter_add(Collection *collection, char *idname, char *label);
/**
* Remove a collection exporter from the collection.
*/
void BKE_collection_exporter_remove(Collection *collection, CollectionExport *data);
/**
* Move a collection exporter from one position to another.
*/
bool BKE_collection_exporter_move(Collection *collection, const int from, const int to);
/**
* Assigns a unique name to the collection exporter.
*/
void BKE_collection_exporter_name_set(const ListBaseT<CollectionExport> *exporters,
CollectionExport *data,
const char *newname);
/**
* Free all data owned by the collection importers/exporters.
*/
void BKE_collection_importer_free_data(CollectionImport *data);
void BKE_collection_exporter_free_data(CollectionExport *data);
/**
* Remove a collection, optionally removing its child objects or moving
* them to parent collections.
*/
bool BKE_collection_delete(Main *bmain, Collection *collection, bool hierarchy);
/**
* Make a deep copy (aka duplicate) of the given collection and all of its children, recursively.
*
* \param duplicate_flags: Controls which sub-data are also duplicated
* (see #eDupli_ID_Flags in DNA_userdef_types.h).
* \param duplicate_options: Additional context information about current duplicate call (e.g. if
* it's part of a higher-level duplication or not, etc.). (see #eLibIDDuplicateFlags in
* BKE_lib_id.hh).
*
* \warning By default, this functions will clear all \a bmain #ID.idnew pointers
* (#BKE_main_id_newptr_and_tag_clear), and take care of post-duplication updates like remapping to
* new IDs (#BKE_libblock_relink_to_newid) and rebuilding of the collection hierarchy information
* (#BKE_main_collection_sync).
* If \a #LIB_ID_DUPLICATE_IS_SUBPROCESS duplicate option is passed on (typically when duplication
* is called recursively from another parent duplication operation), the caller is responsible to
* handle all of these operations.
*
* \note Caller MUST handle updates of the depsgraph (#DAG_relations_tag_update).
*/
Collection *BKE_collection_duplicate(Main *bmain,
Collection *parent,
CollectionChild *child_old,
Collection *collection,
eDupli_ID_Flags duplicate_flags,
/*eLibIDDuplicateFlags*/ uint duplicate_options);
/* Master Collection for Scene */
#define BKE_SCENE_COLLECTION_NAME "Scene Collection"
Collection *BKE_collection_master_add(Scene *scene);
/**
* Check if the collection contains any geometry that can be rendered. Otherwise there's nothing to
* display in the preview, so don't generate one.
* Objects and sub-collections hidden in the render will be skipped.
*/
bool BKE_collection_contains_geometry_recursive(const Collection *collection);
/* Collection Objects */
bool BKE_collection_has_object(Collection *collection, const Object *ob);
bool BKE_collection_has_object_recursive(Collection *collection, Object *ob);
bool BKE_collection_has_object_recursive_instanced(Collection *collection, Object *ob);
/**
* Find whether an evaluated object's original ID is contained or instanced by any object in this
* collection. The collection is expected to be an evaluated data-block too.
*/
bool BKE_collection_has_object_recursive_instanced_orig_id(Collection *collection_eval,
Object *object_eval);
Collection *BKE_collection_object_find(Main *bmain,
Scene *scene,
Collection *collection,
Object *ob);
CollectionChild *BKE_collection_child_find(Collection *parent, Collection *collection);
bool BKE_collection_is_empty(const Collection *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(Main *bmain, Collection *collection, Object *ob);
/**
* Add object to given collection, similar to #BKE_collection_object_add.
*
* However, it additionally ensures that the selected collection is also part of the given
* `view_layer`, if non-NULL. Otherwise, the object is not added to any collection.
*/
bool BKE_collection_viewlayer_object_add(Main *bmain,
const ViewLayer *view_layer,
Collection *collection,
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(Main *bmain, Collection *collection, Object *ob);
/**
* Add \a ob_dst to all scene collections that reference object \a ob_src is in.
* Used for copying objects.
*
* Logic is very similar to #BKE_collection_add_from_object()
*/
void BKE_collection_object_add_from(Main *bmain, Scene *scene, Object *ob_src, Object *ob_dst);
/**
* Remove ob from collection.
*/
bool BKE_collection_object_remove(Main *bmain, Collection *collection, Object *ob, bool free_us);
/**
* Replace one object with another in a collection (managing user counts).
*/
bool BKE_collection_object_replace(Main *bmain,
Collection *collection,
Object *ob_old,
Object *ob_new);
/**
* Move object from a collection into another
*
* If source collection is NULL move it from all the existing collections.
*/
void BKE_collection_object_move(
Main *bmain, Scene *scene, Collection *collection_dst, Collection *collection_src, Object *ob);
/**
* Remove object from all collections of scene
*/
bool BKE_scene_collections_object_remove(Main *bmain, Scene *scene, Object *ob, bool free_us);
/**
* Check all collections in \a bmain (including embedded ones in scenes) for invalid
* CollectionObject (either with NULL object pointer, or duplicates), and remove them.
*
* \note In case of duplicates, the first CollectionObject in the list is kept, all others are
* removed.
*/
void BKE_collections_object_remove_invalids(Main *bmain);
/**
* Remove all NULL children from parent collections of changed \a collection.
* This is used for library remapping, where these pointers have been set to NULL.
* Otherwise this should never happen.
*
* \note caller must ensure #BKE_main_collection_sync_remap() is called afterwards!
*
* \param parent_collection: The collection owning the pointers that were remapped. May be \a NULL,
* in which case whole \a bmain database of collections is checked.
* \param child_collection: The collection that was remapped to another pointer. May be \a NULL,
* in which case whole \a bmain database of collections is checked.
*/
void BKE_collections_child_remove_nulls(Main *bmain,
Collection *parent_collection,
Collection *child_collection);
/* Dependencies. */
bool BKE_collection_is_in_scene(Collection *collection);
void BKE_collections_after_lib_link(Main *bmain);
bool BKE_collection_object_cyclic_check(Main *bmain, Object *object, Collection *collection);
/* Object list cache. */
ListBaseT<Base> BKE_collection_object_cache_get(Collection *collection);
ListBaseT<Base> BKE_collection_object_cache_instanced_get(Collection *collection);
/**
* Free the object cache of given `collection` and all of its ancestors (recursively).
*
* \param bmain: The Main database owning the collection. May be `nullptr`, only used if doing
* depsgraph tagging.
* \param id_create_flag: Flags controlling ID creation, used here to enable or
* not depsgraph tagging of affected IDs
* (e.g. #LIB_ID_CREATE_NO_DEG_TAG would prevent depsgraph tagging).
*/
void BKE_collection_object_cache_free(const Main *bmain,
Collection *collection,
const int id_create_flag);
/**
* Free the object cache of all collections in given `bmain`, including master collections of
* scenes.
*/
void BKE_main_collections_object_cache_free(const Main *bmain);
Base *BKE_collection_or_layer_objects(const Main &bmain,
const Scene *scene,
ViewLayer *view_layer,
Collection *collection);
/* Editing. */
/**
* Return Scene Collection for a given session_uid.
*/
Collection *BKE_collection_from_session_uid(Scene *scene, uint64_t session_uid);
/**
* Return Collection for a given session_uid and its owner Scene.
*/
Collection *BKE_collection_from_session_uid(Main *bmain,
uint64_t session_uid,
Scene **r_scene = nullptr);
/**
* The automatic/fallback name of a new collection.
*/
void BKE_collection_new_name_get(Collection *collection_parent,
char r_name[/*MAX_ID_NAME - 2*/ 256]);
/**
* The name to show in the interface.
*/
const char *BKE_collection_ui_name_get(Collection *collection);
/* Collection children */
bool BKE_collection_child_add(Main *bmain, Collection *parent, Collection *child);
bool BKE_collection_child_add_no_sync(Main *bmain, Collection *parent, Collection *child);
bool BKE_collection_child_remove(Main *bmain, Collection *parent, Collection *child);
bool BKE_collection_move(Main *bmain,
Collection *to_parent,
Collection *from_parent,
Collection *relative,
bool relative_after,
Collection *collection);
/**
* Find potential cycles in collections.
*
* \param new_ancestor: the potential new owner of given \a collection,
* or the collection to check if the later is NULL.
* \param collection: the collection we want to add to \a new_ancestor,
* may be NULL if we just want to ensure \a new_ancestor does not already have cycles.
* \return true if a cycle is found.
*/
bool BKE_collection_cycle_find(Collection *new_ancestor, Collection *collection);
/**
* Find and fix potential cycles in collections.
*
* \param collection: The collection to check for existing cycles.
* \return true if cycles are found and fixed.
*/
bool BKE_collection_cycles_fix(Main *bmain, Collection *collection);
bool BKE_collection_has_collection(const Collection *parent, const Collection *collection);
/**
* Return parent collection which is not linked.
*/
Collection *BKE_collection_parent_editable_find_recursive(const ViewLayer *view_layer,
Collection *collection);
/**
* Rebuild parent relationships from child ones, for all children of given \a collection.
*
* \note Given collection is assumed to already have valid parents.
*/
void BKE_collection_parent_relations_rebuild(Collection *collection);
/**
* Rebuild parent relationships from child ones, for all collections in given \a bmain.
*/
void BKE_main_collections_parent_relations_rebuild(Main *bmain);
/**
* Perform some validation on integrity of the data of this collection.
*
* \return `true` if everything is OK, false if some errors are detected. */
bool BKE_collection_validate(Collection *collection);
/* .blend file I/O */
/**
* Perform some pre-writing cleanup on the COllection data itself (_not_ in any sub-data
* referenced by pointers). To be called before writing the Collection struct itself.
*/
void BKE_collection_blend_write_prepare_nolib(BlendWriter *writer, Collection *collection);
void BKE_collection_blend_write_nolib(BlendWriter *writer, Collection *collection);
void BKE_collection_blend_read_data(BlendDataReader *reader, Collection *collection, ID *owner_id);
/* Iteration callbacks. */
using BKE_scene_objects_Cb = void (*)(Object *ob, void *data);
using BKE_scene_collections_Cb = void (*)(Collection *ob, void *data);
/* Iteration over objects in collection. */
#define FOREACH_COLLECTION_VISIBLE_OBJECT_RECURSIVE_BEGIN(_collection, _object, _mode) \
{ \
int _base_flag = (_mode == DAG_EVAL_VIEWPORT) ? BASE_ENABLED_VIEWPORT : BASE_ENABLED_RENDER; \
int _object_visibility_flag = (_mode == DAG_EVAL_VIEWPORT) ? OB_HIDE_VIEWPORT : \
OB_HIDE_RENDER; \
[[maybe_unused]] int _base_id = 0; \
for (Base *_base = static_cast<Base *>(BKE_collection_object_cache_get(_collection).first); \
_base; \
_base = _base->next, _base_id++) \
{ \
Object *_object = _base->object; \
if ((_base->flag & _base_flag) && \
(_object->visibility_flag & _object_visibility_flag) == 0) {
#define FOREACH_COLLECTION_VISIBLE_OBJECT_RECURSIVE_END \
} \
} \
} \
((void)0)
#define FOREACH_COLLECTION_OBJECT_RECURSIVE_BEGIN(_collection, _object) \
for (Base *_base = static_cast<Base *>(BKE_collection_object_cache_get(_collection).first); \
_base; \
_base = _base->next) \
{ \
Object *_object = _base->object; \
BLI_assert(_object != NULL);
#define FOREACH_COLLECTION_OBJECT_RECURSIVE_END \
} \
((void)0)
/* Iteration over collections in scene. */
/**
* Only use this in non-performance critical situations
* (it iterates over all scene collections twice)
*/
void BKE_scene_collections_iterator_begin(BLI_Iterator *iter, void *data_in);
void BKE_scene_collections_iterator_next(BLI_Iterator *iter);
void BKE_scene_collections_iterator_end(BLI_Iterator *iter);
void BKE_scene_objects_iterator_begin(BLI_Iterator *iter, void *data_in);
void BKE_scene_objects_iterator_next(BLI_Iterator *iter);
void BKE_scene_objects_iterator_end(BLI_Iterator *iter);
/**
* Iterate over objects in the scene based on a flag.
*
* \note The object->flag is tested against flag.
*/
struct SceneObjectsIteratorExData {
Main *bmain;
Scene *scene;
int flag;
void *iter_data;
};
void BKE_scene_objects_iterator_begin_ex(BLI_Iterator *iter, void *data_in);
void BKE_scene_objects_iterator_next_ex(BLI_Iterator *iter);
void BKE_scene_objects_iterator_end_ex(BLI_Iterator *iter);
/**
* Generate a new #Set (or extend given `objects_set` if not NULL) with all objects referenced by
* all collections of given `scene`.
*
* \note This will include objects without a base currently
* (because they would belong to excluded collections only e.g.).
*/
Set<Object *> *BKE_scene_objects_as_set(Scene *scene, Set<Object *> *objects_set);
#define FOREACH_SCENE_COLLECTION_BEGIN(scene, _instance) \
ITER_BEGIN (BKE_scene_collections_iterator_begin, \
BKE_scene_collections_iterator_next, \
BKE_scene_collections_iterator_end, \
scene, \
Collection *, \
_instance)
#define FOREACH_SCENE_COLLECTION_END ITER_END
#define FOREACH_COLLECTION_BEGIN(_bmain, _scene, Type, _instance) \
{ \
Type _instance; \
Collection *_instance_next; \
bool is_scene_collection = (_scene) != NULL; \
\
if (_scene) { \
_instance_next = (_scene)->master_collection; \
} \
else { \
_instance_next = static_cast<Collection *>((_bmain)->collections.first); \
} \
\
while ((_instance = _instance_next)) { \
if (is_scene_collection) { \
_instance_next = static_cast<Collection *>((_bmain)->collections.first); \
is_scene_collection = false; \
} \
else { \
_instance_next = static_cast<Collection *>(_instance->id.next); \
}
#define FOREACH_COLLECTION_END \
} \
} \
((void)0)
#define FOREACH_SCENE_OBJECT_BEGIN(scene, _instance) \
ITER_BEGIN (BKE_scene_objects_iterator_begin, \
BKE_scene_objects_iterator_next, \
BKE_scene_objects_iterator_end, \
scene, \
Object *, \
_instance)
#define FOREACH_SCENE_OBJECT_END ITER_END
} // namespace blender

View File

@@ -0,0 +1,179 @@
/* SPDX-FileCopyrightText: Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
/** \file
* \ingroup bke
*/
#include "BLI_math_vector_types.hh"
#include "DNA_listBase.h"
namespace blender {
struct BVHTree;
struct Collection;
struct CollisionModifierData;
struct Depsgraph;
struct ListBase;
struct Object;
/* -------------------------------------------------------------------- */
/** \name Collision Data Types
*
* Used for collisions in `collision.cc`.
* \{ */
/* COLLISION FLAGS */
enum COLLISION_FLAGS {
COLLISION_IN_FUTURE = (1 << 1),
#ifdef WITH_ELTOPO
COLLISION_USE_COLLFACE = (1 << 2),
COLLISION_IS_EDGES = (1 << 3),
#endif
COLLISION_INACTIVE = (1 << 4),
};
struct CollPair {
unsigned int face1; /* cloth face */
unsigned int face2; /* object face */
float distance;
float normal[3];
float vector[3]; /* unnormalized collision vector: p2-p1 */
float pa[3], pb[3]; /* collision point p1 on face1, p2 on face2 */
int flag;
float time; /* collision time, from 0 up to 1 */
/* mesh-mesh collision */
#ifdef WITH_ELTOPO /* Either ap* or bp* can be set, but not both. */
float bary[3];
int ap1, ap2, ap3, collp, bp1, bp2, bp3;
int collface;
#else
int ap1, ap2, ap3, bp1, bp2, bp3;
#endif
/* Barycentric weights of the collision point. */
float aw1, aw2, aw3, bw1, bw2, bw3;
int pointsb[4];
};
struct EdgeCollPair {
unsigned int p11, p12, p21, p22;
float normal[3];
float vector[3];
float time;
int lastsign;
float pa[3], pb[3]; /* collision point p1 on face1, p2 on face2 */
};
struct FaceCollPair {
unsigned int p11, p12, p13, p21;
float normal[3];
float vector[3];
float time;
int lastsign;
float pa[3], pb[3]; /* collision point p1 on face1, p2 on face2 */
};
/** \} */
/* Forward declarations. */
/* -------------------------------------------------------------------- */
/** \name BVH Tree Utilities
*
* Used in `modifier.cc` from `collision.cc`.
* \{ */
struct BVHTree *bvhtree_build_from_mvert(const float (*positions)[3],
const int3 *vert_tris,
int tri_num,
float epsilon);
void bvhtree_update_from_mvert(struct BVHTree *bvhtree,
const float (*positions)[3],
const float (*positions_moving)[3],
const int3 *vert_tris,
int tri_num,
bool moving);
/** \} */
/* -------------------------------------------------------------------- */
/** \name Collision Modifier Data
* \{ */
/**
* Move Collision modifier object inter-frame with step = [0,1]
*
* \param step: is limited from 0 (frame start position) to 1 (frame end position).
*/
void collision_move_object(struct CollisionModifierData *collmd,
float step,
float prevstep,
bool moving_bvh);
void collision_get_collider_velocity(float vel_old[3],
float vel_new[3],
struct CollisionModifierData *collmd,
struct CollPair *collpair);
/** \} */
/* -------------------------------------------------------------------- */
/** \name Collision Relations
*
* Collision relations for dependency graph build.
* \{ */
struct CollisionRelation {
struct CollisionRelation *next, *prev;
struct Object *ob;
};
/**
* Create list of collision relations in the collection or entire scene.
* This is used by the depsgraph to build relations, as well as faster
* lookup of colliders during evaluation.
*/
ListBaseT<CollisionRelation> *BKE_collision_relations_create(struct Depsgraph *depsgraph,
struct Collection *collection,
unsigned int modifier_type);
void BKE_collision_relations_free(ListBaseT<CollisionRelation> *relations);
/* Collision object lists for physics simulation evaluation. */
/**
* Create effective list of colliders from relations built beforehand.
* Self will be excluded.
*/
struct Object **BKE_collision_objects_create(struct Depsgraph *depsgraph,
struct Object *self,
struct Collection *collection,
unsigned int *numcollobj,
unsigned int modifier_type);
void BKE_collision_objects_free(struct Object **objects);
/** \} */
/* -------------------------------------------------------------------- */
/** \name Collision Cache
* \{ */
struct ColliderCache {
struct ColliderCache *next, *prev;
struct Object *ob;
struct CollisionModifierData *collmd;
};
/**
* Create effective list of colliders from relations built beforehand.
* Self will be excluded.
*/
ListBaseT<ColliderCache> *BKE_collider_cache_create(struct Depsgraph *depsgraph,
struct Object *self,
struct Collection *collection);
void BKE_collider_cache_free(ListBaseT<ColliderCache> **colliders);
/** \} */
} // namespace blender

View File

@@ -0,0 +1,33 @@
/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
/** \file
* \ingroup bke
*/
namespace blender {
struct CBData;
struct ColorBand;
struct IDTypeForeachColorFunctionCallback;
/** #ColorBand.data length. */
#define MAXCOLORBAND 32
void BKE_colorband_init(ColorBand *coba, bool rangetype);
void BKE_colorband_init_from_table_rgba(ColorBand *coba,
const float (*array)[4],
int array_len,
bool filter_samples);
ColorBand *BKE_colorband_add(bool rangetype);
bool BKE_colorband_evaluate(const ColorBand *coba, float in, float out[4]);
void BKE_colorband_evaluate_table_rgba(const ColorBand *coba, float **array, int *size);
CBData *BKE_colorband_element_add(ColorBand *coba, float position);
bool BKE_colorband_element_remove(ColorBand *coba, int index);
void BKE_colorband_update_sort(ColorBand *coba);
void BKE_colorband_foreach_working_space_color(ColorBand *coba,
const IDTypeForeachColorFunctionCallback &fn);
} // namespace blender

View File

@@ -0,0 +1,246 @@
/* SPDX-FileCopyrightText: 2006 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
/** \file
* \ingroup bke
*/
#include "BLI_math_vector_types.hh"
#include <cstdint>
namespace blender {
struct BlendDataReader;
struct BlendWriter;
struct ColorManagedColorspaceSettings;
struct ColorManagedDisplaySettings;
struct ColorManagedViewSettings;
struct CurveMap;
struct CurveMapPoint;
struct CurveMapping;
struct Histogram;
struct ImBuf;
struct Scopes;
struct rctf;
enum eBezTriple_Handle : uint8_t;
void BKE_curvemapping_set_defaults(CurveMapping *cumap,
int tot,
float minx,
float miny,
float maxx,
float maxy,
eBezTriple_Handle default_handle_type);
CurveMapping *BKE_curvemapping_add(int tot, float minx, float miny, float maxx, float maxy);
void BKE_curvemapping_free_data_single(CurveMapping *cumap, int index);
void BKE_curvemapping_free_data(CurveMapping *cumap);
void BKE_curvemapping_free(CurveMapping *cumap);
void BKE_curvemapping_copy_data_single(
CurveMapping *target, const CurveMapping *cumap, int to_idx, int from_idx, bool make_copy);
void BKE_curvemapping_copy_data(CurveMapping *target, const CurveMapping *cumap);
CurveMapping *BKE_curvemapping_copy(const CurveMapping *cumap);
void BKE_curvemapping_set_black_white_ex(const float black[3],
const float white[3],
float r_bwmul[3]);
void BKE_curvemapping_set_black_white(CurveMapping *cumap,
const float black[3],
const float white[3]);
enum class CurveMapSlopeType : int8_t {
Negative = 0,
Positive = 1,
PositiveNegative = 2,
};
/**
* Reset the view for current curve.
*/
void BKE_curvemapping_reset_view(CurveMapping *cumap);
void BKE_curvemap_reset(CurveMap *cuma, const rctf *clipr, int preset, CurveMapSlopeType slope);
/**
* When the current point is deselected, activate the closest remaining point
* by index. The function searches for the nearest valid index relative to the previously
* active index, not the nearest point by distance.
*/
void BKE_curvemap_activate_nearest_point(struct CurveMap *cuma, const int i_last);
/**
* Removes with flag set.
*/
void BKE_curvemap_remove(CurveMap *cuma, short flag);
/**
* Remove specified point.
*/
bool BKE_curvemap_remove_point(CurveMap *cuma, CurveMapPoint *point);
CurveMapPoint *BKE_curvemap_insert(CurveMap *cuma, float x, float y);
/**
* Shift all selected points.
*/
void BKE_curvemap_translate_selection(CurveMap *cuma, const blender::float2 &offset);
/**
* \param type: #eBezTriple_Handle
*/
void BKE_curvemap_handle_set(CurveMap *cuma, int type);
CurveMapPoint *BKE_curvemap_active_get(CurveMap *cuma);
/**
* \note only does current curvemap!.
*/
void BKE_curvemapping_changed(CurveMapping *cumap, bool rem_doubles);
void BKE_curvemapping_changed_all(CurveMapping *cumap);
/**
* Call before _all_ evaluation functions.
*/
void BKE_curvemapping_init(CurveMapping *cumap);
/**
* Keep these `const CurveMap` - to help with thread safety.
* \note Single curve, no table check.
* \note Table should be verified.
*/
float BKE_curvemap_evaluateF(const CurveMapping *cumap, const CurveMap *cuma, float value);
/**
* Single curve, with table check.
* Works with curve 'cur'.
*/
float BKE_curvemapping_evaluateF(const CurveMapping *cumap, int cur, float value);
/**
* Vector case.
*/
void BKE_curvemapping_evaluate3F(const CurveMapping *cumap, float vecout[3], const float vecin[3]);
/**
* RGB case, no black/white points, no pre-multiply.
*/
void BKE_curvemapping_evaluateRGBF(const CurveMapping *cumap,
float vecout[3],
const float vecin[3]);
/**
* Byte version of #BKE_curvemapping_evaluateRGBF.
*/
void BKE_curvemapping_evaluate_premulRGB(const CurveMapping *cumap,
unsigned char vecout_byte[3],
const unsigned char vecin_byte[3]);
/**
* Same as #BKE_curvemapping_evaluate_premulRGBF
* but black/bwmul are passed as args for the compositor
* where they can change per pixel.
*
* Use in conjunction with #BKE_curvemapping_set_black_white_ex
*
* \param black: Use instead of cumap->black
* \param bwmul: Use instead of cumap->bwmul
*/
void BKE_curvemapping_evaluate_premulRGBF_ex(const CurveMapping *cumap,
float vecout[3],
const float vecin[3],
const float black[3],
const float bwmul[3]);
/**
* RGB with black/white points and pre-multiply. tables are checked.
*/
void BKE_curvemapping_evaluate_premulRGBF(const CurveMapping *cumap,
float vecout[3],
const float vecin[3]);
bool BKE_curvemapping_RGBA_does_something(const CurveMapping *cumap);
void BKE_curvemapping_table_F(const CurveMapping *cumap, float **array, int *size);
void BKE_curvemapping_table_RGBA(const CurveMapping *cumap, float **array, int *size);
int BKE_curvemapping_num_channels(const CurveMapping *cumap);
/** Get the minimum x value of each curve map table. */
void BKE_curvemapping_get_range_minimums(const CurveMapping *curve_mapping, float minimums[4]);
/**
* Get the reciprocal of the difference between the maximum and the minimum x value of each curve
* map table. Evaluation parameters can be multiplied by this value to be normalized. If the
* difference is zero, 1^8 is returned.
*/
void BKE_curvemapping_compute_range_dividers(const CurveMapping *curve_mapping, float dividers[4]);
/**
* Compute the slopes at the start and end points of each curve map. The slopes are multiplied by
* the range of the curve map to compensate for parameter normalization. If the slope is vertical,
* 1^8 is returned.
*/
void BKE_curvemapping_compute_slopes(const CurveMapping *curve_mapping,
float start_slopes[4],
float end_slopes[4]);
/**
* Check if the curve map at the index is identity, that is, does nothing.
* A curve map is said to be identity if:
* - The curve mapping uses extrapolation.
* - Its range is 1.
* - The slope at its start point is 1.
* - The slope at its end point is 1.
* - The number of points is 2.
* - The start point is at (0, 0).
* - The end point is at (1, 1).
* Note that this could return false even if the curve map is identity, this happens in the case
* when more than 2 points exist in the curve map but all points are collinear. */
bool BKE_curvemapping_is_map_identity(const CurveMapping *curve_mapping, int index);
/**
* Call when you do images etc, needs restore too. also verifies tables.
* non-const (these modify the curve).
*/
void BKE_curvemapping_premultiply(CurveMapping *cumap, bool restore);
void BKE_curvemapping_blend_write(BlendWriter *writer, const CurveMapping *cumap);
void BKE_curvemapping_curves_blend_write(BlendWriter *writer, const CurveMapping *cumap);
/**
* \note `cumap` itself has been read already.
*/
void BKE_curvemapping_blend_read(BlendDataReader *reader, CurveMapping *cumap);
void BKE_histogram_update_sample_line(Histogram *hist,
ImBuf *ibuf,
const ColorManagedViewSettings *view_settings,
const ColorManagedDisplaySettings *display_settings);
void BKE_scopes_update(Scopes *scopes,
ImBuf *ibuf,
const ColorManagedViewSettings *view_settings,
const ColorManagedDisplaySettings *display_settings);
void BKE_scopes_free(Scopes *scopes);
void BKE_scopes_new(Scopes *scopes);
void BKE_color_managed_display_settings_init(ColorManagedDisplaySettings *settings);
void BKE_color_managed_display_settings_copy(ColorManagedDisplaySettings *new_settings,
const ColorManagedDisplaySettings *settings);
/**
* Initialize view settings to the default.
*/
void BKE_color_managed_view_settings_init(ColorManagedViewSettings *view_settings,
const ColorManagedDisplaySettings *display_settings,
const char *view_transform);
void BKE_color_managed_view_settings_copy(ColorManagedViewSettings *new_settings,
const ColorManagedViewSettings *settings);
/**
* Copy view settings that are not related to the curve mapping. Keep the curve mapping unchanged
* in the new_settings.
*/
void BKE_color_managed_view_settings_copy_keep_curve_mapping(
ColorManagedViewSettings *new_settings, const ColorManagedViewSettings *settings);
void BKE_color_managed_view_settings_free(ColorManagedViewSettings *settings);
void BKE_color_managed_view_settings_blend_write(BlendWriter *writer,
const ColorManagedViewSettings *settings);
void BKE_color_managed_view_settings_blend_read_data(BlendDataReader *reader,
ColorManagedViewSettings *settings);
void BKE_color_managed_colorspace_settings_init(
ColorManagedColorspaceSettings *colorspace_settings);
void BKE_color_managed_colorspace_settings_copy(
ColorManagedColorspaceSettings *colorspace_settings,
const ColorManagedColorspaceSettings *settings);
bool BKE_color_managed_colorspace_settings_equals(const ColorManagedColorspaceSettings *settings1,
const ColorManagedColorspaceSettings *settings2);
} // namespace blender

View File

@@ -0,0 +1,43 @@
/* SPDX-FileCopyrightText: 2024 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup bke
*/
#pragma once
#include <string>
#include "BLI_set.hh"
namespace blender {
struct Scene;
struct ViewLayer;
struct bContext;
struct DepsNodeHandle;
struct bNodeTree;
namespace bke::compositor {
/* Get the set of all passes used by the compositor for the given view layer, identified by their
* pass names. This might be a superset of the passes actually supported by the render engine, in
* which case, the compositor will return an invalid output and issue a warning. */
Set<std::string> get_used_passes(const Scene &scene, const ViewLayer *view_layer);
/* Checks if the viewport compositor is currently being used. This is similar to
* DRWContext::is_viewport_compositor_enabled but checks all 3D views. */
bool is_viewport_compositor_used(const bContext &context);
/* Note: Links to the File Output node do not guarantee it will write a result to disk, e.g. if
* Menu Switch nodes exists but it's a good estimation without evaluating the node tree. */
bool node_tree_has_linked_file_output(const bNodeTree *node_tree);
/* Add the depsgraph relations needed by the compositor node tree of the given scene. A handle for
* the compositor output depsgraph node is given to be the target of the relation. */
void add_depsgraph_relations(Scene &scene, DepsNodeHandle *compositor_output_depsgraph_node);
} // namespace bke::compositor
} // namespace blender

View File

@@ -0,0 +1,104 @@
/* SPDX-FileCopyrightText: 2025 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
#include "BKE_compute_context_cache_fwd.hh"
#include "BKE_compute_contexts.hh"
#include "BLI_linear_allocator.hh"
#include "BLI_map.hh"
#include "BLI_vector.hh"
namespace blender::bke {
/**
* When traversing the computation of a node tree (like in `socket_usage_inference.cc` or
* `partial_eval.cc`) one often enters and exists the same compute contexts. The cache implemented
* here avoids re-creating the same compute contexts over and over again. While requiring less
* memory and having potentially better performance, it can also be used to ensure that the same
* compute context will always have the same pointer, even if it's created in two different places.
*
* Constructing compute contexts through this cache can also be a bit more convenient.
*/
class ComputeContextCache {
/** Allocator used to allocate the compute contexts. */
LinearAllocator<> allocator_;
/** The allocated computed contexts that need to be destructed in the end. */
Vector<destruct_ptr<ComputeContext>> cache_;
Map<std::pair<const ComputeContext *, uint32_t>, const DataBlockComputeContext *>
data_block_contexts_cache_;
Map<std::pair<const ComputeContext *, int>, const ModifierComputeContext *>
modifier_contexts_cache_;
Map<const ComputeContext *, const OperatorComputeContext *> operator_contexts_cache_;
Map<const ComputeContext *, const ShaderComputeContext *> shader_contexts_cache_;
Map<std::pair<const ComputeContext *, int32_t>, const GroupNodeComputeContext *>
group_node_contexts_cache_;
Map<std::pair<const ComputeContext *, int32_t>, const SimulationZoneComputeContext *>
simulation_zone_contexts_cache_;
Map<std::pair<const ComputeContext *, std::pair<int32_t, int>>, const RepeatZoneComputeContext *>
repeat_zone_contexts_cache_;
Map<std::pair<const ComputeContext *, std::pair<int32_t, int>>,
const ForeachGeometryElementZoneComputeContext *>
foreach_geometry_element_zone_contexts_cache_;
Map<std::pair<const ComputeContext *, int32_t>, const EvaluateClosureComputeContext *>
evaluate_closure_contexts_cache_;
public:
const DataBlockComputeContext &for_data_block(const ComputeContext *parent,
const uint32_t orig_session_uid,
const ID *id = nullptr);
const DataBlockComputeContext &for_data_block(const ComputeContext *parent, const ID &id);
const ModifierComputeContext &for_modifier(const ComputeContext *parent,
const NodesModifierData &nmd);
const ModifierComputeContext &for_modifier(const ComputeContext *parent, int modifier_uid);
const OperatorComputeContext &for_operator(const ComputeContext *parent);
const OperatorComputeContext &for_operator(const ComputeContext *parent, const bNodeTree &tree);
const ShaderComputeContext &for_shader(const ComputeContext *parent, const bNodeTree *tree);
const GroupNodeComputeContext &for_group_node(const ComputeContext *parent,
int32_t node_id,
const bNodeTree *tree = nullptr);
const SimulationZoneComputeContext &for_simulation_zone(const ComputeContext *parent,
int output_node_id);
const SimulationZoneComputeContext &for_simulation_zone(const ComputeContext *parent,
const bNode &output_node);
const RepeatZoneComputeContext &for_repeat_zone(const ComputeContext *parent,
int32_t output_node_id,
int iteration);
const RepeatZoneComputeContext &for_repeat_zone(const ComputeContext *parent,
const bNode &output_node,
int iteration);
const ForeachGeometryElementZoneComputeContext &for_foreach_geometry_element_zone(
const ComputeContext *parent, int32_t output_node_id, int index);
const ForeachGeometryElementZoneComputeContext &for_foreach_geometry_element_zone(
const ComputeContext *parent, const bNode &output_node, int index);
const EvaluateClosureComputeContext &for_evaluate_closure(
const ComputeContext *parent,
int32_t node_id,
const bNodeTree *tree = nullptr,
const std::optional<nodes::ClosureSourceLocation> &closure_source_location = std::nullopt);
/**
* A fallback that does not use caching and can be used for any compute context.
* More constructors like the ones above can be added as they become necessary.
*/
template<typename T, typename... Args> const T &for_any_uncached(Args &&...args)
{
destruct_ptr<T> compute_context = allocator_.construct<T>(std::forward<Args>(args)...);
const T &result = *compute_context;
cache_.append(std::move(compute_context));
return result;
}
};
} // namespace blender::bke

View File

@@ -0,0 +1,11 @@
/* SPDX-FileCopyrightText: 2025 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
namespace blender::bke {
class ComputeContextCache;
}

View File

@@ -0,0 +1,263 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup bke
*/
#pragma once
/**
* This file implements some specific compute contexts for concepts in Blender.
*
* All compute contexts have to store the data that's required to uniquely identify them and to
* compute its hash. Some compute contexts contain some optional additional data that provides more
* information to code that uses the contexts.
*/
#include <optional>
#include "BLI_compute_context.hh"
#include "NOD_geometry_nodes_closure_location.hh"
namespace blender {
struct bNode;
struct bNodeTree;
struct NodesModifierData;
struct ID;
namespace nodes {
class Closure;
}
namespace bke {
class DataBlockComputeContext : public ComputeContext {
private:
uint32_t orig_session_uid_;
const ID *id_ = nullptr;
public:
DataBlockComputeContext(const ComputeContext *parent, const ID &id);
DataBlockComputeContext(const ComputeContext *parent,
uint32_t orig_session_uid,
const ID *id = nullptr);
uint32_t orig_session_uid() const
{
return orig_session_uid_;
}
const ID *id() const
{
return id_;
}
private:
ComputeContextHash compute_hash() const override;
void print_current_in_line(std::ostream &stream) const override;
};
class ModifierComputeContext : public ComputeContext {
private:
/** #ModifierData.persistent_uid. */
int modifier_uid_;
/** The modifier data that this context is for. This may be null. */
const NodesModifierData *nmd_ = nullptr;
public:
ModifierComputeContext(const ComputeContext *parent, const NodesModifierData &nmd);
ModifierComputeContext(const ComputeContext *parent, int modifier_uid);
int modifier_uid() const
{
return modifier_uid_;
}
const NodesModifierData *nmd() const
{
return nmd_;
}
private:
ComputeContextHash compute_hash() const override;
void print_current_in_line(std::ostream &stream) const override;
};
class NodeComputeContext : public ComputeContext {
private:
int32_t node_id_;
/** This is optional and may not be known always when the compute context is created. */
const bNodeTree *tree_ = nullptr;
public:
NodeComputeContext(const ComputeContext *parent,
int32_t node_id,
const bNodeTree *tree = nullptr);
int32_t node_id() const
{
return node_id_;
}
const bNodeTree *tree() const
{
return tree_;
}
const bNode *node() const;
private:
ComputeContextHash compute_hash() const override;
void print_current_in_line(std::ostream &stream) const override;
};
class GroupNodeComputeContext : public NodeComputeContext {
public:
using NodeComputeContext::NodeComputeContext;
};
class SimulationZoneComputeContext : public ComputeContext {
private:
int32_t output_node_id_;
public:
SimulationZoneComputeContext(const ComputeContext *parent, int output_node_id);
SimulationZoneComputeContext(const ComputeContext *parent, const bNode &node);
int32_t output_node_id() const
{
return output_node_id_;
}
private:
ComputeContextHash compute_hash() const override;
void print_current_in_line(std::ostream &stream) const override;
};
class RepeatZoneComputeContext : public ComputeContext {
private:
int32_t output_node_id_;
int iteration_;
public:
RepeatZoneComputeContext(const ComputeContext *parent, int32_t output_node_id, int iteration);
RepeatZoneComputeContext(const ComputeContext *parent, const bNode &node, int iteration);
int32_t output_node_id() const
{
return output_node_id_;
}
int iteration() const
{
return iteration_;
}
private:
ComputeContextHash compute_hash() const override;
void print_current_in_line(std::ostream &stream) const override;
};
class ForeachGeometryElementZoneComputeContext : public ComputeContext {
private:
int32_t output_node_id_;
int index_;
public:
ForeachGeometryElementZoneComputeContext(const ComputeContext *parent,
int32_t output_node_id,
int index);
ForeachGeometryElementZoneComputeContext(const ComputeContext *parent,
const bNode &node,
int index);
int32_t output_node_id() const
{
return output_node_id_;
}
int index() const
{
return index_;
}
private:
ComputeContextHash compute_hash() const override;
void print_current_in_line(std::ostream &stream) const override;
};
class EvaluateClosureComputeContext : public NodeComputeContext {
private:
std::optional<nodes::ClosureSourceLocation> closure_source_location_;
public:
EvaluateClosureComputeContext(
const ComputeContext *parent,
int32_t node_id,
const bNodeTree *tree = nullptr,
const std::optional<nodes::ClosureSourceLocation> &closure_source_location = std::nullopt);
std::optional<nodes::ClosureSourceLocation> closure_source_location() const
{
return closure_source_location_;
}
/**
* True if there is a parent context that evaluates the same closure already. This can only be
* used when the #ClosureSourceLocation is available.
*/
bool is_recursive() const;
};
class ClosureToListComputeContext : public NodeComputeContext {
private:
int32_t node_id_;
int list_index_;
public:
ClosureToListComputeContext(const ComputeContext *parent, int32_t node_id, int list_index);
private:
ComputeContextHash compute_hash() const override;
void print_current_in_line(std::ostream &stream) const override;
};
class OperatorComputeContext : public ComputeContext {
private:
/** The tree that is executed. May be null. */
const bNodeTree *tree_ = nullptr;
public:
OperatorComputeContext();
OperatorComputeContext(const ComputeContext *parent);
OperatorComputeContext(const ComputeContext *parent, const bNodeTree &tree);
const bNodeTree *tree() const
{
return tree_;
}
private:
ComputeContextHash compute_hash() const override;
void print_current_in_line(std::ostream &stream) const override;
};
class ShaderComputeContext : public ComputeContext {
private:
const bNodeTree *tree_ = nullptr;
public:
ShaderComputeContext(const ComputeContext *parent = nullptr, const bNodeTree *tree = nullptr);
private:
ComputeContextHash compute_hash() const override;
void print_current_in_line(std::ostream &stream) const override;
};
} // namespace bke
} // namespace blender

View File

@@ -0,0 +1,397 @@
/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
/** \file
* \ingroup bke
*/
#include "DNA_listBase.h"
namespace blender {
struct BlendDataReader;
struct BlendWriter;
struct Depsgraph;
struct ID;
struct Object;
struct Scene;
struct bConstraint;
struct bConstraintTarget;
struct bPoseChannel;
enum eBConstraint_Types : short;
/* ---------------------------------------------------------------------------- */
/* special struct for use in constraint evaluation */
struct bConstraintOb {
/** to get evaluated armature. */
struct Depsgraph *depsgraph;
/** for system time, part of de-globalization, code nicer later with local time (ton) */
struct Scene *scene;
/** if pchan, then armature that it comes from, otherwise constraint owner */
struct Object *ob;
/** pose channel that owns the constraints being evaluated */
struct bPoseChannel *pchan;
/** Armature bone of the above pchan. */
struct Bone *pchan_armbone;
/** matrix where constraints are accumulated + solved */
float matrix[4][4];
/** original matrix (before constraint solving) */
float startmat[4][4];
/** space matrix for custom object space */
float space_obj_world_matrix[4][4];
/** type of owner. */
short type;
/**
* Rotation order for constraint owner
* (as defined in #eEulerRotationOrders in BLI_math_rotation.h).
*/
short rotOrder;
};
/* ---------------------------------------------------------------------------- */
/* Callback format for performing operations on ID-pointers for Constraints */
typedef void (*ConstraintIDFunc)(struct bConstraint *con,
struct ID **idpoin,
bool is_reference,
void *userdata);
/* ....... */
/**
* Constraint Type-Info (shorthand in code = `cti`):
* This struct provides function pointers for runtime, so that functions can be
* written more generally (with fewer/no special exceptions for various constraints).
*
* Callers of these functions must check that they actually point to something useful,
* as some constraints don't define some of these.
*
* WARNING:
* it is not too advisable to reorder order of members of this struct,
* as you'll have to edit quite a few #NUM_CONSTRAINT_TYPES of these
* structs.
*/
struct bConstraintTypeInfo {
/* Admin/identity. */
/** CONSTRAINT_TYPE_### */
short type;
/** size in bytes of the struct */
short size;
/** name of constraint in interface */
char name[32];
/** name of struct for SDNA */
char struct_name[32];
/* data management function pointers - special handling */
/** free any data that is allocated separately (optional) */
void (*free_data)(struct bConstraint *con);
/** run the provided callback function on all the ID-blocks linked to the constraint */
void (*id_looper)(struct bConstraint *con, ConstraintIDFunc func, void *userdata);
/** copy any special data that is allocated separately (optional) */
void (*copy_data)(struct bConstraint *con, struct bConstraint *src);
/**
* Set settings for data that will be used for #bConstraint.data
* (memory already allocated using #MEM_new_zeroed).
*/
void (*new_data)(void *cdata);
/* target handling function pointers */
/**
* For multi-target constraints: return that list;
* otherwise make a temporary list (returns number of targets).
*/
int (*get_constraint_targets)(struct bConstraint *con, ListBaseT<bConstraintTarget> *list);
/**
* For single-target constraints only:
* flush data back to source data, and the free memory used.
*/
void (*flush_constraint_targets)(struct bConstraint *con,
ListBaseT<bConstraintTarget> *list,
bool no_copy);
/* evaluation */
/**
* Set the ct->matrix for the given constraint target (at the given ctime).
*
* \returns Whether the constraint has a valid target. This can be an explicitly-given target,
* but can also be dynamically chosen (for example for auto-IK).
*
* Note that ct->matrix may still be updated (typically to the identity matrix) even when this
* function returns false.
*/
bool (*get_target_matrix)(struct Depsgraph *depsgraph,
struct bConstraint *con,
struct bConstraintOb *cob,
struct bConstraintTarget *ct,
float ctime);
/**
* Evaluate the constraint for the given time.
* solved as separate loop.
*/
void (*evaluate_constraint)(struct bConstraint *con,
struct bConstraintOb *cob,
ListBaseT<bConstraintTarget> *targets);
};
/* Function Prototypes for bConstraintTypeInfo's */
/**
* This function should always be used to get the appropriate type-info, as it
* has checks which prevent segfaults in some weird cases.
*/
const bConstraintTypeInfo *BKE_constraint_typeinfo_get(struct bConstraint *con);
/**
* This function should be used for getting the appropriate type-info when only
* a constraint type is known.
*/
const bConstraintTypeInfo *BKE_constraint_typeinfo_from_type(int type);
/* ---------------------------------------------------------------------------- */
/* Constraint function prototypes */
/**
* Find the first available, non-duplicate name for a given constraint.
*/
void BKE_constraint_unique_name(struct bConstraint *con, ListBaseT<bConstraint> *list);
/**
* Allocate and duplicate a single constraint, outside of any object/pose context.
*/
struct bConstraint *BKE_constraint_duplicate_ex(struct bConstraint *src, int flag, bool do_extern);
/**
* Add a copy of the given constraint for the given bone.
*/
struct bConstraint *BKE_constraint_copy_for_pose(struct Object *ob,
struct bPoseChannel *pchan,
struct bConstraint *src);
/**
* Add a copy of the given constraint for the given object.
*/
struct bConstraint *BKE_constraint_copy_for_object(struct Object *ob, struct bConstraint *src);
void BKE_constraints_free(ListBaseT<bConstraint> *list);
/**
* Free all constraints from a constraint-stack.
*/
void BKE_constraints_free_ex(ListBaseT<bConstraint> *list, bool do_id_user);
void BKE_constraints_copy(ListBaseT<bConstraint> *dst,
const ListBaseT<bConstraint> *src,
bool do_extern);
/**
* Duplicate all of the constraints in a constraint stack.
*/
void BKE_constraints_copy_ex(ListBaseT<bConstraint> *dst,
const ListBaseT<bConstraint> *src,
int flag,
bool do_extern);
/**
* Run the given callback on all ID-blocks in list of constraints.
*
* \param flag: the `IDWALK_` flags controlling the behavior of the foreach_id code, see
* `BKE_lib_query.hh`
*/
void BKE_constraints_id_loop(ListBaseT<bConstraint> *list,
ConstraintIDFunc func,
const int flag,
void *userdata);
void BKE_constraint_free_data(struct bConstraint *con);
/**
* Free data of a specific constraint if it has any info.
* Be sure to run #BIK_clear_data() when freeing an IK constraint,
* unless #DAG_relations_tag_update is called.
*/
void BKE_constraint_free_data_ex(struct bConstraint *con, bool do_id_user);
bool BKE_constraint_target_uses_bbone(struct bConstraint *con, struct bConstraintTarget *ct);
/* Constraint API function prototypes */
/**
* Finds the 'active' constraint in a constraint stack.
*/
struct bConstraint *BKE_constraints_active_get(ListBaseT<bConstraint> *list);
/**
* Set the given constraint as the active one (clearing all the others).
*/
void BKE_constraints_active_set(ListBaseT<bConstraint> *list, struct bConstraint *con);
struct bConstraint *BKE_constraints_find_name(ListBaseT<bConstraint> *list, const char *name);
/**
* Finds the constraint that owns the given target within the object.
*/
struct bConstraint *BKE_constraint_find_from_target(struct Object *ob,
struct bConstraintTarget *tgt,
struct bPoseChannel **r_pchan);
/**
* Check whether given constraint is not local (i.e. from linked data) when the object is a library
* override.
*
* \param con: May be NULL, in which case we consider it as a non-local constraint case.
*/
bool BKE_constraint_is_nonlocal_in_liboverride(const struct Object *ob,
const struct bConstraint *con);
/**
* Add new constraint for the given object.
*/
struct bConstraint *BKE_constraint_add_for_object(struct Object *ob,
const char *name,
eBConstraint_Types type);
/**
* Add new constraint for the given bone.
*/
struct bConstraint *BKE_constraint_add_for_pose(struct Object *ob,
struct bPoseChannel *pchan,
const char *name,
eBConstraint_Types type);
/**
* Remove the specified constraint from the given constraint stack.
*/
bool BKE_constraint_remove_ex(ListBaseT<bConstraint> *list,
struct Object *ob,
struct bConstraint *con);
/**
* Apply the specified constraint in the given constraint stack.
*/
bool BKE_constraint_apply_for_object(struct Depsgraph *depsgraph,
struct Scene *scene,
struct Object *ob,
struct bConstraint *con);
bool BKE_constraint_apply_and_remove_for_object(struct Depsgraph *depsgraph,
struct Scene *scene,
ListBaseT<bConstraint> *constraints,
struct Object *ob,
struct bConstraint *con);
bool BKE_constraint_apply_for_pose(struct Depsgraph *depsgraph,
struct Scene *scene,
struct Object *ob,
struct bPoseChannel *pchan,
struct bConstraint *con);
bool BKE_constraint_apply_and_remove_for_pose(struct Depsgraph *depsgraph,
struct Scene *scene,
ListBaseT<bConstraint> *constraints,
struct Object *ob,
struct bConstraint *con,
struct bPoseChannel *pchan);
void BKE_constraint_panel_expand(struct bConstraint *con);
/* Constraint Evaluation function prototypes */
/**
* Package an object/bone for use in constraint evaluation.
*
* This function MEM_new_zeroed's a #bConstraintOb struct,
* that will need to be freed after evaluation.
*/
struct bConstraintOb *BKE_constraints_make_evalob(struct Depsgraph *depsgraph,
struct Scene *scene,
struct Object *ob,
void *subdata,
short datatype);
/**
* Cleanup after constraint evaluation.
*/
void BKE_constraints_clear_evalob(struct bConstraintOb *cob);
/**
* This function is responsible for the correct transformations/conversions
* of a matrix from one space to another for constraint evaluation.
* For now, this is only implemented for objects and pose-channels.
*/
void BKE_constraint_mat_convertspace(struct Object *ob,
struct bPoseChannel *pchan,
struct bConstraintOb *cob,
float mat[4][4],
short from,
short to,
bool keep_scale);
/**
* This function is a relic from the prior implementations of the constraints system, when all
* constraints either had one or no targets. It used to be called during the main constraint
* solving loop, but is now only used for the remaining cases for a few constraints.
*
* None of the actual calculations of the matrices should be done here! Also, this function is
* not to be used by any new constraints, particularly any that have multiple targets.
*
* NOTE: ownertype=CONSTRAINT_OBTYPE_BONE is NOT supported by this function. Computing that also
* needs the bone-owning object, and that is not passed here.
*/
void BKE_constraint_target_matrix_get(struct Depsgraph *depsgraph,
struct Scene *scene,
struct bConstraint *con,
int index,
short ownertype,
void *ownerdata,
float mat[4][4],
float ctime);
/**
* Retrieves the list of all constraint targets, including the custom space target.
* Must be followed by a call to BKE_constraint_targets_flush to free memory.
*
* \param r_targets: Pointer to the list to be initialized with target data.
* \returns the number of targets stored in the list.
*/
int BKE_constraint_targets_get(struct bConstraint *con, ListBaseT<bConstraintTarget> *r_targets);
/**
* Copies changed data from the list produced by #BKE_constraint_targets_get back to the constraint
* data structures and frees memory.
*
* \param targets: List of targets filled by BKE_constraint_targets_get.
* \param no_copy: Only free memory without copying changes (read-only mode).
*/
void BKE_constraint_targets_flush(struct bConstraint *con,
ListBaseT<bConstraintTarget> *targets,
bool no_copy);
/**
* Get the list of targets required for solving a constraint.
*/
void BKE_constraint_targets_for_solving_get(struct Depsgraph *depsgraph,
struct bConstraint *con,
struct bConstraintOb *ob,
ListBaseT<bConstraintTarget> *targets,
float ctime);
/**
* Initialize the Custom Space matrix inside `cob` (if required by the constraint).
*
* \param cob: Constraint evaluation context (contains the matrix to be initialized).
* \param con: Constraint that is about to be evaluated.
*/
void BKE_constraint_custom_object_space_init(struct bConstraintOb *cob, struct bConstraint *con);
/**
* This function is called whenever constraints need to be evaluated. Currently, all
* constraints that can be evaluated are every time this gets run.
*
* #BKE_constraints_make_evalob and #BKE_constraints_clear_evalob should be called before and
* after running this function, to sort out cob.
*/
void BKE_constraints_solve(struct Depsgraph *depsgraph,
ListBaseT<bConstraint> *conlist,
struct bConstraintOb *cob,
float ctime);
void BKE_constraint_blend_write(struct BlendWriter *writer, ListBaseT<bConstraint> *conlist);
void BKE_constraint_blend_read_data(struct BlendDataReader *reader,
struct ID *id_owner,
ListBaseT<bConstraint> *lb);
} // namespace blender

View File

@@ -0,0 +1,517 @@
/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
/** \file
* \ingroup bke
*/
#include <variant>
#include "BLI_enum_flags.hh"
#include "BLI_string_ref.hh"
#include "BLI_vector.hh"
#include "DNA_listBase.h"
#include "DNA_object_enums.h"
#include "RNA_types.hh"
namespace blender {
struct ARegion;
struct AssetLibraryReference;
struct AssetWeakReference;
struct Base;
struct bGPDframe;
struct bGPDlayer;
struct bPoseChannel;
struct bScreen;
struct CacheFile;
struct Collection;
struct Depsgraph;
struct EditBone;
struct ID;
struct Image;
struct LayerCollection;
struct Main;
struct Mask;
struct MovieClip;
struct Object;
struct PointerRNA;
struct RegionView3D;
struct RenderEngineType;
struct ReportList;
struct Scene;
struct ScrArea;
struct SpaceAction;
struct SpaceClip;
struct SpaceClip;
struct SpaceConsole;
struct SpaceFile;
struct SpaceGraph;
struct SpaceImage;
struct SpaceInfo;
struct SpaceLink;
struct SpaceNla;
struct SpaceNode;
struct SpaceOutliner;
struct SpaceProperties;
struct SpaceSeq;
struct SpaceSpreadsheet;
struct SpaceText;
struct SpaceTopBar;
struct SpaceUserPref;
struct StructRNA;
struct Text;
struct ToolSettings;
struct View3D;
struct ViewLayer;
struct wmGizmoGroup;
struct wmMsgBus;
struct wmWindow;
struct wmWindowManager;
struct WorkSpace;
/**
* Context logging control flags.
*/
enum class CTX_LogFlag : uint8_t {
/** Enable logging of context member access. */
Access = (1 << 0),
/** Hide missing/None values from logging. */
HideMissing = (1 << 1),
};
ENUM_OPERATORS(CTX_LogFlag);
/* Structs */
struct bContext;
struct bContextDataResult;
/* Result of context lookups.
* The specific values are important, and used implicitly in ctx_data_get(). Some functions also
* still accept/return `int` instead, to ensure that the compiler uses the correct storage size
* when mixing C/C++ code. */
enum eContextResult {
/* The context member was found, and its data is available. */
CTX_RESULT_OK = 1,
/* The context member was not found. */
CTX_RESULT_MEMBER_NOT_FOUND = 0,
/* The context member was found, but its data is not available.
* For example, "active_bone" is a valid context member, but has not data in Object mode. */
CTX_RESULT_NO_DATA = -1,
};
/* Function mapping a context member name to its value. */
using bContextDataCallback = int /*eContextResult*/ (*)(const bContext *C,
const char *member,
bContextDataResult *result);
struct bContextStoreEntry {
std::string name;
std::variant<PointerRNA, std::string, int64_t> value;
};
struct bContextStore {
Vector<bContextStoreEntry> entries;
bool used = false;
};
namespace asset_system {
class AssetRepresentation;
}
/* for the context's rna mode enum
* keep aligned with data_mode_strings in context.cc */
enum eContextObjectMode {
CTX_MODE_EDIT_MESH = 0,
CTX_MODE_EDIT_CURVE,
CTX_MODE_EDIT_SURFACE,
CTX_MODE_EDIT_TEXT,
CTX_MODE_EDIT_ARMATURE,
CTX_MODE_EDIT_METABALL,
CTX_MODE_EDIT_LATTICE,
CTX_MODE_EDIT_CURVES,
CTX_MODE_EDIT_GREASE_PENCIL,
CTX_MODE_EDIT_POINTCLOUD,
CTX_MODE_POSE,
CTX_MODE_SCULPT,
CTX_MODE_PAINT_WEIGHT,
CTX_MODE_PAINT_VERTEX,
CTX_MODE_PAINT_TEXTURE,
CTX_MODE_PARTICLE,
CTX_MODE_OBJECT,
CTX_MODE_PAINT_GPENCIL_LEGACY,
CTX_MODE_EDIT_GPENCIL_LEGACY,
CTX_MODE_SCULPT_GPENCIL_LEGACY,
CTX_MODE_WEIGHT_GPENCIL_LEGACY,
CTX_MODE_VERTEX_GPENCIL_LEGACY,
CTX_MODE_SCULPT_CURVES,
CTX_MODE_PAINT_GREASE_PENCIL,
CTX_MODE_SCULPT_GREASE_PENCIL,
CTX_MODE_WEIGHT_GREASE_PENCIL,
CTX_MODE_VERTEX_GREASE_PENCIL,
};
#define CTX_MODE_NUM (CTX_MODE_VERTEX_GREASE_PENCIL + 1)
/* Context */
bContext *CTX_create();
void CTX_free(bContext *C);
bContext *CTX_copy(const bContext *C);
/* Stored Context */
bContextStore *CTX_store_add(Vector<std::unique_ptr<bContextStore>> &contexts,
StringRef name,
const PointerRNA *ptr);
bContextStore *CTX_store_add(Vector<std::unique_ptr<bContextStore>> &contexts,
StringRef name,
StringRef str);
bContextStore *CTX_store_add(Vector<std::unique_ptr<bContextStore>> &contexts,
StringRef name,
int64_t value);
bContextStore *CTX_store_add_all(Vector<std::unique_ptr<bContextStore>> &contexts,
const bContextStore *context);
const bContextStore *CTX_store_get(const bContext *C);
void CTX_store_set(bContext *C, const bContextStore *store);
const PointerRNA *CTX_store_ptr_lookup(const bContextStore *store,
StringRef name,
const StructRNA *type = nullptr);
std::optional<StringRefNull> CTX_store_string_lookup(const bContextStore *store, StringRef name);
std::optional<int64_t> CTX_store_int_lookup(const bContextStore *store, StringRef name);
/* Set a temporary flag to indicate when writing via RNA is disallowed. */
void CTX_rna_disallow_write_set_p(bContext *C, const bool *rna_disallow_writes);
/** Needed to store if Python is initialized or not. */
bool CTX_py_init_get(const bContext *C);
void CTX_py_init_set(bContext *C, bool value);
void *CTX_py_dict_get(const bContext *C);
void *CTX_py_dict_get_orig(const bContext *C);
struct bContext_PyState {
void *py_context;
void *py_context_orig;
};
void CTX_py_state_push(bContext *C, bContext_PyState *pystate, void *value);
void CTX_py_state_pop(bContext *C, bContext_PyState *pystate);
/* Window Manager Context */
wmWindowManager *CTX_wm_manager(const bContext *C);
wmWindow *CTX_wm_window(const bContext *C);
WorkSpace *CTX_wm_workspace(const bContext *C);
bScreen *CTX_wm_screen(const bContext *C);
ScrArea *CTX_wm_area(const bContext *C);
SpaceLink *CTX_wm_space_data(const bContext *C);
ARegion *CTX_wm_region(const bContext *C);
void *CTX_wm_region_data(const bContext *C);
ARegion *CTX_wm_region_popup(const bContext *C);
wmGizmoGroup *CTX_wm_gizmo_group(const bContext *C);
wmMsgBus *CTX_wm_message_bus(const bContext *C);
ReportList *CTX_wm_reports(const bContext *C);
View3D *CTX_wm_view3d(const bContext *C);
RegionView3D *CTX_wm_region_view3d(const bContext *C);
SpaceText *CTX_wm_space_text(const bContext *C);
SpaceImage *CTX_wm_space_image(const bContext *C);
SpaceConsole *CTX_wm_space_console(const bContext *C);
SpaceProperties *CTX_wm_space_properties(const bContext *C);
SpaceFile *CTX_wm_space_file(const bContext *C);
SpaceSeq *CTX_wm_space_seq(const bContext *C);
SpaceOutliner *CTX_wm_space_outliner(const bContext *C);
SpaceNla *CTX_wm_space_nla(const bContext *C);
SpaceNode *CTX_wm_space_node(const bContext *C);
SpaceGraph *CTX_wm_space_graph(const bContext *C);
SpaceAction *CTX_wm_space_action(const bContext *C);
SpaceInfo *CTX_wm_space_info(const bContext *C);
SpaceUserPref *CTX_wm_space_userpref(const bContext *C);
SpaceClip *CTX_wm_space_clip(const bContext *C);
SpaceTopBar *CTX_wm_space_topbar(const bContext *C);
SpaceSpreadsheet *CTX_wm_space_spreadsheet(const bContext *C);
void CTX_wm_manager_set(bContext *C, wmWindowManager *wm);
void CTX_wm_window_set(bContext *C, wmWindow *win);
void CTX_wm_screen_set(bContext *C, bScreen *screen); /* to be removed */
void CTX_wm_area_set(bContext *C, ScrArea *area);
void CTX_wm_region_set(bContext *C, ARegion *region);
void CTX_wm_region_popup_set(bContext *C, ARegion *region_popup);
void CTX_wm_gizmo_group_set(bContext *C, wmGizmoGroup *gzgroup);
/**
* Values to create the message that describes the reason poll failed.
*
* \note This must be called in the same context as the poll function that created it.
*/
struct bContextPollMsgDyn_Params {
/** The result is allocated. */
char *(*get_fn)(bContext *C, void *user_data);
/** Optionally free the user-data. */
void (*free_fn)(bContext *C, void *user_data);
void *user_data;
};
const char *CTX_wm_operator_poll_msg_get(bContext *C, bool *r_free);
/**
* Set a message to be shown when the operator is disabled in the UI.
*
* \note even though the function name does not include the word "disabled", the
* message is only shown when the operator (in the UI) is in fact disabled.
*
* \note even though the function name suggests this is limited to situations
* when the poll function returns false, this is not the case. Even when the
* operator is disabled because it is added to a disabled ui::Layout, this message
* will show.
*/
void CTX_wm_operator_poll_msg_set(bContext *C, const char *msg);
void CTX_wm_operator_poll_msg_set_dynamic(bContext *C, const bContextPollMsgDyn_Params *params);
void CTX_wm_operator_poll_msg_clear(bContext *C);
/* Data Context
*
* - The dir #ListBaseT consists of #LinkData items.
*/
/** Data type, needed so we can tell between a NULL pointer and an empty list. */
enum class ContextDataType : uint8_t {
Pointer = 0,
Collection,
Property,
String,
Int64,
};
PointerRNA CTX_data_pointer_get(const bContext *C, const char *member);
PointerRNA CTX_data_pointer_get_type(const bContext *C, const char *member, StructRNA *type);
PointerRNA CTX_data_pointer_get_type_silent(const bContext *C,
const char *member,
StructRNA *type);
Vector<PointerRNA> CTX_data_collection_get(const bContext *C, const char *member);
/**
* For each pointer in collection_pointers, remap it to point to `ptr->propname`.
*
* Example:
*
* lb = CTX_data_collection_get(C, "selected_pose_bones"); // lb contains pose bones.
* CTX_data_collection_remap_property(lb, "color"); // lb now contains bone colors.
*/
void CTX_data_collection_remap_property(MutableSpan<PointerRNA> collection_pointers,
const char *propname);
std::optional<StringRefNull> CTX_data_string_get(const bContext *C, const char *member);
std::optional<int64_t> CTX_data_int_get(const bContext *C, const char *member);
/**
* \param C: Context.
* \param use_store: Use 'C->wm.store'.
* \param use_rna: Use Include the properties from #RNA_Context.
* \param use_all: Don't skip values (currently only "scene").
*/
ListBaseT<LinkData> CTX_data_dir_get_ex(const bContext *C,
bool use_store,
bool use_rna,
bool use_all);
ListBaseT<LinkData> CTX_data_dir_get(const bContext *C);
int /*eContextResult*/ CTX_data_get(const bContext *C,
const char *member,
PointerRNA *r_ptr,
Vector<PointerRNA> *r_lb,
PropertyRNA **r_prop,
int *r_index,
StringRef *r_str,
std::optional<int64_t> *r_int_value,
ContextDataType *r_type);
void CTX_data_id_pointer_set(bContextDataResult *result, ID *id);
void CTX_data_pointer_set_ptr(bContextDataResult *result, const PointerRNA *ptr);
void CTX_data_pointer_set(bContextDataResult *result, ID *id, StructRNA *type, void *data);
void CTX_data_id_list_add(bContextDataResult *result, ID *id);
void CTX_data_list_add_ptr(bContextDataResult *result, const PointerRNA *ptr);
void CTX_data_list_add(bContextDataResult *result, ID *id, StructRNA *type, void *data);
/**
* Stores a property in a result. Make sure to also call
* `CTX_data_type_set(result, ContextDataType::Property)`.
* \param result: The result to store the property in.
* \param prop: The property to store.
* \param index: The particular index in the property to store.
*/
void CTX_data_prop_set(bContextDataResult *result, PropertyRNA *prop, int index);
void CTX_data_dir_set(bContextDataResult *result, const char **dir);
void CTX_data_type_set(bContextDataResult *result, ContextDataType type);
ContextDataType CTX_data_type_get(bContextDataResult *result);
bool CTX_data_equals(const char *member, const char *str);
bool CTX_data_dir(const char *member);
#define CTX_DATA_BEGIN(C, Type, instance, member) \
{ \
Vector<PointerRNA> ctx_data_list; \
CTX_data_##member(C, &ctx_data_list); \
for (PointerRNA &ctx_link : ctx_data_list) { \
Type instance = (Type)ctx_link.data;
#define CTX_DATA_END \
} \
} \
(void)0
#define CTX_DATA_BEGIN_WITH_ID(C, Type, instance, member, Type_id, instance_id) \
CTX_DATA_BEGIN (C, Type, instance, member) \
Type_id instance_id = (Type_id)ctx_link.owner_id;
int ctx_data_list_count(const bContext *C, bool (*func)(const bContext *, Vector<PointerRNA> *));
#define CTX_DATA_COUNT(C, member) ctx_data_list_count(C, CTX_data_##member)
/* Data Context Members */
Main *CTX_data_main(const bContext *C);
Scene *CTX_data_scene(const bContext *C);
Scene *CTX_data_sequencer_scene(const bContext *C);
/**
* This is tricky. Sometimes the user overrides the render_layer
* but not the scene_collection. In this case what to do?
*
* If the scene_collection is linked to the #ViewLayer we use it.
* Otherwise we fall back to the active one of the #ViewLayer.
*/
LayerCollection *CTX_data_layer_collection(const bContext *C);
Collection *CTX_data_collection(const bContext *C);
ViewLayer *CTX_data_view_layer(const bContext *C);
RenderEngineType *CTX_data_engine_type(const bContext *C);
ToolSettings *CTX_data_tool_settings(const bContext *C);
const char *CTX_data_mode_string(const bContext *C);
enum eContextObjectMode CTX_data_mode_enum_ex(const Object *obedit,
const Object *ob,
eObjectMode object_mode);
enum eContextObjectMode CTX_data_mode_enum(const bContext *C);
void CTX_data_main_set(bContext *C, Main *bmain);
void CTX_data_scene_set(bContext *C, Scene *scene);
/* Only Outliner currently! */
bool CTX_data_selected_ids(const bContext *C, Vector<PointerRNA> *list);
bool CTX_data_selected_editable_objects(const bContext *C, Vector<PointerRNA> *list);
bool CTX_data_selected_editable_bases(const bContext *C, Vector<PointerRNA> *list);
bool CTX_data_editable_objects(const bContext *C, Vector<PointerRNA> *list);
bool CTX_data_editable_bases(const bContext *C, Vector<PointerRNA> *list);
bool CTX_data_selected_objects(const bContext *C, Vector<PointerRNA> *list);
bool CTX_data_selected_bases(const bContext *C, Vector<PointerRNA> *list);
bool CTX_data_visible_objects(const bContext *C, Vector<PointerRNA> *list);
bool CTX_data_visible_bases(const bContext *C, Vector<PointerRNA> *list);
bool CTX_data_selectable_objects(const bContext *C, Vector<PointerRNA> *list);
bool CTX_data_selectable_bases(const bContext *C, Vector<PointerRNA> *list);
Object *CTX_data_active_object(const bContext *C);
Base *CTX_data_active_base(const bContext *C);
Object *CTX_data_edit_object(const bContext *C);
Image *CTX_data_edit_image(const bContext *C);
Text *CTX_data_edit_text(const bContext *C);
MovieClip *CTX_data_edit_movieclip(const bContext *C);
Mask *CTX_data_edit_mask(const bContext *C);
CacheFile *CTX_data_edit_cachefile(const bContext *C);
bool CTX_data_selected_nodes(const bContext *C, Vector<PointerRNA> *list);
EditBone *CTX_data_active_bone(const bContext *C);
bool CTX_data_selected_bones(const bContext *C, Vector<PointerRNA> *list);
bool CTX_data_selected_editable_bones(const bContext *C, Vector<PointerRNA> *list);
bool CTX_data_visible_bones(const bContext *C, Vector<PointerRNA> *list);
bool CTX_data_editable_bones(const bContext *C, Vector<PointerRNA> *list);
bPoseChannel *CTX_data_active_pose_bone(const bContext *C);
PointerRNA CTX_data_active_pose_bone_ptr(const bContext *C);
bool CTX_data_selected_pose_bones(const bContext *C, Vector<PointerRNA> *list);
bool CTX_data_selected_pose_bones_from_active_object(const bContext *C, Vector<PointerRNA> *list);
bool CTX_data_visible_pose_bones(const bContext *C, Vector<PointerRNA> *list);
const AssetLibraryReference *CTX_wm_asset_library_ref(const bContext *C);
class asset_system::AssetRepresentation *CTX_wm_asset(const bContext *C);
bool CTX_wm_interface_locked(const bContext *C);
/**
* Gets pointer to the dependency graph.
* If it doesn't exist yet, it will be allocated.
*
* The result dependency graph is NOT guaranteed to be up-to-date neither from relation nor from
* evaluated data points of view.
*
* \note Can not be used if access to a fully evaluated data-block is needed.
*/
Depsgraph *CTX_data_depsgraph_pointer(const bContext *C);
/**
* Get dependency graph which is expected to be fully evaluated.
*
* In the release builds it is the same as CTX_data_depsgraph_pointer(). In the debug builds extra
* sanity checks are done. Additionally, this provides more semantic meaning to what is exactly
* expected to happen.
*/
Depsgraph *CTX_data_expect_evaluated_depsgraph(const bContext *C);
/**
* Gets fully updated and evaluated dependency graph.
*
* All the relations and evaluated objects are guaranteed to be up to date.
*
* \note Will be expensive if there are relations or objects tagged for update.
* \note If there are pending updates depsgraph hooks will be invoked.
* \warning In many cases, runtime data on associated objects will be destroyed & recreated.
* \warning Returns null pointer if #rna_write_check is true and RNA writing is disallowed. If
* #rna_write_check is false then RNA writes must be allowed when calling this function.
*/
Depsgraph *CTX_data_ensure_evaluated_depsgraph(const bContext *C, bool rna_write_check = false);
/* Will Return NULL if depsgraph is not allocated yet.
* Only used by handful of operators which are run on file load.
*/
Depsgraph *CTX_data_depsgraph_on_load(const bContext *C);
/**
* Set context member logging flags.
*/
void CTX_member_logging_flag_set(bContext *C, CTX_LogFlag flag);
/**
* Get context member logging flag.
*/
CTX_LogFlag CTX_member_logging_flag_get(const bContext *C);
/**
* \return true when context access should be logged.
*/
bool CTX_member_logging_get(const bContext *C);
/**
* Check if writing to RNA is allowed.
*
* RNA can use this to disable writes during callbacks, such as when accessing the evaluated
* depsgraph (#150024).
*/
bool CTX_member_rna_write_check(const bContext *C);
} // namespace blender

View File

@@ -0,0 +1,18 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup bke
*/
#pragma once
namespace blender {
/**
* Register cpp types and their relations for later use.
*/
void BKE_cpp_types_init();
} // namespace blender

View File

@@ -0,0 +1,133 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup bke
*/
#pragma once
#include "BLI_array.hh"
#include "BLI_math_matrix.hh"
#include "BLI_span.hh"
namespace blender {
struct BMEditMesh;
struct Depsgraph;
struct Mesh;
struct Object;
struct ReportList;
struct Scene;
namespace bke::greasepencil {
class Drawing;
} // namespace bke::greasepencil
namespace bke::crazyspace {
/**
* Contains information about how points have been deformed during evaluation.
* This allows mapping edits on evaluated data back to original data in some cases.
*/
struct GeometryDeformation {
/**
* Positions of the deformed points. This may also point to the original position if no
* deformation data is available.
*/
Span<float3> positions;
/**
* Matrices that transform point translations on original data into corresponding translations in
* evaluated data. This may be empty if not available.
*/
Span<float3x3> deform_mats;
float3 translation_from_deformed_to_original(const int position_i,
const float3 &translation) const
{
if (this->deform_mats.is_empty()) {
return translation;
}
const float3x3 &deform_mat = this->deform_mats[position_i];
return math::transform_point(math::invert(deform_mat), translation);
}
};
/**
* During evaluation of the object, deformation data may have been generated for this object. This
* function either retrieves the deformation data from the evaluated object, or falls back to
* returning the original data.
*/
GeometryDeformation get_evaluated_curves_deformation(const Object *ob_eval, const Object &ob_orig);
GeometryDeformation get_evaluated_curves_deformation(const Depsgraph &depsgraph,
const Object &ob_orig);
GeometryDeformation get_evaluated_grease_pencil_drawing_deformation(
const Object *ob_eval, const Object &ob_orig, const bke::greasepencil::Drawing &drawing_orig);
GeometryDeformation get_evaluated_grease_pencil_drawing_deformation(
const Depsgraph &depsgraph,
const Object &ob_orig,
const bke::greasepencil::Drawing &drawing_orig);
} // namespace bke::crazyspace
/**
* Disable subdivision-surface temporal, get mapped coordinates, and enable it.
*/
Array<float3> BKE_crazyspace_get_mapped_editverts(Depsgraph *depsgraph, Object *obedit);
void BKE_crazyspace_set_quats_editmesh(BMEditMesh *em,
Span<float3> origcos,
Span<float3> mappedcos,
float (*quats)[4],
bool use_select);
void BKE_crazyspace_set_quats_mesh(Mesh *mesh,
Span<float3> origcos,
Span<float3> mappedcos,
float (*quats)[4]);
/**
* Returns an array of deform matrices for crazy-space correction,
* and the number of modifiers left.
*/
int BKE_crazyspace_get_first_deform_matrices_editbmesh(Depsgraph *depsgraph,
Scene *,
Object *,
BMEditMesh *em,
Array<float3x3, 0> &deformmats,
Array<float3, 0> &deformcos);
int BKE_sculpt_get_first_deform_matrices(Depsgraph *depsgraph,
Scene *scene,
Object *ob,
Array<float3x3, 0> &deformmats,
Array<float3, 0> &deformcos);
void BKE_crazyspace_build_sculpt(Depsgraph *depsgraph,
Scene *scene,
Object *ob,
Array<float3x3, 0> &deformmats,
Array<float3, 0> &deformcos);
/* -------------------------------------------------------------------- */
/** \name Crazy-Space API
* \{ */
void BKE_crazyspace_api_eval(Depsgraph *depsgraph,
Scene *scene,
Object *object,
ReportList *reports);
void BKE_crazyspace_api_displacement_to_deformed(Object *object,
ReportList *reports,
int vertex_index,
const float displacement[3],
float r_displacement_deformed[3]);
void BKE_crazyspace_api_displacement_to_original(Object *object,
ReportList *reports,
int vertex_index,
const float displacement_deformed[3],
float r_displacement[3]);
void BKE_crazyspace_api_eval_clear(Object *object);
/** \} */
} // namespace blender

View File

@@ -0,0 +1,63 @@
/* SPDX-FileCopyrightText: 2020 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup bke
*/
#pragma once
#include "BLI_sys_types.h"
#include "DNA_layer_types.h"
namespace blender {
/* Forward declarations. */
struct CryptomatteSession;
struct Main;
struct Material;
struct Object;
struct RenderResult;
struct Scene;
struct CryptomatteSession *BKE_cryptomatte_init();
struct CryptomatteSession *BKE_cryptomatte_init_from_render_result(
const struct RenderResult *render_result);
/* Initializes a cryptomatte session from the view layers of the given scene. If build_meta_data is
* true, the object and material IDs in the view layer will be hashed and added to the Cryptomatte
* layers, allowing hash-name lookups. */
struct CryptomatteSession *BKE_cryptomatte_init_from_scene(const struct Scene *scene,
bool build_meta_data);
struct CryptomatteSession *BKE_cryptomatte_init_from_view_layer(
const struct ViewLayer *view_layer);
void BKE_cryptomatte_free(struct CryptomatteSession *session);
void BKE_cryptomatte_add_layer(struct CryptomatteSession *session, const char *layer_name);
uint32_t BKE_cryptomatte_hash(const char *name, int name_len);
uint32_t BKE_cryptomatte_object_hash(struct CryptomatteSession *session,
const char *layer_name,
const struct Object *object);
uint32_t BKE_cryptomatte_material_hash(struct CryptomatteSession *session,
const char *layer_name,
const struct Material *material);
uint32_t BKE_cryptomatte_asset_hash(struct CryptomatteSession *session,
const char *layer_name,
const struct Object *object);
float BKE_cryptomatte_hash_to_float(uint32_t cryptomatte_hash);
/**
* Find an ID in the given main that matches the given encoded float.
*/
bool BKE_cryptomatte_find_name(const struct CryptomatteSession *session,
float encoded_hash,
char *r_name,
int name_maxncpy);
char *BKE_cryptomatte_entries_to_matte_id(struct NodeCryptomatte *node_storage);
void BKE_cryptomatte_matte_id_to_entries(struct NodeCryptomatte *node_storage,
const char *matte_id);
void BKE_cryptomatte_store_metadata(const struct CryptomatteSession *session,
struct RenderResult *render_result);
} // namespace blender

View File

@@ -0,0 +1,144 @@
/* SPDX-FileCopyrightText: 2020 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup bke
*/
#pragma once
#include <algorithm>
#include <optional>
#include <string>
#include "BKE_cryptomatte.h"
#include "BLI_hash_mm3.hh"
#include "BLI_map.hh"
#include "BLI_string_ref.hh"
namespace blender {
struct ID;
namespace bke::cryptomatte {
/**
* Format to a cryptomatte meta data key.
*
* Cryptomatte stores meta data. The keys are formatted containing a hash that
* is generated from its layer name.
*
* The output of this function is:
* 'cryptomatte/{hash of layer_name}/{key_name}'.
*/
std::string BKE_cryptomatte_meta_data_key(StringRef layer_name, StringRefNull key_name);
/**
* Extract the cryptomatte layer name from the given `render_pass_name`.
*
* Cryptomatte passes are formatted with a trailing number for storing multiple samples that belong
* to the same cryptomatte layer. This function would remove the trailing numbers to determine the
* cryptomatte layer name.
*
* # Example
*
* A render_pass_name could be 'View Layer.CryptoMaterial02'. The cryptomatte layer would be 'View
* Layer.CryptoMaterial'.
*
* \note The return type is a sub-string of `render_pass_name` and therefore cannot outlive the
* `render_pass_name` internal data.
*/
StringRef BKE_cryptomatte_extract_layer_name(StringRef render_pass_name);
struct CryptomatteHash {
uint32_t hash;
CryptomatteHash(uint32_t hash);
CryptomatteHash(const char *name, int name_len)
{
hash = BLI_hash_mm3(reinterpret_cast<const unsigned char *>(name), name_len, 0);
}
static CryptomatteHash from_hex_encoded(StringRef hex_encoded);
std::string hex_encoded() const;
/**
* Convert a cryptomatte hash to a float.
*
* Cryptomatte hashes are stored in float textures and images. The conversion is taken from the
* cryptomatte specification. See Floating point conversion section in
* https://github.com/Psyop/Cryptomatte/blob/master/specification/cryptomatte_specification.pdf.
*
* The conversion uses as many 32 bit floating point values as possible to minimize hash
* collisions. Unfortunately not all 32 bits can be used as NaN and Inf can be problematic.
*
* Note that this conversion assumes to be running on a L-endian system.
*/
float float_encoded() const
{
uint32_t mantissa = hash & ((1 << 23) - 1);
uint32_t exponent = (hash >> 23) & ((1 << 8) - 1);
exponent = std::max(exponent, uint32_t(1));
exponent = std::min(exponent, uint32_t(254));
exponent = exponent << 23;
uint32_t sign = (hash >> 31);
sign = sign << 31;
uint32_t float_bits = sign | exponent | mantissa;
float f;
memcpy(&f, &float_bits, sizeof(uint32_t));
return f;
}
};
struct CryptomatteLayer {
Map<std::string, CryptomatteHash> hashes;
MEM_CXX_CLASS_ALLOC_FUNCS("cryptomatte:CryptomatteLayer")
static std::unique_ptr<CryptomatteLayer> read_from_manifest(StringRefNull manifest);
uint32_t add_ID(const ID &id);
void add_hash(StringRef name, CryptomatteHash cryptomatte_hash);
std::string manifest() const;
std::optional<std::string> operator[](float encoded_hash) const;
};
struct CryptomatteStampDataCallbackData {
CryptomatteSession *session;
Map<std::string, std::string> hash_to_layer_name;
/**
* Extract the hash from a stamp data key.
*
* Cryptomatte keys are formatted as "cryptomatte/{layer_hash}/{attribute}".
*/
static StringRef extract_layer_hash(StringRefNull key);
/* C type callback function (StampCallback). */
static void extract_layer_names(void *_data,
const char *propname,
char *propvalue,
int propvalue_maxncpy);
/* C type callback function (StampCallback). */
static void extract_layer_manifest(void *_data,
const char *propname,
char *propvalue,
int propvalue_maxncpy);
};
const Vector<std::string> &BKE_cryptomatte_layer_names_get(const CryptomatteSession &session);
CryptomatteLayer *BKE_cryptomatte_layer_get(CryptomatteSession &session, StringRef layer_name);
struct CryptomatteSessionDeleter {
void operator()(CryptomatteSession *session)
{
BKE_cryptomatte_free(session);
}
};
using CryptomatteSessionPtr = std::unique_ptr<CryptomatteSession, CryptomatteSessionDeleter>;
} // namespace bke::cryptomatte
} // namespace blender

View File

@@ -0,0 +1,465 @@
/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
/** \file
* \ingroup bke
*/
#include <optional>
#include "BLI_array.hh"
#include "BLI_bounds_types.hh"
#include "BLI_math_matrix_types.hh"
#include "BLI_math_vector_types.hh"
#include "BLI_span.hh"
#include "BLI_sys_types.h"
#include "DNA_curve_types.h"
#include "DNA_listBase.h"
namespace blender {
struct BezTriple;
struct BevList;
struct BMEditMesh;
struct BPoint;
struct Curve;
struct Depsgraph;
struct DispList;
struct Main;
struct MDeformVert;
struct Nurb;
struct Object;
struct rctf;
struct TextBox;
enum ObjectType : short;
using eBezTriple_Flag__Alias = int;
struct CurveCache {
ListBaseT<DispList> disp;
ListBaseT<BevList> bev;
ListBaseT<Nurb> deformed_nurbs;
/* This array contains the accumulative length of the curve segments.
* So you can see this as a "total distance traveled" along the curve.
* The first entry is the length between point 0 and 1 while the last is the
* total length of the curve.
*
* Used by #BKE_where_on_path. */
const float *anim_path_accum_length;
};
/* Definitions needed for shape keys */
struct CVKeyIndex {
void *orig_cv;
int key_index, nu_index, pt_index, vertex_index;
bool switched;
};
enum eNurbHandleTest_Mode {
/** Read the selection from each handle. */
NURB_HANDLE_TEST_EACH = 1,
/**
* When the knot (center point) is selected treat the handles as selected too.
* Otherwise use the same behavior as #NURB_HANDLE_TEST_EACH.
*/
NURB_HANDLE_TEST_KNOT_OR_EACH = 2,
/**
* When the knot is selected, treat all handles as selected, otherwise none.
* \note Typically used when handles are hidden.
*/
NURB_HANDLE_TEST_KNOT_ONLY = 3,
};
#define KNOTSU(nu) \
((nu)->orderu + (nu)->pntsu + (((nu)->flagu & CU_NURB_CYCLIC) ? ((nu)->orderu - 1) : 0))
#define KNOTSV(nu) \
((nu)->orderv + (nu)->pntsv + (((nu)->flagv & CU_NURB_CYCLIC) ? ((nu)->orderv - 1) : 0))
/* Non cyclic nurbs have 1 less segment */
#define SEGMENTSU(nu) (((nu)->flagu & CU_NURB_CYCLIC) ? (nu)->pntsu : (nu)->pntsu - 1)
#define SEGMENTSV(nu) (((nu)->flagv & CU_NURB_CYCLIC) ? (nu)->pntsv : (nu)->pntsv - 1)
#define CU_DO_RADIUS(cu, nu) \
((((cu)->flag & (CU_PATH_RADIUS | CU_3D)) || (cu)->bevobj || (cu)->extrude != 0.0f || \
(cu)->bevel_radius != 0.0f) ? \
1 : \
0)
#define CU_IS_2D(cu) (((cu)->flag & CU_3D) == 0)
/* not 3d and not unfilled */
#define CU_DO_2DFILL(cu) (CU_IS_2D(cu) && (((cu)->flag & (CU_FRONT | CU_BACK)) != 0))
/* ** Curve ** */
/**
* Frees edit-curve entirely.
*/
void BKE_curve_editfont_free(Curve *cu);
void BKE_curve_init(Curve *cu, ObjectType curve_type);
Curve *BKE_curve_add(Main *bmain, const char *name, ObjectType type);
void BKE_curve_type_test(Object *ob, bool dimension_update);
void BKE_curve_dimension_update(Curve *cu);
void BKE_curve_texspace_calc(Curve *cu);
void BKE_curve_texspace_ensure(Curve *cu);
/* Basic vertex data functions. */
std::optional<Bounds<float3>> BKE_curve_minmax(const Curve *cu, bool use_radius);
bool BKE_curve_center_median(Curve *cu, float cent[3]);
void BKE_curve_transform_ex(
Curve *cu, const float mat[4][4], bool do_keys, bool do_props, float unit_scale);
void BKE_curve_transform(Curve *cu, const float mat[4][4], bool do_keys, bool do_props);
void BKE_curve_translate(Curve *cu, const float offset[3], bool do_keys);
void BKE_curve_material_index_remove(Curve *cu, int index);
bool BKE_curve_material_index_used(const Curve *cu, int index);
void BKE_curve_material_index_clear(Curve *cu);
bool BKE_curve_material_index_validate(Curve *cu);
void BKE_curve_material_remap(Curve *cu, const unsigned int *remap, unsigned int remap_len);
void BKE_curve_smooth_flag_set(Curve *cu, bool use_smooth);
/**
* \return edit-nurbs or normal nurbs list.
*/
ListBaseT<Nurb> *BKE_curve_nurbs_get(Curve *cu);
const ListBaseT<Nurb> *BKE_curve_nurbs_get_for_read(const Curve *cu);
int BKE_curve_nurb_vert_index_get(const Nurb *nu, const void *vert);
void BKE_curve_nurb_active_set(Curve *cu, const Nurb *nu);
Nurb *BKE_curve_nurb_active_get(Curve *cu);
/**
* Get active vert for curve.
*/
void *BKE_curve_vert_active_get(Curve *cu);
/**
* Set active nurb and active vert for curve.
*/
void BKE_curve_nurb_vert_active_set(Curve *cu, const Nurb *nu, const void *vert);
/**
* Get points to the active nurb and active vert for curve.
*/
bool BKE_curve_nurb_vert_active_get(Curve *cu, Nurb **r_nu, void **r_vert);
void BKE_curve_nurb_vert_active_validate(Curve *cu);
Array<float3> BKE_curve_nurbs_vert_coords_alloc(const ListBaseT<Nurb> *lb);
void BKE_curve_nurbs_vert_coords_get(const ListBaseT<Nurb> *lb, MutableSpan<float3> vert_coords);
void BKE_curve_nurbs_vert_coords_apply_with_mat4(ListBaseT<Nurb> *lb,
const Span<float3>,
const float4x4 &transform,
bool constrain_2d);
void BKE_curve_nurbs_vert_coords_apply(ListBaseT<Nurb> *lb,
const Span<float3> vert_coords,
bool constrain_2d);
Array<float3> BKE_curve_nurbs_key_vert_coords_alloc(const ListBaseT<Nurb> *lb, const float *key);
void BKE_curve_nurbs_key_vert_tilts_apply(ListBaseT<Nurb> *lb, const float *key);
void BKE_curve_editNurb_keyIndex_delCV(CVKeyIndexMap *keyindex, const void *cv);
void BKE_curve_editNurb_keyIndex_free(CVKeyIndexMap **keyindex);
void BKE_curve_editNurb_free(Curve *cu);
/**
* Get list of nurbs from edit-nurbs structure.
*/
ListBaseT<Nurb> *BKE_curve_editNurbs_get(Curve *cu);
const ListBaseT<Nurb> *BKE_curve_editNurbs_get_for_read(const Curve *cu);
void BKE_curve_bevelList_free(ListBaseT<BevList> *bev);
void BKE_curve_bevelList_make(Object *ob, const ListBaseT<Nurb> *nurbs, bool for_render);
ListBaseT<DispList> BKE_curve_bevel_make(const Curve *curve);
/**
* Forward differencing method for bezier curve.
*/
void BKE_curve_forward_diff_bezier(
float q0, float q1, float q2, float q3, float *p, int it, int stride);
/**
* Forward differencing method for first derivative of cubic bezier curve.
*/
void BKE_curve_forward_diff_tangent_bezier(
float q0, float q1, float q2, float q3, float *p, int it, int stride);
void BKE_curve_rect_from_textbox(const Curve *cu, const TextBox *tb, rctf *r_rect);
/**
* This function is almost the same as #BKE_fcurve_correct_bezpart,
* but doesn't allow as large a tangent.
*/
void BKE_curve_correct_bezpart(const float v1[2], float v2[2], float v3[2], const float v4[2]);
/* ** Nurbs ** */
bool BKE_nurbList_index_get_co(ListBaseT<Nurb> *nurb, int index, float r_co[3]);
int BKE_nurbList_verts_count(const ListBaseT<Nurb> *nurb);
int BKE_nurbList_verts_count_without_handles(const ListBaseT<Nurb> *nurb);
void BKE_nurbList_free(ListBaseT<Nurb> *lb);
void BKE_nurbList_duplicate(ListBaseT<Nurb> *lb1, const ListBaseT<Nurb> *lb2);
/**
* \param code:
* - 1 (#HD_AUTO): set auto-handle.
* - 2 (#HD_VECT): set vector-handle.
* - 3 (#HD_ALIGN) it toggle, vector-handles become #HD_FREE.
*
* - 5: Set align, like 3 but no toggle.
* - 6: Clear align (setting #HD_FREE), like 3 but no toggle.
*/
void BKE_nurbList_handles_set(ListBaseT<Nurb> *editnurb,
eNurbHandleTest_Mode handle_mode,
eBezTriple_Handle code);
void BKE_nurbList_handles_recalculate(ListBaseT<Nurb> *editnurb,
bool calc_length,
eBezTriple_Flag flag);
void BKE_nurbList_handles_autocalc(ListBaseT<Nurb> *editnurb, eBezTriple_Flag flag);
void BKE_nurbList_flag_set(ListBaseT<Nurb> *editnurb, eBezTriple_Flag flag, bool set);
/**
* Set \a flag for every point that already has \a from_flag set.
*/
bool BKE_nurbList_flag_set_from_flag(ListBaseT<Nurb> *editnurb,
eBezTriple_Flag from_flag,
eBezTriple_Flag flag);
void BKE_nurb_free(Nurb *nu);
Nurb *BKE_nurb_duplicate(const Nurb *nu);
/**
* Copy the nurb but allow for different number of points (to be copied after this).
*/
Nurb *BKE_nurb_copy(Nurb *src, int pntsu, int pntsv);
void BKE_nurb_project_2d(Nurb *nu);
float BKE_nurb_calc_length(const Nurb *nu, int resolution);
/**
* \param coord_array: has to be `(3 * 4 * resolu * resolv)` in size, and zero-ed.
*/
void BKE_nurb_makeFaces(const Nurb *nu, float *coord_array, int rowstride, int resolu, int resolv);
/**
* \param coord_array: Has to be `(3 * 4 * pntsu * resolu)` in size and zero-ed
* \param tilt_array: set when non-NULL
* \param radius_array: set when non-NULL
*/
void BKE_nurb_makeCurve(const Nurb *nu,
float *coord_array,
float *tilt_array,
float *radius_array,
float *weight_array,
int resolu,
int stride);
/**
* Calculate the length for arrays filled in by #BKE_curve_calc_coords_axis.
*/
unsigned int BKE_curve_calc_coords_axis_len(unsigned int bezt_array_len,
unsigned int resolu,
bool is_cyclic,
bool use_cyclic_duplicate_endpoint);
/**
* Calculate an array for the entire curve (cyclic or non-cyclic).
* \note Call for each axis.
*
* \param use_cyclic_duplicate_endpoint: Duplicate values at the beginning & end of the array.
*/
void BKE_curve_calc_coords_axis(const BezTriple *bezt_array,
unsigned int bezt_array_len,
unsigned int resolu,
bool is_cyclic,
bool use_cyclic_duplicate_endpoint,
/* Array parameters. */
unsigned int axis,
unsigned int stride,
float *r_points);
void BKE_nurb_knot_alloc_u(Nurb *nu);
void BKE_nurb_knot_calc_u(Nurb *nu);
void BKE_nurb_knot_calc_v(Nurb *nu);
/* nurb checks if they can be drawn, also clamp order func */
bool BKE_nurb_check_valid_u(const Nurb *nu);
bool BKE_nurb_check_valid_v(const Nurb *nu);
bool BKE_nurb_check_valid_uv(const Nurb *nu);
bool BKE_nurb_valid_message(int pnts,
short order,
short flag,
short type,
bool is_surf,
int dir,
char *message_dst,
size_t maxncpy);
bool BKE_nurb_order_clamp_u(Nurb *nu);
bool BKE_nurb_order_clamp_v(Nurb *nu);
void BKE_nurb_direction_switch(Nurb *nu);
/**
* \note caller must ensure active vertex remains valid.
*/
bool BKE_nurb_type_convert(Nurb *nu, eNurbType type, bool use_handles, const char **r_err_msg);
/**
* Be sure to call #BKE_nurb_knot_calc_u / #BKE_nurb_knot_calc_v after this.
*/
void BKE_nurb_points_add(Nurb *nu, int number);
void BKE_nurb_bezierPoints_add(Nurb *nu, int number);
int BKE_nurb_index_from_uv(Nurb *nu, int u, int v);
void BKE_nurb_index_to_uv(Nurb *nu, int index, int *r_u, int *r_v);
BezTriple *BKE_nurb_bezt_get_next(Nurb *nu, BezTriple *bezt);
BezTriple *BKE_nurb_bezt_get_prev(Nurb *nu, BezTriple *bezt);
BPoint *BKE_nurb_bpoint_get_next(Nurb *nu, BPoint *bp);
BPoint *BKE_nurb_bpoint_get_prev(Nurb *nu, BPoint *bp);
void BKE_nurb_bezt_calc_normal(Nurb *nu, BezTriple *bezt, float r_normal[3]);
void BKE_nurb_bezt_calc_plane(Nurb *nu, BezTriple *bezt, float r_plane[3]);
void BKE_nurb_bpoint_calc_normal(Nurb *nu, BPoint *bp, float r_normal[3]);
void BKE_nurb_bpoint_calc_plane(Nurb *nu, BPoint *bp, float r_plane[3]);
/**
* Recalculate the handles of a nurb bezier-triple. Acts based on handle selection with `SELECT`
* flag. To use a different flag, use #BKE_nurb_handle_calc_ex().
*/
void BKE_nurb_handle_calc(
BezTriple *bezt, BezTriple *prev, BezTriple *next, bool is_fcurve, char smoothing);
/**
* Variant of #BKE_nurb_handle_calc() that allows calculating based on a different select flag.
*
* \param handle_sel_flag: The flag (bezt.f1/2/3) value to use to determine selection.
* Usually #SELECT, but may want to use a different one at times
* (if caller does not operate on selection).
*/
void BKE_nurb_handle_calc_ex(BezTriple *bezt,
BezTriple *prev,
BezTriple *next,
eBezTriple_Flag__Alias handle_sel_flag,
bool is_fcurve,
char smoothing);
/**
* Similar to #BKE_nurb_handle_calc but for curves and figures out the previous and next for us.
*/
void BKE_nurb_handle_calc_simple(Nurb *nu, BezTriple *bezt);
void BKE_nurb_handle_calc_simple_auto(Nurb *nu, BezTriple *bezt);
void BKE_nurb_handle_smooth_fcurve(BezTriple *bezt, int total, bool cyclic);
void BKE_nurb_handles_calc(Nurb *nu);
void BKE_nurb_handles_autocalc(Nurb *nu, eBezTriple_Flag flag);
/**
* Return a flag for the handles to treat as "selected":
* `1 << 0`, `1 << 1`, `1 << 2` map to handles 1 2 & 3.
*/
short BKE_nurb_bezt_handle_test_calc_flag(const BezTriple *bezt,
const eBezTriple_Flag__Alias sel_flag,
const eNurbHandleTest_Mode handle_mode);
/**
* Update selected handle types to ensure valid state, e.g. deduce "Auto" types to concrete ones.
* Thereby \a sel_flag defines what qualifies as selected.
* Use when something has changed handle positions.
*
* The caller needs to recalculate handles.
*
* \param sel_flag: The flag (bezt.f1/2/3) value to use to determine selection. Usually `SELECT`,
* but may want to use a different one at times (if caller does not operate on * selection).
* \param handle_mode: Interpret the selection base on modes in #eNurbHandleTest_Mode.
*/
void BKE_nurb_bezt_handle_test(BezTriple *bezt,
eBezTriple_Flag__Alias sel_flag,
const eNurbHandleTest_Mode handle_mode,
bool use_around_local);
void BKE_nurb_handles_test(Nurb *nu, eNurbHandleTest_Mode handle_mode, bool use_around_local);
/* **** Depsgraph evaluation **** */
void BKE_curve_eval_geometry(Depsgraph *depsgraph, Curve *curve);
/* Draw Cache */
enum {
BKE_CURVE_BATCH_DIRTY_ALL = 0,
BKE_CURVE_BATCH_DIRTY_SELECT,
};
void BKE_curve_batch_cache_dirty_tag(Curve *cu, int mode);
void BKE_curve_batch_cache_free(Curve *cu);
extern void (*BKE_curve_batch_cache_dirty_tag_cb)(Curve *cu, int mode);
extern void (*BKE_curve_batch_cache_free_cb)(Curve *cu);
/* -------------------------------------------------------------------- */
/** \name Decimate Curve (`curve_decimate.cc`)
*
* Simplify curve data.
* \{ */
unsigned int BKE_curve_decimate_bezt_array(BezTriple *bezt_array,
unsigned int bezt_array_len,
unsigned int resolu,
bool is_cyclic,
eBezTriple_Flag flag_test,
eBezTriple_Flag flag_set,
float error_sq_max,
unsigned int error_target_len);
void BKE_curve_decimate_nurb(Nurb *nu,
unsigned int resolu,
float error_sq_max,
unsigned int error_target_len);
/** \} */
/* -------------------------------------------------------------------- */
/** \name Deform 3D Coordinates by Curve (`curve_deform.cc`)
* \{ */
void BKE_curve_deform_coords(const Object *ob_curve,
const Object *ob_target,
float (*vert_coords)[3],
int vert_coords_len,
const MDeformVert *dvert,
int defgrp_index,
short flag,
short defaxis);
void BKE_curve_deform_coords_with_editmesh(const Object *ob_curve,
const Object *ob_target,
float (*vert_coords)[3],
int vert_coords_len,
int defgrp_index,
short flag,
short defaxis,
const BMEditMesh *em_target);
/**
* \param orco: Input vec and orco = local coord in curve space
* orco is original not-animated or deformed reference point.
*
* The result written to `vec` and `r_mat`.
*/
void BKE_curve_deform_co(const Object *ob_curve,
const Object *ob_target,
const float orco[3],
float vec[3],
int no_rot_axis,
float r_mat[3][3]);
/** \} */
/* `curve_convert.cc` */
/* Create a new curve from the given object at its current state. This only works for curve and
* text objects, otherwise NULL is returned.
*
* If apply_modifiers is true and the object is a curve one, then spline deform modifiers are
* applied on the control points of the splines.
*/
Curve *BKE_curve_new_from_object(Object *object, Depsgraph *depsgraph, bool apply_modifiers);
} // namespace blender

View File

@@ -0,0 +1,37 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
/** \file
* \ingroup bke
*/
#include "DNA_curves_types.h"
#include "DNA_listBase.h"
namespace blender {
struct Curve;
struct Nurb;
namespace bke {
/**
* Convert the old curve type to the new data type. Caller owns the returned pointer.
*/
Curves *curve_legacy_to_curves(const Curve &curve_legacy);
/**
* Convert the old curve type to the new data type using a specific list of #Nurb for the actual
* geometry data. Caller owns the returned pointer.
*/
Curves *curve_legacy_to_curves(const Curve &curve_legacy, const ListBaseT<Nurb> &nurbs_list);
/**
* Determine Curves knot mode from legacy flag.
*/
KnotsMode knots_mode_from_legacy(short flag);
} // namespace bke
} // namespace blender

View File

@@ -0,0 +1,46 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup bke
*/
#pragma once
#include "BLI_virtual_array_fwd.hh"
#include "BKE_attribute_filter.hh"
namespace blender {
struct Mesh;
namespace bke {
class CurvesGeometry;
/**
* Extrude all splines in the profile curve along the path of every spline in the curve input.
* Transfer curve attributes to the mesh.
*
* \note Normal calculation is by far the slowest part of calculations relating to the result mesh.
* Although it would be a sensible decision to use the better topology information available while
* generating the mesh to also generate the normals, that work may wasted if the output mesh is
* changed anyway in a way that affects the normals. So currently this code uses the safer /
* simpler solution of deferring normal calculation to the rest of Blender.
*/
Mesh *curve_to_mesh_sweep(const CurvesGeometry &main,
const CurvesGeometry &profile,
const VArray<float> &scales,
bool fill_caps,
const bke::AttributeFilter &attribute_filter = {});
/**
* Create a loose-edge mesh based on the evaluated path of the curve's splines.
* Transfer curve attributes to the mesh.
*/
Mesh *curve_to_wire_mesh(const CurvesGeometry &curve,
const bke::AttributeFilter &attribute_filter = {});
} // namespace bke
} // namespace blender

View File

@@ -0,0 +1,178 @@
/* SPDX-FileCopyrightText: 2019 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
/** \file
* \ingroup bke
*/
#include "BLI_math_vector_types.hh"
#include "DNA_curveprofile_types.h"
namespace blender {
struct BlendDataReader;
struct BlendWriter;
struct CurveProfile;
struct CurveProfilePoint;
/**
* Sets the default settings and clip range for the profile widget.
* Does not generate either table.
*/
void BKE_curveprofile_set_defaults(struct CurveProfile *profile);
/**
* Returns a pointer to a newly allocated curve profile, using the given preset.
*/
struct CurveProfile *BKE_curveprofile_add(eCurveProfilePresets preset);
void BKE_curveprofile_free_data(struct CurveProfile *profile);
void BKE_curveprofile_free(struct CurveProfile *profile);
void BKE_curveprofile_copy_data(struct CurveProfile *target, const struct CurveProfile *profile);
struct CurveProfile *BKE_curveprofile_copy(const struct CurveProfile *profile);
/**
* Move a point's handle, accounting for the alignment of handles with the #HD_ALIGN type.
*
* \param handle_1: Whether to move the 1st or 2nd control point.
* \param delta: The *relative* change in the handle's position.
* \note Requires #BKE_curveprofile_update call after.
* \return Whether the handle moved from its start position.
*/
bool BKE_curveprofile_move_handle(struct CurveProfilePoint *point,
bool handle_1,
bool snap,
const float delta[2]);
/**
* Moves a control point, accounting for clipping and snapping, and moving free handles.
*
* \param snap: Whether to snap the point to the grid
* \param delta: The *relative* change of the point's location.
* \return Whether the point moved from its start position.
* \note Requires #BKE_curveprofile_update call after.
*/
bool BKE_curveprofile_move_point(struct CurveProfile *profile,
struct CurveProfilePoint *point,
bool snap,
const float delta[2]);
void BKE_curveprofile_translate_selection(struct CurveProfile *profile,
const blender::float2 &offset);
/**
* Removes a specific point from the path of control points.
* \note Requires #BKE_curveprofile_update call after.
*/
bool BKE_curveprofile_remove_point(struct CurveProfile *profile, struct CurveProfilePoint *point);
/**
* Removes every point in the widget with the supplied flag set, except for the first and last.
*
* \param flag: #CurveProfilePoint.flag.
*
* \note Requires #BKE_curveprofile_update call after.
*/
void BKE_curveprofile_remove_by_flag(struct CurveProfile *profile, short flag);
/**
* Adds a new point at the specified location. The choice for which points to place the new vertex
* between is made by checking which control point line segment is closest to the new point and
* placing the new vertex in between that segment's points.
*
* \note Requires #BKE_curveprofile_update call after.
*/
struct CurveProfilePoint *BKE_curveprofile_insert(struct CurveProfile *profile, float x, float y);
/**
* Sets the handle type of the selected control points.
* \param type_1: Handle type for the left handle.
* \param type_2: Handle type for the right handle.
*
* \note Valid handle types are: #HD_VECT, #HD_AUTO, #HD_FREE, or #HD_ALIGN.
* \note Requires #BKE_curveprofile_update call after.
*/
void BKE_curveprofile_selected_handle_set(struct CurveProfile *profile, int type_1, int type_2);
/**
* Flips the profile across the diagonal so that its orientation is reversed.
*
* \note Requires #BKE_curveprofile_update call after.
*/
void BKE_curveprofile_reverse(struct CurveProfile *profile);
/**
* Reset the view to the clipping rectangle.
*/
void BKE_curveprofile_reset_view(struct CurveProfile *profile);
/**
* Resets the profile to the current preset.
*
* \note Requires #BKE_curveprofile_update call after.
*/
void BKE_curveprofile_reset(struct CurveProfile *profile);
/**
* When the current point is deselected, activate the closest remaining point
* by index. The function searches for the nearest valid index relative to the previously
* active index, not the nearest point by distance.
*/
void BKE_curveprofile_activate_nearest_point(struct CurveProfile *profile, const int i_last);
int BKE_curveprofile_table_size(const struct CurveProfile *profile);
/**
* Refreshes the higher resolution table sampled from the input points. A call to this or
* #BKE_curveprofile_update is needed before evaluation functions that use the table.
* Also sets the number of segments used for the display preview of the locations
* of the sampled points.
*/
void BKE_curveprofile_init(struct CurveProfile *profile, short segments_len);
/* Called for a complete update of the widget after modifications */
enum {
PROF_UPDATE_NONE = 0,
PROF_UPDATE_REMOVE_DOUBLES = (1 << 0),
PROF_UPDATE_CLIP = (1 << 1),
};
/**
* Should be called after the widget is changed. Does profile and remove double checks and more
* importantly, recreates the display / evaluation and segments tables.
* \param update_flags: Bit-field with fields defined in header file.
* Controls removing doubles and clipping.
*/
void BKE_curveprofile_update(struct CurveProfile *profile, int update_flags);
/**
* Does a single evaluation along the profile's path.
* Travels down (length_portion * path) length and returns the position at that point.
* Where length portion is the fraction of the total path length where we want the location.
*
* \param length_portion: The portion (0 to 1) of the path's full length to sample at.
* \note Requires #BKE_curveprofile_init or #BKE_curveprofile_update call before to fill table.
*/
void BKE_curveprofile_evaluate_length_portion(const struct CurveProfile *profile,
float length_portion,
float *x_out,
float *y_out);
CurveProfilePoint *BKE_curveprofile_active_get(CurveProfile *profile);
/**
* Return a pointer to the location of the active point or handle associated with a selected point.
*/
float *BKE_curveprofile_active_location_get(struct CurveProfilePoint *point);
void BKE_curveprofile_blend_write(struct BlendWriter *writer, const struct CurveProfile *profile);
/**
* Expects that the curve profile itself has been read already.
*/
void BKE_curveprofile_blend_read(struct BlendDataReader *reader, struct CurveProfile *profile);
} // namespace blender

View File

@@ -0,0 +1,50 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
#include "DNA_curves_types.h"
#include "BLI_string_ref.hh"
namespace blender {
/** \file
* \ingroup bke
* \brief Low-level operations for curves that cannot be defined in the C++ header yet.
*/
struct Curves;
struct Depsgraph;
struct Main;
struct Object;
struct Scene;
struct Curves *BKE_curves_add(struct Main *bmain, const char *name);
bool BKE_curves_attribute_required(const struct Curves *curves, StringRef name);
void BKE_curves_material_remap(struct Curves *curves, const unsigned int *remap, int remap_num);
/* Depsgraph */
struct Curves *BKE_curves_copy_for_eval(const struct Curves *curves_src);
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);
} // namespace blender

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,645 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
#include "BKE_curves.hh"
/** \file
* \ingroup bke
* \brief Low-level operations for curves.
*/
#include "BLI_function_ref.hh"
#include "BLI_generic_pointer.hh"
#include "BLI_index_range.hh"
namespace blender::bke::curves {
/* -------------------------------------------------------------------- */
/** \name Utility Structs
* \{ */
/**
* Reference to a piecewise segment on a spline curve.
*/
struct CurveSegment {
/**
* Index of the previous control/evaluated point on the curve. First point on the segment.
*/
int index;
/**
* Index of the next control/evaluated point on the curve. Last point on the curve segment.
* Should be 0 for looped segments.
*/
int next_index;
};
/**
* Reference to a point on a piecewise curve (spline).
*
* Tracks indices of the neighboring control/evaluated point pair associated with the segment
* in which the point resides. Referenced point within the segment is defined by a
* normalized parameter in the range [0, 1].
*/
struct CurvePoint : public CurveSegment {
/**
* Normalized parameter in the range [0, 1] defining the point on the piecewise segment.
* Note that the curve point representation is not unique at segment endpoints.
*/
float parameter;
/**
* True if the parameter is an integer and references a control/evaluated point.
*/
inline bool is_controlpoint() const;
/*
* Compare if the points are equal.
*/
inline bool operator==(const CurvePoint &other) const;
inline bool operator!=(const CurvePoint &other) const;
/**
* Compare if 'this' point comes before 'other'. Loop segment for cyclical curves counts
* as the first (least) segment.
*/
inline bool operator<(const CurvePoint &other) const;
};
/**
* Cyclical index range. Allows iteration over a plain 'IndexRange' interval on form [start, end)
* while also supporting treating the underlying array as a cyclic array where the last index is
* followed by the first index in the 'cyclical' range. The cyclical index range can then be
* considered a combination of the intervals separated by the last index of the underlying array,
* namely [start, range_size) and [0, end) where start/end is the indices iterated between and
* range_size is the size of the underlying array. To cycle the underlying array the interval
* [0, range_size) can be iterated over an arbitrary amount of times in between.
*/
class IndexRangeCyclic {
/**
* Index to the start and end of the iterated range.
*/
int start_ = 0;
int end_ = 0;
/**
* Size of the underlying iterable range.
*/
int range_size_ = 0;
/**
* Number of times the range end is passed when the range is iterated.
*/
int cycles_ = 0;
public:
constexpr IndexRangeCyclic() = default;
~IndexRangeCyclic() = default;
constexpr IndexRangeCyclic(const int start,
const int end,
const int iterable_range_size,
const int cycles)
: start_(start), end_(end), range_size_(iterable_range_size), cycles_(cycles)
{
}
/**
* Create an iterator over the cyclical interval [start_index, end_index).
*/
constexpr IndexRangeCyclic(const int start, const int end, const int iterable_range_size)
: start_(start),
end_(end == iterable_range_size ? 0 : end),
range_size_(iterable_range_size),
cycles_(end < start)
{
}
/**
* Create a cyclical iterator of the specified size.
*
* \param start_index: Point on the curve that define the starting point of the interval.
* \param iterator_size: Number of elements to iterate (size of the iterated cyclical range).
* \param iterable_range_size: Size of the underlying range (superset to the cyclical range).
*/
static IndexRangeCyclic get_range_from_size(const int start_index,
const int iterator_size,
const int iterable_range_size)
{
BLI_assert(start_index >= 0);
BLI_assert(iterator_size >= 0);
BLI_assert(iterable_range_size > 0);
const int num_until_loop = iterable_range_size - start_index;
if (iterator_size < num_until_loop) {
return IndexRangeCyclic(start_index, start_index + iterator_size, iterable_range_size, 0);
}
const int num_remaining = iterator_size - num_until_loop;
/* Integer division (rounded down). */
const int num_full_cycles = num_remaining / iterable_range_size;
const int end_index = num_remaining - num_full_cycles * iterable_range_size;
return IndexRangeCyclic(start_index, end_index, iterable_range_size, num_full_cycles + 1);
}
/**
* Create a cyclical iterator for all control points within the interval [start_point, end_point]
* including any control point at the start or end point.
*
* \param start_point: Point on the curve that define the starting point of the interval.
* \param end_point: Point on the curve that define the end point of the interval (included).
* \param iterable_range_size: Size of the underlying range (superset to the cyclical range).
*/
static IndexRangeCyclic get_range_between_endpoints(const CurvePoint start_point,
const CurvePoint end_point,
const int iterable_range_size)
{
BLI_assert(iterable_range_size > 0);
const int start_index = start_point.parameter == 0.0 ? start_point.index :
start_point.next_index;
int end_index = end_point.parameter == 0.0 ? end_point.index : end_point.next_index;
int cycles;
if (end_point.is_controlpoint()) {
BLI_assert(end_index < iterable_range_size);
++end_index;
if (end_index == iterable_range_size) {
end_index = 0;
}
/* end_point < start_point but parameter is irrelevant (end_point is controlpoint), and loop
* when equal due to increment. */
cycles = end_index <= start_index;
}
else {
cycles = end_point < start_point || end_index < start_index;
}
return IndexRangeCyclic(start_index, end_index, iterable_range_size, cycles);
}
/**
* Next index within the iterable range.
*/
template<typename IndexT> constexpr IndexT next_index(const IndexT index, const bool cyclic)
{
static_assert((is_same_any_v<IndexT, int, int>), "Expected signed integer type.");
const IndexT next_index = index + 1;
if (next_index == this->size_range()) {
return cyclic ? 0 : index;
}
return next_index;
}
/**
* Previous index within the iterable range.
*/
template<typename IndexT> constexpr IndexT previous_index(const IndexT index, const bool cyclic)
{
static_assert((is_same_any_v<IndexT, int, int64_t>), "Expected signed integer type.");
const IndexT prev_index = index - 1;
if (prev_index < 0) {
return cyclic ? this->size_range() - 1 : 0;
}
return prev_index;
}
/**
* Increment the range by adding `n` loops to the range. This invokes undefined behavior when n
* is negative.
*/
constexpr IndexRangeCyclic push_loop(const int n = 1) const
{
return {this->start_, this->end_, this->range_size_, this->cycles_ + n};
}
/**
* Increment the range by adding the given number of indices to the beginning of the iterated
* range. This invokes undefined behavior when n is negative.
*/
constexpr IndexRangeCyclic push_front(const int n = 1) const
{
BLI_assert(n >= 0);
int new_start = this->start_ - n;
int num_cycles = this->cycles_;
if (new_start < 0) {
const int new_cycles = n / this->size_range(); /* Integer division (floor) */
const int remainder = new_start + this->size_range() * new_cycles;
const bool underflow = remainder < 0;
new_start = remainder + (underflow ? this->size_range() : 0);
num_cycles += new_cycles + int(underflow);
}
BLI_assert(num_cycles >= 0);
BLI_assert(num_cycles > 0 ||
(new_start <= this->end_ || (this->end_ == 0 && new_start < this->size_range())));
return {new_start, this->end_, this->range_size_, num_cycles};
}
/**
* Increment the range by adding the given number of indices to the end of the iterated range.
* This invokes undefined behavior when n is negative.
*/
constexpr IndexRangeCyclic push_back(const int n = 1) const
{
BLI_assert(n >= 0);
int new_end = this->end_ + n;
int num_cycles = this->cycles_;
if (this->size_range() <= new_end) {
const int new_cycles = n / this->size_range(); /* Integer division (floor) */
const int remainder = new_end - this->size_range() * new_cycles;
const bool overflow = remainder >= this->size_range();
new_end = remainder - (overflow ? this->size_range() : 0);
num_cycles += new_cycles + int(overflow);
}
BLI_assert(num_cycles >= 0);
BLI_assert(num_cycles > 0 || (this->start_ <= new_end || new_end == 0));
return {this->start_, new_end, this->range_size_, num_cycles};
}
/**
* Returns a new range with n indices removed from the beginning of the range.
* This invokes undefined behavior.
*/
constexpr IndexRangeCyclic drop_front(const int n = 1) const
{
BLI_assert(n >= 0);
int new_start = this->start_ + n;
int num_cycles = this->cycles_;
if (this->size_range() <= new_start) {
const int dropped_cycles = n / this->size_range(); /* Integer division (floor) */
const int remainder = new_start - this->size_range() * dropped_cycles;
const bool overflow = remainder >= this->size_range();
new_start = remainder - (overflow ? this->size_range() : 0);
num_cycles -= dropped_cycles + int(overflow);
}
BLI_assert(num_cycles >= 0);
BLI_assert(num_cycles > 0 ||
(new_start <= this->end_ || (this->end_ == 0 && new_start < this->size_range())));
return {new_start, this->end_, this->range_size_, num_cycles};
}
/**
* Returns a new range with n indices removed from the end of the range.
* This invokes undefined behavior when n is negative or n is larger then the underlying range.
*/
constexpr IndexRangeCyclic drop_back(const int n = 1) const
{
BLI_assert(n >= 0);
int new_end = this->end_ - n;
int num_cycles = this->cycles_;
if (0 >= new_end) {
const int dropped_cycles = n / this->size_range(); /* Integer division (floor) */
const int remainder = new_end + this->size_range() * dropped_cycles;
const bool underflow = remainder < 0;
new_end = remainder + (underflow ? this->size_range() : 0);
num_cycles -= dropped_cycles + int(underflow);
}
BLI_assert(num_cycles >= 0);
BLI_assert(num_cycles > 0 || (this->start_ <= new_end || new_end == 0));
return {this->start_, new_end, this->range_size_, num_cycles};
}
/**
* Get the index range for the curve buffer.
*/
constexpr IndexRange curve_range() const
{
return IndexRange(0, this->size_range());
}
/**
* Range between the first element up to the end of the range.
*/
constexpr IndexRange range_before_loop() const
{
return IndexRange(this->start_, this->size_before_loop());
}
/**
* Range between the first element in the iterable range up to the last element in the range.
*/
constexpr IndexRange range_after_loop() const
{
return IndexRange(0, this->size_after_loop());
}
/**
* Number of elements in the underlying iterable range.
*/
constexpr int size_range() const
{
return this->range_size_;
}
/**
* Number of elements between the first element in the range up to the last element in the curve.
*/
constexpr int size_before_loop() const
{
return this->range_size_ - this->start_;
}
/**
* Number of elements between the first element in the iterable range up to the last element in
* the range.
*/
constexpr int size_after_loop() const
{
return this->end_;
}
/**
* Number of elements iterated by the cyclical index range.
*/
constexpr int size() const
{
if (this->cycles_ > 0) {
return this->size_before_loop() + this->end_ + (this->cycles_ - 1) * this->range_size_;
}
return int(this->end_ - this->start_);
}
/**
* Return the number of times the iterator will cycle before ending.
*/
constexpr int cycles() const
{
return this->cycles_;
}
constexpr int first() const
{
return this->start_;
}
constexpr int last() const
{
BLI_assert(this->size() > 0);
return int(this->end_ - 1);
}
constexpr int one_after_last() const
{
return this->end_;
}
constexpr bool operator==(const IndexRangeCyclic &other) const
{
return this->start_ == other.start_ && this->end_ == other.end_ &&
this->cycles_ == other.cycles_ && this->range_size_ == other.range_size_;
}
constexpr bool operator!=(const IndexRangeCyclic &other) const
{
return !this->operator==(other);
}
struct CyclicIterator; /* Forward declaration */
constexpr CyclicIterator begin() const
{
return CyclicIterator(this->range_size_, this->start_, 0);
}
constexpr CyclicIterator end() const
{
return CyclicIterator(this->range_size_, this->end_, this->cycles_);
}
struct CyclicIterator {
int index_, range_end_, cycles_;
constexpr CyclicIterator(const int range_end, const int index, const int cycles)
: index_(index), range_end_(range_end), cycles_(cycles)
{
BLI_assert(0 <= index && index <= range_end);
}
constexpr CyclicIterator(const CyclicIterator &copy) = default;
~CyclicIterator() = default;
constexpr CyclicIterator &operator=(const CyclicIterator &copy)
{
if (this == &copy) {
return *this;
}
this->index_ = copy.index_;
this->range_end_ = copy.range_end_;
this->cycles_ = copy.cycles_;
return *this;
}
constexpr CyclicIterator &operator++()
{
this->index_++;
if (this->index_ == this->range_end_) {
this->index_ = 0;
this->cycles_++;
}
return *this;
}
void increment(const int n)
{
for (int i = 0; i < n; i++) {
++*this;
}
}
constexpr const int &operator*() const
{
return this->index_;
}
constexpr bool operator==(const CyclicIterator &other) const
{
return this->index_ == other.index_ && this->cycles_ == other.cycles_;
}
constexpr bool operator!=(const CyclicIterator &other) const
{
return !this->operator==(other);
}
};
};
/** \} */
/* -------------------------------------------------------------------- */
/** \name Utility Functions
* \{ */
IndexMask curve_to_point_selection(OffsetIndices<int> points_by_curve,
const IndexMask &curve_selection,
LinearAllocator<> &memory);
/**
* Create a mask for all curves that have at least one point in the point mask.
*/
IndexMask point_to_curve_selection(OffsetIndices<int> points_by_curve,
const IndexMask &point_mask,
LinearAllocator<> &memory);
IndexMask curve_type_point_selection(const bke::CurvesGeometry &curves,
CurveType curve_type,
LinearAllocator<> &memory);
void fill_points(OffsetIndices<int> points_by_curve,
const IndexMask &curve_selection,
GPointer value,
GMutableSpan dst);
template<typename T>
void fill_points(const OffsetIndices<int> points_by_curve,
const IndexMask &curve_selection,
const T &value,
MutableSpan<T> dst)
{
fill_points(points_by_curve, curve_selection, &value, dst);
}
/**
* Create new curves with the same number of curves as the input, but no points. Copy all curve
* domain attributes to the new curves, except the offsets encoding the size of each curve.
*
* Used for operations that change the number of points but not the number of curves, allowing
* creation of the new offsets directly inside the new array.
*
* \warning The returned curves have invalid offsets!
*/
bke::CurvesGeometry copy_only_curve_domain(const bke::CurvesGeometry &src_curves);
IndexMask indices_for_type(const VArray<int8_t> &types,
const std::array<int, CURVE_TYPES_NUM> &type_counts,
const CurveType type,
const IndexMask &selection,
LinearAllocator<> &memory);
void foreach_curve_by_type(const VArray<int8_t> &types,
const std::array<int, CURVE_TYPES_NUM> &type_counts,
const IndexMask &selection,
FunctionRef<void(IndexMask)> catmull_rom_fn,
FunctionRef<void(IndexMask)> poly_fn,
FunctionRef<void(IndexMask)> bezier_fn,
FunctionRef<void(IndexMask)> nurbs_fn);
using SelectedCallback = FunctionRef<void(
int curve_i, IndexRange curve_points, Span<IndexRange> selected_point_ranges)>;
using UnselectedCallback = FunctionRef<void(IndexRange curves, IndexRange unselected_points)>;
/**
* Calls callback function for each curve having selected points.
*
* \param mask: selected points.
* \param points_by_curve: The offsets of every curve into arrays on the points domain.
* \param selected_fn: callback function called for each curve with at least one point selected.
*/
void foreach_selected_point_ranges_per_curve(const IndexMask &mask,
OffsetIndices<int> points_by_curve,
SelectedCallback selected_fn);
/**
* Calls callback function for each curve having selected points.
* Calls second callback for groups of curves with no points selected.
*
* \param mask: selected points.
* \param points_by_curve: The offsets of every curve into arrays on the points domain.
* \param selected_fn: callback function called for each curve with at least one point selected.
* \param unselected_fn: callback function called for groups of curves with no selected points.
*/
void foreach_selected_point_ranges_per_curve(const IndexMask &mask,
OffsetIndices<int> points_by_curve,
SelectedCallback selected_fn,
UnselectedCallback unselected_fn);
namespace bezier {
/**
* Return a flat array of all the bezier positions including the left and right handles.
* The layout is
* `[handle_left#0, position#0, handle_right#0, handle_left#1, position#1, handle_right#1, ...]`
*/
Array<float3> retrieve_all_positions(const bke::CurvesGeometry &curves,
const IndexMask &curves_selection);
/**
* Write to `handle_position_left`, `position`, and `handle_position_right` from a lat array of
* positions.
* \param curves_selection: The curves to write to.
* \param all_positions: All positions of the selected bezier curves. The size of \a all_positions
* must be equal to 3 * the size of \a curves_selection.
*/
void write_all_positions(bke::CurvesGeometry &curves,
const IndexMask &curves_selection,
Span<float3> all_positions);
} // namespace bezier
namespace nurbs {
/**
* Gathers NURBS custom knots of selected curves from one `CurvesGeometry` instance to another.
* Should be used to implement operator's custom knot copying logic.
* `dst_curve_offset` can be used to append knots to already existing ones in the `CurvesGeometry`.
*/
void gather_custom_knots(const bke::CurvesGeometry &src,
const IndexMask &src_curves,
int dst_curve_offset,
bke::CurvesGeometry &dst);
/**
* Overwrites `NURBS_KNOT_MODE_CUSTOM` to given ones for regular and cyclic curves.
* The purpose is to update knot modes for curves when knot copying or calculation is not
* possible or too complex. Curve operators not supporting NURBS custom knots should call this
* function with `IndexMask` `CurvesGeometry.curves_range()`, if resulting curves are created by
* copying all attributes. This way `NURBS_KNOT_MODE_CUSTOM` values might be copied though custom
* knots not.
*/
void update_custom_knot_modes(const IndexMask &mask,
const KnotsMode mode_for_regular,
const KnotsMode mode_for_cyclic,
bke::CurvesGeometry &curves);
/**
* Copies NURBS custom knots from one `CurvesGeometry` instance to another excluding
* `exclude_curves`.
* For excluded curves with `NURBS_KNOT_MODE_CUSTOM` knot mode is overwritten to
* `NURBS_KNOT_MODE_NORMAL`.
*/
void copy_custom_knots(const bke::CurvesGeometry &src_curves,
const IndexMask &exclude_curves,
bke::CurvesGeometry &dst_curves);
} // namespace nurbs
/** \} */
/* -------------------------------------------------------------------- */
/** \name #CurvePoint Inline Methods
* \{ */
inline bool CurvePoint::is_controlpoint() const
{
return parameter == 0.0 || parameter == 1.0;
}
inline bool CurvePoint::operator==(const CurvePoint &other) const
{
return (parameter == other.parameter && index == other.index) ||
(parameter == 1.0 && other.parameter == 0.0 && next_index == other.index) ||
(parameter == 0.0 && other.parameter == 1.0 && index == other.next_index);
}
inline bool CurvePoint::operator!=(const CurvePoint &other) const
{
return !this->operator==(other);
}
inline bool CurvePoint::operator<(const CurvePoint &other) const
{
if (index == other.index) {
return parameter < other.parameter;
}
/* Use next index for cyclic comparison due to loop segment < first segment. */
return next_index < other.next_index &&
!(next_index == other.index && parameter == 1.0 && other.parameter == 0.0);
}
/** \} */
} // namespace blender::bke::curves

View File

@@ -0,0 +1,708 @@
/* SPDX-FileCopyrightText: 2006 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup bke
* \brief CustomData interface, see also DNA_customdata_types.h.
*/
#pragma once
#include <optional>
#include "BLI_generic_virtual_array.hh"
#include "BLI_implicit_sharing.h"
#include "BLI_memory_counter_fwd.hh"
#include "BLI_span.hh"
#include "BLI_string_ref.hh"
#include "BLI_sys_types.h"
#include "BLI_vector.hh"
#include "BKE_volume_enums.hh"
#include "DNA_customdata_types.h"
namespace blender {
struct BlendDataReader;
struct BlendWriter;
struct BMesh;
struct CustomData_MeshMasks;
struct CustomData;
struct CustomDataTransferLayerMap;
struct ID;
struct MeshPairRemap;
namespace bke {
enum class AttrDomain : int8_t;
}
/**
* These names are used as prefixes for UV layer names to find the associated boolean
* layers. They should never be longer than 2 chars, as #MAX_CUSTOMDATA_LAYER_NAME
* has 4 extra bytes above what can be used for the base layer name, and these
* prefixes are placed between 2 '.'s at the start of the layer name.
* For example The uv vert selection layer of a layer named `UVMap.001`
* will be called `.pn.UVMap.001`.
*/
#define UV_PINNED_NAME "pn"
/**
* UV map related customdata offsets into BMesh attribute blocks. See #BM_uv_map_offsets_get.
* Defined in #BKE_customdata.hh to avoid including bmesh.hh in many unrelated areas.
* An offset of -1 means that the corresponding layer does not exist.
*/
struct BMUVOffsets {
int uv;
int pin;
};
/** All values reference none layers. */
#define BMUVOFFSETS_NONE {-1, -1}
/** A data type large enough to hold 1 element from any custom-data layer type. */
struct CDBlockBytes {
unsigned char data[64];
};
extern const CustomData_MeshMasks CD_MASK_BAREMESH;
extern const CustomData_MeshMasks CD_MASK_BAREMESH_ORIGINDEX;
extern const CustomData_MeshMasks CD_MASK_MESH;
extern const CustomData_MeshMasks CD_MASK_DERIVEDMESH;
extern const CustomData_MeshMasks CD_MASK_BMESH;
extern const CustomData_MeshMasks CD_MASK_EVERYTHING;
/** For ORIGINDEX layer type, indicates no original index for this element. */
#define ORIGINDEX_NONE -1
/* Initializes a CustomData object with the same layer setup as source and
* memory space for totelem elements. mask must be an array of length
* CD_NUMTYPES elements, that indicate if a layer can be copied. */
/** Add/copy/merge allocation types. */
enum eCDAllocType {
/** Allocate and set to default, which is usually just zeroed memory. */
CD_SET_DEFAULT = 2,
/**
* Default construct new layer values. Does nothing for trivial types. This should be used
* if all layer values will be set by the caller after creating the layer.
*/
CD_CONSTRUCT = 5,
};
inline eCustomDataMask CD_TYPE_AS_MASK(eCustomDataType type)
{
return eCustomDataMask(1) << eCustomDataMask(type);
}
void customData_mask_layers__print(const CustomData_MeshMasks *mask);
using cd_interp = void (*)(const void **sources, const float *weights, int count, void *dest);
using cd_copy = void (*)(const void *source, void *dest, int count);
using cd_set_default_value = void (*)(void *data, int count);
using cd_free = void (*)(void *data, int count);
using cd_validate = bool (*)(void *item, uint totitems, bool do_fixes);
/**
* Update mask_dst with layers defined in mask_src (equivalent to a bit-wise OR).
*/
void CustomData_MeshMasks_update(CustomData_MeshMasks *mask_dst,
const CustomData_MeshMasks *mask_src);
/**
* Return True if all layers set in \a mask_required are also set in \a mask_ref
*/
bool CustomData_MeshMasks_are_matching(const CustomData_MeshMasks *mask_ref,
const CustomData_MeshMasks *mask_required);
/**
* Checks if the layer at physical offset \a layer_n (in data->layers) support math
* the below operations.
*/
bool CustomData_layer_has_math(const CustomData *data, int layer_n);
bool CustomData_layer_has_interp(const CustomData *data, int layer_n);
/**
* Checks if any of the custom-data layers has math.
*/
bool CustomData_has_math(const CustomData *data);
bool CustomData_has_interp(const CustomData *data);
/**
* A non bmesh version would have to check `layer->data`.
*/
bool CustomData_bmesh_has_free(const CustomData *data);
/**
* Copies the "value" (e.g. `uv_map` UV or `mloopcol` colors) from one block to
* another, while not overwriting anything else (e.g. flags). probably only
* implemented for `uv_map/mloopcol`, for now.
*/
void CustomData_data_copy_value(eCustomDataType type, const void *source, void *dest);
void CustomData_data_set_default_value(eCustomDataType type, void *elem);
/**
* Mixes the "value" (e.g. `uv_map` UV or `mloopcol` colors) from one block into
* another, while not overwriting anything else (e.g. flags).
*/
void CustomData_data_mix_value(
eCustomDataType type, const void *source, void *dest, int mixmode, float mixfactor);
/**
* Compares if data1 is equal to data2. type is a valid #CustomData type
* enum (e.g. #CD_PROP_FLOAT). the layer type's equal function is used to compare
* the data, if it exists, otherwise #memcmp is used.
*/
bool CustomData_data_equals(eCustomDataType type, const void *data1, const void *data2);
void CustomData_data_initminmax(eCustomDataType type, void *min, void *max);
void CustomData_data_dominmax(eCustomDataType type, const void *data, void *min, void *max);
void CustomData_data_multiply(eCustomDataType type, void *data, float fac);
void CustomData_data_add(eCustomDataType type, void *data1, const void *data2);
/**
* Initializes a CustomData object with the same layer setup as source. `mask` is a bit-field where
* `(mask & (1 << (layer type)))` indicates if a layer should be copied or not. Data layers using
* implicit-sharing will not actually be copied but will be shared between source and destination.
*
* \warning Does not free or release any internal resources in `dest` CustomData, code must call
* #CustomData_free first if needed.
*/
void CustomData_init_from(const CustomData *source,
CustomData *dest,
eCustomDataMask mask,
int totelem);
/**
* Initializes a CustomData object with the same layers as source. The data is not copied from the
* source. Instead, the new layers are initialized using the given `alloctype`.
*
* \warning Does not free or release any internal resources in `dest` CustomData, code must call
* #CustomData_free first if needed.
*/
void CustomData_init_layout_from(const CustomData *source,
CustomData *dest,
eCustomDataMask mask,
eCDAllocType alloctype,
int totelem);
/**
* \note Ideally this would not be a public function but versioning needs it.
*/
void CustomData_update_typemap(CustomData *data);
/**
* Copies all layers from source to destination that don't exist there yet.
*/
bool CustomData_merge(const CustomData *source,
CustomData *dest,
eCustomDataMask mask,
int totelem);
/**
* Copies all layers from source to destination that don't exist there yet. The layer data is not
* copied. Instead the newly created layers are initialized using the given `alloctype`.
*/
bool CustomData_merge_layout(const CustomData *source,
CustomData *dest,
eCustomDataMask mask,
eCDAllocType alloctype,
int totelem);
/**
* Reallocate custom data to a new element count. If the new size is larger, the new values use
* the #CD_CONSTRUCT behavior, so trivial types must be initialized by the caller. After being
* resized, the #CustomData does not contain any referenced layers.
*/
void CustomData_realloc(CustomData *data,
int old_size,
int new_size,
eCDAllocType alloctype = CD_CONSTRUCT);
/**
* BMesh version of CustomData_merge_layout; merges the layouts of source and `dest`,
* then goes through the mesh and makes sure all the custom-data blocks are
* consistent with the new layout.
*/
bool CustomData_bmesh_merge_layout(const CustomData *source,
CustomData *dest,
eCustomDataMask mask,
eCDAllocType alloctype,
BMesh *bm,
char htype);
/**
* NULL's all members and resets the #CustomData.typemap.
*
* \warning Does not free or release any internal resources.
*/
void CustomData_reset(CustomData *data);
/**
* Frees data associated with a CustomData object (doesn't free the object itself, though).
*/
void CustomData_free(CustomData *data);
/**
* Adds a layer of the given type to the #CustomData object. The new layer is initialized based on
* the given alloctype.
* \return The layer data.
*/
void *CustomData_add_layer(CustomData *data,
eCustomDataType type,
eCDAllocType alloctype,
int totelem);
/**
* Adds a layer of the given type to the #CustomData object. The new layer takes ownership of the
* passed in `layer_data`. If a #ImplicitSharingInfo is passed in, its user count is increased.
*/
const void *CustomData_add_layer_with_data(CustomData *data,
eCustomDataType type,
void *layer_data,
int totelem,
const ImplicitSharingInfo *sharing_info);
/**
* Same as #CustomData_add_layer but accepts a name.
*/
void *CustomData_add_layer_named(
CustomData *data, eCustomDataType type, eCDAllocType alloctype, int totelem, StringRef name);
const void *CustomData_add_layer_named_with_data(CustomData *data,
eCustomDataType type,
void *layer_data,
int totelem,
StringRef name,
const ImplicitSharingInfo *sharing_info);
/**
* Frees the active or first data layer with the give type.
* returns 1 on success, 0 if no layer with the given type is found
*
* In edit-mode, use #EDBM_data_layer_free instead of this function.
*/
bool CustomData_free_layer(CustomData *data, eCustomDataType type, int index);
bool CustomData_free_layer_named(CustomData *data, StringRef name);
/**
* Frees the layer index with the give type.
* returns 1 on success, 0 if no layer with the given type is found.
*
* In edit-mode, use #EDBM_data_layer_free instead of this function.
*/
bool CustomData_free_layer_active(CustomData *data, eCustomDataType type);
/**
* Same as #CustomData_free_layer_active, but free all layers with type.
* \return True if at least one layer was removed.
*/
bool CustomData_free_layers(CustomData *data, eCustomDataType type);
/**
* Returns true if a layer with the specified type exists.
*/
bool CustomData_has_layer(const CustomData *data, eCustomDataType type);
bool CustomData_has_layer_named(const CustomData *data, eCustomDataType type, StringRef name);
/**
* Returns the number of layers with this type.
*/
int CustomData_number_of_layers(const CustomData *data, eCustomDataType type);
int CustomData_number_of_anonymous_layers(const CustomData *data, eCustomDataType type);
int CustomData_number_of_layers_typemask(const CustomData *data, eCustomDataMask mask);
/**
* Set the #CD_FLAG_NOCOPY flag in custom data layers where the mask is
* zero for the layer type, so only layer types specified by the mask will be copied
*/
void CustomData_set_only_copy(const CustomData *data, eCustomDataMask mask);
/**
* Copies data from one CustomData object to another
* objects need not be compatible, each source layer is copied to the
* first dest layer of correct type (if there is none, the layer is skipped).
*
* NOTE: It's expected that the destination layers are mutable
* (#CustomData_ensure_layers_are_mutable). These copy-functions could ensure that internally, but
* that would cause additional overhead when copying few elements at a time. It would also be
* necessary to pass the total size of the destination layers as parameter to make them mutable
* though. In most cases, these functions are used right after creating a new geometry, in which
* case there are no shared layers anyway.
*/
void CustomData_copy_data(
const CustomData *source, CustomData *dest, int source_index, int dest_index, int count);
void CustomData_copy_data_layer(const CustomData *source,
CustomData *dest,
int src_layer_index,
int dst_layer_index,
int src_index,
int dst_index,
int count);
void CustomData_copy_elements(eCustomDataType type,
const void *src_data,
void *dst_data,
int count);
/**
* Copy all layers from the source to the destination block.
* Allocate the result block if necessary, otherwise free its existing layer data.
*/
void CustomData_bmesh_copy_block(CustomData &data, void *src_block, void **dst_block);
/** Holds the minimal data necessary to copy data blocks from one custom data format to another. */
struct BMCustomDataCopyMap {
struct TrivialCopy {
int size;
int src_offset;
int dst_offset;
};
struct Copy {
cd_copy fn;
int src_offset;
int dst_offset;
};
struct TrivialDefault {
int size;
int dst_offset;
};
struct Default {
cd_set_default_value fn;
int dst_offset;
};
struct Free {
cd_free fn;
int dst_offset;
};
Vector<TrivialCopy> trivial_copies;
Vector<Copy> copies;
Vector<TrivialDefault> trivial_defaults;
Vector<Default> defaults;
Vector<Free> free;
};
/** Precalculate a map for more efficient copying between custom data formats. */
BMCustomDataCopyMap CustomData_bmesh_copy_map_calc(const CustomData &src,
const CustomData &dst,
eCustomDataMask mask_exclude = 0);
/**
* Copy custom data layers for one element between two potentially different formats with a
* precalculated map.
*/
void CustomData_bmesh_copy_block(CustomData &dst_data,
const BMCustomDataCopyMap &map,
const void *src_block,
void **dst_block);
/**
* Copies data of a single layer of a given type.
*/
void CustomData_copy_layer_type_data(const CustomData *source,
CustomData *destination,
eCustomDataType type,
int source_index,
int destination_index,
int count);
/**
* Frees data in a #CustomData object. This is only expected to be called if the data layers are
* not shared (#CustomData_ensure_layers_are_mutable).
*/
void CustomData_free_elem(CustomData *data, int index, int count);
/**
* Interpolate given custom data source items into a single destination one.
*
* \param src_indices: Indices of every source items to interpolate into the destination one.
* \param weights: The weight to apply to each source value individually. If NULL, they will be
* averaged.
* \param count: The number of source items to interpolate.
* \param dest_index: Index of the destination item, in which to put the result of the
* interpolation.
*/
void CustomData_interp(const CustomData *source,
CustomData *dest,
const int *src_indices,
const float *weights,
int count,
int dest_index);
/**
* \note src_blocks_ofs & dst_block_ofs
* must be pointers to the data, offset by layer->offset already.
*/
void CustomData_bmesh_interp_n(CustomData *data,
const void **src_blocks,
const float *weights,
int count,
void *dst_block_ofs,
int n);
void CustomData_bmesh_interp(
CustomData *data, const void **src_blocks, const float *weights, int count, void *dst_block);
/**
* Swap data inside each item, for all layers.
* This only applies to item types that may store several sub-item data
* (e.g. corner data [UVs, VCol, ...] of tessellated faces).
*
* \param corner_indices: A mapping 'new_index -> old_index' of sub-item data.
*/
void CustomData_swap_corners(CustomData *data, int index, const int *corner_indices);
/**
* Custom data layers can be shared through implicit sharing (`BLI_implicit_sharing.h`). This
* function makes sure that the layer is unshared if it was shared, which makes it mutable.
*/
void CustomData_ensure_data_is_mutable(CustomDataLayer *layer, int totelem);
void CustomData_ensure_layers_are_mutable(CustomData *data, int totelem);
/**
* Retrieve a pointer to an element of the active layer of the given \a type, chosen by the
* \a index, if it exists.
*/
void *CustomData_get_for_write(CustomData *data, int index, eCustomDataType type, int totelem);
/**
* Retrieve a pointer to an element of the \a nth layer of the given \a type, chosen by the
* \a index, if it exists.
*/
void *CustomData_get_n_for_write(
CustomData *data, eCustomDataType type, int index, int n, int totelem);
/* BMesh Custom Data Functions.
* Should replace edit-mesh ones with these as well, due to more efficient memory alloc. */
void *CustomData_bmesh_get(const CustomData *data, void *block, eCustomDataType type);
void *CustomData_bmesh_get_n(const CustomData *data, void *block, eCustomDataType type, int n);
/**
* Gets from the layer at physical index `n`,
* \note doesn't check type.
*/
void *CustomData_bmesh_get_layer_n(const CustomData *data, void *block, int n);
bool CustomData_set_layer_name(CustomData *data, eCustomDataType type, int n, StringRef name);
const char *CustomData_get_layer_name(const CustomData *data, eCustomDataType type, int n);
/**
* Retrieve the data array of the active layer of the given \a type, if it exists. Return null
* otherwise.
*/
const void *CustomData_get_layer(const CustomData *data, eCustomDataType type);
void *CustomData_get_layer_for_write(CustomData *data, eCustomDataType type, int totelem);
/**
* Retrieve the data array of the \a nth layer of the given \a type, if it exists. Return null
* otherwise.
*/
const void *CustomData_get_layer_n(const CustomData *data, eCustomDataType type, int n);
void *CustomData_get_layer_n_for_write(CustomData *data, eCustomDataType type, int n, int totelem);
/**
* Retrieve the data array of the layer with the given \a name and \a type, if it exists. Return
* null otherwise.
*/
const void *CustomData_get_layer_named(const CustomData *data,
eCustomDataType type,
StringRef name);
void *CustomData_get_layer_named_for_write(CustomData *data,
eCustomDataType type,
StringRef name,
int totelem);
int CustomData_get_offset(const CustomData *data, eCustomDataType type);
int CustomData_get_offset_named(const CustomData *data, eCustomDataType type, StringRef name);
int CustomData_get_n_offset(const CustomData *data, eCustomDataType type, int n);
int CustomData_get_layer_index(const CustomData *data, eCustomDataType type);
int CustomData_get_layer_index_n(const CustomData *data, eCustomDataType type, int n);
int CustomData_get_named_layer_index(const CustomData *data, eCustomDataType type, StringRef name);
int CustomData_get_named_layer_index_notype(const CustomData *data, StringRef name);
int CustomData_get_active_layer_index(const CustomData *data, eCustomDataType type);
int CustomData_get_render_layer_index(const CustomData *data, eCustomDataType type);
int CustomData_get_named_layer(const CustomData *data, eCustomDataType type, StringRef name);
int CustomData_get_active_layer(const CustomData *data, eCustomDataType type);
int CustomData_get_render_layer(const CustomData *data, eCustomDataType 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 CustomData *data, eCustomDataType type);
/**
* Returns name of the default layer of the given type or NULL
* if no such active layer is defined.
*/
const char *CustomData_get_render_layer_name(const CustomData *data, eCustomDataType type);
void CustomData_bmesh_set_n(
CustomData *data, void *block, eCustomDataType type, int n, const void *source);
/**
* Sets the nth layer of type as active.
*/
void CustomData_set_layer_active(CustomData *data, eCustomDataType type, int n);
void CustomData_set_layer_render(CustomData *data, eCustomDataType type, int n);
/**
* For using with an index from #CustomData_get_active_layer_index and
* #CustomData_get_render_layer_index.
*/
void CustomData_set_layer_active_index(CustomData *data, eCustomDataType type, int n);
void CustomData_set_layer_render_index(CustomData *data, eCustomDataType type, int n);
/**
* Adds flag to the layer flags.
*/
void CustomData_set_layer_flag(CustomData *data, eCustomDataType type, eCustomDataLayer_Flag flag);
void CustomData_bmesh_set_default(CustomData *data, void **block);
void CustomData_bmesh_free_block(CustomData *data, void **block);
void CustomData_bmesh_alloc_block(CustomData *data, void **block);
/**
* Same as #CustomData_bmesh_free_block but zero the memory rather than freeing.
*/
void CustomData_bmesh_free_block_data(CustomData *data, void *block);
int CustomData_sizeof(eCustomDataType type);
/**
* Get the name of a layer type.
*/
const char *CustomData_layertype_name(eCustomDataType type);
/**
* Can only ever be one of these.
*/
bool CustomData_layertype_is_singleton(eCustomDataType type);
/**
* Has dynamically allocated members.
* This is useful to know if operations such as #memcmp are
* valid when comparing data from two layers.
*/
bool CustomData_layertype_is_dynamic(eCustomDataType type);
/**
* \return Maximum number of layers of given \a type, -1 means 'no limit'.
*/
int CustomData_layertype_layers_max(eCustomDataType type);
/** \return The maximum size in bytes needed for a layer name with the given prefix. */
int CustomData_name_maxncpy_calc(StringRef name);
/**
* Make sure the name of layer at index is unique.
*/
void CustomData_set_layer_unique_name(CustomData *data, int index);
/**
* For file reading compatibility, returns false if the layer was freed,
* only after this test passes, `layer->data` should be assigned.
*/
bool CustomData_verify_versions(CustomData *data, int index);
/* BMesh specific custom-data stuff. */
void CustomData_bmesh_init_pool(CustomData *data, int totelem, char htype);
/* External file storage */
void CustomData_external_add(
CustomData *data, ID *id, eCustomDataType type, int totelem, const char *filepath);
void CustomData_external_remove(CustomData *data, ID *id, eCustomDataType type, int totelem);
bool CustomData_external_test(CustomData *data, eCustomDataType type);
void CustomData_external_write(
CustomData *data, ID *id, eCustomDataMask mask, int totelem, int free);
void CustomData_external_read(CustomData *data, ID *id, eCustomDataMask mask, int totelem);
void CustomData_external_reload(CustomData *data, ID *id, eCustomDataMask mask, int totelem);
/* Mesh-to-mesh transfer data. */
using cd_datatransfer_interp = void (*)(const CustomDataTransferLayerMap *laymap,
void *dest,
const void **sources,
const float *weights,
int count,
float mix_factor);
enum {
ME_VERT = 1 << 0,
ME_EDGE = 1 << 1,
ME_POLY = 1 << 2,
ME_LOOP = 1 << 3,
};
struct CustomDataTransferLayerMap {
int data_type = 0;
int mix_mode = 0;
float mix_factor = 0.0f;
/** If non-NULL, array of weights, one for each dest item, replaces mix_factor. */
const float *mix_weights = nullptr;
/** Data source array (can be regular CD data, vertices/edges/etc., key-blocks...). */
std::variant<const void *, GVArray> data_src;
/** Data dest array (same type as dat_src). */
std::variant<void *, GMutableVArraySpan> data_dst = nullptr;
/** Split from #bke::GSpanAttributeWriter to avoid including BKE_attribute.hh. */
std::function<void()> tag_modified_fn;
/** Index to affect in data_src (used e.g. for vgroups). */
int data_src_n = 0;
/** Index to affect in data_dst (used e.g. for vgroups). */
int data_dst_n = 0;
/** Size of one element of data_src/data_dst. */
size_t elem_size = 0;
/** Size of actual data we transfer. */
size_t data_size = 0;
/** Offset of actual data we transfer (in element contained in data_src/dst). */
size_t data_offset = 0;
/** Opaque pointer, to be used by specific interp callback (e.g. transform-space for normals). */
void *interp_data = nullptr;
cd_datatransfer_interp interp = nullptr;
};
/**
* Those functions assume src_n and dst_n layers of given type exist in resp. src and dst.
*/
void CustomData_data_transfer(const MeshPairRemap *me_remap, CustomDataTransferLayerMap *laymap);
/* .blend file I/O */
/**
* Prepare given custom data for file writing.
*
* \param data: The custom-data to tweak for .blend file writing (modified in place).
* \param layers_to_write: A reduced set of layers to be written to file.
*
* \warning This function invalidates the custom data struct by changing the layer counts and the
* #layers pointer, and by invalidating the type map. It expects to work on a shallow copy of
* the struct.
*/
void CustomData_blend_write_prepare(CustomData &data,
Vector<CustomDataLayer, 16> &layers_to_write);
/**
* \param layers_to_write: Layers created by #CustomData_blend_write_prepare.
*/
void CustomData_blend_write(BlendWriter *writer,
CustomData *data,
Span<CustomDataLayer> layers_to_write,
int count,
eCustomDataMask cddata_mask,
ID *id);
void CustomData_blend_read(BlendDataReader *reader, CustomData *data, int count);
size_t CustomData_get_elem_size(const CustomDataLayer *layer);
void CustomData_count_memory(const CustomData &data, int totelem, MemoryCounter &memory);
#ifndef NDEBUG
struct DynStr;
/** Use to inspect mesh data when debugging. */
void CustomData_debug_info_from_layers(const CustomData *data, const char *indent, DynStr *dynstr);
#endif /* !NDEBUG */
namespace bke {
std::optional<VolumeGridType> custom_data_type_to_volume_grid_type(eCustomDataType type);
std::optional<eCustomDataType> volume_grid_type_to_custom_data_type(VolumeGridType type);
} // namespace bke
} // namespace blender

View File

@@ -0,0 +1,47 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
/** \file
* \ingroup bke
*/
#include <stddef.h>
namespace blender {
#define CDF_TYPE_IMAGE 0
#define CDF_TYPE_MESH 1
#define CDF_LAYER_NAME_MAX 64
struct CDataFile;
struct CDataFileLayer;
/* Create/Free */
CDataFile *cdf_create(int type);
void cdf_free(CDataFile *cdf);
/* File read/write/remove */
bool cdf_read_open(CDataFile *cdf, const char *filepath);
bool cdf_read_layer(CDataFile *cdf, const CDataFileLayer *blay);
bool cdf_read_data(CDataFile *cdf, unsigned int size, void *data);
void cdf_read_close(CDataFile *cdf);
bool cdf_write_open(CDataFile *cdf, const char *filepath);
bool cdf_write_layer(CDataFile *cdf, CDataFileLayer *blay);
bool cdf_write_data(CDataFile *cdf, unsigned int size, const void *data);
void cdf_write_close(CDataFile *cdf);
void cdf_remove(const char *filepath);
/* Layers */
CDataFileLayer *cdf_layer_find(CDataFile *cdf, int type, const char *name);
CDataFileLayer *cdf_layer_add(CDataFile *cdf, int type, const char *name, size_t datasize);
} // namespace blender

View File

@@ -0,0 +1,93 @@
/* SPDX-FileCopyrightText: 2014 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup bke
*/
#pragma once
#include "DNA_modifier_enums.h"
namespace blender {
struct Depsgraph;
struct Object;
struct ReportList;
struct SpaceTransform;
void BKE_object_data_transfer_dttypes_to_cdmask(int dtdata_types,
struct CustomData_MeshMasks *r_data_masks);
/**
* Check what each layer type can do:
* - If it is actually handled by transfer-data.
* - If it supports advanced mixing.
*/
bool BKE_object_data_transfer_get_dttypes_capacity(int dtdata_types,
bool *r_advanced_mixing,
bool *r_threshold);
int BKE_object_data_transfer_get_dttypes_item_types(int dtdata_types);
int BKE_object_data_transfer_dttype_to_cdtype(int dtdata_type);
int BKE_object_data_transfer_dttype_to_srcdst_index(int dtdata_type);
/**
* Transfer data *layout* of selected types from source to destination object.
* By default, it only creates new data layers if needed on \a ob_dst.
* If \a use_delete is true, it will also delete data layers on \a ob_dst that do not match those
* from \a ob_src, to get (as much as possible) exact copy of source data layout.
*/
void BKE_object_data_transfer_layout(struct Depsgraph *depsgraph,
struct Object *ob_src,
struct Object *ob_dst,
int data_types,
bool use_delete,
const int fromlayers_select[DT_MULTILAYER_INDEX_MAX],
const int tolayers_select[DT_MULTILAYER_INDEX_MAX]);
bool BKE_object_data_transfer_mesh(struct Depsgraph *depsgraph,
struct Object *ob_src,
struct Object *ob_dst,
int data_types,
bool use_create,
int map_vert_mode,
int map_edge_mode,
int map_loop_mode,
int map_face_mode,
struct SpaceTransform *space_transform,
bool auto_transform,
float max_distance,
float ray_radius,
float islands_handling_precision,
const int fromlayers_select[DT_MULTILAYER_INDEX_MAX],
const int tolayers_select[DT_MULTILAYER_INDEX_MAX],
int mix_mode,
float mix_factor,
const char *vgroup_name,
bool invert_vgroup,
struct ReportList *reports);
bool BKE_object_data_transfer_ex(struct Depsgraph *depsgraph,
struct Object *ob_src,
struct Object *ob_dst,
struct Mesh *me_dst,
int data_types,
bool use_create,
int map_vert_mode,
int map_edge_mode,
int map_loop_mode,
int map_face_mode,
struct SpaceTransform *space_transform,
bool auto_transform,
float max_distance,
float ray_radius,
float islands_handling_precision,
const int fromlayers_select[DT_MULTILAYER_INDEX_MAX],
const int tolayers_select[DT_MULTILAYER_INDEX_MAX],
int mix_mode,
float mix_factor,
const char *vgroup_name,
bool invert_vgroup,
struct ReportList *reports);
} // namespace blender

View File

@@ -0,0 +1,368 @@
/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
#include "DNA_listBase.h"
#include "DNA_meshdata_types.h"
#include "BLI_math_vector_types.hh"
#include "BLI_offset_indices.hh"
#include "BLI_span.hh"
#include "BLI_string_ref.hh"
#include "BLI_vector_set.hh"
#include "BLI_virtual_array_fwd.hh"
namespace blender {
/** \file
* \ingroup bke
* \brief support for deformation groups and hooks.
*/
struct BlendDataReader;
struct BlendWriter;
struct ID;
struct MDeformVert;
struct MDeformWeight;
struct Object;
struct bDeformGroup;
bool BKE_id_supports_vertex_groups(const ID *id);
bool BKE_object_supports_vertex_groups(const Object *ob);
const ListBaseT<bDeformGroup> *BKE_object_defgroup_list(const Object *ob);
ListBaseT<bDeformGroup> *BKE_object_defgroup_list_mutable(Object *ob);
int BKE_object_defgroup_count(const Object *ob);
/**
* \note For historical reasons, the index starts at 1 rather than 0.
*/
int BKE_object_defgroup_active_index_get(const Object *ob);
/**
* \note For historical reasons, the index starts at 1 rather than 0.
*/
void BKE_object_defgroup_active_index_set(Object *ob, int new_index);
/**
* Return the ID's vertex group names.
* Supports Mesh (ME), Lattice (LT), and GreasePencil (GD) IDs.
* \return ListBaseT of bDeformGroup pointers.
*/
const ListBaseT<bDeformGroup> *BKE_id_defgroup_list_get(const ID *id);
ListBaseT<bDeformGroup> *BKE_id_defgroup_list_get_mutable(ID *id);
int BKE_defgroup_name_index(const ListBaseT<bDeformGroup> *defbase, StringRef name);
int BKE_id_defgroup_name_index(const ID *id, StringRef name);
bool BKE_defgroup_listbase_name_find(ListBaseT<bDeformGroup> *defbase,
StringRef name,
int *r_index,
bDeformGroup **r_group);
bool BKE_id_defgroup_name_find(ID *id, StringRef name, int *r_index, bDeformGroup **r_group);
bDeformGroup *BKE_object_defgroup_new(Object *ob, StringRef name);
void BKE_defgroup_copy_list(ListBaseT<bDeformGroup> *outbase,
const ListBaseT<bDeformGroup> *inbase);
bDeformGroup *BKE_defgroup_duplicate(const bDeformGroup *ingroup);
bDeformGroup *BKE_object_defgroup_find_name(const Object *ob, StringRef name);
/**
* Returns flip map for the vertex-groups of `ob`.
*
* \param use_default: How to handle cases where no symmetrical group is found.
* - false: sets these indices to -1, indicating the group should be ignored.
* - true: sets the index to its location in the array (making the group point to itself).
* Enable this for symmetrical actions which apply weight operations on symmetrical vertices
* where the symmetrical group will be used (if found), otherwise the same group is used.
*
* \return An index array `r_flip_map_num` length,
* (aligned with the list result from `BKE_id_defgroup_list_get(ob)`).
* referencing the index of the symmetrical vertex-group of a fall-back value (see `use_default`).
* The caller is responsible for freeing the array.
*/
int *BKE_object_defgroup_flip_map(const Object *ob, bool use_default, int *r_flip_map_num);
/**
* A version of #BKE_object_defgroup_flip_map that ignores locked groups.
*/
int *BKE_object_defgroup_flip_map_unlocked(const Object *ob,
bool use_default,
int *r_flip_map_num);
/**
* A version of #BKE_object_defgroup_flip_map that only takes a single group into account.
*/
int *BKE_object_defgroup_flip_map_single(const Object *ob,
bool use_default,
int defgroup,
int *r_flip_map_num);
int BKE_object_defgroup_flip_index(const Object *ob, int index, bool use_default);
int BKE_object_defgroup_name_index(const Object *ob, StringRef name);
void BKE_object_defgroup_unique_name(bDeformGroup *dg, Object *ob);
void BKE_object_defgroup_set_name(bDeformGroup *dg, Object *ob, const char *new_name);
MDeformWeight *BKE_defvert_find_index(const MDeformVert *dv, int defgroup);
/**
* Ensures that `dv` has a deform weight entry for the specified group (`defgroup`).
*
* \note this function is mirrored in editmesh_tools.cc, for use for edit-vertices.
*/
MDeformWeight *BKE_defvert_ensure_index(MDeformVert *dv, int defgroup);
/**
* Adds the given vertex to the specified vertex group, with given weight.
*
* \warning this does NOT check for existing, assume caller already knows its not there.
*/
void BKE_defvert_add_index_notest(MDeformVert *dv, int defgroup, float weight);
/**
* Removes the given vertex from the vertex group.
*
* \warning This function frees the given #MDeformWeight, do not use it afterward!
*/
void BKE_defvert_remove_group(MDeformVert *dvert, MDeformWeight *dw);
void BKE_defvert_clear(MDeformVert *dvert);
/**
* \return The first group index shared by both deform verts
* or -1 if none are found.
*/
int BKE_defvert_find_shared(const MDeformVert *dvert_a, const MDeformVert *dvert_b);
/**
* \return true if has no weights.
*/
bool BKE_defvert_is_weight_zero(const MDeformVert *dvert, int defgroup_tot);
void BKE_defvert_array_free_elems(MDeformVert *dvert, int totvert);
void BKE_defvert_array_free(MDeformVert *dvert, int totvert);
void BKE_defvert_array_copy(MDeformVert *dst, const MDeformVert *src, int totvert);
float BKE_defvert_find_weight(const MDeformVert *dvert, int defgroup);
/**
* Take care with this the rationale is:
* - if the object has no vertex group. act like vertex group isn't set and return 1.0.
* - if the vertex group exists but the 'defgroup' isn't found on this vertex, _still_ return 0.0.
*
* This is a bit confusing, just saves some checks from the caller.
*/
float BKE_defvert_array_find_weight_safe(const MDeformVert *dvert,
int index,
int defgroup,
bool invert);
/**
* \return The total weight in all groups marked in the selection mask.
*/
float BKE_defvert_total_selected_weight(const MDeformVert *dv,
int defbase_num,
const bool *defbase_sel);
/**
* \return The representative weight of a multi-paint group, used for
* viewport colors and actual painting.
*
* Result equal to sum of weights with auto normalize, and average otherwise.
* Value is not clamped, since painting relies on multiplication being always
* commutative with the collective weight function.
*/
float BKE_defvert_multipaint_collective_weight(const MDeformVert *dv,
int defbase_num,
const bool *defbase_sel,
int defbase_sel_num,
bool is_normalized);
/* This much unlocked weight is considered equivalent to none. */
#define VERTEX_WEIGHT_LOCK_EPSILON 1e-6f
/**
* Computes the display weight for the lock relative weight paint mode.
*
* \return weight divided by 1-locked_weight with division by zero check
*/
float BKE_defvert_calc_lock_relative_weight(float weight,
float locked_weight,
float unlocked_weight);
/**
* Computes the display weight for the lock relative weight paint mode, using weight data.
*
* \return weight divided by unlocked, or 1-locked_weight with division by zero check.
*/
float BKE_defvert_lock_relative_weight(float weight,
const MDeformVert *dv,
int defbase_num,
const bool *defbase_locked,
const bool *defbase_unlocked);
void BKE_defvert_copy(MDeformVert *dvert_dst, const MDeformVert *dvert_src);
/**
* Overwrite weights filtered by vgroup_subset.
* - do nothing if neither are set.
* - add destination weight if needed
*/
void BKE_defvert_copy_subset(MDeformVert *dvert_dst,
const MDeformVert *dvert_src,
const bool *vgroup_subset,
int vgroup_num);
/**
* Overwrite weights filtered by vgroup_subset and with mirroring specified by the flip map
* - do nothing if neither are set.
* - add destination weight if needed
*/
void BKE_defvert_mirror_subset(MDeformVert *dvert_dst,
const MDeformVert *dvert_src,
const bool *vgroup_subset,
int vgroup_num,
const int *flip_map,
int flip_map_num);
/**
* Copy an index from one #MDeformVert to another.
* - do nothing if neither are set.
* - add destination weight if needed.
*/
void BKE_defvert_copy_index(MDeformVert *dvert_dst,
int defgroup_dst,
const MDeformVert *dvert_src,
int defgroup_src);
/**
* Only sync over matching weights, don't add or remove groups
* warning, loop within loop.
*/
void BKE_defvert_sync(MDeformVert *dvert_dst, const MDeformVert *dvert_src, bool use_ensure);
/**
* be sure all flip_map values are valid
*/
void BKE_defvert_sync_mapped(MDeformVert *dvert_dst,
const MDeformVert *dvert_src,
const int *flip_map,
int flip_map_num,
bool use_ensure);
/**
* be sure all flip_map values are valid
*/
void BKE_defvert_remap(MDeformVert *dvert, const int *map, int map_len);
void BKE_defvert_flip(MDeformVert *dvert, const int *flip_map, int flip_map_num);
void BKE_defvert_flip_merged(MDeformVert *dvert, const int *flip_map, int flip_map_num);
/**
* Normalize all the vertex group weights on a vertex.
*
* Note: this ignores whether groups are locked or not, and will therefore
* happily modify even locked groups.
*
* See #BKE_defvert_normalize_ex() for parameter documentation.
*/
void BKE_defvert_normalize(MDeformVert &dvert);
/**
* Normalize a subset of vertex group weights among themselves.
*
* Note: this ignores whether groups are locked or not, and will therefore
* happily modify even locked groups.
*
* See #BKE_defvert_normalize_ex() for parameter documentation.
*/
void BKE_defvert_normalize_subset(MDeformVert &dvert, Span<bool> subset_flags);
/**
* Normalize a subset of vertex group weights among themselves, but leaving
* locked groups unmodified.
*
* See #BKE_defvert_normalize_ex() for parameter documentation.
*/
void BKE_defvert_normalize_lock_map(MDeformVert &dvert,
Span<bool> subset_flags,
Span<bool> lock_flags);
/**
* Normalize the vertex groups of a vertex, with all the bells and whistles.
*
* \param dvert: the vertex weights to be normalized.
*
* \param vgroup_subset: span of bools indicating which vertex groups are
* included vs ignored in this function. True means included, false means
* ignored. Note that this is different than locking: locked groups are not
* *modified*, but their weights are still accounted for in the normalization
* process, whereas ignored groups aren't accounted for at all. May be empty,
* indicating all vertex groups are included. If not empty, its length must
* match the number of vertex groups in the source data (e.g. the mesh).
*
* \param lock_flags: span of bools with `true` indicating the vertex groups
* that are completely locked from modification, even if that prevents
* normalization. May be empty, indicating no locked groups. If not empty, its
* length must match the number of vertex groups in the source data (e.g. the
* mesh).
*
* \param soft_lock_flags: span of bools with `true` indicating a set of vertex
* groups that are "soft locked". The intended use case for this is to "protect"
* weights that have just been set by a tool or operator during post-process
* normalization. When possible, only non-soft-locked weights will be modified
* to achieve normalization, but if necessary soft-locked will also be modified.
* NOTE: in theory this could be used for purposes other than "just set" groups,
* but corner cases are handled with that use case in mind. May be empty,
* indicating no "soft locked" groups. If not empty, its length must match the
* number of vertex groups in the source data (e.g. the mesh).
*/
void BKE_defvert_normalize_ex(MDeformVert &dvert,
Span<bool> vgroup_subset,
Span<bool> lock_flags,
Span<bool> soft_lock_flags);
/* Utilities to 'extract' a given vgroup into a simple float array,
* for verts, but also edges/faces/loops. */
void BKE_defvert_extract_vgroup_to_vertweights(
const MDeformVert *dvert, int defgroup, int verts_num, bool invert_vgroup, float *r_weights);
/**
* The following three make basic interpolation,
* using temp vert_weights array to avoid looking up same weight several times.
*/
void BKE_defvert_extract_vgroup_to_edgeweights(const MDeformVert *dvert,
int defgroup,
int verts_num,
Span<int2> edges,
bool invert_vgroup,
float *r_weights);
void BKE_defvert_extract_vgroup_to_loopweights(const MDeformVert *dvert,
int defgroup,
int verts_num,
Span<int> corner_verts,
bool invert_vgroup,
float *r_weights);
void BKE_defvert_extract_vgroup_to_faceweights(const MDeformVert *dvert,
int defgroup,
int verts_num,
const Span<int> corner_verts,
OffsetIndices<int> faces,
bool invert_vgroup,
float *r_weights);
void BKE_defvert_weight_to_rgb(float r_rgb[3], float weight);
void BKE_defvert_blend_write(BlendWriter *writer, int count, const MDeformVert *dvlist);
void BKE_defvert_blend_read(BlendDataReader *reader, int count, MDeformVert *mdverts);
void BKE_defbase_blend_write(BlendWriter *writer, const ListBaseT<bDeformGroup> *defbase);
namespace bke {
VArray<float> varray_for_deform_verts(Span<MDeformVert> dverts, int defgroup_index);
VMutableArray<float> varray_for_mutable_deform_verts(MutableSpan<MDeformVert> dverts,
int defgroup_index);
void remove_defgroup_index(MutableSpan<MDeformVert> dverts, int defgroup_index);
void gather_deform_verts(Span<MDeformVert> src, Span<int> indices, MutableSpan<MDeformVert> dst);
void gather_deform_verts(Span<MDeformVert> src,
const IndexMask &indices,
MutableSpan<MDeformVert> dst);
struct WeightIndexGetter {
int operator()(const MDeformWeight &value) const
{
return value.def_nr;
}
};
using MDeformWeightSet = CustomIDVectorSet<MDeformWeight, WeightIndexGetter, 64>;
MDeformVert mix_deform_verts(const Span<MDeformVert> src,
const Span<int> indices,
const Span<float> weights,
MDeformWeightSet &dw_buffer);
} // namespace bke
} // namespace blender

View File

@@ -0,0 +1,110 @@
/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
/** \file
* \ingroup bke
* \brief display list (or rather multi purpose list) stuff.
*/
#include "DNA_curve_enums.h"
#include "DNA_listBase.h"
namespace blender {
struct Depsgraph;
struct Nurb;
/** #DispList.type */
enum {
/** A closed polygon (that can be filled). */
DL_POLY = 0,
/** An open polygon. */
DL_SEGM = 1,
/** A grid surface that respects #DL_CYCL_U & #DL_CYCL_V. */
DL_SURF = 2,
/** Triangles. */
DL_INDEX3 = 4,
// DL_VERTCOL = 6, /* UNUSED */
/** Isolated points. */
DL_VERTS = 7,
};
/** #DispList.type */
enum {
/** U/V swapped here compared with #Nurb.flagu, #Nurb.flagv and #CU_NURB_CYCLIC */
DL_CYCL_U = (1 << 0),
DL_CYCL_V = (1 << 1),
DL_FRONT_CURVE = (1 << 2),
DL_BACK_CURVE = (1 << 3),
/**
* Marks geometry whose winding direction was reversed during bevel list generation
* (to produce correct outward-facing normals on extruded surfaces).
*
* Needed to check if the winding has been flipped (CDT filling).
*/
DL_REVERSED = (1 << 4),
};
/* prototypes */
struct Object;
struct Scene;
/* Used for curves, nurbs, meta-balls. */
struct DispList {
struct DispList *next, *prev;
short type, flag;
int parts, nr;
short col, rt; /* Currently only used for smooth flag. */
float *verts, *nors;
int *index;
int charidx;
int totindex; /* indexed array drawing surfaces */
};
DispList *BKE_displist_find(ListBaseT<DispList> *lb, int type);
void BKE_displist_free(ListBaseT<DispList> *lb);
void BKE_displist_make_curveTypes(struct Depsgraph *depsgraph,
const struct Scene *scene,
struct Object *ob,
bool for_render);
void BKE_curve_calc_modifiers_pre(struct Depsgraph *depsgraph,
const struct Scene *scene,
struct Object *ob,
ListBaseT<Nurb> *source_nurb,
ListBaseT<Nurb> *target_nurb,
bool for_render);
bool BKE_displist_surfindex_get(
const struct DispList *dl, int a, int *b, int *p1, int *p2, int *p3, int *p4);
/**
* \param normal_proj: Optional normal that's used to project the scan-fill verts into 2D coords.
* Pass this along if known since it saves time calculating the normal.
* This is also used to initialize #DispList.nors (one normal per display list).
* \param flip_normal: Flip the normal (same as passing \a normal_proj negated).
* \param fill_solver: Triangulation solver (#CU_FILL_SOLVER_SWEEP_LINE, etc.).
* \param fill_rule: Fill rule for CDT solver (#CU_FILL_RULE_EVEN_ODD, etc.).
*/
void BKE_displist_fill(const ListBaseT<DispList> *dispbase,
ListBaseT<DispList> *to,
const float normal_proj[3],
bool flip_normal,
CurveFillSolverType fill_solver,
CurveFillRuleType fill_rule);
float BKE_displist_calc_taper(struct Depsgraph *depsgraph,
const struct Scene *scene,
struct Object *taperobj,
int cur,
int tot);
void BKE_displist_minmax(const ListBaseT<DispList> *dispbase, float min[3], float max[3]);
} // namespace blender

View File

@@ -0,0 +1,120 @@
/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
/** \file
* \ingroup bke
*/
#include "BLI_vector_list.hh"
#include "BKE_geometry_set.hh"
#include "BKE_instances.hh"
namespace blender {
struct Depsgraph;
struct ID;
struct Object;
struct ParticleSystem;
struct Scene;
struct ViewLayer;
struct ViewerPath;
/* ---------------------------------------------------- */
/* Dupli-Geometry */
constexpr int MAX_DUPLI_RECUR = 8;
struct DupliObject {
/* Object whose geometry is instanced. */
Object *ob;
/* Data owned by the object above that is instanced. This might not be the same as `ob->data`. */
ID *ob_data;
float mat[4][4];
float orco[3], uv[2];
short type; /* From #Object::transflag. */
char no_draw;
/** Depth in the instance hierarchy. */
int8_t level;
/* If this dupli object is belongs to a preview, this is non-null. */
const bke::GeometrySet *preview_base_geometry;
/* Index of the top-level instance this dupli is part of or -1 when unused. */
int preview_instance_index;
/* Persistent identifier for a dupli object, for inter-frame matching of
* objects with motion blur, or inter-update matching for syncing. */
int persistent_id[MAX_DUPLI_RECUR];
/* Random ID for shading */
unsigned int random_id;
/* Particle this dupli was generated from. */
ParticleSystem *particle_system;
/* Geometry set stack for instance attributes; for each level lists the
* geometry set and instance index within it.
*
* Only non-null entries are stored, ordered from innermost to outermost.
* To save memory, these arrays are allocated smaller than persistent_id,
* assuming that not every entry will be associated with a GeometrySet; any
* size between 1 and MAX_DUPLI_RECUR can be used without issues.
*/
int instance_idx[4];
const bke::GeometrySet *instance_data[4];
};
using DupliList = VectorList<DupliObject>;
/**
* Fill a Vector of #DupliObject.
*/
void object_duplilist(Depsgraph *depsgraph,
Object *ob,
Set<const Object *> *include_objects,
DupliList &r_duplilist);
/**
* Fill a Vector of #DupliObject for the preview geometry referenced by the #ViewerPath.
*/
void object_duplilist_preview(Depsgraph *depsgraph,
Object *ob,
const ViewerPath *viewer_path,
DupliList &r_duplilist);
/**
* Get the legacy instances of this object. That includes instances coming from these sources:
* - Particles
* - Dupli Verts
* - Dupli Faces
* - "Objects as Font"
*
* This does not include collection instances which are not considered legacy and should be treated
* properly at a higher level.
*
* Also see #get_dupli_generator for the different existing dupli generators.
*/
bke::Instances object_duplilist_legacy_instances(Depsgraph &depsgraph, Object &ob);
/**
* Look up the RGBA value of a uniform shader attribute.
* \return true if the attribute was found; if not, r_value is also set to zero.
*/
bool BKE_object_dupli_find_rgba_attribute(const Object *ob,
const DupliObject *dupli,
const Object *dupli_parent,
const char *name,
float r_value[4]);
/**
* Look up the RGBA value of a view layer/scene/world shader attribute.
* \return true if the attribute was found; if not, r_value is also set to zero.
*/
bool BKE_view_layer_find_rgba_attribute(const Scene *scene,
const ViewLayer *layer,
const char *name,
float r_value[4]);
} // namespace blender

View File

@@ -0,0 +1,146 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
/** \file
* \ingroup bke
*/
namespace blender {
struct Depsgraph;
struct DynamicPaintCanvasSettings;
struct DynamicPaintModifierData;
struct DynamicPaintRuntime;
struct ImgSeqFormatData;
struct Object;
struct PaintAdjData;
struct Scene;
/* Actual surface point */
struct PaintSurfaceData {
ImgSeqFormatData *format_data; /* extra data for image sequence format */
void *type_data; /* data used by specific surface type */
PaintAdjData *adj_data; /* adjacency data for current surface */
struct PaintBakeData *bData; /* temporary per step data used for frame calculation */
int total_points;
};
/* Paint type surface point */
struct PaintPoint {
/* Wet paint is handled at effect layer only
* and mixed to surface when drying */
float e_color[4];
float wetness;
short state;
float color[4];
};
/* height field waves */
struct PaintWavePoint {
float height;
float velocity;
float brush_isect;
short state;
};
/**
* Modifier call. Processes dynamic paint modifier step.
*/
struct Mesh *dynamicPaint_Modifier_do(struct DynamicPaintModifierData *pmd,
struct Depsgraph *depsgraph,
struct Scene *scene,
struct Object *ob,
struct Mesh *mesh);
/**
* Free whole dynamic-paint modifier.
*/
void dynamicPaint_Modifier_free(struct DynamicPaintModifierData *pmd);
void dynamicPaint_Modifier_free_runtime(struct DynamicPaintRuntime *runtime);
void dynamicPaint_Modifier_copy(const struct DynamicPaintModifierData *pmd,
struct DynamicPaintModifierData *tpmd,
int flag);
/**
* Initialize modifier data.
*/
bool dynamicPaint_createType(struct DynamicPaintModifierData *pmd, int type, struct Scene *scene);
/**
* Creates a new surface and adds it to the list
* If scene is null, frame range of 1-250 is used
* A pointer to this surface is returned.
*/
struct DynamicPaintSurface *dynamicPaint_createNewSurface(
struct DynamicPaintCanvasSettings *canvas, struct Scene *scene);
/**
* Clears surface data back to zero.
*/
void dynamicPaint_clearSurface(const struct Scene *scene, struct DynamicPaintSurface *surface);
/**
* Completely (re)initializes surface (only for point cache types).
*/
bool dynamicPaint_resetSurface(const struct Scene *scene, struct DynamicPaintSurface *surface);
void dynamicPaint_freeSurface(const struct DynamicPaintModifierData *pmd,
struct DynamicPaintSurface *surface);
/**
* Free canvas data.
*/
void dynamicPaint_freeCanvas(struct DynamicPaintModifierData *pmd);
/* Free brush data */
void dynamicPaint_freeBrush(struct DynamicPaintModifierData *pmd);
void dynamicPaint_freeSurfaceData(struct DynamicPaintSurface *surface);
/**
* Update cache frame range.
*/
void dynamicPaint_cacheUpdateFrames(struct DynamicPaintSurface *surface);
bool dynamicPaint_outputLayerExists(struct DynamicPaintSurface *surface,
struct Object *ob,
int output);
/**
* Change surface data to defaults on new type.
*/
void dynamicPaintSurface_updateType(struct DynamicPaintSurface *surface);
void dynamicPaintSurface_setUniqueName(struct DynamicPaintSurface *surface, const char *basename);
/**
* Get currently active surface (in user interface).
*/
struct DynamicPaintSurface *get_activeSurface(struct DynamicPaintCanvasSettings *canvas);
/**
* Image sequence baking.
*/
int dynamicPaint_createUVSurface(struct Scene *scene,
struct DynamicPaintSurface *surface,
float *progress,
bool *do_update);
/**
* Calculate a single frame and included sub-frames for surface.
*/
int dynamicPaint_calculateFrame(struct DynamicPaintSurface *surface,
struct Depsgraph *depsgraph,
struct Scene *scene,
struct Object *cObject,
int frame);
void dynamicPaint_outputSurfaceImage(struct DynamicPaintSurface *surface,
const char *filepath,
short output_layer);
/* PaintPoint state */
#define DPAINT_PAINT_NONE -1
#define DPAINT_PAINT_DRY 0
#define DPAINT_PAINT_WET 1
#define DPAINT_PAINT_NEW 2
/* PaintWavePoint state */
#define DPAINT_WAVE_ISECT_CHANGED -1
#define DPAINT_WAVE_NONE 0
#define DPAINT_WAVE_OBSTACLE 1
#define DPAINT_WAVE_REFLECT_ONLY 2
} // namespace blender

View File

@@ -0,0 +1,19 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup bke
*/
#pragma once
namespace blender {
struct Object;
void BKE_editlattice_free(struct Object *ob);
void BKE_editlattice_make(struct Object *obedit);
void BKE_editlattice_load(struct Object *obedit);
} // namespace blender

View File

@@ -0,0 +1,128 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
/** \file
* \ingroup bke
*
* The \link edmesh EDBM module \endlink is for editmode bmesh stuff.
* In contrast, this module is for code shared with blenkernel that's
* only concerned with low level operations on the #BMEditMesh structure.
*/
#include <array>
#include "BLI_array.hh"
#include "BLI_math_vector_types.hh"
#include "bmesh.hh"
namespace blender {
struct BMLoop;
struct BMPartialUpdate;
struct BMesh;
struct BMeshCalcTessellation_Params;
struct Depsgraph;
struct Mesh;
struct Object;
struct Scene;
/**
* This structure is used for mesh edit-mode.
*
* Through this, you get access to both the edit #BMesh, its tessellation,
* and various data that doesn't belong in the #BMesh struct itself
* (mostly related to mesh evaluation).
*
* #Mesh.runtime.edit_mesh stores a pointer to this structure.
*/
struct BMEditMesh {
/* Always owned by an original mesh in edit mode. */
BMesh *bm;
/**
* Face triangulation (tessellation) is stored as triplets of three loops,
* which each define a triangle.
*
* \see #Mesh::corner_tris() as the documentation gives useful hints that apply to this data too.
*/
Array<std::array<BMLoop *, 3>> looptris;
/** Selection mode (#SCE_SELECT_VERTEX, #SCE_SELECT_EDGE & #SCE_SELECT_FACE). */
short selectmode;
/** The active material (zero-based, assigned to newly created faces). */
short mat_nr;
/** Temp variables for x-mirror editing (-1 when the layer does not exist). */
int mirror_cdlayer;
/**
* ID data is older than edit-mode data.
* Set #Main.is_memfile_undo_flush_needed when enabling.
*/
char needs_flush_to_id;
};
/* editmesh.cc */
void BKE_editmesh_looptris_calc_ex(BMEditMesh *em, const BMeshCalcTessellation_Params *params);
void BKE_editmesh_looptris_calc(BMEditMesh *em);
void BKE_editmesh_looptris_calc_with_partial_ex(BMEditMesh *em,
BMPartialUpdate *bmpinfo,
const BMeshCalcTessellation_Params *params);
void BKE_editmesh_looptris_calc_with_partial(BMEditMesh *em, BMPartialUpdate *bmpinfo);
void BKE_editmesh_looptris_and_normals_calc_with_partial(BMEditMesh *em, BMPartialUpdate *bmpinfo);
/**
* Performing the face normal calculation at the same time as tessellation
* gives a reasonable performance boost (approx ~20% faster).
*/
void BKE_editmesh_looptris_and_normals_calc(BMEditMesh *em);
/**
* \note The caller is responsible for ensuring triangulation data,
* typically by calling #BKE_editmesh_looptris_calc.
*/
BMEditMesh *BKE_editmesh_create(BMesh *bm);
BMEditMesh *BKE_editmesh_copy(BMEditMesh *em);
/**
* \brief Return the #BMEditMesh for a given object
*
* \note this function assumes this is a mesh object,
* don't add NULL data check here. caller must do that.
*
* \note Even in edit-mode, evaluated objects may return null since the
* evaluated mesh may have had its edit-mesh cleared.
*
* In this case the caller must either:
* - Use the original mesh to guarantee #BMEditMesh is returned.
* - Null check the result if we want to respect the evaluated result, see: #154739.
*/
BMEditMesh *BKE_editmesh_from_object(Object *ob);
/**
* Return whether the evaluated mesh is a "descendant" of the original mesh: whether it is a
* version of the original mesh propagated during evaluation. This will be false if the mesh was
* taken from an different object during evaluation, with the object info node for example.
*/
bool BKE_editmesh_eval_orig_map_available(const Mesh &mesh_eval, const Mesh *mesh_orig);
/**
* \note Does not free the #BMEditMesh itself.
*/
void BKE_editmesh_free_data(BMEditMesh *em);
Array<float3> BKE_editmesh_vert_coords_alloc(Depsgraph *depsgraph,
BMEditMesh *em,
Scene *scene,
Object *ob);
Array<float3> BKE_editmesh_vert_coords_alloc_orco(BMEditMesh *em);
Span<float3> BKE_editmesh_vert_coords_when_deformed(
Depsgraph *depsgraph, BMEditMesh *em, Scene *scene, Object *obedit, Array<float3> &r_alloc);
void BKE_editmesh_lnorspace_update(BMEditMesh *em);
} // namespace blender

View File

@@ -0,0 +1,96 @@
/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup bke
*/
#pragma once
#include "BLI_math_vector_types.hh"
#include "BLI_span.hh"
namespace blender {
struct BMBVHTree;
struct BMEditMesh;
struct BMFace;
struct BMLoop;
struct BMVert;
struct BMesh;
struct BVHTree;
typedef bool (*BMBVHTree_FaceFilter)(struct BMFace *f, void *userdata);
BMBVHTree *BKE_bmbvh_new_from_editmesh(struct BMEditMesh *em,
int flag,
const float3 *cos_cage,
bool cos_cage_free);
BMBVHTree *BKE_bmbvh_new_ex(struct BMesh *bm,
Span<std::array<BMLoop *, 3>> looptris,
int flag,
const float3 *cos_cage,
bool cos_cage_free,
bool (*test_fn)(struct BMFace *, void *user_data),
void *user_data);
BMBVHTree *BKE_bmbvh_new(struct BMesh *bm,
Span<std::array<BMLoop *, 3>> looptris,
int flag,
const float3 *cos_cage,
bool cos_cage_free);
void BKE_bmbvh_free(BMBVHTree *tree);
struct BVHTree *BKE_bmbvh_tree_get(BMBVHTree *tree);
struct BMFace *BKE_bmbvh_ray_cast(const BMBVHTree *tree,
const float co[3],
const float dir[3],
float radius,
float *r_dist,
float r_hitout[3],
float r_cagehit[3]);
struct BMFace *BKE_bmbvh_ray_cast_filter(const BMBVHTree *tree,
const float co[3],
const float dir[3],
float radius,
float *r_dist,
float r_hitout[3],
float r_cagehit[3],
BMBVHTree_FaceFilter filter_cb,
void *filter_userdata);
/**
* Find a vert closest to co in a sphere of radius dist_max.
*/
struct BMVert *BKE_bmbvh_find_vert_closest(const BMBVHTree *tree,
const float co[3],
float dist_max);
struct BMFace *BKE_bmbvh_find_face_closest(const BMBVHTree *tree,
const float co[3],
float dist_max);
/**
* Overlap indices reference the looptris.
*/
struct BVHTreeOverlap *BKE_bmbvh_overlap(const BMBVHTree *bmtree_a,
const BMBVHTree *bmtree_b,
unsigned int *r_overlap_tot);
/**
* Overlap indices reference the looptris.
*/
struct BVHTreeOverlap *BKE_bmbvh_overlap_self(const BMBVHTree *bmtree,
unsigned int *r_overlap_tot);
/** #BKE_bmbvh_new flag parameter. */
enum {
/** Use with 'cos_cage', returns hits in relation to original geometry. */
BMBVH_RETURN_ORIG = (1 << 0),
/** Restrict to hidden geometry (overrides BMBVH_RESPECT_HIDDEN). */
BMBVH_RESPECT_SELECT = (1 << 1),
/** Omit hidden geometry. */
BMBVH_RESPECT_HIDDEN = (1 << 2),
};
} // namespace blender

View File

@@ -0,0 +1,57 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
/** \file
* \ingroup bke
*/
#include <optional>
#include "BLI_array.hh"
#include "BLI_bounds_types.hh"
#include "BLI_math_vector_types.hh"
namespace blender {
struct BMEditMesh;
namespace bke {
struct EditMeshData {
/**
* Deformed positions calculated by modifiers in the modifier stack that can process an
* edit mesh input. When this is not empty, the other arrays will depend on the values.
*/
Array<float3> vert_positions;
/**
* Lazily initialized vertex normal cache (used when `vert_positions` is set).
* Access via #BKE_editmesh_cache_ensure_vert_normals instead of directly.
*/
Array<float3> vert_normals;
/**
* Lazily initialized face normal cache (used when `vert_positions` is set).
* Access via #BKE_editmesh_cache_ensure_face_normals instead of directly.
*/
Array<float3> face_normals;
/**
* Cache of face centers, also depends on `vert_positions` when it is not empty.
* Access via #BKE_editmesh_cache_ensure_face_centers instead of directly.
*/
Array<float3> face_centers;
};
} // namespace bke
Span<float3> BKE_editmesh_cache_ensure_face_normals(BMEditMesh &em, bke::EditMeshData &emd);
Span<float3> BKE_editmesh_cache_ensure_vert_normals(BMEditMesh &em, bke::EditMeshData &emd);
Span<float3> BKE_editmesh_cache_ensure_face_centers(BMEditMesh &em, bke::EditMeshData &emd);
std::optional<Bounds<float3>> BKE_editmesh_cache_calc_minmax(const BMEditMesh &em,
const bke::EditMeshData &emd);
} // namespace blender

View File

@@ -0,0 +1,33 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
/** \file
* \ingroup bke
*/
#include "BLI_array.hh"
#include "BLI_math_vector_types.hh"
#include "BLI_span.hh"
#include "BLI_string_ref.hh"
namespace blender {
struct BMEditMesh;
/**
* \see #BKE_mesh_calc_loop_tangent, same logic but used arrays instead of #BMesh data.
*/
Array<Array<float4>> BKE_editmesh_uv_tangents_calc(BMEditMesh *em,
Span<float3> face_normals,
Span<float3> corner_normals,
Span<StringRef> uv_names);
Array<float4> BKE_editmesh_orco_tangents_calc(BMEditMesh *em,
Span<float3> face_normals,
Span<float3> corner_normals,
Span<float3> vert_orco);
} // namespace blender

View File

@@ -0,0 +1,282 @@
/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
/** \file
* \ingroup bke
*/
#include "DNA_listBase.h"
namespace blender {
struct Collection;
struct ColliderCache;
struct Depsgraph;
struct RNG;
struct Object;
struct ParticleData;
struct ParticleKey;
struct ParticleSimulationData;
struct Scene;
struct ViewLayer;
enum ePFieldType : short;
struct EffectorWeights *BKE_effector_add_weights(struct Collection *collection);
/* Input to effector code */
struct EffectedPoint {
float *loc;
float *vel;
float *ave; /* angular velocity for particles with dynamic rotation */
float *rot; /* rotation quaternion for particles with dynamic rotation */
float vel_to_frame;
float vel_to_sec;
/* only for particles */
float size, charge;
unsigned int flag;
int index;
struct ParticleSystem *psys; /* particle system the point belongs to */
};
struct GuideEffectorData {
float vec_to_point[3];
float strength;
};
struct EffectorData {
/* Effector point */
float loc[3];
float nor[3];
float vel[3];
float vec_to_point[3];
float distance, falloff;
/* only for effector particles */
float size, charge;
/* only for vortex effector with surface falloff */
float nor2[3], vec_to_point2[3];
int *index; /* point index */
};
/* used for calculating the effector force */
struct EffectorCache {
struct EffectorCache *next, *prev;
struct Depsgraph *depsgraph;
struct Scene *scene;
struct Object *ob;
struct ParticleSystem *psys;
struct SurfaceModifierData *surmd;
struct PartDeflect *pd;
/** Random noise generator, e.g. for wind. */
struct RNG *rng;
/* precalculated for guides */
struct GuideEffectorData *guide_data;
float guide_loc[4], guide_dir[3], guide_radius;
float frame;
int flag;
};
struct EffectorRelation {
struct EffectorRelation *next, *prev;
struct Object *ob;
struct ParticleSystem *psys;
struct PartDeflect *pd;
};
struct PartDeflect *BKE_partdeflect_new(ePFieldType type);
struct PartDeflect *BKE_partdeflect_copy(const struct PartDeflect *pd_src);
void BKE_partdeflect_free(struct PartDeflect *pd);
/**
* Create list of effector relations in the collection or entire scene.
* This is used by the depsgraph to build relations, as well as faster
* lookup of effectors during evaluation.
*/
ListBaseT<EffectorRelation> *BKE_effector_relations_create(struct Depsgraph *depsgraph,
const struct Scene *scene,
struct ViewLayer *view_layer,
struct Collection *collection);
void BKE_effector_relations_free(ListBaseT<EffectorRelation> *lb);
/**
* Create effective list of effectors from relations built beforehand.
*/
ListBaseT<EffectorCache> *BKE_effectors_create(struct Depsgraph *depsgraph,
struct Object *ob_src,
struct ParticleSystem *psys_src,
struct EffectorWeights *weights,
bool use_rotation);
/**
* Generic force/speed system, now used for particles, soft-bodies & dynamic-paint.
*/
void BKE_effectors_apply(ListBaseT<EffectorCache> *effectors,
ListBaseT<ColliderCache> *colliders,
struct EffectorWeights *weights,
struct EffectedPoint *point,
float *force,
float *wind_force,
float *impulse);
void BKE_effectors_free(ListBaseT<EffectorCache> *lb);
void pd_point_from_particle(struct ParticleSimulationData *sim,
struct ParticleData *pa,
struct ParticleKey *state,
struct EffectedPoint *point);
void pd_point_from_loc(
struct Scene *scene, float *loc, float *vel, int index, struct EffectedPoint *point);
void pd_point_from_soft(
struct Scene *scene, float *loc, float *vel, int index, struct EffectedPoint *point);
/* needed for boids */
float effector_falloff(struct EffectorCache *eff,
struct EffectorData *efd,
struct EffectedPoint *point,
struct EffectorWeights *weights);
bool closest_point_on_surface(struct SurfaceModifierData *surmd,
const float co[3],
float surface_co[3],
float surface_nor[3],
float surface_vel[3]);
bool get_effector_data(struct EffectorCache *eff,
struct EffectorData *efd,
struct EffectedPoint *point,
int real_velocity);
/* Required for `particle_system.cc`. */
#if 0
void do_physical_effector(struct EffectorData *eff,
struct EffectorPoint *point,
float *total_force);
float effector_falloff(struct EffectorData *eff,
struct EffectorPoint *point,
struct EffectorWeights *weights);
#endif
/* EffectedPoint->flag */
#define PE_WIND_AS_SPEED 1
#define PE_USE_NORMAL_DATA 4
/* EffectorData->flag */
#define PE_VELOCITY_TO_IMPULSE 1
/* ======== Simulation Debugging ======== */
unsigned int BKE_sim_debug_data_hash(int i);
unsigned int BKE_sim_debug_data_hash_combine(unsigned int kx, unsigned int ky);
/* _VA_SIM_DEBUG_HASH#(i, ...): combined hash value of multiple integers */
/* Internal helpers. */
#define _VA_SIM_DEBUG_HASH1(a) (BKE_sim_debug_data_hash(a))
#define _VA_SIM_DEBUG_HASH2(a, b) \
(BKE_sim_debug_data_hash_combine(BKE_sim_debug_data_hash(a), _VA_SIM_DEBUG_HASH1(b)))
#define _VA_SIM_DEBUG_HASH3(a, b, c) \
(BKE_sim_debug_data_hash_combine(BKE_sim_debug_data_hash(a), _VA_SIM_DEBUG_HASH2(b, c)))
#define _VA_SIM_DEBUG_HASH4(a, b, c, d) \
(BKE_sim_debug_data_hash_combine(BKE_sim_debug_data_hash(a), _VA_SIM_DEBUG_HASH3(b, c, d)))
#define _VA_SIM_DEBUG_HASH5(a, b, c, d, e) \
(BKE_sim_debug_data_hash_combine(BKE_sim_debug_data_hash(a), _VA_SIM_DEBUG_HASH4(b, c, d, e)))
#define _VA_SIM_DEBUG_HASH6(a, b, c, d, e, f) \
(BKE_sim_debug_data_hash_combine(BKE_sim_debug_data_hash(a), _VA_SIM_DEBUG_HASH5(b, c, d, e, f)))
#define _VA_SIM_DEBUG_HASH7(a, b, c, d, e, f, g) \
(BKE_sim_debug_data_hash_combine(BKE_sim_debug_data_hash(a), \
_VA_SIM_DEBUG_HASH6(b, c, d, e, f, g)))
#define _VA_SIM_DEBUG_HASH8(a, b, c, d, e, f, g, h) \
(BKE_sim_debug_data_hash_combine(BKE_sim_debug_data_hash(a), \
_VA_SIM_DEBUG_HASH7(b, c, d, e, f, g, h)))
#define SIM_DEBUG_HASH(...) VA_NARGS_CALL_OVERLOAD(_VA_SIM_DEBUG_HASH, __VA_ARGS__)
struct SimDebugElement {
unsigned int category_hash;
unsigned int hash;
int type;
float color[3];
float v1[3], v2[3];
char str[64];
};
enum eSimDebugElement_Type {
SIM_DEBUG_ELEM_DOT,
SIM_DEBUG_ELEM_CIRCLE,
SIM_DEBUG_ELEM_LINE,
SIM_DEBUG_ELEM_VECTOR,
SIM_DEBUG_ELEM_STRING,
};
struct SimDebugData {
struct GHash *gh;
};
extern SimDebugData *_sim_debug_data;
void BKE_sim_debug_data_set_enabled(bool enable);
bool BKE_sim_debug_data_get_enabled();
void BKE_sim_debug_data_free();
void BKE_sim_debug_data_add_element(int type,
const float v1[3],
const float v2[3],
const char *str,
float r,
float g,
float b,
const char *category,
unsigned int hash);
void BKE_sim_debug_data_remove_element(unsigned int hash);
#define BKE_sim_debug_data_add_dot(p, r, g, b, category, ...) \
{ \
const float v2[3] = {0.0f, 0.0f, 0.0f}; \
BKE_sim_debug_data_add_element( \
SIM_DEBUG_ELEM_DOT, p, v2, NULL, r, g, b, category, SIM_DEBUG_HASH(__VA_ARGS__)); \
}
#define BKE_sim_debug_data_add_circle(p, radius, r, g, b, category, ...) \
{ \
const float v2[3] = {radius, 0.0f, 0.0f}; \
BKE_sim_debug_data_add_element( \
SIM_DEBUG_ELEM_CIRCLE, p, v2, NULL, r, g, b, category, SIM_DEBUG_HASH(__VA_ARGS__)); \
}
#define BKE_sim_debug_data_add_line(p1, p2, r, g, b, category, ...) \
{ \
BKE_sim_debug_data_add_element( \
SIM_DEBUG_ELEM_LINE, p1, p2, NULL, r, g, b, category, SIM_DEBUG_HASH(__VA_ARGS__)); \
}
#define BKE_sim_debug_data_add_vector(p, d, r, g, b, category, ...) \
{ \
BKE_sim_debug_data_add_element( \
SIM_DEBUG_ELEM_VECTOR, p, d, NULL, r, g, b, category, SIM_DEBUG_HASH(__VA_ARGS__)); \
}
#define BKE_sim_debug_data_add_string(p, str, r, g, b, category, ...) \
{ \
BKE_sim_debug_data_add_element( \
SIM_DEBUG_ELEM_STRING, p, NULL, str, r, g, b, category, SIM_DEBUG_HASH(__VA_ARGS__)); \
}
#define BKE_sim_debug_data_remove(...) \
BKE_sim_debug_data_remove_element(SIM_DEBUG_HASH(__VA_ARGS__))
void BKE_sim_debug_data_clear();
void BKE_sim_debug_data_clear_category(const char *category);
} // namespace blender

View File

@@ -0,0 +1,690 @@
/* SPDX-FileCopyrightText: 2024 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
/** \file
* \ingroup bke
*/
#include "BLI_math_vector_types.hh"
#include "BLI_span.hh"
#include "BLI_string_ref.hh"
#include "DNA_curve_types.h"
#include "DNA_listBase.h"
namespace blender {
struct ChannelDriver;
struct FCM_EnvelopeData;
struct FCurve;
struct FModifier;
struct FCurvePathCache;
struct AnimData;
struct AnimationEvalContext;
struct BezTriple;
struct BlendDataReader;
struct BlendWriter;
struct LibraryForeachIDData;
struct PathResolvedRNA;
struct PointerRNA;
struct PropertyRNA;
struct StructRNA;
struct bAction;
struct bContext;
/* ************** F-Curve Modifiers *************** */
/**
* F-Curve Modifier Type-Info (`fmi`):
* This struct provides function pointers for runtime, so that functions can be
* written more generally (with fewer/no special exceptions for various modifiers).
*
* Callers of these functions must check that they actually point to something useful,
* as some constraints don't define some of these.
*
* \warning it is not too advisable to reorder order of members of this struct,
* as you'll have to edit quite a few (#FMODIFIER_NUM_TYPES) of these structs.
*/
struct FModifierTypeInfo {
/* Admin/identity. */
/** #FMODIFIER_TYPE_* */
short type;
/** size in bytes of the struct. */
short size;
/** #eFMI_Action_Types. */
short acttype;
/** #eFMI_Requirement_Flags. */
short requires_flag;
/** name of modifier in interface. */
char name[64];
/** name of struct for SDNA. */
char struct_name[64];
/** Size of buffer that can be reused between time and value evaluation. */
uint storage_size;
/* data management function pointers - special handling */
/** Free any data that is allocated separately (optional). */
void (*free_data)(FModifier *fcm);
/** Copy any special data that is allocated separately (optional). */
void (*copy_data)(FModifier *fcm, const FModifier *src);
/**
* Set settings for data that will be used for FCuModifier.data
* (memory already allocated using #MEM_new_zeroed). */
void (*new_data)(void *mdata);
/** Verifies that the modifier settings are valid */
void (*verify_data)(FModifier *fcm);
/* evaluation */
/** Evaluate time that the modifier requires the F-Curve to be evaluated at */
float (*evaluate_modifier_time)(
const FCurve *fcu, const FModifier *fcm, float cvalue, float evaltime, void *storage);
/** Evaluate the modifier for the given time and 'accumulated' value */
void (*evaluate_modifier)(
const FCurve *fcu, const FModifier *fcm, float *cvalue, float evaltime, void *storage);
};
/* Values which describe the behavior of a FModifier Type */
enum eFMI_Action_Types {
/* modifier only modifies values outside of data range */
FMI_TYPE_EXTRAPOLATION = 0,
/* modifier leaves data-points alone, but adjusts the interpolation between and around them */
FMI_TYPE_INTERPOLATION,
/* modifier only modifies the values of points (but times stay the same) */
FMI_TYPE_REPLACE_VALUES,
/* modifier generates a curve regardless of what came before */
FMI_TYPE_GENERATE_CURVE,
};
/* Flags for the requirements of a FModifier Type */
enum eFMI_Requirement_Flags {
/* modifier requires original data-points (kind of beats the purpose of a modifier stack?) */
FMI_REQUIRES_ORIGINAL_DATA = (1 << 0),
/* modifier doesn't require on any preceding data (i.e. it will generate a curve).
* Use in conjunction with FMI_TYPE_GENRATE_CURVE
*/
FMI_REQUIRES_NOTHING = (1 << 1),
/* refer to modifier instance */
FMI_REQUIRES_RUNTIME_CHECK = (1 << 2),
};
/* Function Prototypes for FModifierTypeInfo's */
/**
* This function should always be used to get the appropriate type-info,
* as it has checks which prevent segfaults in some weird cases.
*/
const FModifierTypeInfo *fmodifier_get_typeinfo(const FModifier *fcm);
/**
* This function should be used for getting the appropriate type-info when only
* a F-Curve modifier type is known.
*/
const FModifierTypeInfo *get_fmodifier_typeinfo(int type);
/* ---------------------- */
/**
* Add a new F-Curve Modifier to the given F-Curve of a certain type.
*/
FModifier *add_fmodifier(ListBaseT<FModifier> *modifiers, int type, FCurve *owner_fcu);
/**
* Make a copy of the specified F-Modifier.
*/
FModifier *copy_fmodifier(const FModifier *src);
/**
* Duplicate all of the F-Modifiers in the Modifier stacks.
*/
void copy_fmodifiers(ListBaseT<FModifier> *dst, const ListBaseT<FModifier> *src);
/**
* Remove and free the given F-Modifier from the given stack.
*/
bool remove_fmodifier(ListBaseT<FModifier> *modifiers, FModifier *fcm);
/**
* Remove all of a given F-Curve's modifiers.
*/
void free_fmodifiers(ListBaseT<FModifier> *modifiers);
/**
* Find the active F-Modifier.
*/
FModifier *find_active_fmodifier(ListBaseT<FModifier> *modifiers);
/**
* Set the active F-Modifier.
*/
void set_active_fmodifier(ListBaseT<FModifier> *modifiers, FModifier *fcm);
/**
* Do we have any modifiers which match certain criteria.
*
* \param mtype: Type of modifier (if 0, doesn't matter).
* \param acttype: Type of action to perform (if -1, doesn't matter).
*/
bool list_has_suitable_fmodifier(const ListBaseT<FModifier> *modifiers, int mtype, short acttype);
struct FModifiersStackStorage {
uint modifier_count;
uint size_per_modifier;
void *buffer;
};
uint evaluate_fmodifiers_storage_size_per_modifier(const ListBaseT<FModifier> *modifiers);
/**
* Evaluate time modifications imposed by some F-Curve Modifiers.
*
* - This step acts as an optimization to prevent the F-Curve stack being evaluated
* several times by modifiers requesting the time be modified, as the final result
* would have required using the modified time
* - Modifiers only ever receive the unmodified time, as subsequent modifiers should be
* working on the 'global' result of the modified curve, not some localized segment,
* so \a evaltime gets set to whatever the last time-modifying modifier likes.
* - We start from the end of the stack, as only the last one matters for now.
*
* \param fcu: Can be NULL.
*/
float evaluate_time_fmodifiers(FModifiersStackStorage *storage,
const ListBaseT<FModifier> *modifiers,
const FCurve *fcu,
float cvalue,
float evaltime);
/**
* Evaluates the given set of F-Curve Modifiers using the given data
* Should only be called after evaluate_time_fmodifiers() has been called.
*/
void evaluate_value_fmodifiers(FModifiersStackStorage *storage,
const ListBaseT<FModifier> *modifiers,
const FCurve *fcu,
float *cvalue,
float evaltime);
/**
* Bake modifiers for given F-Curve to curve sample data, in the frame range defined
* by start and end (inclusive).
*/
void fcurve_bake_modifiers(FCurve *fcu, int start, int end);
int BKE_fcm_envelope_find_index(FCM_EnvelopeData *array,
float frame,
int arraylen,
bool *r_exists);
/* ************** F-Curves API ******************** */
/* threshold for binary-searching keyframes - threshold here should be good enough for now,
* but should become userpref */
#define BEZT_BINARYSEARCH_THRESH 0.01f /* was 0.00001, but giving errors */
/* -------- Data Management -------- */
FCurve *BKE_fcurve_create();
/**
* Frees the F-Curve itself too, so make sure #BLI_remlink is called before calling this.
*/
void BKE_fcurve_free(FCurve *fcu);
/**
* Duplicate a F-Curve.
*/
FCurve *BKE_fcurve_copy(const FCurve *fcu);
/**
* Frees a list of F-Curves.
*/
void BKE_fcurves_free(ListBaseT<FCurve> *list);
/**
* Duplicate a list of F-Curves.
*/
void BKE_fcurves_copy(ListBaseT<FCurve> *dst, ListBaseT<FCurve> *src);
/**
* Set the RNA path of a F-Curve.
*/
void BKE_fcurve_rnapath_set(FCurve &fcu, StringRef rna_path);
/* Set fcurve modifier name and ensure uniqueness.
* Pass new name string when it's been edited otherwise pass empty string. */
void BKE_fmodifier_name_set(FModifier *fcm, const char *name);
/**
* Disable modifiers that requires original data and are not first in the stack.
*/
void BKE_fmodifier_ensure_flag(ListBaseT<FModifier> *modifiers);
/**
* Callback used by lib_query to walk over all ID usages
* (mimics `foreach_id` callback of #IDTypeInfo structure).
*
* Note that this is only relevant when the F-Curve is a driver. Otherwise it
* won't refer to any other ID.
*/
void BKE_fcurve_foreach_id(FCurve *fcu, LibraryForeachIDData *data);
/**
* Find the F-Curve affecting the given RNA-access path + index,
* in the list of F-Curves provided.
*
* \note ONLY use this on a list of F-Curves that is NOT from an Action. Example
* of a good use would be on `adt->drivers`, or `nlastrip->fcurves`.
*
* \see #animrig::fcurve_find_in_action
* \see #animrig::fcurve_find_in_action_slot
*/
FCurve *BKE_fcurve_find(ListBaseT<FCurve> *list, const char rna_path[], int array_index);
/**
* Quick way to loop over all f-curves of a given 'path'.
*/
FCurve *BKE_fcurve_iter_step(FCurve *fcu_iter, const char rna_path[]);
/**
* High level function to get an f-curve from C without having the RNA.
*
* If there is an action assigned to the `id`'s #AnimData, it will be searched for a matching
* F-curve first. Drivers are searched only if no valid action F-curve could be found.
*
* \note Return pointer parameter (`r_driven`) is optional and may be NULL.
*
* \warning In case no animation (from an Action) F-curve is found, returned value is always NULL.
* This means that this function will set `r_driven` to True in case a valid driver F-curve is
* found, but will not return said F-curve. In other words:
* - Animated with FCurve: returns the `FCurve*` and `*r_driven = false`.
* - Animated with driver: returns `NULL` and `*r_driven = true`.
* - Not animated: returns `NULL` and `*r_driven = false`.
*/
FCurve *id_data_find_fcurve(
ID *id, void *data, StructRNA *type, const char *prop_name, int index, bool *r_driven);
/**
* Find an F-Curve from its rna path and index.
*
* The search order is as follows. The first match will be returned:
* - Action
* - Drivers
*
* \note Typically, indices in RNA arrays are stored separately in F-curves, so the rna_path
* should not include them (e.g. `rna_path='location[0]'` will not match any F-Curve on an Object,
* but `rna_path='location', rna_index=0` will if it exists).
*
* \note Return pointer parameters (`r_action`, `r_driven` and `r_special`) are all optional and
* may be NULL.
*
* \note since Actions may have multiple layers all containing an F-Curve for this
* property, what is returned is a best-effort guess. The topmost layer has priority, and it is
* assumed that when it has a strip, it's infinite.
*/
FCurve *BKE_animadata_fcurve_find_by_rna_path(AnimData *animdata,
const char *rna_path,
const int rna_index,
bAction **r_action,
bool *r_driven);
/**
* Find an f-curve based on an rna property.
*/
FCurve *BKE_fcurve_find_by_rna(PointerRNA *ptr,
PropertyRNA *prop,
int rnaindex,
AnimData **r_adt,
bAction **r_action,
bool *r_driven,
bool *r_special);
/**
* Same as #BKE_fcurve_find_by_rna, but takes a context data,
* temp hack needed for complex paths like texture ones.
*
* \param r_special: Optional, ignored when NULL. Set to `true` if the given RNA `ptr` is a NLA
* strip, and the returned F-curve comes from this NLA strip.
*/
FCurve *BKE_fcurve_find_by_rna_context_ui(bContext *C,
const PointerRNA *ptr,
PropertyRNA *prop,
int rnaindex,
AnimData **r_animdata,
bAction **r_action,
bool *r_driven,
bool *r_special);
/**
* Binary search algorithm for finding where to 'insert' #BezTriple with given frame number.
* Returns the index to insert at (data already at that index will be offset if replace is 0)
*/
int BKE_fcurve_bezt_binarysearch_index(const BezTriple array[],
float frame,
int arraylen,
bool *r_replace);
/**
* Calculate the x range of the given F-Curve's data.
* \return true if a range has been found.
*/
bool BKE_fcurve_calc_range(const FCurve *fcu, float *r_min, float *r_max, bool selected_keys_only);
/**
* Calculate the x and y extents of F-Curve's data.
*
* \param selected_keys_only: if true, only selected keyframes are considered for the bounds.
* \param include_handles: if true, the handles are considered for the bounds, otherwise only the
* key point itself.
* \param frame_range: Only calculate the bounds of the FCurve in the given range.
* Does the full range if NULL.
*
* \return true if the bounds have been found.
*/
bool BKE_fcurve_calc_bounds(const FCurve *fcu,
bool selected_keys_only,
bool include_handles,
const float frame_range[2],
rctf *r_bounds);
/**
* Return an array of keyed frames, rounded to `interval`.
*
* \param interval: Set to 1.0 to round to whole keyframes, 0.5 for in-between key-frames, etc.
*
* \note An interval of zero could be supported (this implies no rounding at all),
* however this risks very small differences in float values being treated as separate keyframes.
*/
float *BKE_fcurves_calc_keyed_frames_ex(FCurve **fcurve_array,
int fcurve_array_len,
float interval,
int *r_frames_len);
float *BKE_fcurves_calc_keyed_frames(FCurve **fcurve_array,
int fcurve_array_len,
int *r_frames_len);
/**
* Set the index that stores the FCurve's active keyframe, assuming that \a active_bezt
* is already part of `fcu->bezt`. If NULL, set active keyframe index to "none."
*/
void BKE_fcurve_active_keyframe_set(FCurve *fcu, const BezTriple *active_bezt);
/**
* Get the active keyframe index, with sanity checks for point bounds.
*/
int BKE_fcurve_active_keyframe_index(const FCurve *fcu);
/**
* Move the indexed keyframe to the given value,
* and move the handles with it to ensure the slope remains the same.
*/
void BKE_fcurve_keyframe_move_time_with_handles(BezTriple *keyframe, const float new_time);
void BKE_fcurve_keyframe_move_value_with_handles(BezTriple *keyframe, float new_value);
/* .............. */
/**
* Are keyframes on F-Curve of any use (to final result, and to show in editors)?
* Usability of keyframes refers to whether they should be displayed,
* and also whether they will have any influence on the final result.
*/
bool BKE_fcurve_are_keyframes_usable(const FCurve &fcu);
/**
* Can keyframes be added to F-Curve?
* Keyframes can only be added if they are already visible.
*/
bool BKE_fcurve_is_keyframable(const FCurve &fcu);
bool BKE_fcurve_is_protected(const FCurve &fcu);
/**
* Are any of the keyframe control points selected on the F-Curve?
*/
bool BKE_fcurve_has_selected_control_points(const FCurve &fcu);
/**
* Deselect all keyframes within that FCurve.
*/
void BKE_fcurve_deselect_all_keys(FCurve &fcu);
/**
* Checks if the F-Curve has a Cycles modifier with simple settings
* that warrant transition smoothing.
*/
bool BKE_fcurve_is_cyclic(const FCurve &fcu);
/* Type of infinite cycle for a curve. */
enum eFCU_Cycle_Type {
FCU_CYCLE_NONE = 0,
/* The cycle repeats identically to the base range. */
FCU_CYCLE_PERFECT,
/* The cycle accumulates the change between start and end keys. */
FCU_CYCLE_OFFSET,
};
/**
* Checks if the F-Curve has a Cycles modifier, and returns the type of the cycle behavior.
*/
eFCU_Cycle_Type BKE_fcurve_get_cycle_type(const FCurve &fcu);
/**
* Recompute bezier handles of all three given BezTriples, so that `bezt` can be inserted between
* `prev` and `next` without changing the resulting curve shape.
*
* \param r_pdelta: return Y difference between `bezt` and the original curve value at its X
* position.
* \return Whether the split was successful.
*/
bool BKE_fcurve_bezt_subdivide_handles(BezTriple *bezt,
BezTriple *prev,
BezTriple *next,
float *r_pdelta);
/**
* Resize the FCurve 'bezt' array to fit the given length.
*
* This potentially moves the entire array, and thus pointers from before this call should be
* considered invalid / dangling.
*
* \param new_totvert: new number of elements in the FCurve's `bezt` array.
*
* \note When increasing the size of the array, newly added elements (that is, in the
* [old_totvert..new_totvert] interval) are zero-initialized.
*/
void BKE_fcurve_bezt_resize(FCurve &fcu, int new_totvert);
/**
* Merge the two given BezTriple arrays `a` and `b` into a newly allocated BezTriple array of size
* `r_merged_size`. In case of keys on identical frames, `a` takes precedence.
* Does not free `a` or `b`.
* Assumes that both arrays are sorted for the x-position.
* Has a complexity of O(N) with respect to the length of `size_a` + `size_b`.
*
* \return The merged BezTriple array of length `r_merged_size`.
*/
BezTriple *BKE_bezier_array_merge(
const BezTriple *a, int size_a, const BezTriple *b, int size_b, int *r_merged_size);
/**
* Delete a keyframe from an F-curve at a specific index.
*/
void BKE_fcurve_delete_key(FCurve *fcu, int index);
/**
* Delete an index range of keyframes from an F-curve. This is more performant than individually
* removing keys.
* Has a complexity of O(N) with respect to number of keys in `fcu`.
*
* \param index_range: is right exclusive.
*/
void BKE_fcurve_delete_keys(FCurve &fcu, uint2 index_range);
/**
* Delete selected keyframes from an F-curve.
*/
bool BKE_fcurve_delete_keys_selected(FCurve &fcu);
/**
* Delete all keyframes from an F-curve.
*/
void BKE_fcurve_delete_keys_all(FCurve &fcu);
/**
* Called during transform/snapping to make sure selected keyframes replace
* any other keyframes which may reside on that frame (that is not selected).
*
* \param sel_flag: The flag (bezt.f1/2/3) value to use to determine selection. Usually `SELECT`,
* but may want to use a different one at times (if caller does not operate on
* selection).
*/
void BKE_fcurve_merge_duplicate_keys(FCurve *fcu, const int sel_flag, const bool use_handle);
/**
* Ensure the FCurve is a proper function, such that every X-coordinate of the
* timeline has only one value of the FCurve. In other words, removes duplicate
* keyframes.
*
* Contrary to #BKE_fcurve_merge_duplicate_keys, which is intended for
* interactive use, and where selection matters, this is a simpler deduplication
* where the last duplicate "wins".
*
* Assumes the keys are sorted (see #sort_time_fcurve).
*
* After deduplication, call `BKE_fcurve_handles_recalc(fcu);`
*/
void BKE_fcurve_deduplicate_keys(FCurve *fcu);
/* -------- Curve Sanity -------- */
/**
* This function recalculates the handles of an F-Curve. Acts based on selection with `SELECT`
* flag. To use a different flag, use #BKE_fcurve_handles_recalc_ex().
*
* If the BezTriples have been rearranged, sort them first before using this.
*/
void BKE_fcurve_handles_recalc(FCurve &fcu);
/**
* Variant of #BKE_fcurve_handles_recalc() that allows calculating based on a different select
* flag.
*
* \param handle_sel_flag: The flag (bezt.f1/2/3) value to use to determine selection.
* Usually `SELECT`, but may want to use a different one at times
* (if caller does not operate on selection).
*/
void BKE_fcurve_handles_recalc_ex(FCurve &fcu, eBezTriple_Flag handle_sel_flag);
enum class HandleSide {
LEFT,
RIGHT,
};
/**
* For the given keyframe, update the handle mode of one side to be in a valid state based on the
* opposite side. For example if one side is set to "Aligned" the other has to copy that, otherwise
* it wouldn't be actually aligned. This is useful in cases where the user explicitly sets on
* handle type.
*
* \param source_side: The source side from which to update the handle flags. This side will not be
* affected.
*/
void BKE_fcurve_update_handle_flag_from_opposite(BezTriple &key, HandleSide source_side);
/**
* Update handles, making sure the handle-types are valid (e.g. correctly deduced from an "Auto"
* type), and recalculating their position vectors.
* Use when something has changed handle positions.
*
* \param sel_flag: The flag (bezt.f1/2/3) value to use to determine selection. Usually `SELECT`,
* but may want to use a different one at times (if caller does not operate on selection).
* \param use_handle: Check selection state of individual handles, otherwise always update both
* handles if the key is selected.
*/
void testhandles_fcurve(FCurve *fcu, eBezTriple_Flag sel_flag, bool use_handle);
/**
* This function sorts BezTriples so that they are arranged in chronological order,
* as tools working on F-Curves expect that the BezTriples are in order.
*/
void sort_time_fcurve(FCurve &fcu);
/**
* This function tests if any BezTriples are out of order, thus requiring a sort.
*/
bool test_time_fcurve(FCurve &fcu);
/**
* The length of each handle is not allowed to be more
* than the horizontal distance between (v1-v4).
* This is to prevent curve loops.
*
* This function is very similar to #BKE_curve_correct_bezpart(), but allows a steeper tangent for
* more snappy animations. This is not desired for other areas in which curves are used, though.
*/
void BKE_fcurve_correct_bezpart(const float v1[2], float v2[2], float v3[2], const float v4[2]);
/* -------- Evaluation -------- */
/**
* Evaluate a non-driver F-Curve.
*/
float evaluate_fcurve(const FCurve *fcu, float evaltime);
/**
* Evaluate the F-Curve; if this is a driver, that aspect is ignored and only its F-Curve is
* evaluated.
*/
float evaluate_fcurve_only_curve(const FCurve *fcu, float evaltime);
/**
* Evaluate a non-driver F-Curve, without applying its modifiers.
*/
float evaluate_fcurve_unmodified(const FCurve *fcu, float evaltime);
float evaluate_fcurve_driver(PathResolvedRNA *anim_rna,
FCurve *fcu,
ChannelDriver *driver_orig,
const AnimationEvalContext *anim_eval_context);
/**
* Checks if the curve has valid keys, drivers or modifiers that produce an actual curve.
*/
bool BKE_fcurve_is_empty(const FCurve *fcu);
/**
* Calculate the value of the given F-Curve at the given frame,
* and store it's value in #FCurve.curval.
*/
float calculate_fcurve(PathResolvedRNA *anim_rna,
FCurve *fcu,
const AnimationEvalContext *anim_eval_context);
/* ************* F-Curve Samples API ******************** */
/* -------- Defines -------- */
/**
* Basic signature for F-Curve sample-creation function.
*
* \param fcu: the F-Curve being operated on.
* \param data: pointer to some specific data that may be used by one of the callbacks.
*/
typedef float (*FcuSampleFunc)(FCurve *fcu, void *data, float evaltime);
/* ----- Sampling Callbacks ------ */
/**
* Basic sampling callback which acts as a wrapper for #evaluate_fcurve()
* 'data' arg here is unneeded here.
*/
float fcurve_samplingcb_evalcurve(FCurve *fcu, void *data, float evaltime);
/* -------- Main Methods -------- */
/**
* Main API function for creating a set of sampled curve data, given some callback function
* used to retrieve the values to store.
*/
void fcurve_store_samples(FCurve *fcu, void *data, int start, int end, FcuSampleFunc sample_cb);
/**
* Convert baked/sampled f-curves into bezt/regular f-curves.
*/
void fcurve_samples_to_keyframes(FCurve *fcu, int start, int end);
/* ************* F-Curve .blend file API ******************** */
void BKE_fmodifiers_blend_write(BlendWriter *writer, ListBaseT<FModifier> *fmodifiers);
void BKE_fmodifiers_blend_read_data(BlendDataReader *reader,
ListBaseT<FModifier> *fmodifiers,
FCurve *curve);
/**
* Write the FCurve's data to the writer.
* If this is used to write an FCurve, be sure to call `writer->write_struct(fcurve);`
* before calling this function.
*/
void BKE_fcurve_blend_write_data(BlendWriter *writer, FCurve *fcu);
void BKE_fcurve_blend_write_listbase(BlendWriter *writer, ListBaseT<FCurve> *fcurves);
void BKE_fcurve_blend_read_data(BlendDataReader *reader, FCurve *fcu);
void BKE_fcurve_blend_read_data_listbase(BlendDataReader *reader, ListBaseT<FCurve> *fcurves);
} // namespace blender

View File

@@ -0,0 +1,194 @@
/* SPDX-FileCopyrightText: 2009 Blender Authors, Joshua Leung. All rights reserved.
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
/** \file
* \ingroup bke
*/
#include "DNA_listBase.h"
namespace blender {
struct AnimationEvalContext;
struct ChannelDriver;
struct DriverTarget;
struct DriverVar;
struct FCurve;
struct ListBase;
struct PathResolvedRNA;
struct PointerRNA;
struct PropertyRNA;
struct Scene;
struct ViewLayer;
enum eDriverVar_Types : char;
/* ************** F-Curve Drivers ***************** */
/* With these iterators for convenience, the variables "tarIndex" and "dtar" can be
* accessed directly from the code using them, but it is not recommended that their
* values be changed to point at other slots...
*/
/* convenience looper over ALL driver targets for a given variable (even the unused ones) */
#define DRIVER_TARGETS_LOOPER_BEGIN(dvar) \
{ \
auto *dtar = &(dvar)->targets[0]; \
int tarIndex = 0; \
for (; tarIndex < MAX_DRIVER_TARGETS; tarIndex++, dtar++)
/* convenience looper over USED driver targets only */
#define DRIVER_TARGETS_USED_LOOPER_BEGIN(dvar) \
{ \
auto *dtar = &(dvar)->targets[0]; \
int tarIndex = 0; \
for (; tarIndex < (dvar)->num_targets; tarIndex++, dtar++)
/* tidy up for driver targets loopers */
#define DRIVER_TARGETS_LOOPER_END \
} \
((void)0)
/* ---------------------- */
/**
* This frees the driver itself.
*/
void fcurve_free_driver(struct FCurve *fcu);
/**
* This makes a copy of the given driver.
*/
struct ChannelDriver *fcurve_copy_driver(const struct ChannelDriver *driver);
/**
* Get property from which the specific property can be found from.
*
* This depends on the type of `dvar`:
*
* - For the Single Property the `r_prop` is a pointer to an ID, which is used to resolve the
* target rna_path.
*
* - For Transform Channel, Rotational Difference, Distance the `r_prop` is a pointer to an
* object from which transformation is read.
*
* - For Context Property the `r_prop` points to a resolved data corresponding to the
* dtar->context_property accessed from the given evaluated context. This could either be an ID
* property for Active Scene, or a data property for Active View Layer.
*
* If the target property can not be resolved false is returned.
*/
struct DriverTargetContext {
struct Scene *scene;
struct ViewLayer *view_layer;
};
bool driver_get_target_property(const DriverTargetContext *driver_target_context,
struct DriverVar *dvar,
struct DriverTarget *dtar,
struct PointerRNA *r_prop);
/**
* Copy driver variables from src_vars list to dst_vars list.
*/
void driver_variables_copy(ListBaseT<DriverVar> *dst_vars, const ListBaseT<DriverVar> *src_vars);
/**
* Compute channel values for a rotational Transform Channel driver variable.
*/
void BKE_driver_target_matrix_to_rot_channels(
float mat[4][4], int auto_order, int rotation_mode, int channel, bool angles, float r_buf[4]);
/**
* Perform actual freeing driver variable and remove it from the given list.
*/
void driver_free_variable(ListBaseT<DriverVar> *variables, struct DriverVar *dvar);
/**
* Free the driver variable and do extra updates.
*/
void driver_free_variable_ex(struct ChannelDriver *driver, struct DriverVar *dvar);
/**
* Change the type of driver variable.
*/
void driver_change_variable_type(struct DriverVar *dvar, eDriverVar_Types type);
/**
* Validate driver variable name (after being renamed).
*/
void driver_variable_name_validate(struct DriverVar *dvar);
/**
* Ensure the driver variable's name is unique.
*
* Assumes the driver variable has already been assigned to the driver, so that
* the `prev/next` pointers can be used to find the other variables.
*/
void driver_variable_unique_name(struct DriverVar *dvar);
/**
* Add a new driver variable.
*/
struct DriverVar *driver_add_new_variable(struct ChannelDriver *driver);
/**
* Evaluate a Driver Variable to get a value that contributes to the final.
*/
float driver_get_variable_value(const struct AnimationEvalContext *anim_eval_context,
struct ChannelDriver *driver,
struct DriverVar *dvar);
enum eDriverVariablePropertyResult {
/** The property reference has been successfully resolved and can be accessed. */
DRIVER_VAR_PROPERTY_SUCCESS,
/** Evaluation should use the fallback value. */
DRIVER_VAR_PROPERTY_FALLBACK,
/** The target property could not be resolved. */
DRIVER_VAR_PROPERTY_INVALID,
/**
* The property was resolved (output parameters are set),
* but the array index is out of bounds.
*/
DRIVER_VAR_PROPERTY_INVALID_INDEX
};
/**
* Same as 'dtar_get_prop_val'. but get the RNA property.
*/
eDriverVariablePropertyResult driver_get_variable_property(
const struct AnimationEvalContext *anim_eval_context,
struct ChannelDriver *driver,
struct DriverVar *dvar,
struct DriverTarget *dtar,
bool allow_no_index,
struct PointerRNA *r_ptr,
struct PropertyRNA **r_prop,
int *r_index);
/**
* Check if the expression in the driver conforms to the simple subset.
*/
bool BKE_driver_has_simple_expression(struct ChannelDriver *driver);
/**
* Check if the expression in the driver may depend on the current frame.
*/
bool BKE_driver_expression_depends_on_time(struct ChannelDriver *driver);
/**
* Reset cached compiled expression data.
*/
void BKE_driver_invalidate_expression(struct ChannelDriver *driver,
bool expr_changed,
bool varname_changed);
/**
* Evaluate an Channel-Driver to get a 'time' value to use
* instead of `anim_eval_context->eval_time`.
*
* - `anim_eval_context->eval_time` is the frame at which F-Curve is being evaluated.
* - Has to return a float value.
* - \a driver_orig is where we cache Python expressions, in case of copy-on-eval
*/
float evaluate_driver(struct PathResolvedRNA *anim_rna,
struct ChannelDriver *driver,
struct ChannelDriver *driver_orig,
const struct AnimationEvalContext *anim_eval_context);
} // namespace blender

View File

@@ -0,0 +1,90 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup bke
*/
#include "BLI_span.hh"
#include "BLI_string_ref.hh"
#include "BLI_vector.hh"
#include "DNA_windowmanager_types.h"
#include "RNA_types.hh"
namespace blender {
struct bContext;
namespace bke {
#define FH_MAX_FILE_EXTENSIONS_STR 512
struct FileHandlerType {
/** Unique name. */
char idname[OP_MAX_TYPENAME];
/** For UI text. */
char label[OP_MAX_TYPENAME];
/** Import operator name. */
char import_operator[OP_MAX_TYPENAME];
/** Export operator name. */
char export_operator[OP_MAX_TYPENAME];
/**
* Formatted string of file extensions supported by the file handler, each extension should
* start with a `.` and be separated by `;`. For Example: `".blend;.ble"`.
*/
char file_extensions_str[FH_MAX_FILE_EXTENSIONS_STR];
/** Check if file handler can be used on file drop. */
bool (*poll_drop)(const bContext *C, FileHandlerType *file_handle_type);
/** List of file extensions supported by the file handler. */
Vector<std::string> file_extensions;
/** RNA integration. */
ExtensionRNA rna_ext;
/**
* Return a vector of indices in #paths of file paths supported by the file handler.
*/
Vector<int64_t> filter_supported_paths(const Span<std::string> paths) const;
/**
* Generate a default file name for use with this file handler.
*/
std::string get_default_filename(StringRefNull name);
};
/**
* Adds a new `file_handler` to the `file_handlers` list, also loads all the file extensions from
* the formatted `FileHandlerType.file_extensions_str` string to `FileHandlerType.file_extensions`
* list.
*
* The new `file_handler` is expected to have a unique `FileHandlerType.idname`.
*/
void file_handler_add(std::unique_ptr<FileHandlerType> file_handler);
/** Returns a `file_handler` that have a specific `idname`, otherwise return `nullptr`. */
FileHandlerType *file_handler_find(StringRef idname);
/**
* Removes and frees a specific `file_handler` from the `file_handlers` list, the `file_handler`
* pointer will be not longer valid for use.
*/
void file_handler_remove(FileHandlerType *file_handler);
/** Return pointers to all registered file handlers. */
Span<std::unique_ptr<FileHandlerType>> file_handlers();
/**
* Return a vector of file handlers that support any file path in `paths` and the call to
* `poll_drop` returns #true. Caller must check if each file handler have a valid
* `import_operator`.
*/
Vector<FileHandlerType *> file_handlers_poll_file_drop(const bContext *C,
const Span<std::string> paths);
} // namespace bke
} // namespace blender

View File

@@ -0,0 +1,106 @@
/* SPDX-FileCopyrightText: Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
/** \file
* \ingroup bke
*/
namespace blender {
struct Depsgraph;
struct FluidDomainSettings;
struct FluidEffectorSettings;
struct FluidFlowSettings;
struct FluidModifierData;
struct Main;
struct Scene;
enum eFluidDomain_BorderFlags : int;
enum eFluidDomain_FileFormat : char;
enum eFluidDomain_ParticleTypes : int;
enum eFluidDomain_Type : short;
enum eFluidEffector_Type : short;
enum eFluidFlow_Behavior : short;
enum eFluidFlow_Type : short;
enum eParticleType : short;
typedef float (*BKE_Fluid_BresenhamFn)(
float *result, const float *input, int res[3], int *pixel, float *tRay, float correct);
struct Mesh *BKE_fluid_modifier_do(struct FluidModifierData *fmd,
struct Depsgraph *depsgraph,
struct Scene *scene,
struct Object *ob,
struct Mesh *mesh);
void BKE_fluid_modifier_free(struct FluidModifierData *fmd);
void BKE_fluid_modifier_reset(struct FluidModifierData *fmd);
void BKE_fluid_modifier_create_type_data(struct FluidModifierData *fmd);
void BKE_fluid_modifier_copy(const struct FluidModifierData *fmd,
struct FluidModifierData *tfmd,
int flag);
bool BKE_fluid_reallocate_fluid(struct FluidDomainSettings *fds, int res[3], int free_old);
void BKE_fluid_reallocate_copy_fluid(struct FluidDomainSettings *fds,
int o_res[3],
int n_res[3],
const int o_min[3],
const int n_min[3],
const int o_max[3],
int o_shift[3],
int n_shift[3]);
void BKE_fluid_cache_free_all(struct FluidDomainSettings *fds, struct Object *ob);
void BKE_fluid_cache_free(struct FluidDomainSettings *fds, struct Object *ob, int cache_map);
void BKE_fluid_cache_new_name_for_current_session(int maxlen, char *r_name);
/**
* Get fluid velocity and density at given coordinates.
* \returns fluid density or -1.0f if outside domain.
*/
float BKE_fluid_get_velocity_at(struct Object *ob, float position[3], float velocity[3]);
int BKE_fluid_get_data_flags(struct FluidDomainSettings *fds);
void BKE_fluid_particle_system_create(struct Main *bmain,
struct Object *ob,
const char *pset_name,
const char *parts_name,
const char *psys_name,
eParticleType psys_type);
void BKE_fluid_particle_system_destroy(struct Object *ob, int particle_type);
void BKE_fluid_cache_startframe_set(struct FluidDomainSettings *settings, int value);
void BKE_fluid_cache_endframe_set(struct FluidDomainSettings *settings, int value);
void BKE_fluid_cachetype_mesh_set(struct FluidDomainSettings *settings,
eFluidDomain_FileFormat cache_mesh_format);
void BKE_fluid_cachetype_data_set(struct FluidDomainSettings *settings,
eFluidDomain_FileFormat cache_data_format);
void BKE_fluid_cachetype_particle_set(struct FluidDomainSettings *settings,
eFluidDomain_FileFormat cache_particle_format);
void BKE_fluid_cachetype_noise_set(struct FluidDomainSettings *settings,
eFluidDomain_FileFormat cache_noise_format);
void BKE_fluid_collisionextents_set(struct FluidDomainSettings *settings,
eFluidDomain_BorderFlags value,
bool clear);
void BKE_fluid_particles_set(struct FluidDomainSettings *settings,
eFluidDomain_ParticleTypes value,
bool clear);
void BKE_fluid_domain_type_set(struct Object *object,
struct FluidDomainSettings *settings,
eFluidDomain_Type type);
void BKE_fluid_flow_type_set(struct Object *object,
struct FluidFlowSettings *settings,
eFluidFlow_Type type);
void BKE_fluid_effector_type_set(struct Object *object,
struct FluidEffectorSettings *settings,
eFluidEffector_Type type);
void BKE_fluid_fields_sanitize(struct FluidDomainSettings *settings);
void BKE_fluid_flow_behavior_set(struct Object *object,
struct FluidFlowSettings *settings,
eFluidFlow_Behavior behavior);
} // namespace blender

View File

@@ -0,0 +1,53 @@
/* SPDX-FileCopyrightText: 2013 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
/** \file
* \ingroup bke
*/
namespace blender {
struct FreestyleConfig;
struct FreestyleLineSet;
struct FreestyleModuleConfig;
struct Main;
/* RNA aliases */
struct FreestyleModuleSettings;
struct FreestyleSettings;
/* FreestyleConfig */
void BKE_freestyle_config_init(struct FreestyleConfig *config);
void BKE_freestyle_config_free(struct FreestyleConfig *config, bool do_id_user);
void BKE_freestyle_config_copy(struct FreestyleConfig *new_config,
const struct FreestyleConfig *config,
int flag);
/* FreestyleConfig.modules */
struct FreestyleModuleConfig *BKE_freestyle_module_add(struct FreestyleConfig *config);
bool BKE_freestyle_module_delete(struct FreestyleConfig *config,
struct FreestyleModuleConfig *module_conf);
/**
* Reinsert \a module_conf offset by \a direction from current position.
* \return if position of \a module_conf changed.
*/
bool BKE_freestyle_module_move(struct FreestyleConfig *config,
struct FreestyleModuleConfig *module_conf,
int direction);
/* FreestyleConfig.linesets */
struct FreestyleLineSet *BKE_freestyle_lineset_add(struct Main *bmain,
struct FreestyleConfig *config,
const char *name);
bool BKE_freestyle_lineset_delete(struct FreestyleConfig *config,
struct FreestyleLineSet *lineset);
struct FreestyleLineSet *BKE_freestyle_lineset_get_active(struct FreestyleConfig *config);
short BKE_freestyle_lineset_get_active_index(struct FreestyleConfig *config);
void BKE_freestyle_lineset_set_active_index(struct FreestyleConfig *config, short index);
void BKE_freestyle_lineset_unique_name(struct FreestyleConfig *config,
struct FreestyleLineSet *lineset);
} // namespace blender

View File

@@ -0,0 +1,70 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
#include "BKE_curves.hh"
#include "BKE_mesh_types.hh"
#include "DNA_lattice_types.h"
/** \file
* \ingroup bke
*/
namespace blender::bke::compare_geometry {
enum class GeoMismatch : int8_t;
/**
* Convert the mismatch to a human-readable string for display.
*/
const char *mismatch_to_string(const GeoMismatch &mismatch);
/**
* \brief Checks if the two meshes are different, returning the type of mismatch if any. Changes in
* index order are detected, but treated as a mismatch.
*
* \details Instead of just blindly comparing the two meshes, the code tries to determine if they
* are isomorphic. Two meshes are considered isomorphic, if, for each domain, there is a bijection
* between the two meshes such that the bijections preserve connectivity.
*
* In general, determining if two graphs are isomorphic is a very difficult problem (no polynomial
* time algorithm is known). Because we have more information than just connectivity (attributes),
* we can compute it in a more reasonable time in most cases.
*
* \returns The type of mismatch that was detected, if there is any.
*/
std::optional<GeoMismatch> compare_meshes(const Mesh &mesh1, const Mesh &mesh2, float threshold);
/**
* \brief Checks if the two curves geometries are different, returning the type of mismatch if any.
* Changes in index order are detected, but treated as a mismatch.
*
* \returns The type of mismatch that was detected, if there is any.
*/
std::optional<GeoMismatch> compare_curves(const CurvesGeometry &curves1,
const CurvesGeometry &curves2,
float threshold);
/**
* \brief Checks if the two lattices are different, returning the type of mismatch if any.
*
* \returns The type of mismatch that was detected, if there is any.
*/
std::optional<GeoMismatch> compare_lattices(const Lattice &lattice1,
const Lattice &lattice2,
float threshold);
/**
* \brief Checks if two Grease Pencil geometries are different, returning the type of mismatch if
* any.
*
* \returns The type of mismatch that was detected, if there is any.
*/
std::optional<GeoMismatch> compare_grease_pencil(const GreasePencil &grease_pencil_1,
const GreasePencil &grease_pencil_2,
float threshold);
} // namespace blender::bke::compare_geometry

View File

@@ -0,0 +1,582 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
/** \file
* \ingroup bke
*
* Common field utilities and field definitions for geometry components.
*/
#include "BKE_anonymous_attribute_id.hh"
#include "BKE_geometry_set.hh"
#include "BLI_fixed_string.hh"
#include "FN_field_evaluation.hh"
namespace blender {
struct Mesh;
struct PointCloud;
namespace bke {
class CurvesGeometry;
class GeometryFieldInput;
namespace greasepencil {
class Drawing;
}
class MeshFieldContext : public fn::FieldContext {
private:
const Mesh &mesh_;
AttrDomain domain_;
public:
MeshFieldContext(const Mesh &mesh, AttrDomain domain);
const Mesh &mesh() const
{
return mesh_;
}
AttrDomain domain() const
{
return domain_;
}
};
class CurvesFieldContext : public fn::FieldContext {
private:
const CurvesGeometry &curves_;
AttrDomain domain_;
const Curves *curves_id_ = nullptr;
public:
CurvesFieldContext(const CurvesGeometry &curves, AttrDomain domain);
CurvesFieldContext(const Curves &curves_id, AttrDomain domain);
const CurvesGeometry &curves() const
{
return curves_;
}
const Curves *curves_id() const
{
return curves_id_;
}
AttrDomain domain() const
{
return domain_;
}
};
class PointCloudFieldContext : public fn::FieldContext {
private:
const PointCloud &pointcloud_;
public:
PointCloudFieldContext(const PointCloud &pointcloud) : pointcloud_(pointcloud) {}
const PointCloud &pointcloud() const
{
return pointcloud_;
}
};
class GreasePencilFieldContext : public fn::FieldContext {
private:
const GreasePencil &grease_pencil_;
public:
GreasePencilFieldContext(const GreasePencil &grease_pencil) : grease_pencil_(grease_pencil) {}
const GreasePencil &grease_pencil() const
{
return grease_pencil_;
}
};
class GreasePencilLayerFieldContext : public fn::FieldContext {
private:
const GreasePencil &grease_pencil_;
AttrDomain domain_;
int layer_index_;
public:
GreasePencilLayerFieldContext(const GreasePencil &grease_pencil,
AttrDomain domain,
int layer_index)
: grease_pencil_(grease_pencil), domain_(domain), layer_index_(layer_index)
{
}
const GreasePencil &grease_pencil() const
{
return grease_pencil_;
}
AttrDomain domain() const
{
return domain_;
}
int layer_index() const
{
return layer_index_;
}
GVArray get_varray_for_input(const fn::FieldInput &field_input,
const IndexMask &mask,
ResourceScope &scope) const override;
};
class InstancesFieldContext : public fn::FieldContext {
private:
const Instances &instances_;
public:
InstancesFieldContext(const Instances &instances) : instances_(instances) {}
const Instances &instances() const
{
return instances_;
}
};
/**
* A field context that can represent meshes, curves, point clouds, instances or grease pencil
* layers, used for field inputs that can work for multiple geometry types.
*/
class GeometryFieldContext : public fn::FieldContext {
private:
/**
* Store the geometry as a void pointer instead of a #GeometryComponent to allow referencing data
* that doesn't correspond directly to a geometry component type, in this case #CurvesGeometry
* instead of #Curves.
*/
const void *geometry_;
const GeometryComponent::Type type_;
AttrDomain domain_;
const Curves *curves_id_ = nullptr;
/**
* Only used when the type is grease pencil and the domain is either points or curves
* (not layers).
*/
int grease_pencil_layer_index_;
friend GeometryFieldInput;
public:
GeometryFieldContext(const GeometryFieldContext &other, AttrDomain domain);
GeometryFieldContext(const GeometryComponent &component, AttrDomain domain);
GeometryFieldContext(const void *geometry,
GeometryComponent::Type type,
AttrDomain domain,
int grease_pencil_layer_index);
GeometryFieldContext(const Mesh &mesh, AttrDomain domain);
GeometryFieldContext(const CurvesGeometry &curves, AttrDomain domain);
GeometryFieldContext(const Curves &curves_id, AttrDomain domain);
GeometryFieldContext(const GreasePencil &grease_pencil);
GeometryFieldContext(const GreasePencil &grease_pencil, AttrDomain domain, int layer_index);
GeometryFieldContext(const PointCloud &points);
GeometryFieldContext(const Instances &instances);
const void *geometry() const
{
return geometry_;
}
GeometryComponent::Type type() const
{
return type_;
}
AttrDomain domain() const
{
return domain_;
}
int grease_pencil_layer_index() const
{
BLI_assert(this->type_ == GeometryComponent::Type::GreasePencil);
BLI_assert(ELEM(this->domain_, AttrDomain::Layer, AttrDomain::Curve, AttrDomain::Point));
return grease_pencil_layer_index_;
}
std::optional<AttributeAccessor> attributes() const;
const Mesh *mesh() const;
const CurvesGeometry *curves() const;
const PointCloud *pointcloud() const;
const GreasePencil *grease_pencil() const;
const greasepencil::Drawing *grease_pencil_layer_drawing() const;
const Instances *instances() const;
const CurvesGeometry *curves_or_strokes() const;
const Curves *curves_id() const;
};
/**
* Information about a field input's relationship with the domain of the data it represents.
*
* If the native field domain is `Point/Face`, evaluating the field on the corner domain is
* identical to evaluating it on the native domain and copying the values to corners afterwards. If
* the native field domain is `Curve`, evaluating it on the point domain is identical to evaluating
* it on the curve domain and copying the values to the points afterwards. Of course, copying the
* values to the more complex domain can be skipped if the algorithm can be optimized to use the
* data directly from the smaller domain.
*/
struct NativeFieldDomain {
/**
* The input may depend on the order of the domain. For example, the index field, can't be
* transparently evaluated on a different domain unlike attribute fields which are more flexible
* because of domain interpolation.
*/
struct None {};
/** The input represents data on a specific domain. */
struct Domain {
AttrDomain domain;
};
/** Interpolating the field between domains does not change the value. */
struct Constant {};
std::variant<None, Domain, Constant> variant;
NativeFieldDomain(const None & /*tag*/) : variant(None{}) {}
NativeFieldDomain(const Domain &domain) : variant(domain) {}
NativeFieldDomain(const Constant & /*tag*/) : variant(Constant{}) {}
};
class GeometryFieldInput : public fn::FieldInput {
public:
using fn::FieldInput::FieldInput;
GVArray get_varray_for_context(const fn::FieldContext &context,
const IndexMask &mask,
ResourceScope &scope) const override;
virtual GVArray get_varray_for_context(const GeometryFieldContext &context,
const IndexMask &mask) const = 0;
virtual std::optional<AttrDomain> preferred_domain(const GeometryComponent &component) const;
virtual NativeFieldDomain native_domain_info(const GeometryComponent &component) const;
};
class MeshFieldInput : public fn::FieldInput {
public:
using fn::FieldInput::FieldInput;
GVArray get_varray_for_context(const fn::FieldContext &context,
const IndexMask &mask,
ResourceScope &scope) const override;
virtual GVArray get_varray_for_context(const Mesh &mesh,
AttrDomain domain,
const IndexMask &mask) const = 0;
virtual std::optional<AttrDomain> preferred_domain(const Mesh &mesh) const;
virtual NativeFieldDomain native_domain_info(const Mesh &mesh) const;
};
class CurvesFieldInput : public fn::FieldInput {
public:
using fn::FieldInput::FieldInput;
GVArray get_varray_for_context(const fn::FieldContext &context,
const IndexMask &mask,
ResourceScope &scope) const override;
virtual GVArray get_varray_for_context(const CurvesGeometry &curves,
AttrDomain domain,
const IndexMask &mask) const = 0;
virtual std::optional<AttrDomain> preferred_domain(const CurvesGeometry &curves) const;
};
class PointCloudFieldInput : public fn::FieldInput {
public:
using fn::FieldInput::FieldInput;
GVArray get_varray_for_context(const fn::FieldContext &context,
const IndexMask &mask,
ResourceScope &scope) const override;
virtual GVArray get_varray_for_context(const PointCloud &pointcloud,
const IndexMask &mask) const = 0;
};
class InstancesFieldInput : public fn::FieldInput {
public:
using fn::FieldInput::FieldInput;
GVArray get_varray_for_context(const fn::FieldContext &context,
const IndexMask &mask,
ResourceScope &scope) const override;
virtual GVArray get_varray_for_context(const Instances &instances,
const IndexMask &mask) const = 0;
};
class AttributeFieldInput : public GeometryFieldInput {
private:
std::string name_;
std::optional<std::string> socket_inspection_name_;
public:
AttributeFieldInput(std::string name,
const CPPType &type,
std::optional<std::string> socket_inspection_name = std::nullopt)
: GeometryFieldInput(type, name),
name_(std::move(name)),
socket_inspection_name_(std::move(socket_inspection_name))
{
}
static fn::GField from(std::string name,
const CPPType &type,
std::optional<std::string> socket_inspection_name = std::nullopt)
{
return fn::GField::from_input<AttributeFieldInput>(
std::move(name), type, std::move(socket_inspection_name));
}
template<typename T>
static fn::Field<T> from(std::string name,
std::optional<std::string> socket_inspection_name = std::nullopt)
{
return from(std::move(name), CPPType::get<T>(), std::move(socket_inspection_name))
.template typed<T>();
}
StringRefNull attribute_name() const
{
return name_;
}
GVArray get_varray_for_context(const GeometryFieldContext &context,
const IndexMask &mask) const override;
std::string socket_inspection_name() const override;
void hash_unique(UniqueHashBytes &hash, fn::FieldHashDeep &deep_hash_cache) const override;
std::optional<AttrDomain> preferred_domain(const GeometryComponent &component) const override;
NativeFieldDomain native_domain_info(const GeometryComponent &component) const override;
template<typename T, FixedString FStr> static const fn::Field<T> &get_field()
{
static const auto field = fn::Field<T>::template from_input<AttributeFieldInput>(
FStr.data, CPPType::get<T>());
/* Use a non-owning wrapper to avoid unnecessary reference counting of a static field. */
static const auto field_ref = fn::Field<T>::from_non_owning_ref(field);
return field_ref;
}
};
class AttributeExistsFieldInput final : public bke::GeometryFieldInput {
private:
std::string name_;
public:
AttributeExistsFieldInput(std::string name, const CPPType &type)
: GeometryFieldInput(type, name), name_(std::move(name))
{
}
static fn::Field<bool> from(std::string name)
{
const CPPType &type = CPPType::get<bool>();
return fn::GField::from_input<AttributeExistsFieldInput>(std::move(name), type).typed<bool>();
}
GVArray get_varray_for_context(const bke::GeometryFieldContext &context,
const IndexMask &mask) const final;
void hash_unique(UniqueHashBytes &hash, fn::FieldHashDeep &deep_hash_cache) const override;
NativeFieldDomain native_domain_info(const GeometryComponent &component) const override;
};
class NamedLayerSelectionFieldInput final : public bke::GeometryFieldInput {
private:
std::string layer_name_;
public:
NamedLayerSelectionFieldInput(std::string layer_name)
: bke::GeometryFieldInput(CPPType::get<bool>(), "Named Layer node"),
layer_name_(std::move(layer_name))
{
}
GVArray get_varray_for_context(const bke::GeometryFieldContext &context,
const IndexMask &mask) const final;
void hash_unique(UniqueHashBytes &hash, fn::FieldHashDeep &deep_hash_cache) const override;
std::optional<AttrDomain> preferred_domain(const GeometryComponent &component) const override;
};
class IDAttributeFieldInput : public GeometryFieldInput {
public:
IDAttributeFieldInput() : GeometryFieldInput(CPPType::get<int>()) {}
GVArray get_varray_for_context(const GeometryFieldContext &context,
const IndexMask &mask) const override;
std::string socket_inspection_name() const override;
void hash_unique(UniqueHashBytes &hash, fn::FieldHashDeep &deep_hash_cache) const override;
/** Cached field to avoid allocating a new one every time. */
static const fn::Field<int> &get_field();
};
VArray<float3> curve_normals_varray(const CurvesGeometry &curves, AttrDomain domain);
VArray<float3> mesh_normals_varray(const Mesh &mesh,
const IndexMask &mask,
AttrDomain domain,
bool no_corner_normals = false,
bool true_normals = false);
class NormalFieldInput : public GeometryFieldInput {
bool legacy_corner_normals_ = false;
bool true_normals_ = false;
public:
NormalFieldInput(const bool legacy_corner_normals = false, const bool true_normals = false)
: GeometryFieldInput(CPPType::get<float3>()),
legacy_corner_normals_(legacy_corner_normals),
true_normals_(true_normals)
{
}
GVArray get_varray_for_context(const GeometryFieldContext &context,
const IndexMask &mask) const override;
std::string socket_inspection_name() const override;
void hash_unique(UniqueHashBytes &hash, fn::FieldHashDeep &deep_hash_cache) const override;
NativeFieldDomain native_domain_info(const GeometryComponent &component) const override;
/** Cached normal field to avoid allocating a new one every time. */
static const fn::Field<float3> &get_field();
};
class CurveLengthFieldInput final : public CurvesFieldInput {
public:
CurveLengthFieldInput();
GVArray get_varray_for_context(const CurvesGeometry &curves,
AttrDomain domain,
const IndexMask &mask) const final;
void hash_unique(UniqueHashBytes &hash, fn::FieldHashDeep &deep_hash_cache) const override;
std::optional<AttrDomain> preferred_domain(const bke::CurvesGeometry &curves) const final;
};
class EvaluateAtIndexInput final : public bke::GeometryFieldInput {
private:
fn::Field<int> index_field_;
fn::GField value_field_;
AttrDomain value_field_domain_;
public:
EvaluateAtIndexInput(fn::Field<int> index_field,
fn::GField value_field,
AttrDomain value_field_domain);
GVArray get_varray_for_context(const bke::GeometryFieldContext &context,
const IndexMask &mask) const final;
void hash_unique(UniqueHashBytes &hash, fn::FieldHashDeep &deep_hash_cache) const override;
std::optional<AttrDomain> preferred_domain(const GeometryComponent & /*component*/) const final
{
return value_field_domain_;
}
};
class SampleIndexFunction : public mf::MultiFunction {
GeometrySet src_geometry_;
fn::GField src_field_;
AttrDomain domain_;
mf::Signature signature_;
mutable CacheMutex mutex_;
mutable std::optional<bke::GeometryFieldContext> geometry_context_;
mutable std::unique_ptr<fn::FieldEvaluator> evaluator_;
mutable const GVArray *src_data_ = nullptr;
public:
SampleIndexFunction(GeometrySet geometry, fn::GField src_field, AttrDomain domain);
void prepare_for_execution() const override;
void call(const IndexMask &mask, mf::Params params, mf::Context /*context*/) const override;
void hash_unique(UniqueHashBytes &hash) const override;
static const GeometryComponent *find_source_component(const GeometrySet &geometry,
AttrDomain domain);
};
class EvaluateOnDomainInput final : public bke::GeometryFieldInput {
private:
fn::GField src_field_;
AttrDomain src_domain_;
public:
EvaluateOnDomainInput(fn::GField field, AttrDomain domain);
GVArray get_varray_for_context(const bke::GeometryFieldContext &context,
const IndexMask & /*mask*/) const final;
void hash_unique(UniqueHashBytes &hash, fn::FieldHashDeep &deep_hash_cache) const override;
void foreach_recursive_field(FunctionRef<void(const fn::GField &)> fn) const override;
std::optional<AttrDomain> preferred_domain(
const GeometryComponent & /*component*/) const override;
NativeFieldDomain native_domain_info(const GeometryComponent & /*component*/) const override;
};
bool try_capture_fields_on_geometry(MutableAttributeAccessor attributes,
const fn::FieldContext &field_context,
Span<StringRef> names,
AttrDomain domain,
const fn::Field<bool> &selection,
Span<fn::GField> fields);
inline bool try_capture_field_on_geometry(MutableAttributeAccessor attributes,
const fn::FieldContext &field_context,
const StringRef name,
AttrDomain domain,
const fn::Field<bool> &selection,
const fn::GField &field)
{
return try_capture_fields_on_geometry(
attributes, field_context, {name}, domain, selection, {field});
}
bool try_capture_fields_on_geometry(GeometryComponent &component,
Span<StringRef> names,
AttrDomain domain,
Span<fn::GField> fields);
inline bool try_capture_field_on_geometry(GeometryComponent &component,
const StringRef name,
AttrDomain domain,
const fn::GField &field)
{
return try_capture_fields_on_geometry(component, {name}, domain, {field});
}
bool try_capture_fields_on_geometry(GeometryComponent &component,
Span<StringRef> names,
AttrDomain domain,
const fn::Field<bool> &selection,
Span<fn::GField> fields);
inline bool try_capture_field_on_geometry(GeometryComponent &component,
const StringRef name,
AttrDomain domain,
const fn::Field<bool> &selection,
const fn::GField &field)
{
return try_capture_fields_on_geometry(component, {name}, domain, selection, {field});
}
/**
* Try to find the geometry domain that the field should be evaluated on. If it is not obvious
* which domain is correct, none is returned.
*/
std::optional<AttrDomain> try_detect_field_domain(const GeometryComponent &component,
const fn::GField &field);
/**
* Try to detect the domain that the field's inputs represent. If any field may depend on the
* order of the domain, none is returned, and if the fields will give the same value regardless of
* the domain, none is also returned.
*/
std::optional<AttrDomain> try_detect_native_field_domain(const GeometryComponent &component,
const fn::GField &field);
} // namespace bke
} // namespace blender

View File

@@ -0,0 +1,46 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
/** \file
* \ingroup bke
*/
#include "BLI_compute_context.hh"
#include "BLI_map.hh"
#include "BLI_math_matrix_types.hh"
namespace blender::bke {
/**
* A gizmo is identified by a gizmo node (like `Linear Gizmo`) in a specific compute context (e.g.
* the path of group nodes to get from the geometry nodes modifier to the group containing the
* gizmo node).
*/
struct NodeGizmoID {
/**
* Storing only the hash of the compute context is enough here and is cheaper than making a deep
* copy of the actual compute context.
*/
ComputeContextHash compute_context_hash;
int node_id;
friend bool operator==(const NodeGizmoID &a, const NodeGizmoID &b) = default;
uint64_t hash() const
{
return get_default_hash(this->compute_context_hash, this->node_id);
}
};
struct GizmoEditHints {
/**
* Additional transform that is applied to the gizmo because the corresponding geometry has been
* transformed the same.
*/
Map<NodeGizmoID, float4x4> gizmo_transforms;
};
} // namespace blender::bke

View File

@@ -0,0 +1,31 @@
/* SPDX-FileCopyrightText: 2024 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup bke
*/
#pragma once
#include <memory>
#include <string>
#include "BLI_set.hh"
namespace blender::bke {
/**
* A set of anonymous attribute names that is passed around in geometry nodes.
*/
class GeometryNodesReferenceSet {
public:
/**
* This uses `std::shared_ptr` because attributes sets are passed around by value during geometry
* nodes evaluation, and this makes it very small if there is no name. Also it makes copying very
* cheap.
*/
std::shared_ptr<Set<std::string>> names;
};
} // namespace blender::bke

View File

@@ -0,0 +1,826 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
/** \file
* \ingroup bke
*/
#include <iosfwd>
#include "BLI_bounds_types.hh"
#include "BLI_function_ref.hh"
#include "BLI_implicit_sharing_ptr.hh"
#include "BLI_map.hh"
#include "BLI_math_vector_types.hh"
#include "BLI_memory_counter_fwd.hh"
#include "BLI_mutex.hh"
/* For #Map. */
#include "BKE_attribute.hh"
namespace blender {
struct Curves;
struct Curve;
struct Mesh;
struct PointCloud;
struct Volume;
struct GreasePencil;
namespace bke {
struct AttributeDomainAndType;
class AttributeAccessor;
struct AttributeMetaData;
class CurvesEditHints;
class Instances;
class GeometryComponent;
class GreasePencilEditHints;
class MutableAttributeAccessor;
enum class AttrDomain : int8_t;
struct GizmoEditHints;
} // namespace bke
namespace nodes {
class Bundle;
using BundlePtr = ImplicitSharingPtr<Bundle>;
} // namespace nodes
namespace bke {
#define GEO_COMPONENT_TYPE_ENUM_SIZE 7
enum class GeometryOwnershipType {
/* The geometry is owned. This implies that it can be changed. */
Owned = 0,
/* The geometry can be changed, but someone else is responsible for freeing it. */
Editable = 1,
/* The geometry cannot be changed and someone else is responsible for freeing it. */
ReadOnly = 2,
};
using GeometryComponentPtr = ImplicitSharingPtr<GeometryComponent>;
/**
* This is the base class for specialized geometry component types. A geometry component uses
* implicit sharing to avoid read-only copies. It also integrates with attribute API, which
* generalizes storing and modifying generic information on a geometry.
*/
class GeometryComponent : public ImplicitSharingMixin {
public:
/**
* Each geometry component has a specific type. The type determines what kind of data the
* component stores. Functions modifying a geometry will usually just modify a subset of the
* component types.
* \note These values are stored in files, so they should not be reordered.
*/
enum class Type {
Mesh = 0,
PointCloud = 1,
Instance = 2,
Volume = 3,
Curve = 4,
Edit = 5,
GreasePencil = 6,
};
private:
Type type_;
public:
GeometryComponent(Type type);
~GeometryComponent() override = default;
static GeometryComponentPtr create(Type component_type);
int attribute_domain_size(AttrDomain domain) const;
/**
* Get access to the attributes in this geometry component. May return none if the geometry does
* not support the attribute system.
*/
virtual std::optional<AttributeAccessor> attributes() const;
virtual std::optional<MutableAttributeAccessor> attributes_for_write();
virtual void count_memory(MemoryCounter &memory) const;
/**
* Copies the component. The returned component only has a single user and is therefor mutable.
*/
virtual GeometryComponentPtr copy() const = 0;
/** Remove referenced data from the geometry component. */
virtual void clear() = 0;
/* Direct data is everything except for instances of objects/collections.
* If this returns true, the geometry set can be cached and is still valid after e.g. modifier
* evaluation ends. Instances can only be valid as long as the data they instance is valid. */
virtual bool owns_direct_data() const = 0;
virtual void ensure_owns_direct_data() = 0;
Type type() const;
virtual bool is_empty() const;
private:
void delete_self() override;
void delete_data_only() override;
};
template<typename T>
inline constexpr bool is_geometry_component_v = std::is_base_of_v<GeometryComponent, T>;
/**
* A geometry set is a container for multiple kinds of geometry. It does not own geometry directly
* itself, instead geometry is owned by multiple #GeometryComponents, and the geometry set
* increases the user count of each component, so they avoid losing the data. This means
* individual components might be shared between multiple geometries and other code. Shared
* components are copied automatically when write access is requested.
*
* The components usually do not store data directly, but keep a reference to a data
* structure defined elsewhere. There is at most one component of each type:
* - #MeshComponent
* - #CurveComponent
* - #PointCloudComponent
* - #InstancesComponent
* - #VolumeComponent
* - #GreasePencilComponent
*
* Copying a geometry set is a relatively cheap operation, because it does not copy the referenced
* geometry components, so #GeometrySet can often be passed or moved by value.
*/
struct GeometrySet {
private:
/* Indexed by #GeometryComponent::Type. */
std::array<GeometryComponentPtr, GEO_COMPONENT_TYPE_ENUM_SIZE> components_;
nodes::BundlePtr bundle_;
/**
* A user defined name for this geometry. It is not expected to be unique. Its main
* purpose is help debugging instance trees. It may eventually also be used when exporting
* instance trees or when creating separate objects from them.
*/
std::string name_;
public:
/**
* The methods are defaulted here so that they are not instantiated in every translation unit.
*/
GeometrySet();
GeometrySet(const GeometrySet &other);
GeometrySet(GeometrySet &&other);
~GeometrySet();
GeometrySet &operator=(const GeometrySet &other);
GeometrySet &operator=(GeometrySet &&other);
/**
* This method can only be used when the geometry set is mutable. It returns a mutable geometry
* component of the given type.
*/
GeometryComponent &get_component_for_write(GeometryComponent::Type component_type);
template<typename Component> Component &get_component_for_write()
{
BLI_STATIC_ASSERT(is_geometry_component_v<Component>, "");
return static_cast<Component &>(this->get_component_for_write(Component::static_type));
}
/**
* Get the component of the given type. Might return null if the component does not exist yet.
*/
const GeometryComponent *get_component(GeometryComponent::Type component_type) const;
template<typename Component> const Component *get_component() const
{
BLI_STATIC_ASSERT(is_geometry_component_v<Component>, "");
return static_cast<const Component *>(get_component(Component::static_type));
}
bool has(const GeometryComponent::Type component_type) const;
template<typename Component> bool has() const
{
BLI_STATIC_ASSERT(is_geometry_component_v<Component>, "");
return this->has(Component::static_type);
}
template<typename Component> bool has_component() const
{
BLI_STATIC_ASSERT(is_geometry_component_v<Component>, "");
return components_[int(Component::static_type)];
}
void remove(const GeometryComponent::Type component_type);
template<typename Component> void remove()
{
BLI_STATIC_ASSERT(is_geometry_component_v<Component>, "");
return this->remove(Component::static_type);
}
/**
* Remove all geometry components with types that are not in the provided list.
*/
void keep_only(Span<GeometryComponent::Type> component_types);
void add(const GeometryComponent &component);
/**
* Get all geometry components in this geometry set for read-only access.
*/
Vector<const GeometryComponent *> get_components() const;
std::optional<Bounds<float3>> compute_boundbox_without_instances(bool use_radius = true,
bool use_subdiv = false) const;
friend std::ostream &operator<<(std::ostream &stream, const GeometrySet &geometry_set);
/**
* Remove all geometry components from the geometry set.
*/
void clear();
bool owns_direct_data() const;
/**
* Make sure that the geometry can be cached. This does not ensure ownership of object/collection
* instances. This is necessary because sometimes components only have read-only or editing
* access to their data, which might be freed later if this geometry set outlasts the data.
*/
void ensure_owns_direct_data();
/**
* Same as #ensure_owns_direct_data but also turns object/collection instances into geometry
* instances so that they can be owned.
*/
void ensure_owns_all_data();
/**
* Typically, multiple #GeometrySet may share the same #GeometryComponent. This is fine as long
* as we can guarantee that the data is read-only. However, if some geometry is available in
* Python, that guarantee is not possible currently. For that case it can make sense that the
* #GeometrySet is the unique owner of the geometries it contains.
*/
void ensure_no_shared_components();
using AttributeForeachCallback = FunctionRef<void(
StringRef name, const AttributeMetaData &meta_data, const GeometryComponent &component)>;
void attribute_foreach(Span<GeometryComponent::Type> component_types,
bool include_instances,
AttributeForeachCallback callback) const;
struct GatheredAttributes {
VectorSet<StringRef, 16> names;
Vector<AttributeDomainAndType, 16> kinds;
void add(const StringRef name, const AttributeDomainAndType &kind);
};
Vector<GeometryComponent::Type> gather_component_types(bool include_instances,
bool ignore_empty) const;
/* Utility methods for creation. */
/**
* Create a new geometry set that only contains the given mesh.
*/
static GeometrySet from_mesh(Mesh *mesh,
GeometryOwnershipType ownership = GeometryOwnershipType::Owned);
/**
* Create a new geometry set that only contains the given volume.
*/
static GeometrySet from_volume(Volume *volume,
GeometryOwnershipType ownership = GeometryOwnershipType::Owned);
/**
* Create a new geometry set that only contains the given point cloud.
*/
static GeometrySet from_pointcloud(
PointCloud *pointcloud, GeometryOwnershipType ownership = GeometryOwnershipType::Owned);
/**
* Create a new geometry set that only contains the given curves.
*/
static GeometrySet from_curves(Curves *curves,
GeometryOwnershipType ownership = GeometryOwnershipType::Owned);
/**
* Create a new geometry set that only contains the given instances.
*/
static GeometrySet from_instances(std::unique_ptr<Instances> instances);
static GeometrySet from_instances(
Instances *instances, GeometryOwnershipType ownership = GeometryOwnershipType::Owned);
/**
* Create a new geometry set that only contains the given Grease Pencil data.
*/
static GeometrySet from_grease_pencil(
GreasePencil *grease_pencil, GeometryOwnershipType ownership = GeometryOwnershipType::Owned);
/* Utility methods for access. */
/**
* Returns true when the geometry set has a mesh component that has a mesh.
*/
bool has_mesh() const;
/**
* Returns true when the geometry set has a point cloud component that has a point cloud.
*/
bool has_pointcloud() const;
/**
* Returns true when the geometry set has an instances component that has at least one instance.
*/
bool has_instances() const;
/**
* Returns true when the geometry set has a volume component that has a volume.
*/
bool has_volume() const;
/**
* Returns true when the geometry set has a curves component that has a curves data-block.
*/
bool has_curves() const;
/**
* Returns true when the geometry set has any data that is not an instance.
*/
bool has_realized_data() const;
/**
* Returns true when the geometry set has a Grease Pencil component that has grease pencil data.
*/
bool has_grease_pencil() const;
/**
* Return true if the geometry set has any component that isn't empty.
*/
bool is_empty() const;
/**
* Returns a read-only mesh or null.
*/
const Mesh *get_mesh() const;
/**
* Returns a read-only point cloud of null.
*/
const PointCloud *get_pointcloud() const;
/**
* Returns a read-only volume or null.
*/
const Volume *get_volume() const;
/**
* Returns a read-only curves data-block or null.
*/
const Curves *get_curves() const;
/**
* Returns read-only instances or null.
*/
const Instances *get_instances() const;
/**
* Returns read-only curve edit hints or null.
*/
const CurvesEditHints *get_curve_edit_hints() const;
/**
* Returns read-only Grease Pencil edit hints or null.
*/
const GreasePencilEditHints *get_grease_pencil_edit_hints() const;
/**
* Returns read-only gizmo edit hints or null.
*/
const GizmoEditHints *get_gizmo_edit_hints() const;
/**
* Returns a read-only Grease Pencil data-block or null.
*/
const GreasePencil *get_grease_pencil() const;
/**
* Returns a mutable mesh or null. No ownership is transferred.
*/
Mesh *get_mesh_for_write();
/**
* Returns a mutable point cloud or null. No ownership is transferred.
*/
PointCloud *get_pointcloud_for_write();
/**
* Returns a mutable volume or null. No ownership is transferred.
*/
Volume *get_volume_for_write();
/**
* Returns a mutable curves data-block or null. No ownership is transferred.
*/
Curves *get_curves_for_write();
/**
* Returns mutable instances or null. No ownership is transferred.
*/
Instances *get_instances_for_write();
/**
* Returns mutable curve edit hints or null.
*/
CurvesEditHints *get_curve_edit_hints_for_write();
/**
* Returns mutable Grease Pencil edit hints or null.
*/
GreasePencilEditHints *get_grease_pencil_edit_hints_for_write();
/**
* Returns mutable gizmo edit hints or null.
*/
GizmoEditHints *get_gizmo_edit_hints_for_write();
/**
* Returns a mutable Grease Pencil data-block or null. No ownership is transferred.
*/
GreasePencil *get_grease_pencil_for_write();
/* Utility methods for replacement. */
/**
* Clear the existing mesh and replace it with the given one.
*/
void replace_mesh(Mesh *mesh, GeometryOwnershipType ownership = GeometryOwnershipType::Owned);
/**
* Clear the existing point cloud and replace with the given one.
*/
void replace_pointcloud(PointCloud *pointcloud,
GeometryOwnershipType ownership = GeometryOwnershipType::Owned);
/**
* Clear the existing volume and replace with the given one.
*/
void replace_volume(Volume *volume,
GeometryOwnershipType ownership = GeometryOwnershipType::Owned);
/**
* Clear the existing curves data-block and replace it with the given one.
*/
void replace_curves(Curves *curves,
GeometryOwnershipType ownership = GeometryOwnershipType::Owned);
/**
* Clear the existing instances and replace them with the given one.
*/
void replace_instances(Instances *instances,
GeometryOwnershipType ownership = GeometryOwnershipType::Owned);
/**
* Clear the existing Grease Pencil data-block and replace it with the given one.
*/
void replace_grease_pencil(GreasePencil *grease_pencil,
GeometryOwnershipType ownership = GeometryOwnershipType::Owned);
bool has_bundle() const;
const nodes::Bundle *bundle() const;
const nodes::BundlePtr &bundle_ptr() const;
nodes::BundlePtr &bundle_ptr();
nodes::Bundle &bundle_for_write();
void copy_bundle_from(const GeometrySet &other);
void merge_bundle_from(const GeometrySet &other);
void set_name(std::string name);
StringRefNull name() const;
friend bool operator==(const GeometrySet &a, const GeometrySet &b)
{
/* This compares only the component pointers, not the actual geometry data. */
return Span(a.components_) == Span(b.components_) && a.name_ == b.name_ &&
a.bundle_ == b.bundle_;
}
uint64_t hash() const
{
/* This should have the same data that's also taken into account in #operator==. */
return get_default_hash(Span(components_), name_, bundle_.get());
}
void count_memory(MemoryCounter &memory) const;
private:
/**
* Retrieve the pointer to a component without creating it if it does not exist,
* unlike #get_component_for_write.
*/
GeometryComponent *get_component_ptr(GeometryComponent::Type type);
template<typename Component> Component *get_component_ptr()
{
BLI_STATIC_ASSERT(is_geometry_component_v<Component>, "");
return static_cast<Component *>(get_component_ptr(Component::static_type));
}
};
/**
* A geometry component that can store a mesh, using the #Mesh data-block.
* Attributes are stored on any of the four attribute domains.
*/
class MeshComponent : public GeometryComponent {
private:
Mesh *mesh_ = nullptr;
GeometryOwnershipType ownership_ = GeometryOwnershipType::Owned;
public:
MeshComponent();
MeshComponent(Mesh *mesh, GeometryOwnershipType ownership = GeometryOwnershipType::Owned);
~MeshComponent() override;
GeometryComponentPtr copy() const override;
void clear() override;
bool has_mesh() const;
/**
* Clear the component and replace it with the new mesh.
*/
void replace(Mesh *mesh, GeometryOwnershipType ownership = GeometryOwnershipType::Owned);
/**
* Return the mesh and clear the component. The caller takes over responsibility for freeing the
* mesh (if the component was responsible before).
*/
Mesh *release();
/**
* Get the mesh from this component. This method can be used by multiple threads at the same
* time. Therefore, the returned mesh should not be modified. No ownership is transferred.
*/
const Mesh *get() const;
/**
* Get the mesh from this component. This method can only be used when the component is mutable,
* i.e. it is not shared. The returned mesh can be modified. No ownership is transferred.
*/
Mesh *get_for_write();
bool is_empty() const final;
bool owns_direct_data() const override;
void ensure_owns_direct_data() override;
void count_memory(MemoryCounter &memory) const override;
static constexpr GeometryComponent::Type static_type = Type::Mesh;
std::optional<AttributeAccessor> attributes() const final;
std::optional<MutableAttributeAccessor> attributes_for_write() final;
};
/**
* A geometry component that stores a point cloud, corresponding to the #PointCloud data structure.
* While a point cloud is technically a subset of a mesh in some respects, it is useful because of
* its simplicity, partly on a conceptual level for the user, but also in the code, though partly
* for historical reasons. Point clouds can also be rendered in special ways, based on the built-in
* `radius` attribute.
*
* Attributes on point clouds are all stored in contiguous arrays in its #CustomData,
* which makes them efficient to process, relative to some legacy built-in mesh attributes.
*/
class PointCloudComponent : public GeometryComponent {
private:
PointCloud *pointcloud_ = nullptr;
GeometryOwnershipType ownership_ = GeometryOwnershipType::Owned;
public:
PointCloudComponent();
PointCloudComponent(PointCloud *pointcloud,
GeometryOwnershipType ownership = GeometryOwnershipType::Owned);
~PointCloudComponent() override;
GeometryComponentPtr copy() const override;
void clear() override;
bool has_pointcloud() const;
/**
* Clear the component and replace it with the new point cloud.
*/
void replace(PointCloud *pointcloud,
GeometryOwnershipType ownership = GeometryOwnershipType::Owned);
/**
* Return the point cloud and clear the component. The caller takes over responsibility for
* freeing the point cloud (if the component was responsible before).
*/
PointCloud *release();
/**
* Get the point cloud from this component. This method can be used by multiple threads at the
* same time. Therefore, the returned point cloud should not be modified. No ownership is
* transferred.
*/
const PointCloud *get() const;
/**
* Get the point cloud from this component. This method can only be used when the component is
* mutable, i.e. it is not shared. The returned point cloud can be modified. No ownership is
* transferred.
*/
PointCloud *get_for_write();
bool is_empty() const final;
bool owns_direct_data() const override;
void ensure_owns_direct_data() override;
void count_memory(MemoryCounter &memory) const override;
std::optional<AttributeAccessor> attributes() const final;
std::optional<MutableAttributeAccessor> attributes_for_write() final;
static constexpr GeometryComponent::Type static_type = Type::PointCloud;
};
/**
* A geometry component that stores a group of curves, corresponding the #Curves data-block
* and the #CurvesGeometry type. Attributes are stored on the control point domain and the
* curve domain.
*/
class CurveComponent : public GeometryComponent {
private:
Curves *curves_ = nullptr;
GeometryOwnershipType ownership_ = GeometryOwnershipType::Owned;
/**
* Because rendering #Curves isn't fully working yet, we must provide a #Curve for the render
* engine and depsgraph object iterator in some cases. This allows using the old curve rendering
* even when the new curve data structure is used.
*/
mutable Curve *curve_for_render_ = nullptr;
mutable Mutex curve_for_render_mutex_;
public:
CurveComponent();
CurveComponent(Curves *curve, GeometryOwnershipType ownership = GeometryOwnershipType::Owned);
~CurveComponent() override;
GeometryComponentPtr copy() const override;
void clear() override;
bool has_curves() const;
/**
* Clear the component and replace it with the new curve.
*/
void replace(Curves *curve, GeometryOwnershipType ownership = GeometryOwnershipType::Owned);
Curves *release();
const Curves *get() const;
Curves *get_for_write();
bool is_empty() const final;
bool owns_direct_data() const override;
void ensure_owns_direct_data() override;
void count_memory(MemoryCounter &memory) const override;
/**
* Create empty curve data used for rendering the spline's wire edges.
* \note See comment on #curve_for_render_ for further explanation.
*/
const Curve *get_curve_for_render() const;
std::optional<AttributeAccessor> attributes() const final;
std::optional<MutableAttributeAccessor> attributes_for_write() final;
static constexpr GeometryComponent::Type static_type = Type::Curve;
};
/**
* A geometry component that stores #Instances.
*/
class InstancesComponent : public GeometryComponent {
private:
Instances *instances_ = nullptr;
GeometryOwnershipType ownership_ = GeometryOwnershipType::Owned;
public:
InstancesComponent();
InstancesComponent(Instances *instances,
GeometryOwnershipType ownership = GeometryOwnershipType::Owned);
~InstancesComponent() override;
GeometryComponentPtr copy() const override;
void clear() override;
const Instances *get() const;
Instances *get_for_write();
void replace(Instances *instances,
GeometryOwnershipType ownership = GeometryOwnershipType::Owned);
bool is_empty() const final;
Instances *release();
bool owns_direct_data() const override;
void ensure_owns_direct_data() override;
void count_memory(MemoryCounter &memory) const override;
std::optional<AttributeAccessor> attributes() const final;
std::optional<MutableAttributeAccessor> attributes_for_write() final;
static constexpr GeometryComponent::Type static_type = Type::Instance;
};
/**
* A geometry component that stores volume grids, corresponding to the #Volume data structure.
* This component does not implement an attribute API, partly because storage of sparse volume
* information in grids is much more complicated than it is for other types
*/
class VolumeComponent : public GeometryComponent {
private:
Volume *volume_ = nullptr;
GeometryOwnershipType ownership_ = GeometryOwnershipType::Owned;
public:
VolumeComponent();
~VolumeComponent() override;
GeometryComponentPtr copy() const override;
void clear() override;
bool has_volume() const;
/**
* Clear the component and replace it with the new volume.
*/
void replace(Volume *volume, GeometryOwnershipType ownership = GeometryOwnershipType::Owned);
/**
* Return the volume and clear the component. The caller takes over responsibility for freeing
* the volume (if the component was responsible before).
*/
Volume *release();
/**
* Get the volume from this component. This method can be used by multiple threads at the same
* time. Therefore, the returned volume should not be modified. No ownership is transferred.
*/
const Volume *get() const;
/**
* Get the volume from this component. This method can only be used when the component is
* mutable, i.e. it is not shared. The returned volume can be modified. No ownership is
* transferred.
*/
Volume *get_for_write();
bool owns_direct_data() const override;
void ensure_owns_direct_data() override;
void count_memory(MemoryCounter &memory) const override;
static constexpr GeometryComponent::Type static_type = Type::Volume;
};
/**
* When the original data is in some edit mode, we want to propagate some additional information
* through object evaluation. This information can be used by edit modes to support working on
* evaluated data.
*
* This component is added at the beginning of modifier evaluation.
*/
class GeometryComponentEditData final : public GeometryComponent {
public:
/**
* Information about how original curves are manipulated during evaluation. This data is used so
* that curve sculpt tools can work on evaluated data. It is not stored in #CurveComponent
* because the data remains valid even when there is no actual curves geometry anymore, for
* example, when the curves have been converted to a mesh.
*/
std::unique_ptr<CurvesEditHints> curves_edit_hints_;
/**
* Information about how drawings on the grease pencil layers are manipulated during evaluation.
*/
std::unique_ptr<GreasePencilEditHints> grease_pencil_edit_hints_;
/**
* Propagated information for how gizmos should be transformed along with the geometry.
*/
std::unique_ptr<GizmoEditHints> gizmo_edit_hints_;
GeometryComponentEditData();
GeometryComponentPtr copy() const final;
bool owns_direct_data() const final;
void ensure_owns_direct_data() final;
void clear() override;
/**
* The first node that does topology changing operations on curves should store the curve point
* positions it retrieved as input. Without this, information about the deformed positions is
* lost, which would make curves sculpt mode fall back to using original curve positions instead
* of deformed ones.
*/
static void remember_deformed_positions_if_necessary(GeometrySet &geometry);
static constexpr GeometryComponent::Type static_type = GeometryComponent::Type::Edit;
};
/**
* A geometry component that stores #GreasePencil data.
* The attributes on this component are only on the layer domain. Each individual layer represents
* a #CurvesGeometry with its own curve and point domain. See #CurveComponent.
*/
class GreasePencilComponent : public GeometryComponent {
private:
GreasePencil *grease_pencil_ = nullptr;
GeometryOwnershipType ownership_ = GeometryOwnershipType::Owned;
public:
GreasePencilComponent();
~GreasePencilComponent() override;
GeometryComponentPtr copy() const override;
void clear() override;
bool has_grease_pencil() const;
/**
* Clear the component and replace it with the new \a grease_pencil data.
*/
void replace(GreasePencil *grease_pencil,
GeometryOwnershipType ownership = GeometryOwnershipType::Owned);
/**
* Return the Grease Pencil data and clear the component. The caller takes over responsibility
* for freeing the Grease Pencil data (if the component was responsible before).
*/
GreasePencil *release();
const GreasePencil *get() const;
GreasePencil *get_for_write();
bool is_empty() const final;
bool owns_direct_data() const override;
void ensure_owns_direct_data() override;
static constexpr GeometryComponent::Type static_type = Type::GreasePencil;
std::optional<AttributeAccessor> attributes() const final;
std::optional<MutableAttributeAccessor> attributes_for_write() final;
};
bool attribute_is_builtin_on_component_type(const GeometryComponent::Type type, StringRef name);
} // namespace bke
} // namespace blender

View File

@@ -0,0 +1,27 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup bke
*/
#pragma once
#include "BKE_geometry_set.hh"
namespace blender {
struct Object;
namespace bke {
/**
* \note This doesn't extract instances from the "dupli" system for non-geometry-nodes instances.
*/
GeometrySet object_get_evaluated_geometry_set(const Object &object, bool apply_subdiv = true);
bool object_has_geometry_set_instances(const Object &object);
} // namespace bke
} // namespace blender

View File

@@ -0,0 +1,380 @@
/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
/** \file
* \ingroup bke
* \section aboutglobal Global settings
* Global settings, handles, pointers. This is the root for finding
* any data in Blender. This block is not serialized, but built anew
* for every fresh Blender run.
*/
#include "DNA_listBase.h"
namespace blender {
struct Main;
struct RecentFile;
/**
* Global data, typically accessed from #G.
* See: #BKE_blender_globals_init & #BKE_blender_globals_clear.
*
* \note This is run-time only but some global data is written
* to #FileGlobal which is used to initialize members of #Global.
*/
struct Global {
/**
* Data for the current active blend file.
*
* Note that `CTX_data_main(C)` should be used where possible.
* Otherwise access via #G_MAIN.
*/
Main *main;
/**
* Preview main is stored to avoid loading the preview file in multiple scenarios.
* It is actually shared between shader node previews and asset previews.
*/
Main *pr_main;
/** Last saved location for images. */
char filepath_last_image[/*FILE_MAX*/ 1024];
/** Last used location for library link/append. */
char filepath_last_library[/*FILE_MAX*/ 1024];
/**
* Last saved location for .blend files.
* This is used for recovery in case of a crash.
* It is set when a .blend file is loaded or when saving (manually or through autosave).
*/
char filepath_last_blend[/*FILE_MAX*/ 1024];
/**
* Strings of recently opened files to show in the file menu.
* A list of #RecentFile read from #BLENDER_HISTORY_FILE.
*/
ListBaseT<RecentFile> recent_files;
/**
* Set when Escape been pressed or `Ctrl-C` pressed in background mode.
* Used for render quit and some other background tasks such as baking.
*/
bool is_break;
/**
* Blender is running without any Windows or OpenGLES context.
* Typically set by the `--background` command-line argument.
*
* Also enabled when build defines `WITH_PYTHON_MODULE` or `WITH_HEADLESS` are set
* (which use background mode by definition).
*/
bool background;
/**
* Skip reading the startup file and user preferences.
* Also disable saving the preferences on exit (see #G_FLAG_USERPREF_NO_SAVE_ON_EXIT),
* see via the command line argument: `--factory-startup`.
*/
bool factory_startup;
/**
* Set when the user is interactively moving (transforming) content.
* see: #G_TRANSFORM_OBJ and related flags.
*/
short moving;
/** To indicate render is busy, prevent render-window events, animation playback etc. */
bool is_rendering;
/**
* Debug value, can be set from the UI and python, used for testing nonstandard features.
* DO NOT abuse it with generic checks like `if (G.debug_value > 0)`. Do not use it as bitflags.
* Only precise specific values should be checked for, to avoid unpredictable side-effects.
* Please document here the value(s) you are using (or a range of values reserved to some area):
* * -16384 and below: Reserved for python (add-ons) usage.
* * -1: Disable faster motion paths computation (since 08/2018).
* * 1 - 30: EEVEE debug/stats values (01/2018).
* * 31: Enable the Select Debug Engine. Only available with #WITH_DRAW_DEBUG (08/2021).
* * 101: Enable UI debug drawing of full-screen area's corner widget (10/2014).
* * 102: Enable extra items in string search UI (05/2022).
* * 666: Use quicker batch delete for outliners' delete hierarchy (01/2019).
* * 777: Enable UI node panel's sockets polling (11/2011).
* * 799: Enable some mysterious new depsgraph behavior (05/2015).
* * 1112: Disable new Cloth internal springs handling (09/2014).
* * 1234: Disable new dyntopo code fixing skinny faces generation (04/2015).
* * 3001: Enable additional Fluid modifier (Mantaflow) options (02/2020).
* * 4000: Line Art state output and debugging logs (03/2021).
* * 4001: Mesh topology information in the spreadsheet (01/2022).
* * 16384 and above: Reserved for python (add-ons) usage.
*/
short debug_value;
/**
* Saved to the blend file as #FileGlobal.globalf
*
* \note Currently this is only used for runtime options, adding flags to #G_FLAG_ALL_READFILE
* will cause them to be written and read to files.
*/
int f;
struct {
/**
* Logging vars (different loggers may use).
* Set via `--log-level` command line argument.
*/
int level;
/**
* FILE handle or use `stderr` (we own this so close when done).
* Set via `--log-file` command line argument.
*/
void *file;
} log;
/**
* Debug flag, #G_DEBUG, #G_DEBUG_PYTHON & friends, set via:
* - Command line arguments: `--debug`, `--debug-memory` ... etc.
* - Python API: `bpy.app.debug`, `bpy.app.debug_memory` ... etc.
*/
int debug;
/**
* When true, various geometry processing algorithms randomize the order of elements (e.g.
* vertices or edges) in the output. In many cases, we don't make guarantees about the exact
* order of elements. So if users depend on the indices with e.g. geometry nodes, their file can
* break in a different Blender version. Explicitly turning on randomization can help protect
* oneself against such breakages.
*/
bool randomize_geometry_element_order;
/**
* Control behavior of file reading/writing.
*
* This variable is written to / read from #FileGlobal.fileflags.
* See: #G_FILE_COMPRESS and related flags.
*/
int fileflags;
/**
* Message to show when loading a `.blend` file attempts to execute
* a Python script or driver-expression when doing so is disallowed.
*
* Set when `(G.f & G_FLAG_SCRIPT_AUTOEXEC_FAIL) == 0`,
* so users can be alerted to the reason why the file may not be behaving as expected.
* Typically Python drivers.
*/
char autoexec_fail[200];
/**
* Triggers a GPU capture if the name matches a DebugScope.
* Set using `--debug-gpu-scope-capture "debug_scope"`.
*/
char gpu_debug_scope_name[100];
/**
* Save final shader string to disk.
* Set using `--debug-gpu-shader-source "shader_name"`.
*/
char gpu_debug_shader_source_name[100];
bool profile_gpu;
};
/* **************** GLOBAL ********************* */
/** #Global.f */
enum {
G_FLAG_RENDER_VIEWPORT = (1 << 0),
G_FLAG_PICKSEL = (1 << 2),
/** Support simulating events (for testing). */
G_FLAG_EVENT_SIMULATE = (1 << 3),
G_FLAG_USERPREF_NO_SAVE_ON_EXIT = (1 << 4),
/**
* Internet access is allowed (offline mode when disabled).
*
* \note This setting doesn't prevent network access, rather it is a setting to
* disallow built-in utilities and 3rd party scripts from accessing the internet.
* While this isn't enforced, it is considered a bug when any script sends or receives
* data over the internet while this flag is set.
*/
G_FLAG_INTERNET_ALLOW = (1 << 10),
/* NOTE: storing both online/offline override is needed so changing the preference
* can be disabled when launching Blender with `--offline-mode`.
* This means that users in a controlled environment can launch
* in offline-mode and the option can't be changed afterwards. */
/** Launched with `--offline-mode` (overrides #USER_INTERNET_ALLOW when set). */
G_FLAG_INTERNET_OVERRIDE_PREF_ONLINE = (1 << 11),
/** Launched with `--offline-mode` (overrides #USER_INTERNET_ALLOW when set). */
G_FLAG_INTERNET_OVERRIDE_PREF_OFFLINE = (1 << 12),
G_FLAG_SCRIPT_AUTOEXEC = (1 << 13),
/** When this flag is set ignore the preferences #USER_SCRIPT_AUTOEXEC_DISABLE. */
G_FLAG_SCRIPT_OVERRIDE_PREF = (1 << 14),
G_FLAG_SCRIPT_AUTOEXEC_FAIL = (1 << 15),
G_FLAG_SCRIPT_AUTOEXEC_FAIL_QUIET = (1 << 16),
/** When this flag is set the active GPU Backend is different than the user wants to use. */
G_FLAG_GPU_BACKEND_FALLBACK = (1 << 17),
G_FLAG_GPU_BACKEND_FALLBACK_QUIET = (1 << 18),
};
#define G_FLAG_INTERNET_OVERRIDE_PREF_ANY \
(G_FLAG_INTERNET_OVERRIDE_PREF_ONLINE | G_FLAG_INTERNET_OVERRIDE_PREF_OFFLINE)
/** Don't overwrite these flags when reading a file. */
#define G_FLAG_ALL_RUNTIME \
(G_FLAG_SCRIPT_AUTOEXEC | G_FLAG_SCRIPT_OVERRIDE_PREF | G_FLAG_INTERNET_ALLOW | \
G_FLAG_INTERNET_OVERRIDE_PREF_ONLINE | G_FLAG_INTERNET_OVERRIDE_PREF_OFFLINE | \
G_FLAG_EVENT_SIMULATE | G_FLAG_USERPREF_NO_SAVE_ON_EXIT | G_FLAG_GPU_BACKEND_FALLBACK | \
G_FLAG_GPU_BACKEND_FALLBACK_QUIET | \
\
/* #BPY_python_reset is responsible for resetting these flags on file load. */ \
G_FLAG_SCRIPT_AUTOEXEC_FAIL | G_FLAG_SCRIPT_AUTOEXEC_FAIL_QUIET)
/** Flags to read from blend file. */
#define G_FLAG_ALL_READFILE 0
/** #Global.debug */
enum {
G_DEBUG = (1 << 0), /* general debug flag, print more info in unexpected cases */
G_DEBUG_PYTHON = (1 << 2), /* extra python info */
G_DEBUG_EVENTS = (1 << 3), /* input/window/screen events */
G_DEBUG_HANDLERS = (1 << 4), /* events handling */
G_DEBUG_WM = (1 << 5), /* operator, undo */
G_DEBUG_JOBS = (1 << 6), /* jobs time profiling */
G_DEBUG_FREESTYLE = (1 << 7), /* freestyle messages */
G_DEBUG_DEPSGRAPH_BUILD = (1 << 8), /* depsgraph construction messages */
G_DEBUG_DEPSGRAPH_EVAL = (1 << 9), /* depsgraph evaluation messages */
G_DEBUG_DEPSGRAPH_TAG = (1 << 10), /* depsgraph tagging messages */
G_DEBUG_DEPSGRAPH_TIME = (1 << 11), /* depsgraph timing statistics and messages */
G_DEBUG_DEPSGRAPH_NO_THREADS = (1 << 12), /* single threaded depsgraph */
G_DEBUG_DEPSGRAPH_PRETTY = (1 << 13), /* use pretty colors in depsgraph messages */
G_DEBUG_DEPSGRAPH_UID = (1 << 14), /* Verify validness of session-wide identifiers
* assigned to ID datablocks */
G_DEBUG_DEPSGRAPH = (G_DEBUG_DEPSGRAPH_BUILD | G_DEBUG_DEPSGRAPH_EVAL | G_DEBUG_DEPSGRAPH_TAG |
G_DEBUG_DEPSGRAPH_TIME | G_DEBUG_DEPSGRAPH_UID),
G_DEBUG_SIMDATA = (1 << 15), /* sim debug data display */
G_DEBUG_GPU = (1 << 16), /* gpu debug */
G_DEBUG_IO = (1 << 17), /* IO Debugging. */
G_DEBUG_GPU_FORCE_WORKAROUNDS = (1 << 18), /* Force GPU workarounds bypassing detection. */
G_DEBUG_GPU_FORCE_VULKAN_LOCAL_READ = (1 << 19), /* Force GPU dynamic rendering local read. */
G_DEBUG_GPU_COMPILE_SHADERS = (1 << 20), /* Compile all statically defined shaders. . */
G_DEBUG_GPU_RENDERDOC = (1 << 21), /* Enable RenderDoc integration. */
G_DEBUG_GPU_SHADER_DEBUG_INFO = (1 << 22), /* Enable the generation of shader debug info. */
G_DEBUG_GPU_NO_TEXTURE_POOL = (1 << 23), /* Disable memory aliasing in the texture pool. */
G_DEBUG_XR = (1 << 24), /* XR/OpenXR messages */
G_DEBUG_XR_TIME = (1 << 25), /* XR/OpenXR timing messages */
G_DEBUG_GHOST = (1 << 26), /* Debug GHOST module. */
G_DEBUG_WINTAB = (1 << 27), /* Debug Wintab. */
G_DEBUG_GPU_SHADER_NO_PREPROCESSOR = (1 << 28), /* Disable the preprocessor (implies NO_DCE). */
G_DEBUG_GPU_SHADER_NO_DCE = (1 << 29), /* Disable Dead Code Elimination. */
G_DEBUG_GPU_DEVICE_NO_FALLBACK = (1 << 30), /* Fail when the requested GPU device is invalid. */
};
#define G_DEBUG_ALL \
(G_DEBUG | G_DEBUG_PYTHON | G_DEBUG_EVENTS | G_DEBUG_WM | G_DEBUG_JOBS | G_DEBUG_FREESTYLE | \
G_DEBUG_DEPSGRAPH | G_DEBUG_IO | G_DEBUG_GHOST | G_DEBUG_WINTAB)
/** #Global.fileflags */
enum {
G_FILE_AUTOPACK = (1 << 0),
G_FILE_COMPRESS = (1 << 1),
/**
* When in background mode, do not automatically build a depsgraph when loading a blend-file.
* Typically set by the `--disable-depsgraph-on-file-load` command-line argument.
*
* NOTE: This is a temporary option, it is intended to be removed in blender 5.0.
* The default behavior will then be changed to never automatically generate a depsgraph on
* file-load when in background mode.
*/
G_BACKGROUND_NO_DEPSGRAPH = 1 << 2,
/**
* Do not perform automatic resync of library overrides on blend-file load.
*
* NOTE: runtime version of #UserDef_Experimental.no_override_auto_resync, both values are OR'ed
* together.
*/
G_LIBOVERRIDE_NO_AUTO_RESYNC = 1 << 3,
// G_FILE_DEPRECATED_9 = (1 << 9),
G_FILE_NO_UI = (1 << 10),
/* Bits 11 to 22 (inclusive) are deprecated & need to be cleared */
/**
* On read, use #FileGlobal.filename instead of the real location on-disk,
* needed for recovering temp files so relative paths resolve.
*
* \note In some ways it would be nicer to make this an argument passed to file loading.
* In practice this means recover needs to be passed around to too many low level functions,
* so keep this as a flag.
*/
G_FILE_RECOVER_READ = (1 << 23),
/**
* On write, assign use #FileGlobal.filename, otherwise leave it blank,
* needed so files can be recovered at their original locations.
*
* \note only #BLENDER_QUIT_FILE and auto-save files include recovery information.
* As users/developers may not want their paths exposed in publicly distributed files.
*/
G_FILE_RECOVER_WRITE = (1 << 24),
/** BMesh option to save as older mesh format */
// #define G_FILE_MESH_COMPAT (1 << 26)
// #define G_FILE_GLSL_NO_ENV_LIGHTING (1 << 28) /* deprecated */
/**
* This file contains a single asset and its dependencies. Users may edit the asset through the
* UI and save the changes, at which point the file will be regenerated by the asset system to
* include the edits (API in #BKE_asset_edit.hh). Stored with an .asset.blend file extension.
*/
G_FILE_ASSET_EDIT_FILE = (1 << 29),
};
/**
* Run-time only #G.fileflags which are never read or written to/from Blend files.
* This means we can change the values without worrying about do-versions.
*/
#define G_FILE_FLAG_ALL_RUNTIME \
(G_BACKGROUND_NO_DEPSGRAPH | G_LIBOVERRIDE_NO_AUTO_RESYNC | G_FILE_NO_UI | \
G_FILE_RECOVER_READ | G_FILE_RECOVER_WRITE)
/** #Global.moving, signals drawing in (3d) window to denote transform */
enum {
G_TRANSFORM_OBJ = (1 << 0),
G_TRANSFORM_EDIT = (1 << 1),
G_TRANSFORM_SEQ = (1 << 2),
G_TRANSFORM_FCURVES = (1 << 3),
/**
* Set while interactively transforming screen data (e.g. resizing areas & regions).
*
* \note Ideally this flag would also be used for resizing the window however that's handled
* outside by the windowing environment, so this is only used for internal windowing operations.
*/
G_TRANSFORM_WM = (1 << 4),
/**
* Set when transforming the cursor itself.
* Used as a hint to draw the cursor (even when hidden).
* Otherwise it's not possible to see what's being transformed.
*/
G_TRANSFORM_CURSOR = (1 << 5),
};
/** Defined in `blender.cc` */
extern Global G;
/**
* Stupid macro to hide the few *valid* usages of `G.main` (from startup/exit code e.g.),
* helps with cleanup task.
*/
#define G_MAIN (G).main
} // namespace blender

View File

@@ -0,0 +1,64 @@
/* SPDX-FileCopyrightText: 2008 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
/** \file
* \ingroup bke
*/
namespace blender {
struct Object;
struct bGPDframe;
struct bGPDspoint;
struct bGPDstroke;
struct bGPdata;
/* Stroke geometry utilities. */
/**
* Get points of stroke always flat to view not affected
* by camera view or view position.
* \param points: Array of grease pencil points (3D)
* \param totpoints: Total of points
* \param points2d: Result array of 2D points
* \param r_direction: Return Concave (-1), Convex (1), or Auto-detect (0)
*/
void BKE_gpencil_stroke_2d_flat(const struct bGPDspoint *points,
int totpoints,
float (*points2d)[2],
int *r_direction);
/**
* Triangulate stroke to generate data for filling areas.
* \param gps: Grease pencil stroke
*/
void BKE_gpencil_stroke_fill_triangulate(struct bGPDstroke *gps);
/**
* Recalc all internal geometry data for the stroke
* \param gpd: Grease pencil data-block
* \param gps: Grease pencil stroke
*/
void BKE_gpencil_stroke_geometry_update(struct bGPdata *gpd, struct bGPDstroke *gps);
/**
* Update Stroke UV data.
* \param gps: Grease pencil stroke
*/
void BKE_gpencil_stroke_uv_update(struct bGPDstroke *gps);
/**
* Split the given stroke into several new strokes, partitioning
* it based on whether the stroke points have a particular flag
* is set (e.g. #GP_SPOINT_SELECT in most cases, but not always).
*/
struct bGPDstroke *BKE_gpencil_stroke_delete_tagged_points(struct bGPdata *gpd,
struct bGPDframe *gpf,
struct bGPDstroke *gps,
struct bGPDstroke *next_stroke,
int tag_flags,
bool select,
bool flat_cap,
int limit);
} // namespace blender

View File

@@ -0,0 +1,260 @@
/* SPDX-FileCopyrightText: 2008 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
/** \file
* \ingroup bke
*/
#include "DNA_listBase.h"
namespace blender {
struct BlendDataReader;
struct Brush;
struct CurveMapping;
struct Depsgraph;
struct MDeformVert;
struct Main;
struct Material;
struct Object;
struct Scene;
struct SpaceImage;
struct ToolSettings;
struct ViewLayer;
struct bDeformGroup;
struct bGPDcurve;
struct bGPDframe;
struct bGPDlayer;
struct bGPDlayer_Mask;
struct bGPDstroke;
struct bGPdata;
struct bGPDpalette;
#define GPENCIL_SIMPLIFY(scene) \
((scene->r.mode & R_SIMPLIFY) && (scene->r.simplify_gpencil & SIMPLIFY_GPENCIL_ENABLE))
#define GPENCIL_SIMPLIFY_ONPLAY(playing) \
(((playing == true) && (scene->r.simplify_gpencil & SIMPLIFY_GPENCIL_ON_PLAY)) || \
((scene->r.simplify_gpencil & SIMPLIFY_GPENCIL_ON_PLAY) == 0))
#define GPENCIL_SIMPLIFY_FILL(scene, playing) \
((GPENCIL_SIMPLIFY_ONPLAY(playing) && GPENCIL_SIMPLIFY(scene) && \
(scene->r.simplify_gpencil & SIMPLIFY_GPENCIL_FILL)))
#define GPENCIL_SIMPLIFY_FX(scene, playing) \
((GPENCIL_SIMPLIFY_ONPLAY(playing) && GPENCIL_SIMPLIFY(scene) && \
(scene->r.simplify_gpencil & SIMPLIFY_GPENCIL_FX)))
#define GPENCIL_SIMPLIFY_TINT(scene) \
(GPENCIL_SIMPLIFY(scene) && (scene->r.simplify_gpencil & SIMPLIFY_GPENCIL_TINT))
#define GPENCIL_SIMPLIFY_AA(scene) \
(GPENCIL_SIMPLIFY(scene) && (scene->r.simplify_gpencil & SIMPLIFY_GPENCIL_AA))
/* Vertex Color macros. */
#define GPENCIL_USE_VERTEX_COLOR(toolsettings) \
(((toolsettings)->gp_paint->mode == GPPAINT_FLAG_USE_VERTEXCOLOR))
/* ------------ Grease-Pencil API ------------------ */
/* clean vertex groups weights */
void BKE_gpencil_free_point_weights(struct MDeformVert *dvert);
void BKE_gpencil_free_stroke_weights(struct bGPDstroke *gps);
void BKE_gpencil_free_stroke_editcurve(struct bGPDstroke *gps);
/** Free stroke, doesn't unlink from any #ListBase. */
void BKE_gpencil_free_stroke(struct bGPDstroke *gps);
/** Free strokes belonging to a gp-frame. */
bool BKE_gpencil_free_strokes(struct bGPDframe *gpf);
/** Free all of a gp-layer's frames. */
void BKE_gpencil_free_frames(struct bGPDlayer *gpl);
/** Free all of the gp-layers for a viewport (list should be `&gpd->layers` or so). */
void BKE_gpencil_free_layers(ListBaseT<bGPDlayer> *list);
/** Free all of the palettes & colors (list should be `&gpd->palettes` or so). */
void BKE_gpencil_free_legacy_palette_data(ListBaseT<bGPDpalette> *list);
/** Free (or release) any data used by this grease pencil (does not free the gpencil itself). */
void BKE_gpencil_free_data(struct bGPdata *gpd, bool free_all);
void BKE_gpencil_free_layer_masks(struct bGPDlayer *gpl);
/**
* Tag data-block for depsgraph update.
* Wrapper to avoid include Depsgraph tag functions in other modules.
* \param gpd: Grease pencil data-block.
*/
void BKE_gpencil_tag(struct bGPdata *gpd);
/**
* Add a new gp-frame to the given layer.
* \param gpl: Grease pencil layer
* \param cframe: Frame number
* \return Pointer to new frame
*/
struct bGPDframe *BKE_gpencil_frame_addnew(struct bGPDlayer *gpl, int cframe);
/**
* Add a copy of the active gp-frame to the given layer.
* \param gpl: Grease pencil layer
* \param cframe: Frame number
* \return Pointer to new frame
*/
struct bGPDframe *BKE_gpencil_frame_addcopy(struct bGPDlayer *gpl, int cframe);
/**
* Add a new gp-layer and make it the active layer.
* \param gpd: Grease pencil data-block
* \param name: Name of the layer
* \param setactive: Set as active
* \param add_to_header: Used to force the layer added at header
* \return Pointer to new layer
*/
struct bGPDlayer *BKE_gpencil_layer_addnew(struct bGPdata *gpd,
const char *name,
bool setactive,
bool add_to_header);
/**
* Add a new grease pencil data-block.
* \param bmain: Main pointer
* \param name: Name of the datablock
* \return Pointer to new data-block
*/
struct bGPdata *BKE_gpencil_data_addnew(struct Main *bmain, const char name[]);
/**
* Make a copy of a given gpencil frame.
* \param gpf_src: Source grease pencil frame
* \return Pointer to new frame
*/
struct bGPDframe *BKE_gpencil_frame_duplicate(const struct bGPDframe *gpf_src, bool dup_strokes);
/**
* Make a copy of a given gpencil layer.
* \param gpl_src: Source grease pencil layer
* \return Pointer to new layer
*/
struct bGPDlayer *BKE_gpencil_layer_duplicate(const struct bGPDlayer *gpl_src,
bool dup_frames,
bool dup_strokes);
/**
* Make a copy of a given grease-pencil stroke.
* \param gps_src: Source grease pencil strokes.
* \param dup_points: Duplicate points data.
* \param dup_curve: Duplicate curve data.
* \return Pointer to new stroke.
*/
struct bGPDstroke *BKE_gpencil_stroke_duplicate(struct bGPDstroke *gps_src,
bool dup_points,
bool dup_curve);
/**
* Make a copy of a given gpencil data-block.
*
* XXX: Should this be deprecated?
*/
struct bGPdata *BKE_gpencil_data_duplicate(struct Main *bmain,
const struct bGPdata *gpd,
bool internal_copy);
/* Stroke and Fill - Alpha Visibility Threshold */
#define GPENCIL_ALPHA_OPACITY_THRESH 0.001f
#define GPENCIL_STRENGTH_MIN 0.003f
/**
* Check if the given layer is able to be edited or not.
* \param gpl: Grease pencil layer
* \return True if layer is editable
*/
bool BKE_gpencil_layer_is_editable(const struct bGPDlayer *gpl);
/* How gpencil_layer_getframe() should behave when there
* is no existing GP-Frame on the frame requested.
*/
enum eGP_GetFrame_Mode {
/* Use the preceding gp-frame (i.e. don't add anything) */
GP_GETFRAME_USE_PREV = 0,
/* Add a new empty/blank frame */
GP_GETFRAME_ADD_NEW = 1,
/* Make a copy of the active frame */
GP_GETFRAME_ADD_COPY = 2,
};
/**
* Get the appropriate gp-frame from a given layer
* - this sets the layer's `actframe` var (if allowed to)
* - extension beyond range (if first gp-frame is after all frame in interest and cannot add)
*
* \param gpl: Grease pencil layer
* \param cframe: Frame number
* \param addnew: Add option
* \return Pointer to new frame
*/
struct bGPDframe *BKE_gpencil_layer_frame_get(struct bGPDlayer *gpl,
int cframe,
eGP_GetFrame_Mode addnew);
/**
* Look up the gp-frame on the requested frame number, but don't add a new one.
* \param gpl: Grease pencil layer
* \param cframe: Frame number
* \return Pointer to frame
*/
struct bGPDframe *BKE_gpencil_layer_frame_find(struct bGPDlayer *gpl, int cframe);
/**
* Delete the given frame from a layer.
* \param gpl: Grease pencil layer
* \param gpf: Grease pencil frame
* \return True if delete was done
*/
bool BKE_gpencil_layer_frame_delete(struct bGPDlayer *gpl, struct bGPDframe *gpf);
/**
* Get layer by name
* \param gpd: Grease pencil data-block
* \param name: Layer name
* \return Pointer to layer
*/
struct bGPDlayer *BKE_gpencil_layer_named_get(struct bGPdata *gpd, const char *name);
/**
* Get the active grease pencil layer for editing.
* \param gpd: Grease pencil data-block
* \return Pointer to layer
*/
struct bGPDlayer *BKE_gpencil_layer_active_get(struct bGPdata *gpd);
/**
* Set active grease pencil layer.
* \param gpd: Grease pencil data-block
* \param active: Grease pencil layer to set as active
*/
void BKE_gpencil_layer_active_set(struct bGPdata *gpd, struct bGPDlayer *active);
/**
* Delete grease pencil layer.
* \param gpd: Grease pencil data-block
* \param gpl: Grease pencil layer
*/
void BKE_gpencil_layer_delete(struct bGPdata *gpd, struct bGPDlayer *gpl);
/**
* Sort grease pencil frames.
* \param gpl: Grease pencil layer
* \param r_has_duplicate_frames: Duplicated frames flag
*/
void BKE_gpencil_layer_frames_sort(struct bGPDlayer *gpl, bool *r_has_duplicate_frames);
/* Brush */
/**
* Set grease pencil brush material.
* \param brush: Brush
* \param material: Material
*/
void BKE_gpencil_brush_material_set(struct Brush *brush, struct Material *material);
/* vertex groups */
/**
* Make a copy of a given gpencil weights.
* \param gps_src: Source grease pencil stroke
* \param gps_dst: Destination grease pencil stroke
*/
void BKE_gpencil_stroke_weights_duplicate(struct bGPDstroke *gps_src, struct bGPDstroke *gps_dst);
/**
* Create a default palette.
* \param bmain: Main pointer
* \param scene: Scene
*/
void BKE_gpencil_palette_ensure(struct Main *bmain, struct Scene *scene);
void BKE_gpencil_blend_read_data(struct BlendDataReader *reader, struct bGPdata *gpd);
} // namespace blender

View File

@@ -0,0 +1,55 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
/** \file
* \ingroup bke
*/
#include "DNA_gpencil_modifier_types.h" /* Needed for all enum type definitions. */
#include "DNA_listBase.h"
#include "BKE_lib_query.hh" /* For LibraryForeachIDCallbackFlag enum. */
namespace blender {
struct BlendDataReader;
struct GpencilModifierData;
struct ID;
struct Object;
/* NOTE: bake_modifier() called from UI:
* needs to create new data-blocks, hence the need for this. */
typedef void (*GreasePencilIDWalkFunc)(void *user_data,
struct Object *ob,
struct ID **idpoin,
LibraryForeachIDCallbackFlag cb_flag);
/**
* Free grease pencil modifier data
* \param md: Modifier data.
* \param flag: Flags.
*/
void BKE_gpencil_modifier_free_ex(struct GpencilModifierData *md, int flag);
/**
* Free grease pencil modifier data
* \param md: Modifier data.
*/
void BKE_gpencil_modifier_free(struct GpencilModifierData *md);
/**
* Link grease pencil modifier related IDs.
* \param ob: Grease pencil object.
* \param walk: Walk option.
* \param user_data: User data.
*/
void BKE_gpencil_modifiers_foreach_ID_link(struct Object *ob,
GreasePencilIDWalkFunc walk,
void *user_data);
void BKE_gpencil_modifier_blend_read_data(struct BlendDataReader *reader,
ListBaseT<GpencilModifierData> *lb,
struct Object *ob);
} // namespace blender

View File

@@ -0,0 +1,26 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
#include "DNA_grease_pencil_types.h"
namespace blender {
/** \file
* \ingroup bke
* \brief Low-level operations for grease pencil that cannot be defined in the C++ header yet.
*/
enum {
BKE_GREASEPENCIL_BATCH_DIRTY_ALL = 0,
};
extern void (*BKE_grease_pencil_batch_cache_dirty_tag_cb)(GreasePencil *grease_pencil, int mode);
extern void (*BKE_grease_pencil_batch_cache_free_cb)(GreasePencil *grease_pencil);
void BKE_grease_pencil_batch_cache_dirty_tag(GreasePencil *grease_pencil, int mode);
void BKE_grease_pencil_batch_cache_free(GreasePencil *grease_pencil);
} // namespace blender

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,36 @@
/* SPDX-FileCopyrightText: 2026 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
/** \file
* \ingroup bke
*/
#include <optional>
#include "BKE_attribute.hh"
#include "BKE_grease_pencil.hh"
namespace blender::bke::greasepencil {
std::optional<FillCache> fill_cache_from_fill_ids(const VArray<int> &fill_ids);
/* Get the next available fill ID. */
int get_next_available_fill_id(Span<int> fill_ids);
int get_next_available_fill_id(const VArray<int> &fill_ids);
/* Fill the mutable span with the next available fill IDs. */
void gather_next_available_fill_ids(const VArray<int> &fill_ids, MutableSpan<int> r_new_fill_ids);
/* Write to the mutable span the next available fill ids at the indices of the given mask. */
void gather_next_available_fill_ids(const VArray<int> &fill_ids,
const IndexMask &curve_mask,
MutableSpan<int> r_new_fill_ids);
IndexMask selected_mask_to_fills(const IndexMask &selected_mask,
const CurvesGeometry &curves,
AttrDomain selection_domain,
IndexMaskMemory &memory);
void separate_fill_ids(CurvesGeometry &curves, const IndexMask &strokes_to_keep);
} // namespace blender::bke::greasepencil

View File

@@ -0,0 +1,44 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
/** \file
* \ingroup bke
*/
#include <optional>
#include "BLI_set.hh"
namespace blender {
struct BlendfileLinkAppendContext;
struct BlendFileReadReport;
struct GreasePencil;
struct Main;
struct LineartGpencilModifierData;
struct GreasePencilLineartModifierData;
namespace bke::greasepencil::convert {
/** Main entry point to convert all legacy GPData into GreasePencil data and objects. */
void legacy_main(Main &bmain,
BlendfileLinkAppendContext *lapp_context,
BlendFileReadReport &reports);
/**
* Convert material stroke and fill setting to geometry attributes.
* \param filter: Only process a specific set of GreasePencil IDs.
*/
void material_stroke_fill_toggles_to_attributes(Main &bmain,
const std::optional<Set<GreasePencil *>> &filter,
BlendFileReadReport &reports);
void lineart_wrap_v3(const LineartGpencilModifierData *lmd_legacy,
GreasePencilLineartModifierData *lmd);
void lineart_unwrap_v3(LineartGpencilModifierData *lmd_legacy,
const GreasePencilLineartModifierData *lmd);
} // namespace bke::greasepencil::convert
} // namespace blender

Some files were not shown because too many files have changed in this diff Show More