diff options
author | Kévin Dietrich <kevin.dietrich@mailoo.org> | 2021-12-22 15:07:46 +0300 |
---|---|---|
committer | Kévin Dietrich <kevin.dietrich@mailoo.org> | 2021-12-22 15:07:46 +0300 |
commit | 74138fb2d4ac9f0504c8f9f575bf392b49fc7488 (patch) | |
tree | c383efca92710d0a4b3ffce5ad74ac7758f64ce8 /source | |
parent | 2529300f691109b9575ea75ae8367209751c3738 (diff) | |
parent | d2bf60cc17a961789d7c415fc3d2af14afa50f62 (diff) |
Merge branch 'master' into temp-abc-features
Diffstat (limited to 'source')
283 files changed, 8731 insertions, 7350 deletions
diff --git a/source/blender/blenkernel/BKE_action.h b/source/blender/blenkernel/BKE_action.h index ea8ee3f93b1..5dd98dbb9a3 100644 --- a/source/blender/blenkernel/BKE_action.h +++ b/source/blender/blenkernel/BKE_action.h @@ -253,12 +253,28 @@ void BKE_pose_channel_session_uuid_generate(struct bPoseChannel *pchan); */ struct bPoseChannel *BKE_pose_channel_find_name(const struct bPose *pose, const char *name); /** + * Checks if the bone is on a visible armature layer + * + * \return true if on a visible layer, false otherwise. + */ +bool BKE_pose_is_layer_visible(const struct bArmature *arm, const struct bPoseChannel *pchan); +/** * Find the active pose-channel for an object - * (we can't just use pose, as layer info is in armature) * - * \note #Object, not #bPose is used here, as we need layer info from Armature. + * \param check_arm_layer: checks if the bone is on a visible armature layer (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 (layer/active bone) from Armature. + */ +struct bPoseChannel *BKE_pose_channel_active(struct Object *ob, const bool check_arm_layer); +/** + * Find the active pose-channel for an object if it is on a visible armature layer + * (calls #BKE_pose_channel_active with check_arm_layer set to true) + * + * \return #bPoseChannel if found or NULL. + * \note #Object, not #bPose is used here, as we need info (layer/active bone) from Armature. */ -struct bPoseChannel *BKE_pose_channel_active(struct Object *ob); +struct bPoseChannel *BKE_pose_channel_active_if_layer_visible(struct Object *ob); /** * Use this when detecting the "other selected bone", * when we have multiple armatures in pose mode. diff --git a/source/blender/blenkernel/BKE_fcurve_driver.h b/source/blender/blenkernel/BKE_fcurve_driver.h index 18676dfcb38..7e4e0ad3c2a 100644 --- a/source/blender/blenkernel/BKE_fcurve_driver.h +++ b/source/blender/blenkernel/BKE_fcurve_driver.h @@ -105,6 +105,13 @@ void driver_change_variable_type(struct DriverVar *dvar, int type); */ 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); diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h index a2959556810..500d8e2e0ac 100644 --- a/source/blender/blenkernel/BKE_node.h +++ b/source/blender/blenkernel/BKE_node.h @@ -501,16 +501,13 @@ void ntreeFreeLocalTree(struct bNodeTree *ntree); struct bNode *ntreeFindType(const struct bNodeTree *ntree, int type); bool ntreeHasType(const struct bNodeTree *ntree, int type); bool ntreeHasTree(const struct bNodeTree *ntree, const struct bNodeTree *lookup); -void ntreeUpdateTree(struct Main *main, struct bNodeTree *ntree); void ntreeUpdateAllNew(struct Main *main); -/** - * \param tree_update_flag: #eNodeTreeUpdate enum. - */ -void ntreeUpdateAllUsers(struct Main *main, struct ID *id, int tree_update_flag); +void ntreeUpdateAllUsers(struct Main *main, struct ID *id); void ntreeGetDependencyList(struct bNodeTree *ntree, struct bNode ***r_deplist, int *r_deplist_len); +void ntreeUpdateNodeLevels(struct bNodeTree *ntree); /** * XXX: old trees handle output flags automatically based on special output @@ -833,10 +830,6 @@ void nodeClearActive(struct bNodeTree *ntree); void nodeClearActiveID(struct bNodeTree *ntree, short idtype); struct bNode *nodeGetActiveTexture(struct bNodeTree *ntree); -void nodeUpdate(struct bNodeTree *ntree, struct bNode *node); -bool nodeUpdateID(struct bNodeTree *ntree, struct ID *id); -void nodeUpdateInternalLinks(struct bNodeTree *ntree, struct bNode *node); - int nodeSocketIsHidden(const struct bNodeSocket *sock); void ntreeTagUsedSockets(struct bNodeTree *ntree); void nodeSetSocketAvailability(struct bNodeTree *ntree, @@ -960,10 +953,7 @@ bNodePreview *BKE_node_preview_verify( struct bNodeInstanceHash *previews, bNodeInstanceKey key, int xsize, int ysize, bool create); bNodePreview *BKE_node_preview_copy(struct bNodePreview *preview); void BKE_node_preview_free(struct bNodePreview *preview); -void BKE_node_preview_init_tree(struct bNodeTree *ntree, - int xsize, - int ysize, - bool create_previews); +void BKE_node_preview_init_tree(struct bNodeTree *ntree, int xsize, int ysize); void BKE_node_preview_free_tree(struct bNodeTree *ntree); void BKE_node_preview_remove_unused(struct bNodeTree *ntree); void BKE_node_preview_clear(struct bNodePreview *preview); @@ -974,14 +964,6 @@ void BKE_node_preview_merge_tree(struct bNodeTree *to_ntree, struct bNodeTree *from_ntree, bool remove_old); -/** - * Hack warning! this function is only used for shader previews, - * and since it gets called multiple times per pixel for Z-transparency we only add the color once. - * Preview gets cleared before it starts render though. - */ -void BKE_node_preview_set_pixel( - struct bNodePreview *preview, const float col[4], int x, int y, bool do_manage); - /** \} */ /* -------------------------------------------------------------------- */ @@ -1170,7 +1152,6 @@ void BKE_nodetree_remove_layer_n(struct bNodeTree *ntree, #define SH_NODE_SEPRGB 120 #define SH_NODE_COMBRGB 121 #define SH_NODE_HUE_SAT 122 -#define NODE_DYNAMIC 123 #define SH_NODE_OUTPUT_MATERIAL 124 #define SH_NODE_OUTPUT_WORLD 125 diff --git a/source/blender/blenkernel/BKE_node_tree_update.h b/source/blender/blenkernel/BKE_node_tree_update.h new file mode 100644 index 00000000000..ebaa56c89c9 --- /dev/null +++ b/source/blender/blenkernel/BKE_node_tree_update.h @@ -0,0 +1,109 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#pragma once + +/** \file + * \ingroup bke + */ + +struct bNode; +struct bNodeSocket; +struct bNodeTree; +struct bNodeLink; +struct Main; +struct ID; + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Tag tree as changed without providing any more information about what has changed exactly. + * The update process has to assume that everything may have changed. + * + * Using one of the methods below to tag the tree after changes is preffered when possible. + */ +void BKE_ntree_update_tag_all(struct bNodeTree *ntree); + +/** + * More specialized tag functions that may result in a more efficient update. + */ + +void BKE_ntree_update_tag_node_property(struct bNodeTree *ntree, struct bNode *node); +void BKE_ntree_update_tag_node_new(struct bNodeTree *ntree, struct bNode *node); +void BKE_ntree_update_tag_node_removed(struct bNodeTree *ntree); +void BKE_ntree_update_tag_node_internal_link(struct bNodeTree *ntree, struct bNode *node); + +void BKE_ntree_update_tag_socket_property(struct bNodeTree *ntree, struct bNodeSocket *socket); +void BKE_ntree_update_tag_socket_new(struct bNodeTree *ntree, struct bNodeSocket *socket); +void BKE_ntree_update_tag_socket_type(struct bNodeTree *ntree, struct bNodeSocket *socket); +void BKE_ntree_update_tag_socket_availability(struct bNodeTree *ntree, struct bNodeSocket *socket); +void BKE_ntree_update_tag_socket_removed(struct bNodeTree *ntree); + +void BKE_ntree_update_tag_link_changed(struct bNodeTree *ntree); +void BKE_ntree_update_tag_link_removed(struct bNodeTree *ntree); +void BKE_ntree_update_tag_link_added(struct bNodeTree *ntree, struct bNodeLink *link); +void BKE_ntree_update_tag_link_mute(struct bNodeTree *ntree, struct bNodeLink *link); + +/** Used after file loading when run-time data on the tree has not been initialized yet. */ +void BKE_ntree_update_tag_missing_runtime_data(struct bNodeTree *ntree); +/** Used when the interface sockets/values have changed. */ +void BKE_ntree_update_tag_interface(struct bNodeTree *ntree); +/** Used when an id data block changed that might be used by nodes that need to be updated. */ +void BKE_ntree_update_tag_id_changed(struct Main *bmain, struct ID *id); + +typedef struct NodeTreeUpdateExtraParams { + /** + * Data passed into the callbacks. + */ + void *user_data; + + /** + * Called for every tree that has been changed during the update. This can be used to send + * notifiers to trigger redraws or depsgraph updates. + */ + void (*tree_changed_fn)(struct ID *, struct bNodeTree *, void *user_data); + + /** + * Called for every tree whose output value may have changed based on the provided update tags. + * This can be used to tag the depsgraph if necessary. + */ + void (*tree_output_changed_fn)(struct ID *, struct bNodeTree *, void *user_data); +} NodeTreeUpdateExtraParams; + +/** + * Updates #bmain based on changes to node trees. + */ +void BKE_ntree_update_main(struct Main *bmain, struct NodeTreeUpdateExtraParams *params); + +/** + * Same as #BKE_ntree_update_main, but will first only look at the provided tree and only looks + * at #bmain when something relevant for other data-blocks changed. This avoids scanning #bmain in + * many cases. + * + * If #bmain is null, only the provided tree is updated. This should only be used in very rare + * cases because it may result it incorrectly synced data in DNA. + * + * If #tree is null, this is the same as calling #BKE_ntree_update_main. + */ +void BKE_ntree_update_main_tree(struct Main *bmain, + struct bNodeTree *ntree, + struct NodeTreeUpdateExtraParams *params); + +#ifdef __cplusplus +} +#endif diff --git a/source/blender/blenkernel/BKE_undo_system.h b/source/blender/blenkernel/BKE_undo_system.h index 6e1f9468ce4..44893a6f030 100644 --- a/source/blender/blenkernel/BKE_undo_system.h +++ b/source/blender/blenkernel/BKE_undo_system.h @@ -91,7 +91,7 @@ typedef struct UndoStep { /** When this is true, undo/memfile read code is allowed to re-use old data-blocks for unchanged * IDs, and existing depsgraphes. This has to be forbidden in some cases (like renamed IDs). */ bool use_old_bmain_data; - /** For use by undo systems that accumulate changes (text editor, painting). */ + /** For use by undo systems that accumulate changes (mesh-sculpt & image-painting). */ bool is_applied; /* Over alloc 'type->struct_size'. */ } UndoStep; diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt index f6e7f1c2473..02aef4ef79e 100644 --- a/source/blender/blenkernel/CMakeLists.txt +++ b/source/blender/blenkernel/CMakeLists.txt @@ -226,6 +226,7 @@ set(SRC intern/multires_versioning.c intern/nla.c intern/node.cc + intern/node_tree_update.cc intern/type_conversions.cc intern/object.cc intern/object_deform.c @@ -420,6 +421,7 @@ set(SRC BKE_multires.h BKE_nla.h BKE_node.h + BKE_node_tree_update.h BKE_object.h BKE_object_deform.h BKE_object_facemap.h diff --git a/source/blender/blenkernel/intern/action.c b/source/blender/blenkernel/intern/action.c index ddba726ba83..764c043f5ed 100644 --- a/source/blender/blenkernel/intern/action.c +++ b/source/blender/blenkernel/intern/action.c @@ -705,7 +705,12 @@ bool BKE_pose_channels_is_valid(const bPose *pose) #endif -bPoseChannel *BKE_pose_channel_active(Object *ob) +bool BKE_pose_is_layer_visible(const bArmature *arm, const bPoseChannel *pchan) +{ + return (pchan->bone->layer & arm->layer); +} + +bPoseChannel *BKE_pose_channel_active(Object *ob, const bool check_arm_layer) { bArmature *arm = (ob) ? ob->data : NULL; bPoseChannel *pchan; @@ -716,14 +721,21 @@ bPoseChannel *BKE_pose_channel_active(Object *ob) /* find active */ for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) { - if ((pchan->bone) && (pchan->bone == arm->act_bone) && (pchan->bone->layer & arm->layer)) { - return pchan; + if ((pchan->bone) && (pchan->bone == arm->act_bone)) { + if (!check_arm_layer || BKE_pose_is_layer_visible(arm, pchan)) { + return pchan; + } } } return NULL; } +bPoseChannel *BKE_pose_channel_active_if_layer_visible(struct Object *ob) +{ + return BKE_pose_channel_active(ob, true); +} + bPoseChannel *BKE_pose_channel_active_or_first_selected(struct Object *ob) { bArmature *arm = (ob) ? ob->data : NULL; @@ -732,7 +744,7 @@ bPoseChannel *BKE_pose_channel_active_or_first_selected(struct Object *ob) return NULL; } - bPoseChannel *pchan = BKE_pose_channel_active(ob); + bPoseChannel *pchan = BKE_pose_channel_active_if_layer_visible(ob); if (pchan && (pchan->bone->flag & BONE_SELECTED) && PBONE_VISIBLE(arm, pchan->bone)) { return pchan; } diff --git a/source/blender/blenkernel/intern/asset_catalog.cc b/source/blender/blenkernel/intern/asset_catalog.cc index aec622bb71f..eee1f6287c3 100644 --- a/source/blender/blenkernel/intern/asset_catalog.cc +++ b/source/blender/blenkernel/intern/asset_catalog.cc @@ -24,7 +24,7 @@ #include "BKE_asset_catalog.hh" #include "BKE_asset_library.h" -#include "BLI_fileops.h" +#include "BLI_fileops.hh" #include "BLI_path_util.h" /* For S_ISREG() and S_ISDIR() on Windows. */ @@ -32,6 +32,10 @@ # include "BLI_winstuff.h" #endif +#include "CLG_log.h" + +static CLG_LogRef LOG = {"bke.asset_service"}; + namespace blender::bke { const CatalogFilePath AssetCatalogService::DEFAULT_CATALOG_FILENAME = "blender_assets.cats.txt"; @@ -311,6 +315,7 @@ void AssetCatalogService::load_from_disk(const CatalogFilePath &file_or_director BLI_stat_t status; if (BLI_stat(file_or_directory_path.data(), &status) == -1) { /* TODO(@sybren): throw an appropriate exception. */ + CLOG_WARN(&LOG, "path not found: %s", file_or_directory_path.data()); return; } @@ -337,6 +342,7 @@ void AssetCatalogService::load_directory_recursive(const CatalogFilePath &direct if (!BLI_exists(file_path.data())) { /* No file to be loaded is perfectly fine. */ + CLOG_INFO(&LOG, 2, "path not found: %s", file_path.data()); return; } @@ -824,8 +830,12 @@ void AssetCatalogDefinitionFile::parse_catalog_file( const CatalogFilePath &catalog_definition_file_path, AssetCatalogParsedFn catalog_loaded_callback) { - std::fstream infile(catalog_definition_file_path); + fstream infile(catalog_definition_file_path, std::ios::in); + if (!infile.is_open()) { + CLOG_ERROR(&LOG, "%s: unable to open file", catalog_definition_file_path.c_str()); + return; + } bool seen_version_number = false; std::string line; while (std::getline(infile, line)) { @@ -956,7 +966,7 @@ bool AssetCatalogDefinitionFile::write_to_disk_unsafe(const CatalogFilePath &des return false; } - std::ofstream output(dest_file_path); + fstream output(dest_file_path, std::ios::out); /* TODO(@sybren): remember the line ending style that was originally read, then use that to write * the file again. */ diff --git a/source/blender/blenkernel/intern/asset_catalog_test.cc b/source/blender/blenkernel/intern/asset_catalog_test.cc index ba8f8716823..3ff7831b19a 100644 --- a/source/blender/blenkernel/intern/asset_catalog_test.cc +++ b/source/blender/blenkernel/intern/asset_catalog_test.cc @@ -27,6 +27,8 @@ #include "DNA_asset_types.h" #include "DNA_userdef_types.h" +#include "CLG_log.h" + #include "testing/testing.h" namespace blender::bke::tests { @@ -93,6 +95,18 @@ class AssetCatalogTest : public testing::Test { CatalogFilePath asset_library_root_; CatalogFilePath temp_library_path_; + static void SetUpTestSuite() + { + testing::Test::SetUpTestSuite(); + CLG_init(); + } + + static void TearDownTestSuite() + { + CLG_exit(); + testing::Test::TearDownTestSuite(); + } + void SetUp() override { const std::string test_files_dir = blender::tests::flags_test_asset_dir(); @@ -549,6 +563,30 @@ TEST_F(AssetCatalogTest, write_single_file) /* TODO(@sybren): test ordering of catalogs in the file. */ } +TEST_F(AssetCatalogTest, read_write_unicode_filepath) +{ + TestableAssetCatalogService service(asset_library_root_); + const CatalogFilePath load_from_path = asset_library_root_ + "/новый/" + + AssetCatalogService::DEFAULT_CATALOG_FILENAME; + service.load_from_disk(load_from_path); + + const CatalogFilePath save_to_path = use_temp_path() + "новый.cats.txt"; + AssetCatalogDefinitionFile *cdf = service.get_catalog_definition_file(); + ASSERT_NE(nullptr, cdf) << "unable to load " << load_from_path; + EXPECT_TRUE(cdf->write_to_disk(save_to_path)); + + AssetCatalogService loaded_service(save_to_path); + loaded_service.load_from_disk(); + + /* Test that the file was loaded correctly. */ + const bUUID materials_uuid("a2151dff-dead-4f29-b6bc-b2c7d6cccdb4"); + const AssetCatalog *cat = loaded_service.find_catalog(materials_uuid); + ASSERT_NE(nullptr, cat); + EXPECT_EQ(materials_uuid, cat->catalog_id); + EXPECT_EQ(AssetCatalogPath("Материалы"), cat->path); + EXPECT_EQ("Russian Materials", cat->simple_name); +} + TEST_F(AssetCatalogTest, no_writing_empty_files) { const CatalogFilePath temp_lib_root = create_temp_path(); diff --git a/source/blender/blenkernel/intern/blendfile_link_append.c b/source/blender/blenkernel/intern/blendfile_link_append.c index c265a6e2b7d..23e9e6bfbbb 100644 --- a/source/blender/blenkernel/intern/blendfile_link_append.c +++ b/source/blender/blenkernel/intern/blendfile_link_append.c @@ -440,6 +440,16 @@ static bool object_in_any_collection(Main *bmain, Object *ob) return false; } +static bool collection_instantiated_by_any_object(Main *bmain, Collection *collection) +{ + LISTBASE_FOREACH (Object *, ob, &bmain->objects) { + if (ob->type == OB_EMPTY && ob->instance_collection == collection) { + return true; + } + } + return false; +} + static ID *loose_data_instantiate_process_check(LooseDataInstantiateContext *instantiate_context, BlendfileLinkAppendContextItem *item) { @@ -633,12 +643,19 @@ static void loose_data_instantiate_collection_process( * children. */ Collection *collection = (Collection *)id; + /* The collection could be linked/appended together with an Empty object instantiating it, + * better not instantiate the collection in the viewlayer in that case. + * + * Can easily happen when copy/pasting such instantiating empty, see T93839. */ + const bool collection_is_instantiated = collection_instantiated_by_any_object(bmain, + collection); /* Always consider adding collections directly selected by the user. */ - bool do_add_collection = (item->tag & LINK_APPEND_TAG_INDIRECT) == 0; + bool do_add_collection = (item->tag & LINK_APPEND_TAG_INDIRECT) == 0 && + !collection_is_instantiated; /* In linking case, do not enforce instantiating non-directly linked collections/objects. * This avoids cluttering the ViewLayers, user can instantiate themselves specific collections * or objects easily from the Outliner if needed. */ - if (!do_add_collection && do_append) { + if (!do_add_collection && do_append && !collection_is_instantiated) { LISTBASE_FOREACH (CollectionObject *, coll_ob, &collection->gobject) { Object *ob = coll_ob->ob; if (!object_in_any_scene(bmain, ob)) { @@ -726,6 +743,8 @@ static void loose_data_instantiate_object_process(LooseDataInstantiateContext *i * if you want it do it at the editor level. */ const bool object_set_active = false; + const bool is_linking = (lapp_context->params->flag & FILE_LINK) != 0; + /* NOTE: For objects we only view_layer-instantiate duplicated objects that are not yet used * anywhere. */ LinkNode *itemlink; @@ -736,6 +755,17 @@ static void loose_data_instantiate_object_process(LooseDataInstantiateContext *i continue; } + /* In linking case, never instantiate stray objects that are not directly linked. + * + * While this is not ideal (in theory no object should remain un-owned), in case of indirectly + * linked objects, the other solution would be to add them to a local collection, which would + * make them directly linked. Think for now keeping them indirectly linked is more important. + * Ref. T93757. + */ + if (is_linking && (item->tag & LINK_APPEND_TAG_INDIRECT) != 0) { + continue; + } + Object *ob = (Object *)id; if (object_in_any_collection(bmain, ob)) { diff --git a/source/blender/blenkernel/intern/curve.cc b/source/blender/blenkernel/intern/curve.cc index 50b7c15774d..dc2527f9b62 100644 --- a/source/blender/blenkernel/intern/curve.cc +++ b/source/blender/blenkernel/intern/curve.cc @@ -43,7 +43,7 @@ #include "DNA_defaults.h" #include "DNA_material_types.h" -/* for dereferencing pointers */ +/* For dereferencing pointers. */ #include "DNA_key_types.h" #include "DNA_object_types.h" #include "DNA_vfont_types.h" @@ -1438,7 +1438,7 @@ void BKE_nurb_makeFaces(const Nurb *nu, float *coord_array, int rowstride, int r jstart = (int *)MEM_malloc_arrayN(totv, sizeof(float), "makeNurbfaces4"); jend = (int *)MEM_malloc_arrayN(totv, sizeof(float), "makeNurbfaces5"); - /* precalculation of basisv and jstart, jend */ + /* Pre-calculation of `basisv` and `jstart`, `jend`. */ if (nu->flagv & CU_NURB_CYCLIC) { cycl = nu->orderv - 1; } @@ -2104,10 +2104,10 @@ static void tilt_bezpart(const BezTriple *prevbezt, } } -/* make_bevel_list_3D_* funcs, at a minimum these must - * fill in the bezp->quat and bezp->dir values */ +/* `make_bevel_list_3D_*` functions, at a minimum these must + * fill in the #BevPoint.quat and #BevPoint.dir values. */ -/* utility for make_bevel_list_3D_* funcs */ +/** Utility for `make_bevel_list_3D_*` functions. */ static void bevel_list_calc_bisect(BevList *bl) { BevPoint *bevp2, *bevp1, *bevp0; @@ -2329,14 +2329,14 @@ static void make_bevel_list_3D_minimum_twist(BevList *bl) /* Need to correct for the start/end points not matching * do this by calculating the tilt angle difference, then apply - * the rotation gradually over the entire curve + * the rotation gradually over the entire curve. * - * note that the split is between last and second last, rather than first/last as youd expect. + * Note that the split is between last and second last, rather than first/last as you'd expect. * * real order is like this * 0,1,2,3,4 --> 1,2,3,4,0 * - * this is why we compare last with second last + * This is why we compare last with second last. */ float vec_1[3] = {0, 1, 0}, vec_2[3] = {0, 1, 0}, angle, ang_fac, cross_tmp[3]; @@ -2614,14 +2614,13 @@ void BKE_curve_bevelList_free(ListBase *bev) void BKE_curve_bevelList_make(Object *ob, const ListBase *nurbs, const bool for_render) { - /* - * - convert all curves to polys, with indication of resol and flags for double-vertices - * - possibly; do a smart vertice removal (in case Nurb) - * - separate in individual blocks with BoundBox - * - AutoHole detection + /* - Convert all curves to polys, with indication of resolution and flags for double-vertices. + * - Possibly; do a smart vertex removal (in case #Nurb). + * - Separate in individual blocks with #BoundBox. + * - Auto-hole detection. */ - /* this function needs an object, because of tflag and upflag */ + /* This function needs an object, because of `tflag` and `upflag`. */ Curve *cu = (Curve *)ob->data; BezTriple *bezt, *prevbezt; BPoint *bp; @@ -2637,7 +2636,7 @@ void BKE_curve_bevelList_make(Object *ob, const ListBase *nurbs, const bool for_ bool is_editmode = false; ListBase *bev; - /* segbevcount alsp requires seglen. */ + /* segbevcount also requires seglen. */ const bool need_seglen = ELEM( cu->bevfac1_mapping, CU_BEVFAC_MAP_SEGMENT, CU_BEVFAC_MAP_SPLINE) || ELEM(cu->bevfac2_mapping, CU_BEVFAC_MAP_SEGMENT, CU_BEVFAC_MAP_SPLINE); @@ -2805,10 +2804,10 @@ void BKE_curve_bevelList_make(Object *ob, const ListBase *nurbs, const bool for_ } } else { - /* always do all three, to prevent data hanging around */ + /* Always do all three, to prevent data hanging around. */ int j; - /* BevPoint must stay aligned to 4 so sizeof(BevPoint)/sizeof(float) works */ + /* #BevPoint must stay aligned to 4 so `sizeof(BevPoint) / sizeof(float)` works. */ for (j = 0; j < 3; j++) { BKE_curve_forward_diff_bezier(prevbezt->vec[1][j], prevbezt->vec[2][j], @@ -2819,7 +2818,7 @@ void BKE_curve_bevelList_make(Object *ob, const ListBase *nurbs, const bool for_ sizeof(BevPoint)); } - /* if both arrays are nullptr do nothiong */ + /* If both arrays are `nullptr` do nothing. */ tilt_bezpart(prevbezt, bezt, nu, @@ -2839,7 +2838,7 @@ void BKE_curve_bevelList_make(Object *ob, const ListBase *nurbs, const bool for_ sizeof(BevPoint)); } - /* seglen */ + /* `seglen`. */ if (seglen != nullptr) { *seglen = 0; *segbevcount = 0; @@ -2847,7 +2846,7 @@ void BKE_curve_bevelList_make(Object *ob, const ListBase *nurbs, const bool for_ bevp0 = bevp; bevp++; bevp->offset = len_v3v3(bevp0->vec, bevp->vec); - /* match seglen and segbevcount to the cleaned up bevel lists (see STEP 2) */ + /* Match `seglen` and `segbevcount` to the cleaned up bevel lists (see STEP 2). */ if (bevp->offset > threshold) { *seglen += bevp->offset; *segbevcount += 1; @@ -2980,7 +2979,7 @@ void BKE_curve_bevelList_make(Object *ob, const ListBase *nurbs, const bool for_ continue; } - nr = bl->nr - bl->dupe_nr + 1; /* +1 because vectorbezier sets flag too */ + nr = bl->nr - bl->dupe_nr + 1; /* +1 because vector-bezier sets flag too. */ blnew = (BevList *)MEM_mallocN(sizeof(BevList), "makeBevelList4"); memcpy(blnew, bl, sizeof(BevList)); blnew->bevpoints = (BevPoint *)MEM_calloc_arrayN(nr, sizeof(BevPoint), "makeBevelPoints4"); @@ -2992,7 +2991,7 @@ void BKE_curve_bevelList_make(Object *ob, const ListBase *nurbs, const bool for_ blnew->seglen = bl->seglen; blnew->nr = 0; BLI_remlink(bev, bl); - BLI_insertlinkbefore(bev, bl->next, blnew); /* to make sure bevlist is tuned with nurblist */ + BLI_insertlinkbefore(bev, bl->next, blnew); /* Ensure `bevlist` is tuned with `nurblist`. */ bevp0 = bl->bevpoints; bevp1 = blnew->bevpoints; nr = bl->nr; @@ -3114,7 +3113,7 @@ void BKE_curve_bevelList_make(Object *ob, const ListBase *nurbs, const bool for_ BevPoint *bevp = bl->bevpoints; unit_qt(bevp->quat); } - else if (bl->nr == 2) { /* 2 pnt, treat separate */ + else if (bl->nr == 2) { /* 2 points, treat separately. */ make_bevel_list_segment_2D(bl); } else { @@ -3129,7 +3128,7 @@ void BKE_curve_bevelList_make(Object *ob, const ListBase *nurbs, const bool for_ BevPoint *bevp = bl->bevpoints; unit_qt(bevp->quat); } - else if (bl->nr == 2) { /* 2 pnt, treat separate */ + else if (bl->nr == 2) { /* 2 points, treat separately. */ make_bevel_list_segment_3D(bl); } else { @@ -3321,13 +3320,13 @@ static void calchandleNurb_intern(BezTriple *bezt, } if (skip_align || - /* when one handle is free, alignming makes no sense, see: T35952 */ + /* When one handle is free, aligning makes no sense, see: T35952 */ ELEM(HD_FREE, bezt->h1, bezt->h2) || - /* also when no handles are aligned, skip this step */ + /* Also when no handles are aligned, skip this step. */ (!ELEM(HD_ALIGN, bezt->h1, bezt->h2) && !ELEM(HD_ALIGN_DOUBLESIDE, bezt->h1, bezt->h2))) { - /* handles need to be updated during animation and applying stuff like hooks, + /* Handles need to be updated during animation and applying stuff like hooks, * but in such situations it's quite difficult to distinguish in which order - * align handles should be aligned so skip them for now */ + * align handles should be aligned so skip them for now. */ return; } @@ -3996,8 +3995,8 @@ void BKE_nurb_handle_smooth_fcurve(BezTriple *bezt, int total, bool cyclic) } } - /* Find continuous subsequences of free auto handles and smooth them, starting at - * search_base. In cyclic mode these subsequences can span the cycle boundary. */ + /* Find continuous sub-sequences of free auto handles and smooth them, starting at search_base. + * In cyclic mode these sub-sequences can span the cycle boundary. */ int start = search_base, count = 1; for (int i = 1, j = start + 1; i < total; i++, j++) { @@ -4189,13 +4188,13 @@ void BKE_nurb_handles_autocalc(Nurb *nu, uint8_t flag) /* left handle: */ if (flag == 0 || (bezt1->f1 & flag)) { bezt1->h1 = HD_FREE; - /* distance too short: vectorhandle */ + /* Distance too short: vector-handle. */ if (len_squared_v3v3(bezt1->vec[1], bezt0->vec[1]) < eps_sq) { bezt1->h1 = HD_VECT; leftsmall = true; } else { - /* aligned handle? */ + /* Aligned handle? */ if (dist_squared_to_line_v3(bezt1->vec[1], bezt1->vec[0], bezt1->vec[2]) < eps_sq) { align = true; bezt1->h1 = HD_ALIGN; @@ -4209,13 +4208,13 @@ void BKE_nurb_handles_autocalc(Nurb *nu, uint8_t flag) /* right handle: */ if (flag == 0 || (bezt1->f3 & flag)) { bezt1->h2 = HD_FREE; - /* distance too short: vectorhandle */ + /* Distance too short: vector-handle. */ if (len_squared_v3v3(bezt1->vec[1], bezt2->vec[1]) < eps_sq) { bezt1->h2 = HD_VECT; rightsmall = true; } else { - /* aligned handle? */ + /* Aligned handle? */ if (align) { bezt1->h2 = HD_ALIGN; } @@ -4860,7 +4859,7 @@ bool BKE_nurb_type_convert(Nurb *nu, int a, c, nr; if (nu->type == CU_POLY) { - if (type == CU_BEZIER) { /* To Bezier with vecthandles. */ + if (type == CU_BEZIER) { /* To Bezier with vector-handles. */ nr = nu->pntsu; bezt = (BezTriple *)MEM_calloc_arrayN(nr, sizeof(BezTriple), "setsplinetype2"); nu->bezt = bezt; diff --git a/source/blender/blenkernel/intern/curveprofile.cc b/source/blender/blenkernel/intern/curveprofile.cc index 387709fca29..8f387be41d3 100644 --- a/source/blender/blenkernel/intern/curveprofile.cc +++ b/source/blender/blenkernel/intern/curveprofile.cc @@ -46,7 +46,7 @@ struct CurveProfile *BKE_curveprofile_add(eCurveProfilePresets preset) { - CurveProfile *profile = (CurveProfile *)MEM_callocN(sizeof(CurveProfile), __func__); + CurveProfile *profile = MEM_cnew<CurveProfile>(__func__); BKE_curveprofile_set_defaults(profile); profile->preset = preset; diff --git a/source/blender/blenkernel/intern/fcurve_driver.c b/source/blender/blenkernel/intern/fcurve_driver.c index 5496519e53b..ce30f80ba65 100644 --- a/source/blender/blenkernel/intern/fcurve_driver.c +++ b/source/blender/blenkernel/intern/fcurve_driver.c @@ -29,6 +29,7 @@ #include "BLI_alloca.h" #include "BLI_expr_pylike_eval.h" +#include "BLI_listbase.h" #include "BLI_math.h" #include "BLI_string_utils.h" #include "BLI_threads.h" @@ -864,6 +865,12 @@ void driver_variable_name_validate(DriverVar *dvar) } } +void driver_variable_unique_name(DriverVar *dvar) +{ + ListBase variables = BLI_listbase_from_link((Link *)dvar); + BLI_uniquename(&variables, dvar, dvar->name, '_', offsetof(DriverVar, name), sizeof(dvar->name)); +} + DriverVar *driver_add_new_variable(ChannelDriver *driver) { DriverVar *dvar; diff --git a/source/blender/blenkernel/intern/image.c b/source/blender/blenkernel/intern/image.c index f43cf00a310..edd5073da79 100644 --- a/source/blender/blenkernel/intern/image.c +++ b/source/blender/blenkernel/intern/image.c @@ -84,6 +84,7 @@ #include "BKE_lib_id.h" #include "BKE_main.h" #include "BKE_node.h" +#include "BKE_node_tree_update.h" #include "BKE_packedFile.h" #include "BKE_report.h" #include "BKE_scene.h" @@ -3699,16 +3700,8 @@ void BKE_image_signal(Main *bmain, Image *ima, ImageUser *iuser, int signal) BLI_mutex_unlock(ima->runtime.cache_mutex); - /* don't use notifiers because they are not 100% sure to succeeded - * this also makes sure all scenes are accounted for. */ - { - Scene *scene; - for (scene = bmain->scenes.first; scene; scene = scene->id.next) { - if (scene->nodetree) { - nodeUpdateID(scene->nodetree, &ima->id); - } - } - } + BKE_ntree_update_tag_id_changed(bmain, &ima->id); + BKE_ntree_update_main(bmain, NULL); } /* return renderpass for a given pass index and active view */ diff --git a/source/blender/blenkernel/intern/lib_override.c b/source/blender/blenkernel/intern/lib_override.c index 83ed0ee4c08..6e1bac71d3f 100644 --- a/source/blender/blenkernel/intern/lib_override.c +++ b/source/blender/blenkernel/intern/lib_override.c @@ -470,8 +470,9 @@ static void lib_override_group_tag_data_object_to_collection_init_collection_pro } LinkNodePair **collections_linkedlist_p; - if (!BLI_ghash_ensure_p( - data->linked_object_to_instantiating_collections, ob, &collections_linkedlist_p)) { + if (!BLI_ghash_ensure_p(data->linked_object_to_instantiating_collections, + ob, + (void ***)&collections_linkedlist_p)) { *collections_linkedlist_p = BLI_memarena_calloc(data->mem_arena, sizeof(**collections_linkedlist_p)); } diff --git a/source/blender/blenkernel/intern/lib_remap.c b/source/blender/blenkernel/intern/lib_remap.c index 3cea0de32ee..9ea85714b4a 100644 --- a/source/blender/blenkernel/intern/lib_remap.c +++ b/source/blender/blenkernel/intern/lib_remap.c @@ -346,7 +346,7 @@ static void libblock_remap_data_postprocess_obdata_relink(Main *bmain, Object *o static void libblock_remap_data_postprocess_nodetree_update(Main *bmain, ID *new_id) { /* Update all group nodes using a node group. */ - ntreeUpdateAllUsers(bmain, new_id, 0); + ntreeUpdateAllUsers(bmain, new_id); } /** diff --git a/source/blender/blenkernel/intern/linestyle.c b/source/blender/blenkernel/intern/linestyle.c index ac0dbcb715d..95f41ab4b39 100644 --- a/source/blender/blenkernel/intern/linestyle.c +++ b/source/blender/blenkernel/intern/linestyle.c @@ -50,6 +50,7 @@ #include "BKE_linestyle.h" #include "BKE_main.h" #include "BKE_node.h" +#include "BKE_node_tree_update.h" #include "BKE_texture.h" #include "BLO_read_write.h" @@ -2085,5 +2086,5 @@ void BKE_linestyle_default_shader(const bContext *C, FreestyleLineStyle *linesty tosock = BLI_findlink(&output_linestyle->inputs, 0); /* Color */ nodeAddLink(ntree, input_texure, fromsock, output_linestyle, tosock); - ntreeUpdateTree(CTX_data_main(C), ntree); + BKE_ntree_update_main_tree(CTX_data_main(C), ntree, NULL); } diff --git a/source/blender/blenkernel/intern/mesh_convert.cc b/source/blender/blenkernel/intern/mesh_convert.cc index e8054884f26..07c126861f0 100644 --- a/source/blender/blenkernel/intern/mesh_convert.cc +++ b/source/blender/blenkernel/intern/mesh_convert.cc @@ -917,7 +917,7 @@ static void curve_to_mesh_eval_ensure(Object &object) * will have no modifiers. */ Object bevel_object = {{nullptr}}; if (curve.bevobj != nullptr) { - bevel_object = *curve.bevobj; + memcpy(&bevel_object, curve.bevobj, sizeof(bevel_object)); BLI_listbase_clear(&bevel_object.modifiers); BKE_object_runtime_reset(&bevel_object); curve.bevobj = &bevel_object; @@ -926,7 +926,7 @@ static void curve_to_mesh_eval_ensure(Object &object) /* Same thing for taper. */ Object taper_object = {{nullptr}}; if (curve.taperobj != nullptr) { - taper_object = *curve.taperobj; + memcpy(&taper_object, curve.taperobj, sizeof(taper_object)); BLI_listbase_clear(&taper_object.modifiers); BKE_object_runtime_reset(&taper_object); curve.taperobj = &taper_object; @@ -1065,7 +1065,8 @@ static Mesh *mesh_new_from_mesh_object_with_layers(Depsgraph *depsgraph, return nullptr; } - Object object_for_eval = *object; + Object object_for_eval; + memcpy(&object_for_eval, object, sizeof(object_for_eval)); if (object_for_eval.runtime.data_orig != nullptr) { object_for_eval.data = object_for_eval.runtime.data_orig; } @@ -1440,7 +1441,8 @@ void BKE_mesh_nomain_to_mesh(Mesh *mesh_src, /* mesh_src might depend on mesh_dst, so we need to do everything with a local copy */ /* TODO(Sybren): the above claim came from 2.7x derived-mesh code (DM_to_mesh); * check whether it is still true with Mesh */ - Mesh tmp = *mesh_dst; + Mesh tmp; + memcpy(&tmp, mesh_dst, sizeof(tmp)); int totvert, totedge /*, totface */ /* UNUSED */, totloop, totpoly; bool did_shapekeys = false; eCDAllocType alloctype = CD_DUPLICATE; diff --git a/source/blender/blenkernel/intern/movieclip.c b/source/blender/blenkernel/intern/movieclip.c index fc2e7d0a6a3..b0c93a5614d 100644 --- a/source/blender/blenkernel/intern/movieclip.c +++ b/source/blender/blenkernel/intern/movieclip.c @@ -70,6 +70,7 @@ #include "BKE_main.h" #include "BKE_movieclip.h" #include "BKE_node.h" +#include "BKE_node_tree_update.h" #include "BKE_tracking.h" #include "IMB_imbuf.h" @@ -1695,17 +1696,7 @@ void BKE_movieclip_reload(Main *bmain, MovieClip *clip) movieclip_calc_length(clip); - /* same as for image update -- don't use notifiers because they are not 100% sure to succeeded - * (node trees which are not currently visible wouldn't be refreshed) - */ - { - Scene *scene; - for (scene = bmain->scenes.first; scene; scene = scene->id.next) { - if (scene->nodetree) { - nodeUpdateID(scene->nodetree, &clip->id); - } - } - } + BKE_ntree_update_tag_id_changed(bmain, &clip->id); } void BKE_movieclip_update_scopes(MovieClip *clip, MovieClipUser *user, MovieClipScopes *scopes) diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc index be458ed036e..7121ef20207 100644 --- a/source/blender/blenkernel/intern/node.cc +++ b/source/blender/blenkernel/intern/node.cc @@ -74,6 +74,7 @@ #include "BKE_lib_query.h" #include "BKE_main.h" #include "BKE_node.h" +#include "BKE_node_tree_update.h" #include "RNA_access.h" #include "RNA_define.h" @@ -261,8 +262,7 @@ static void ntree_free_data(ID *id) /* XXX hack! node trees should not store execution graphs at all. * This should be removed when old tree types no longer require it. * Currently the execution data for texture nodes remains in the tree - * after execution, until the node tree is updated or freed. - */ + * after execution, until the node tree is updated or freed. */ if (ntree->execdata) { switch (ntree->type) { case NTREE_SHADER: @@ -278,10 +278,10 @@ static void ntree_free_data(ID *id) /* XXX not nice, but needed to free localized node groups properly */ free_localized_node_groups(ntree); - /* unregister associated RNA types */ + /* Unregister associated RNA types. */ ntreeInterfaceTypeFree(ntree); - BLI_freelistN(&ntree->links); /* do first, then unlink_node goes fast */ + BLI_freelistN(&ntree->links); LISTBASE_FOREACH_MUTABLE (bNode *, node, &ntree->nodes) { node_free_node(ntree, node); @@ -522,7 +522,6 @@ static void write_node_socket_default_value(BlendWriter *writer, bNodeSocket *so static void write_node_socket(BlendWriter *writer, bNodeSocket *sock) { - /* actual socket writing */ BLO_write_struct(writer, bNodeSocket, sock); if (sock->prop) { @@ -533,7 +532,6 @@ static void write_node_socket(BlendWriter *writer, bNodeSocket *sock) } static void write_node_socket_interface(BlendWriter *writer, bNodeSocket *sock) { - /* actual socket writing */ BLO_write_struct(writer, bNodeSocket, sock); if (sock->prop) { @@ -547,8 +545,6 @@ void ntreeBlendWrite(BlendWriter *writer, bNodeTree *ntree) { BKE_id_blend_write(writer, &ntree->id); - /* for link_list() speed, we write per list */ - if (ntree->adt) { BKE_animdata_blend_write(writer, ntree->adt); } @@ -572,7 +568,6 @@ void ntreeBlendWrite(BlendWriter *writer, bNodeTree *ntree) } if (node->storage) { - /* could be handlerized at some point, now only 1 exception still */ if (ELEM(ntree->type, NTREE_SHADER, NTREE_GEOMETRY) && ELEM(node->type, SH_NODE_CURVE_VEC, SH_NODE_CURVE_RGB, SH_NODE_CURVE_FLOAT)) { BKE_curvemapping_blend_write(writer, (const CurveMapping *)node->storage); @@ -646,13 +641,13 @@ void ntreeBlendWrite(BlendWriter *writer, bNodeTree *ntree) } if (node->type == CMP_NODE_OUTPUT_FILE) { - /* inputs have own storage data */ + /* Inputs have their own storage data. */ LISTBASE_FOREACH (bNodeSocket *, sock, &node->inputs) { BLO_write_struct(writer, NodeImageMultiFileSocket, sock->storage); } } if (ELEM(node->type, CMP_NODE_IMAGE, CMP_NODE_R_LAYERS)) { - /* write extra socket info */ + /* Write extra socket info. */ LISTBASE_FOREACH (bNodeSocket *, sock, &node->outputs) { BLO_write_struct(writer, NodeImageLayer, sock->storage); } @@ -716,6 +711,7 @@ void ntreeBlendReadData(BlendDataReader *reader, bNodeTree *ntree) ntree->execdata = nullptr; ntree->field_inferencing_interface = nullptr; + BKE_ntree_update_tag_missing_runtime_data(ntree); BLO_read_data_address(reader, &ntree->adt); BKE_animdata_blend_read_data(reader, ntree->adt); @@ -748,7 +744,6 @@ void ntreeBlendReadData(BlendDataReader *reader, bNodeTree *ntree) } if (node->storage) { - /* could be handlerized at some point */ switch (node->type) { case SH_NODE_CURVE_VEC: case SH_NODE_CURVE_RGB: @@ -859,11 +854,6 @@ void ntreeBlendReadData(BlendDataReader *reader, bNodeTree *ntree) /* TODO: should be dealt by new generic cache handling of IDs... */ ntree->previews = nullptr; - if (ntree->type == NTREE_GEOMETRY) { - /* Update field referencing for the geometry nodes modifier. */ - ntree->update |= NTREE_UPDATE_FIELD_INFERENCING; - } - BLO_read_data_address(reader, &ntree->preview); BKE_previewimg_blend_read(reader, ntree->preview); @@ -1096,21 +1086,18 @@ static void node_add_sockets_from_type(bNodeTree *ntree, bNode *node, bNodeType return; } bNodeSocketTemplate *sockdef; - /* bNodeSocket *sock; */ /* UNUSED */ if (ntype->inputs) { sockdef = ntype->inputs; while (sockdef->type != -1) { - /* sock = */ node_add_socket_from_template(ntree, node, sockdef, SOCK_IN); - + node_add_socket_from_template(ntree, node, sockdef, SOCK_IN); sockdef++; } } if (ntype->outputs) { sockdef = ntype->outputs; while (sockdef->type != -1) { - /* sock = */ node_add_socket_from_template(ntree, node, sockdef, SOCK_OUT); - + node_add_socket_from_template(ntree, node, sockdef, SOCK_OUT); sockdef++; } } @@ -1168,8 +1155,7 @@ static void node_init(const struct bContext *C, bNodeTree *ntree, bNode *node) RNA_pointer_create((ID *)ntree, &RNA_Node, node, &ptr); /* XXX Warning: context can be nullptr in case nodes are added in do_versions. - * Delayed init is not supported for nodes with context-based `initfunc_api` at the moment. - */ + * Delayed init is not supported for nodes with context-based `initfunc_api` at the moment. */ BLI_assert(C != nullptr); ntype->initfunc_api(C, &ptr); } @@ -1190,6 +1176,7 @@ static void ntree_set_typeinfo(bNodeTree *ntree, bNodeTreeType *typeinfo) /* Deprecated integer type. */ ntree->type = ntree->typeinfo->type; + BKE_ntree_update_tag_all(ntree); } static void node_set_typeinfo(const struct bContext *C, @@ -1240,6 +1227,7 @@ static void node_socket_set_typeinfo(bNodeTree *ntree, ntree->init &= ~NTREE_TYPE_INIT; } + BKE_ntree_update_tag_socket_type(ntree, sock); } /* Set specific typeinfo pointers in all node trees on register/unregister */ @@ -1383,18 +1371,6 @@ bNodeType *nodeTypeFind(const char *idname) return nullptr; } -static void free_dynamic_typeinfo(bNodeType *ntype) -{ - if (ntype->type == NODE_DYNAMIC) { - if (ntype->inputs) { - MEM_freeN(ntype->inputs); - } - if (ntype->outputs) { - MEM_freeN(ntype->outputs); - } - } -} - /* callback for hash value free function */ static void node_free_type(void *nodetype_v) { @@ -1404,11 +1380,6 @@ static void node_free_type(void *nodetype_v) * or we'd want to update *all* active Mains, which we cannot do anyway currently. */ update_typeinfo(G_MAIN, nullptr, nullptr, nodetype, nullptr, true); - /* XXX deprecated */ - if (nodetype->type == NODE_DYNAMIC) { - free_dynamic_typeinfo(nodetype); - } - delete nodetype->fixed_declaration; nodetype->fixed_declaration = nullptr; @@ -1580,7 +1551,7 @@ static bNodeSocket *make_socket(bNodeTree *ntree, /* if no explicit identifier is given, assign a unique identifier based on the name */ BLI_strncpy(auto_identifier, name, sizeof(auto_identifier)); } - /* make the identifier unique */ + /* Make the identifier unique. */ BLI_uniquename_cb( unique_identifier_check, lb, "socket", '_', auto_identifier, sizeof(auto_identifier)); @@ -1749,7 +1720,7 @@ bNodeSocket *nodeAddSocket(bNodeTree *ntree, BLI_remlink(lb, sock); /* does nothing for new socket */ BLI_addtail(lb, sock); - node->update |= NODE_UPDATE; + BKE_ntree_update_tag_socket_new(ntree, sock); return sock; } @@ -1768,8 +1739,6 @@ bNodeSocket *nodeInsertSocket(bNodeTree *ntree, BLI_remlink(lb, sock); /* does nothing for new socket */ BLI_insertlinkbefore(lb, next_sock, sock); - node->update |= NODE_UPDATE; - return sock; } @@ -2010,10 +1979,7 @@ bNodeSocket *nodeInsertStaticSocket(bNodeTree *ntree, return sock; } -static void node_socket_free(bNodeTree *UNUSED(ntree), - bNodeSocket *sock, - bNode *UNUSED(node), - const bool do_id_user) +static void node_socket_free(bNodeSocket *sock, const bool do_id_user) { if (sock->prop) { IDP_FreePropertyContent_ex(sock->prop, do_id_user); @@ -2048,10 +2014,10 @@ void nodeRemoveSocketEx(struct bNodeTree *ntree, BLI_remlink(&node->inputs, sock); BLI_remlink(&node->outputs, sock); - node_socket_free(ntree, sock, node, do_id_user); + node_socket_free(sock, do_id_user); MEM_freeN(sock); - node->update |= NODE_UPDATE; + BKE_ntree_update_tag_socket_removed(ntree); } void nodeRemoveAllSockets(bNodeTree *ntree, bNode *node) @@ -2063,18 +2029,18 @@ void nodeRemoveAllSockets(bNodeTree *ntree, bNode *node) } LISTBASE_FOREACH_MUTABLE (bNodeSocket *, sock, &node->inputs) { - node_socket_free(ntree, sock, node, true); + node_socket_free(sock, true); MEM_freeN(sock); } BLI_listbase_clear(&node->inputs); LISTBASE_FOREACH_MUTABLE (bNodeSocket *, sock, &node->outputs) { - node_socket_free(ntree, sock, node, true); + node_socket_free(sock, true); MEM_freeN(sock); } BLI_listbase_clear(&node->outputs); - node->update |= NODE_UPDATE; + BKE_ntree_update_tag_socket_removed(ntree); } bNode *nodeFindNodebyName(bNodeTree *ntree, const char *name) @@ -2226,7 +2192,7 @@ bNode *nodeAddNode(const struct bContext *C, bNodeTree *ntree, const char *idnam BLI_strncpy(node->idname, idname, sizeof(node->idname)); node_set_typeinfo(C, ntree, node, nodeTypeFind(idname)); - ntree->update |= NTREE_UPDATE_NODES; + BKE_ntree_update_tag_node_new(ntree, node); return node; } @@ -2236,9 +2202,8 @@ bNode *nodeAddStaticNode(const struct bContext *C, bNodeTree *ntree, int type) const char *idname = nullptr; NODE_TYPES_BEGIN (ntype) { - /* do an extra poll here, because some int types are used - * for multiple node types, this helps find the desired type - */ + /* Do an extra poll here, because some int types are used + * for multiple node types, this helps find the desired type. */ const char *disabled_hint; if (ntype->type == type && (!ntype->poll || ntype->poll(ntype, ntree, &disabled_hint))) { idname = ntype->idname; @@ -2268,9 +2233,8 @@ static void node_socket_copy(bNodeSocket *sock_dst, const bNodeSocket *sock_src, } sock_dst->stack_index = 0; - /* XXX some compositor node (e.g. image, render layers) still store - * some persistent buffer data here, need to clear this to avoid dangling pointers. - */ + /* XXX some compositor nodes (e.g. image, render layers) still store + * some persistent buffer data here, need to clear this to avoid dangling pointers. */ sock_dst->cache = nullptr; } @@ -2285,7 +2249,7 @@ bNode *BKE_node_copy_ex(bNodeTree *ntree, *node_dst = *node_src; - /* can be called for nodes outside a node tree (e.g. clipboard) */ + /* Can be called for nodes outside a node tree (e.g. clipboard). */ if (ntree) { if (unique_name) { nodeUniqueName(ntree, node_dst); @@ -2352,7 +2316,7 @@ bNode *BKE_node_copy_ex(bNodeTree *ntree, } if (ntree) { - ntree->update |= NTREE_UPDATE_NODES; + BKE_ntree_update_tag_node_new(ntree, node_dst); } /* Reset the declaration of the new node. */ @@ -2422,7 +2386,7 @@ bNodeLink *nodeAddLink( { bNodeLink *link = nullptr; - /* test valid input */ + /* Test valid input. */ BLI_assert(fromnode); BLI_assert(tonode); @@ -2449,7 +2413,7 @@ bNodeLink *nodeAddLink( } if (ntree) { - ntree->update |= NTREE_UPDATE_LINKS; + BKE_ntree_update_tag_link_added(ntree, link); } if (link != nullptr && link->tosock->flag & SOCK_MULTI_INPUT) { @@ -2461,7 +2425,7 @@ bNodeLink *nodeAddLink( void nodeRemLink(bNodeTree *ntree, bNodeLink *link) { - /* can be called for links outside a node tree (e.g. clipboard) */ + /* Can be called for links outside a node tree (e.g. clipboard). */ if (ntree) { BLI_remlink(&ntree->links, link); } @@ -2472,7 +2436,7 @@ void nodeRemLink(bNodeTree *ntree, bNodeLink *link) MEM_freeN(link); if (ntree) { - ntree->update |= NTREE_UPDATE_LINKS; + BKE_ntree_update_tag_link_removed(ntree); } } @@ -2572,7 +2536,7 @@ void nodeMuteLinkToggle(bNodeTree *ntree, bNodeLink *link) } if (ntree) { - ntree->update |= NTREE_UPDATE_LINKS; + BKE_ntree_update_tag_link_mute(ntree, link); } } @@ -2583,8 +2547,6 @@ void nodeRemSocketLinks(bNodeTree *ntree, bNodeSocket *sock) nodeRemLink(ntree, link); } } - - ntree->update |= NTREE_UPDATE_LINKS; } bool nodeLinkIsHidden(const bNodeLink *link) @@ -2649,7 +2611,7 @@ void nodeInternalRelink(bNodeTree *ntree, bNode *node) link->flag |= NODE_LINK_MUTED; } - ntree->update |= NTREE_UPDATE_LINKS; + BKE_ntree_update_tag_link_changed(ntree); } else { if (link->tosock->flag & SOCK_MULTI_INPUT) { @@ -2843,8 +2805,11 @@ bool BKE_node_preview_used(const bNode *node) return (node->typeinfo->flag & NODE_PREVIEW) != 0; } -bNodePreview *BKE_node_preview_verify( - bNodeInstanceHash *previews, bNodeInstanceKey key, int xsize, int ysize, bool create) +bNodePreview *BKE_node_preview_verify(bNodeInstanceHash *previews, + bNodeInstanceKey key, + const int xsize, + const int ysize, + const bool create) { bNodePreview *preview = (bNodePreview *)BKE_node_instance_hash_lookup(previews, key); if (!preview) { @@ -2901,9 +2866,8 @@ void BKE_node_preview_free(bNodePreview *preview) static void node_preview_init_tree_recursive(bNodeInstanceHash *previews, bNodeTree *ntree, bNodeInstanceKey parent_key, - int xsize, - int ysize, - bool create_previews) + const int xsize, + const int ysize) { LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { bNodeInstanceKey key = BKE_node_instance_key(parent_key, ntree, node); @@ -2912,17 +2876,16 @@ static void node_preview_init_tree_recursive(bNodeInstanceHash *previews, node->preview_xsize = xsize; node->preview_ysize = ysize; - BKE_node_preview_verify(previews, key, xsize, ysize, create_previews); + BKE_node_preview_verify(previews, key, xsize, ysize, false); } if (node->type == NODE_GROUP && node->id) { - node_preview_init_tree_recursive( - previews, (bNodeTree *)node->id, key, xsize, ysize, create_previews); + node_preview_init_tree_recursive(previews, (bNodeTree *)node->id, key, xsize, ysize); } } } -void BKE_node_preview_init_tree(bNodeTree *ntree, int xsize, int ysize, bool create_previews) +void BKE_node_preview_init_tree(bNodeTree *ntree, int xsize, int ysize) { if (!ntree) { return; @@ -2932,8 +2895,7 @@ void BKE_node_preview_init_tree(bNodeTree *ntree, int xsize, int ysize, bool cre ntree->previews = BKE_node_instance_hash_new("node previews"); } - node_preview_init_tree_recursive( - ntree->previews, ntree, NODE_INSTANCE_KEY_BASE, xsize, ysize, create_previews); + node_preview_init_tree_recursive(ntree->previews, ntree, NODE_INSTANCE_KEY_BASE, xsize, ysize); } static void node_preview_tag_used_recursive(bNodeInstanceHash *previews, @@ -3069,27 +3031,6 @@ void BKE_node_preview_merge_tree(bNodeTree *to_ntree, bNodeTree *from_ntree, boo } } -void BKE_node_preview_set_pixel( - bNodePreview *preview, const float col[4], int x, int y, bool do_manage) -{ - if (preview) { - if (x >= 0 && y >= 0) { - if (x < preview->xsize && y < preview->ysize) { - unsigned char *tar = preview->rect + 4 * ((preview->xsize * y) + x); - - if (do_manage) { - linearrgb_to_srgb_uchar4(tar, col); - } - else { - rgba_float_to_uchar(tar, col); - } - } - // else printf("prv out bound x y %d %d\n", x, y); - } - // else printf("prv out bound x y %d %d\n", x, y); - } -} - /* ************** Free stuff ********** */ void nodeUnlinkNode(bNodeTree *ntree, bNode *node) @@ -3098,9 +3039,6 @@ void nodeUnlinkNode(bNodeTree *ntree, bNode *node) ListBase *lb; if (link->fromnode == node) { lb = &node->outputs; - if (link->tonode) { - link->tonode->update |= NODE_UPDATE; - } } else if (link->tonode == node) { lb = &node->inputs; @@ -3142,10 +3080,6 @@ static void node_free_node(bNodeTree *ntree, bNode *node) /* can be called for nodes outside a node tree (e.g. clipboard) */ if (ntree) { - /* remove all references to this node */ - nodeUnlinkNode(ntree, node); - node_unlink_attached(ntree, node); - BLI_remlink(&ntree->nodes, node); if (ntree->typeinfo->free_node_cache) { @@ -3165,12 +3099,12 @@ static void node_free_node(bNodeTree *ntree, bNode *node) LISTBASE_FOREACH_MUTABLE (bNodeSocket *, sock, &node->inputs) { /* Remember, no ID user refcount management here! */ - node_socket_free(ntree, sock, node, false); + node_socket_free(sock, false); MEM_freeN(sock); } LISTBASE_FOREACH_MUTABLE (bNodeSocket *, sock, &node->outputs) { /* Remember, no ID user refcount management here! */ - node_socket_free(ntree, sock, node, false); + node_socket_free(sock, false); MEM_freeN(sock); } @@ -3189,7 +3123,7 @@ static void node_free_node(bNodeTree *ntree, bNode *node) MEM_freeN(node); if (ntree) { - ntree->update |= NTREE_UPDATE_NODES; + BKE_ntree_update_tag_node_removed(ntree); } } @@ -3197,6 +3131,12 @@ void ntreeFreeLocalNode(bNodeTree *ntree, bNode *node) { /* For removing nodes while editing localized node trees. */ BLI_assert((ntree->id.tag & LIB_TAG_LOCALIZED) != 0); + + /* These two lines assume the caller might want to free a single node and maintain + * a valid state in the node tree. */ + nodeUnlinkNode(ntree, node); + node_unlink_attached(ntree, node); + node_free_node(ntree, node); } @@ -3241,6 +3181,9 @@ void nodeRemoveNode(Main *bmain, bNodeTree *ntree, bNode *node, bool do_id_user) } } + nodeUnlinkNode(ntree, node); + node_unlink_attached(ntree, node); + /* Free node itself. */ node_free_node(ntree, node); } @@ -3266,8 +3209,7 @@ static void free_localized_node_groups(bNodeTree *ntree) /* Only localized node trees store a copy for each node group tree. * Each node group tree in a localized node tree can be freed, * since it is a localized copy itself (no risk of accessing free'd - * data in main, see T37939). - */ + * data in main, see T37939). */ if (!(ntree->id.tag & LIB_TAG_LOCALIZED)) { return; } @@ -3463,7 +3405,7 @@ bNodeTree *ntreeLocalize(bNodeTree *ntree) } } - /* ensures only a single output node is enabled */ + /* Ensures only a single output node is enabled. */ ntreeSetOutput(ntree); bNode *node_src = (bNode *)ntree->nodes.first; @@ -3560,12 +3502,11 @@ bNodeSocket *ntreeAddSocketInterface(bNodeTree *ntree, bNodeSocket *iosock = make_socket_interface(ntree, in_out, idname, name); if (in_out == SOCK_IN) { BLI_addtail(&ntree->inputs, iosock); - ntree->update |= NTREE_UPDATE_GROUP_IN; } else if (in_out == SOCK_OUT) { BLI_addtail(&ntree->outputs, iosock); - ntree->update |= NTREE_UPDATE_GROUP_OUT; } + BKE_ntree_update_tag_interface(ntree); return iosock; } @@ -3578,12 +3519,11 @@ bNodeSocket *ntreeInsertSocketInterface(bNodeTree *ntree, bNodeSocket *iosock = make_socket_interface(ntree, in_out, idname, name); if (in_out == SOCK_IN) { BLI_insertlinkbefore(&ntree->inputs, next_sock, iosock); - ntree->update |= NTREE_UPDATE_GROUP_IN; } else if (in_out == SOCK_OUT) { BLI_insertlinkbefore(&ntree->outputs, next_sock, iosock); - ntree->update |= NTREE_UPDATE_GROUP_OUT; } + BKE_ntree_update_tag_interface(ntree); return iosock; } @@ -3629,7 +3569,7 @@ void ntreeRemoveSocketInterface(bNodeTree *ntree, bNodeSocket *sock) node_socket_interface_free(ntree, sock, true); MEM_freeN(sock); - ntree->update |= NTREE_UPDATE_GROUP; + BKE_ntree_update_tag_interface(ntree); } /* generates a valid RNA identifier from the node tree name */ @@ -3973,10 +3913,13 @@ int nodeSocketIsHidden(const bNodeSocket *sock) return ((sock->flag & (SOCK_HIDDEN | SOCK_UNAVAIL)) != 0); } -void nodeSetSocketAvailability(bNodeTree *UNUSED(ntree), bNodeSocket *sock, bool is_available) +void nodeSetSocketAvailability(bNodeTree *ntree, bNodeSocket *sock, bool is_available) { - /* #ntree is not needed right now, but it's generally necessary when changing the tree because we - * want to tag it as changed in the future. */ + const bool was_available = (sock->flag & SOCK_UNAVAIL) == 0; + if (is_available != was_available) { + BKE_ntree_update_tag_socket_availability(ntree, sock); + } + if (is_available) { sock->flag &= ~SOCK_UNAVAIL; } @@ -4428,7 +4371,7 @@ void ntreeGetDependencyList(struct bNodeTree *ntree, struct bNode ***r_deplist, } /* only updates node->level for detecting cycles links */ -static void ntree_update_node_level(bNodeTree *ntree) +void ntreeUpdateNodeLevels(bNodeTree *ntree) { /* first clear tag */ LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { @@ -4463,749 +4406,42 @@ void ntreeTagUsedSockets(bNodeTree *ntree) } } -static void ntree_update_link_pointers(bNodeTree *ntree) -{ - /* first clear data */ - LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { - LISTBASE_FOREACH (bNodeSocket *, sock, &node->inputs) { - sock->link = nullptr; - } - } - - LISTBASE_FOREACH (bNodeLink *, link, &ntree->links) { - link->tosock->link = link; - } - - ntreeTagUsedSockets(ntree); -} - -static void ntree_validate_links(bNodeTree *ntree) -{ - LISTBASE_FOREACH (bNodeLink *, link, &ntree->links) { - link->flag |= NODE_LINK_VALID; - if (link->fromnode && link->tonode && link->fromnode->level <= link->tonode->level) { - link->flag &= ~NODE_LINK_VALID; - } - else if (ntree->typeinfo->validate_link) { - if (!ntree->typeinfo->validate_link((eNodeSocketDatatype)link->fromsock->type, - (eNodeSocketDatatype)link->tosock->type)) { - link->flag &= ~NODE_LINK_VALID; - } - } - } -} - void ntreeUpdateAllNew(Main *main) { + Vector<bNodeTree *> new_ntrees; + /* Update all new node trees on file read or append, to add/remove sockets * in groups nodes if the group changed, and handle any update flags that * might have been set in file reading or versioning. */ FOREACH_NODETREE_BEGIN (main, ntree, owner_id) { if (owner_id->tag & LIB_TAG_NEW) { - LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { - if (node->typeinfo->group_update_func) { - node->typeinfo->group_update_func(ntree, node); - } - } - - ntreeUpdateTree(nullptr, ntree); + BKE_ntree_update_tag_all(ntree); } } FOREACH_NODETREE_END; + BKE_ntree_update_main(main, nullptr); } -namespace blender::bke::node_field_inferencing { - -static bool is_field_socket_type(eNodeSocketDatatype type) -{ - return ELEM(type, SOCK_FLOAT, SOCK_INT, SOCK_BOOLEAN, SOCK_VECTOR, SOCK_RGBA); -} - -static bool is_field_socket_type(const SocketRef &socket) -{ - return is_field_socket_type((eNodeSocketDatatype)socket.typeinfo()->type); -} - -static bool update_field_inferencing(bNodeTree &btree); - -static InputSocketFieldType get_interface_input_field_type(const NodeRef &node, - const InputSocketRef &socket) -{ - if (!is_field_socket_type(socket)) { - return InputSocketFieldType::None; - } - if (node.is_reroute_node()) { - return InputSocketFieldType::IsSupported; - } - if (node.is_group_output_node()) { - /* Outputs always support fields when the data type is correct. */ - return InputSocketFieldType::IsSupported; - } - if (node.is_undefined()) { - return InputSocketFieldType::None; - } - - const NodeDeclaration *node_decl = node.declaration(); - - /* Node declarations should be implemented for nodes involved here. */ - BLI_assert(node_decl != nullptr); - - /* Get the field type from the declaration. */ - const SocketDeclaration &socket_decl = *node_decl->inputs()[socket.index()]; - const InputSocketFieldType field_type = socket_decl.input_field_type(); - if (field_type == InputSocketFieldType::Implicit) { - return field_type; - } - if (node_decl->is_function_node()) { - /* In a function node, every socket supports fields. */ - return InputSocketFieldType::IsSupported; - } - return field_type; -} - -static OutputFieldDependency get_interface_output_field_dependency(const NodeRef &node, - const OutputSocketRef &socket) -{ - if (!is_field_socket_type(socket)) { - /* Non-field sockets always output data. */ - return OutputFieldDependency::ForDataSource(); - } - if (node.is_reroute_node()) { - /* The reroute just forwards what is passed in. */ - return OutputFieldDependency::ForDependentField(); - } - if (node.is_group_input_node()) { - /* Input nodes get special treatment in #determine_group_input_states. */ - return OutputFieldDependency::ForDependentField(); - } - if (node.is_undefined()) { - return OutputFieldDependency::ForDataSource(); - } - - const NodeDeclaration *node_decl = node.declaration(); - - /* Node declarations should be implemented for nodes involved here. */ - BLI_assert(node_decl != nullptr); - - if (node_decl->is_function_node()) { - /* In a generic function node, all outputs depend on all inputs. */ - return OutputFieldDependency::ForDependentField(); - } - - /* Use the socket declaration. */ - const SocketDeclaration &socket_decl = *node_decl->outputs()[socket.index()]; - return socket_decl.output_field_dependency(); -} - -static FieldInferencingInterface get_dummy_field_inferencing_interface(const NodeRef &node) -{ - FieldInferencingInterface inferencing_interface; - inferencing_interface.inputs.append_n_times(InputSocketFieldType::None, node.inputs().size()); - inferencing_interface.outputs.append_n_times(OutputFieldDependency::ForDataSource(), - node.outputs().size()); - return inferencing_interface; -} - -/** - * Retrieves information about how the node interacts with fields. - * In the future, this information can be stored in the node declaration. This would allow this - * function to return a reference, making it more efficient. - */ -static FieldInferencingInterface get_node_field_inferencing_interface(const NodeRef &node) -{ - /* Node groups already reference all required information, so just return that. */ - if (node.is_group_node()) { - bNodeTree *group = (bNodeTree *)node.bnode()->id; - if (group == nullptr) { - return FieldInferencingInterface(); - } - if (!ntreeIsRegistered(group)) { - /* This can happen when there is a linked node group that was not found (see T92799). */ - return get_dummy_field_inferencing_interface(node); - } - if (group->field_inferencing_interface == nullptr) { - /* Update group recursively. */ - update_field_inferencing(*group); - } - return *group->field_inferencing_interface; - } - - FieldInferencingInterface inferencing_interface; - for (const InputSocketRef *input_socket : node.inputs()) { - inferencing_interface.inputs.append(get_interface_input_field_type(node, *input_socket)); - } - - for (const OutputSocketRef *output_socket : node.outputs()) { - inferencing_interface.outputs.append( - get_interface_output_field_dependency(node, *output_socket)); - } - return inferencing_interface; -} - -/** - * This struct contains information for every socket. The values are propagated through the - * network. - */ -struct SocketFieldState { - /* This socket starts a new field. */ - bool is_field_source = false; - /* This socket can never become a field, because the node itself does not support it. */ - bool is_always_single = false; - /* This socket is currently a single value. It could become a field though. */ - bool is_single = true; - /* This socket is required to be a single value. This can be because the node itself only - * supports this socket to be a single value, or because a node afterwards requires this to be a - * single value. */ - bool requires_single = false; -}; - -static Vector<const InputSocketRef *> gather_input_socket_dependencies( - const OutputFieldDependency &field_dependency, const NodeRef &node) -{ - const OutputSocketFieldType type = field_dependency.field_type(); - Vector<const InputSocketRef *> input_sockets; - switch (type) { - case OutputSocketFieldType::FieldSource: - case OutputSocketFieldType::None: { - break; - } - case OutputSocketFieldType::DependentField: { - /* This output depends on all inputs. */ - input_sockets.extend(node.inputs()); - break; - } - case OutputSocketFieldType::PartiallyDependent: { - /* This output depends only on a few inputs. */ - for (const int i : field_dependency.linked_input_indices()) { - input_sockets.append(&node.input(i)); - } - break; - } - } - return input_sockets; -} - -/** - * Check what the group output socket depends on. Potentially traverses the node tree - * to figure out if it is always a field or if it depends on any group inputs. - */ -static OutputFieldDependency find_group_output_dependencies( - const InputSocketRef &group_output_socket, - const Span<SocketFieldState> field_state_by_socket_id) -{ - if (!is_field_socket_type(group_output_socket)) { - return OutputFieldDependency::ForDataSource(); - } - - /* Use a Set here instead of an array indexed by socket id, because we my only need to look at - * very few sockets. */ - Set<const InputSocketRef *> handled_sockets; - Stack<const InputSocketRef *> sockets_to_check; - - handled_sockets.add(&group_output_socket); - sockets_to_check.push(&group_output_socket); - - /* Keeps track of group input indices that are (indirectly) connected to the output. */ - Vector<int> linked_input_indices; - - while (!sockets_to_check.is_empty()) { - const InputSocketRef *input_socket = sockets_to_check.pop(); - - for (const OutputSocketRef *origin_socket : input_socket->directly_linked_sockets()) { - const NodeRef &origin_node = origin_socket->node(); - const SocketFieldState &origin_state = field_state_by_socket_id[origin_socket->id()]; - - if (origin_state.is_field_source) { - if (origin_node.is_group_input_node()) { - /* Found a group input that the group output depends on. */ - linked_input_indices.append_non_duplicates(origin_socket->index()); - } - else { - /* Found a field source that is not the group input. So the output is always a field. */ - return OutputFieldDependency::ForFieldSource(); - } - } - else if (!origin_state.is_single) { - const FieldInferencingInterface inferencing_interface = - get_node_field_inferencing_interface(origin_node); - const OutputFieldDependency &field_dependency = - inferencing_interface.outputs[origin_socket->index()]; - - /* Propagate search further to the left. */ - for (const InputSocketRef *origin_input_socket : - gather_input_socket_dependencies(field_dependency, origin_node)) { - if (!origin_input_socket->is_available()) { - continue; - } - if (!field_state_by_socket_id[origin_input_socket->id()].is_single) { - if (handled_sockets.add(origin_input_socket)) { - sockets_to_check.push(origin_input_socket); - } - } - } - } - } - } - return OutputFieldDependency::ForPartiallyDependentField(std::move(linked_input_indices)); -} - -static void propagate_data_requirements_from_right_to_left( - const NodeTreeRef &tree, const MutableSpan<SocketFieldState> field_state_by_socket_id) -{ - const NodeTreeRef::ToposortResult toposort_result = tree.toposort( - NodeTreeRef::ToposortDirection::RightToLeft); - - for (const NodeRef *node : toposort_result.sorted_nodes) { - const FieldInferencingInterface inferencing_interface = get_node_field_inferencing_interface( - *node); - - for (const OutputSocketRef *output_socket : node->outputs()) { - SocketFieldState &state = field_state_by_socket_id[output_socket->id()]; - - const OutputFieldDependency &field_dependency = - inferencing_interface.outputs[output_socket->index()]; - - if (field_dependency.field_type() == OutputSocketFieldType::FieldSource) { - continue; - } - if (field_dependency.field_type() == OutputSocketFieldType::None) { - state.requires_single = true; - state.is_always_single = true; - continue; - } - - /* The output is required to be a single value when it is connected to any input that does - * not support fields. */ - for (const InputSocketRef *target_socket : output_socket->directly_linked_sockets()) { - if (target_socket->is_available()) { - state.requires_single |= field_state_by_socket_id[target_socket->id()].requires_single; - } - } - - if (state.requires_single) { - bool any_input_is_field_implicitly = false; - const Vector<const InputSocketRef *> connected_inputs = gather_input_socket_dependencies( - field_dependency, *node); - for (const InputSocketRef *input_socket : connected_inputs) { - if (!input_socket->is_available()) { - continue; - } - if (inferencing_interface.inputs[input_socket->index()] == - InputSocketFieldType::Implicit) { - if (!input_socket->is_logically_linked()) { - any_input_is_field_implicitly = true; - break; - } - } - } - if (any_input_is_field_implicitly) { - /* This output isn't a single value actually. */ - state.requires_single = false; - } - else { - /* If the output is required to be a single value, the connected inputs in the same node - * must not be fields as well. */ - for (const InputSocketRef *input_socket : connected_inputs) { - field_state_by_socket_id[input_socket->id()].requires_single = true; - } - } - } - } - - /* Some inputs do not require fields independent of what the outputs are connected to. */ - for (const InputSocketRef *input_socket : node->inputs()) { - SocketFieldState &state = field_state_by_socket_id[input_socket->id()]; - if (inferencing_interface.inputs[input_socket->index()] == InputSocketFieldType::None) { - state.requires_single = true; - state.is_always_single = true; - } - } - } -} - -static void determine_group_input_states( - const NodeTreeRef &tree, - FieldInferencingInterface &new_inferencing_interface, - const MutableSpan<SocketFieldState> field_state_by_socket_id) -{ - { - /* Non-field inputs never support fields. */ - int index; - LISTBASE_FOREACH_INDEX (bNodeSocket *, group_input, &tree.btree()->inputs, index) { - if (!is_field_socket_type((eNodeSocketDatatype)group_input->type)) { - new_inferencing_interface.inputs[index] = InputSocketFieldType::None; - } - } - } - /* Check if group inputs are required to be single values, because they are (indirectly) - * connected to some socket that does not support fields. */ - for (const NodeRef *node : tree.nodes_by_type("NodeGroupInput")) { - for (const OutputSocketRef *output_socket : node->outputs().drop_back(1)) { - SocketFieldState &state = field_state_by_socket_id[output_socket->id()]; - if (state.requires_single) { - new_inferencing_interface.inputs[output_socket->index()] = InputSocketFieldType::None; - } - } - } - /* If an input does not support fields, this should be reflected in all Group Input nodes. */ - for (const NodeRef *node : tree.nodes_by_type("NodeGroupInput")) { - for (const OutputSocketRef *output_socket : node->outputs().drop_back(1)) { - SocketFieldState &state = field_state_by_socket_id[output_socket->id()]; - const bool supports_field = new_inferencing_interface.inputs[output_socket->index()] != - InputSocketFieldType::None; - if (supports_field) { - state.is_single = false; - state.is_field_source = true; - } - else { - state.requires_single = true; - } - } - SocketFieldState &dummy_socket_state = field_state_by_socket_id[node->outputs().last()->id()]; - dummy_socket_state.requires_single = true; - } -} - -static void propagate_field_status_from_left_to_right( - const NodeTreeRef &tree, const MutableSpan<SocketFieldState> field_state_by_socket_id) -{ - const NodeTreeRef::ToposortResult toposort_result = tree.toposort( - NodeTreeRef::ToposortDirection::LeftToRight); - - for (const NodeRef *node : toposort_result.sorted_nodes) { - if (node->is_group_input_node()) { - continue; - } - - const FieldInferencingInterface inferencing_interface = get_node_field_inferencing_interface( - *node); - - /* Update field state of input sockets, also taking into account linked origin sockets. */ - for (const InputSocketRef *input_socket : node->inputs()) { - SocketFieldState &state = field_state_by_socket_id[input_socket->id()]; - if (state.is_always_single) { - state.is_single = true; - continue; - } - state.is_single = true; - if (input_socket->directly_linked_sockets().is_empty()) { - if (inferencing_interface.inputs[input_socket->index()] == - InputSocketFieldType::Implicit) { - state.is_single = false; - } - } - else { - for (const OutputSocketRef *origin_socket : input_socket->directly_linked_sockets()) { - if (!field_state_by_socket_id[origin_socket->id()].is_single) { - state.is_single = false; - break; - } - } - } - } - - /* Update field state of output sockets, also taking into account input sockets. */ - for (const OutputSocketRef *output_socket : node->outputs()) { - SocketFieldState &state = field_state_by_socket_id[output_socket->id()]; - const OutputFieldDependency &field_dependency = - inferencing_interface.outputs[output_socket->index()]; - - switch (field_dependency.field_type()) { - case OutputSocketFieldType::None: { - state.is_single = true; - break; - } - case OutputSocketFieldType::FieldSource: { - state.is_single = false; - state.is_field_source = true; - break; - } - case OutputSocketFieldType::PartiallyDependent: - case OutputSocketFieldType::DependentField: { - for (const InputSocketRef *input_socket : - gather_input_socket_dependencies(field_dependency, *node)) { - if (!input_socket->is_available()) { - continue; - } - if (!field_state_by_socket_id[input_socket->id()].is_single) { - state.is_single = false; - break; - } - } - break; - } - } - } - } -} - -static void determine_group_output_states(const NodeTreeRef &tree, - FieldInferencingInterface &new_inferencing_interface, - const Span<SocketFieldState> field_state_by_socket_id) -{ - for (const NodeRef *group_output_node : tree.nodes_by_type("NodeGroupOutput")) { - /* Ignore inactive group output nodes. */ - if (!(group_output_node->bnode()->flag & NODE_DO_OUTPUT)) { - continue; - } - /* Determine dependencies of all group outputs. */ - for (const InputSocketRef *group_output_socket : group_output_node->inputs().drop_back(1)) { - OutputFieldDependency field_dependency = find_group_output_dependencies( - *group_output_socket, field_state_by_socket_id); - new_inferencing_interface.outputs[group_output_socket->index()] = std::move( - field_dependency); - } - break; - } -} - -static void update_socket_shapes(const NodeTreeRef &tree, - const Span<SocketFieldState> field_state_by_socket_id) -{ - const eNodeSocketDisplayShape requires_data_shape = SOCK_DISPLAY_SHAPE_CIRCLE; - const eNodeSocketDisplayShape data_but_can_be_field_shape = SOCK_DISPLAY_SHAPE_DIAMOND_DOT; - const eNodeSocketDisplayShape is_field_shape = SOCK_DISPLAY_SHAPE_DIAMOND; - - auto get_shape_for_state = [&](const SocketFieldState &state) { - if (state.is_always_single) { - return requires_data_shape; - } - if (!state.is_single) { - return is_field_shape; - } - if (state.requires_single) { - return requires_data_shape; - } - return data_but_can_be_field_shape; - }; - - for (const InputSocketRef *socket : tree.input_sockets()) { - bNodeSocket *bsocket = socket->bsocket(); - const SocketFieldState &state = field_state_by_socket_id[socket->id()]; - bsocket->display_shape = get_shape_for_state(state); - } - for (const OutputSocketRef *socket : tree.output_sockets()) { - bNodeSocket *bsocket = socket->bsocket(); - const SocketFieldState &state = field_state_by_socket_id[socket->id()]; - bsocket->display_shape = get_shape_for_state(state); - } -} - -static bool update_field_inferencing(bNodeTree &btree) -{ - using namespace blender::nodes; - if (btree.type != NTREE_GEOMETRY) { - return false; - } - - /* Create new inferencing interface for this node group. */ - FieldInferencingInterface *new_inferencing_interface = new FieldInferencingInterface(); - new_inferencing_interface->inputs.resize(BLI_listbase_count(&btree.inputs), - InputSocketFieldType::IsSupported); - new_inferencing_interface->outputs.resize(BLI_listbase_count(&btree.outputs), - OutputFieldDependency::ForDataSource()); - - /* Create #NodeTreeRef to accelerate various queries on the node tree (e.g. linked sockets). */ - const NodeTreeRef tree{&btree}; - - /* Keep track of the state of all sockets. The index into this array is #SocketRef::id(). */ - Array<SocketFieldState> field_state_by_socket_id(tree.sockets().size()); - - propagate_data_requirements_from_right_to_left(tree, field_state_by_socket_id); - determine_group_input_states(tree, *new_inferencing_interface, field_state_by_socket_id); - propagate_field_status_from_left_to_right(tree, field_state_by_socket_id); - determine_group_output_states(tree, *new_inferencing_interface, field_state_by_socket_id); - update_socket_shapes(tree, field_state_by_socket_id); - - /* Update the previous group interface. */ - const bool group_interface_changed = btree.field_inferencing_interface == nullptr || - *btree.field_inferencing_interface != - *new_inferencing_interface; - delete btree.field_inferencing_interface; - btree.field_inferencing_interface = new_inferencing_interface; - - return group_interface_changed; -} - -} // namespace blender::bke::node_field_inferencing - -void ntreeUpdateAllUsers(Main *main, ID *id, const int tree_update_flag) +void ntreeUpdateAllUsers(Main *main, ID *id) { if (id == nullptr) { return; } + bool need_update = false; + /* Update all users of ngroup, to add/remove sockets as needed. */ FOREACH_NODETREE_BEGIN (main, ntree, owner_id) { - bool need_update = false; - LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { if (node->id == id) { - if (node->typeinfo->group_update_func) { - node->typeinfo->group_update_func(ntree, node); - } - + BKE_ntree_update_tag_node_property(ntree, node); need_update = true; } } - - if (need_update) { - ntree->update |= tree_update_flag; - ntreeUpdateTree(tree_update_flag ? main : nullptr, ntree); - } } FOREACH_NODETREE_END; - - if (GS(id->name) == ID_NT) { - bNodeTree *ngroup = (bNodeTree *)id; - if (ngroup->type == NTREE_GEOMETRY && (ngroup->update & NTREE_UPDATE_GROUP)) { - LISTBASE_FOREACH (Object *, object, &main->objects) { - LISTBASE_FOREACH (ModifierData *, md, &object->modifiers) { - if (md->type == eModifierType_Nodes) { - NodesModifierData *nmd = (NodesModifierData *)md; - if (nmd->node_group == ngroup) { - MOD_nodes_update_interface(object, nmd); - } - } - } - } - } - } -} - -void ntreeUpdateTree(Main *bmain, bNodeTree *ntree) -{ - if (!ntree) { - return; - } - - /* Avoid re-entrant updates, can be caused by RNA update callbacks. */ - if (ntree->is_updating) { - return; - } - ntree->is_updating = true; - - if (ntree->update & (NTREE_UPDATE_LINKS | NTREE_UPDATE_NODES)) { - /* set the bNodeSocket->link pointers */ - ntree_update_link_pointers(ntree); - } - - /* update individual nodes */ - LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { - /* node tree update tags override individual node update flags */ - if ((node->update & NODE_UPDATE) || (ntree->update & NTREE_UPDATE)) { - if (node->typeinfo->updatefunc) { - node->typeinfo->updatefunc(ntree, node); - } - - nodeUpdateInternalLinks(ntree, node); - } - } - - /* generic tree update callback */ - if (ntree->typeinfo->update) { - ntree->typeinfo->update(ntree); - } - /* XXX this should be moved into the tree type update callback for tree supporting node groups. - * Currently the node tree interface is still a generic feature of the base NodeTree type. - */ - if (ntree->update & NTREE_UPDATE_GROUP) { - ntreeInterfaceTypeUpdate(ntree); - } - - int tree_user_update_flag = 0; - - if (ntree->update & NTREE_UPDATE) { - /* If the field interface of this node tree has changed, all node trees using - * this group will need to recalculate their interface as well. */ - if (blender::bke::node_field_inferencing::update_field_inferencing(*ntree)) { - tree_user_update_flag |= NTREE_UPDATE_FIELD_INFERENCING; - } - } - - if (bmain) { - ntreeUpdateAllUsers(bmain, &ntree->id, tree_user_update_flag); - } - - if (ntree->update & (NTREE_UPDATE_LINKS | NTREE_UPDATE_NODES)) { - /* node updates can change sockets or links, repeat link pointer update afterward */ - ntree_update_link_pointers(ntree); - - /* update the node level from link dependencies */ - ntree_update_node_level(ntree); - - /* check link validity */ - ntree_validate_links(ntree); - } - - /* clear update flags */ - LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { - node->update = 0; - } - ntree->update = 0; - - ntree->is_updating = false; -} - -void nodeUpdate(bNodeTree *ntree, bNode *node) -{ - /* Avoid re-entrant updates, can be caused by RNA update callbacks. */ - if (ntree->is_updating) { - return; - } - ntree->is_updating = true; - - if (node->typeinfo->updatefunc) { - node->typeinfo->updatefunc(ntree, node); - } - - nodeUpdateInternalLinks(ntree, node); - - /* clear update flag */ - node->update = 0; - - ntree->is_updating = false; -} - -bool nodeUpdateID(bNodeTree *ntree, ID *id) -{ - bool changed = false; - - if (ELEM(nullptr, id, ntree)) { - return changed; - } - - /* Avoid re-entrant updates, can be caused by RNA update callbacks. */ - if (ntree->is_updating) { - return changed; - } - ntree->is_updating = true; - - LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { - if (node->id == id) { - changed = true; - node->update |= NODE_UPDATE_ID; - if (node->typeinfo->updatefunc) { - node->typeinfo->updatefunc(ntree, node); - } - /* clear update flag */ - node->update = 0; - } - } - - LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { - nodeUpdateInternalLinks(ntree, node); - } - - ntree->is_updating = false; - return changed; -} - -void nodeUpdateInternalLinks(bNodeTree *ntree, bNode *node) -{ - BLI_freelistN(&node->internal_links); - if (!node->typeinfo->no_muting) { - node_internal_links_create(ntree, node); + if (need_update) { + BKE_ntree_update_main(main, nullptr); } } diff --git a/source/blender/blenkernel/intern/node_tree_update.cc b/source/blender/blenkernel/intern/node_tree_update.cc new file mode 100644 index 00000000000..427fac747dc --- /dev/null +++ b/source/blender/blenkernel/intern/node_tree_update.cc @@ -0,0 +1,1658 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "BLI_map.hh" +#include "BLI_multi_value_map.hh" +#include "BLI_noise.hh" +#include "BLI_set.hh" +#include "BLI_stack.hh" +#include "BLI_vector_set.hh" + +#include "DNA_anim_types.h" +#include "DNA_modifier_types.h" +#include "DNA_node_types.h" + +#include "BKE_anim_data.h" +#include "BKE_main.h" +#include "BKE_node.h" +#include "BKE_node_tree_update.h" + +#include "MOD_nodes.h" + +#include "NOD_node_declaration.hh" +#include "NOD_node_tree_ref.hh" + +#include "DEG_depsgraph_query.h" + +using namespace blender::nodes; + +/** + * These flags are used by the `changed_flag` field in #bNodeTree, #bNode and #bNodeSocket. + * This enum is not part of the public api. It should be used through the `BKE_ntree_update_tag_*` + * api. + */ +enum eNodeTreeChangedFlag { + NTREE_CHANGED_NOTHING = 0, + NTREE_CHANGED_ANY = (1 << 1), + NTREE_CHANGED_NODE_PROPERTY = (1 << 2), + NTREE_CHANGED_NODE_OUTPUT = (1 << 3), + NTREE_CHANGED_INTERFACE = (1 << 4), + NTREE_CHANGED_LINK = (1 << 5), + NTREE_CHANGED_REMOVED_NODE = (1 << 6), + NTREE_CHANGED_REMOVED_SOCKET = (1 << 7), + NTREE_CHANGED_SOCKET_PROPERTY = (1 << 8), + NTREE_CHANGED_INTERNAL_LINK = (1 << 9), + NTREE_CHANGED_ALL = -1, +}; + +static void add_tree_tag(bNodeTree *ntree, const eNodeTreeChangedFlag flag) +{ + ntree->changed_flag |= flag; +} + +static void add_node_tag(bNodeTree *ntree, bNode *node, const eNodeTreeChangedFlag flag) +{ + add_tree_tag(ntree, flag); + node->changed_flag |= flag; +} + +static void add_socket_tag(bNodeTree *ntree, bNodeSocket *socket, const eNodeTreeChangedFlag flag) +{ + add_tree_tag(ntree, flag); + socket->changed_flag |= flag; +} + +namespace blender::bke { + +namespace node_field_inferencing { + +static bool is_field_socket_type(eNodeSocketDatatype type) +{ + return ELEM(type, SOCK_FLOAT, SOCK_INT, SOCK_BOOLEAN, SOCK_VECTOR, SOCK_RGBA); +} + +static bool is_field_socket_type(const SocketRef &socket) +{ + return is_field_socket_type((eNodeSocketDatatype)socket.typeinfo()->type); +} + +static InputSocketFieldType get_interface_input_field_type(const NodeRef &node, + const InputSocketRef &socket) +{ + if (!is_field_socket_type(socket)) { + return InputSocketFieldType::None; + } + if (node.is_reroute_node()) { + return InputSocketFieldType::IsSupported; + } + if (node.is_group_output_node()) { + /* Outputs always support fields when the data type is correct. */ + return InputSocketFieldType::IsSupported; + } + if (node.is_undefined()) { + return InputSocketFieldType::None; + } + + const NodeDeclaration *node_decl = node.declaration(); + + /* Node declarations should be implemented for nodes involved here. */ + BLI_assert(node_decl != nullptr); + + /* Get the field type from the declaration. */ + const SocketDeclaration &socket_decl = *node_decl->inputs()[socket.index()]; + const InputSocketFieldType field_type = socket_decl.input_field_type(); + if (field_type == InputSocketFieldType::Implicit) { + return field_type; + } + if (node_decl->is_function_node()) { + /* In a function node, every socket supports fields. */ + return InputSocketFieldType::IsSupported; + } + return field_type; +} + +static OutputFieldDependency get_interface_output_field_dependency(const NodeRef &node, + const OutputSocketRef &socket) +{ + if (!is_field_socket_type(socket)) { + /* Non-field sockets always output data. */ + return OutputFieldDependency::ForDataSource(); + } + if (node.is_reroute_node()) { + /* The reroute just forwards what is passed in. */ + return OutputFieldDependency::ForDependentField(); + } + if (node.is_group_input_node()) { + /* Input nodes get special treatment in #determine_group_input_states. */ + return OutputFieldDependency::ForDependentField(); + } + if (node.is_undefined()) { + return OutputFieldDependency::ForDataSource(); + } + + const NodeDeclaration *node_decl = node.declaration(); + + /* Node declarations should be implemented for nodes involved here. */ + BLI_assert(node_decl != nullptr); + + if (node_decl->is_function_node()) { + /* In a generic function node, all outputs depend on all inputs. */ + return OutputFieldDependency::ForDependentField(); + } + + /* Use the socket declaration. */ + const SocketDeclaration &socket_decl = *node_decl->outputs()[socket.index()]; + return socket_decl.output_field_dependency(); +} + +static FieldInferencingInterface get_dummy_field_inferencing_interface(const NodeRef &node) +{ + FieldInferencingInterface inferencing_interface; + inferencing_interface.inputs.append_n_times(InputSocketFieldType::None, node.inputs().size()); + inferencing_interface.outputs.append_n_times(OutputFieldDependency::ForDataSource(), + node.outputs().size()); + return inferencing_interface; +} + +/** + * Retrieves information about how the node interacts with fields. + * In the future, this information can be stored in the node declaration. This would allow this + * function to return a reference, making it more efficient. + */ +static FieldInferencingInterface get_node_field_inferencing_interface(const NodeRef &node) +{ + /* Node groups already reference all required information, so just return that. */ + if (node.is_group_node()) { + bNodeTree *group = (bNodeTree *)node.bnode()->id; + if (group == nullptr) { + return FieldInferencingInterface(); + } + if (!ntreeIsRegistered(group)) { + /* This can happen when there is a linked node group that was not found (see T92799). */ + return get_dummy_field_inferencing_interface(node); + } + if (group->field_inferencing_interface == nullptr) { + /* This shouldn't happen because referenced node groups should always be updated first. */ + BLI_assert_unreachable(); + } + return *group->field_inferencing_interface; + } + + FieldInferencingInterface inferencing_interface; + for (const InputSocketRef *input_socket : node.inputs()) { + inferencing_interface.inputs.append(get_interface_input_field_type(node, *input_socket)); + } + + for (const OutputSocketRef *output_socket : node.outputs()) { + inferencing_interface.outputs.append( + get_interface_output_field_dependency(node, *output_socket)); + } + return inferencing_interface; +} + +/** + * This struct contains information for every socket. The values are propagated through the + * network. + */ +struct SocketFieldState { + /* This socket starts a new field. */ + bool is_field_source = false; + /* This socket can never become a field, because the node itself does not support it. */ + bool is_always_single = false; + /* This socket is currently a single value. It could become a field though. */ + bool is_single = true; + /* This socket is required to be a single value. This can be because the node itself only + * supports this socket to be a single value, or because a node afterwards requires this to be a + * single value. */ + bool requires_single = false; +}; + +static Vector<const InputSocketRef *> gather_input_socket_dependencies( + const OutputFieldDependency &field_dependency, const NodeRef &node) +{ + const OutputSocketFieldType type = field_dependency.field_type(); + Vector<const InputSocketRef *> input_sockets; + switch (type) { + case OutputSocketFieldType::FieldSource: + case OutputSocketFieldType::None: { + break; + } + case OutputSocketFieldType::DependentField: { + /* This output depends on all inputs. */ + input_sockets.extend(node.inputs()); + break; + } + case OutputSocketFieldType::PartiallyDependent: { + /* This output depends only on a few inputs. */ + for (const int i : field_dependency.linked_input_indices()) { + input_sockets.append(&node.input(i)); + } + break; + } + } + return input_sockets; +} + +/** + * Check what the group output socket depends on. Potentially traverses the node tree + * to figure out if it is always a field or if it depends on any group inputs. + */ +static OutputFieldDependency find_group_output_dependencies( + const InputSocketRef &group_output_socket, + const Span<SocketFieldState> field_state_by_socket_id) +{ + if (!is_field_socket_type(group_output_socket)) { + return OutputFieldDependency::ForDataSource(); + } + + /* Use a Set here instead of an array indexed by socket id, because we my only need to look at + * very few sockets. */ + Set<const InputSocketRef *> handled_sockets; + Stack<const InputSocketRef *> sockets_to_check; + + handled_sockets.add(&group_output_socket); + sockets_to_check.push(&group_output_socket); + + /* Keeps track of group input indices that are (indirectly) connected to the output. */ + Vector<int> linked_input_indices; + + while (!sockets_to_check.is_empty()) { + const InputSocketRef *input_socket = sockets_to_check.pop(); + + for (const OutputSocketRef *origin_socket : input_socket->directly_linked_sockets()) { + const NodeRef &origin_node = origin_socket->node(); + const SocketFieldState &origin_state = field_state_by_socket_id[origin_socket->id()]; + + if (origin_state.is_field_source) { + if (origin_node.is_group_input_node()) { + /* Found a group input that the group output depends on. */ + linked_input_indices.append_non_duplicates(origin_socket->index()); + } + else { + /* Found a field source that is not the group input. So the output is always a field. */ + return OutputFieldDependency::ForFieldSource(); + } + } + else if (!origin_state.is_single) { + const FieldInferencingInterface inferencing_interface = + get_node_field_inferencing_interface(origin_node); + const OutputFieldDependency &field_dependency = + inferencing_interface.outputs[origin_socket->index()]; + + /* Propagate search further to the left. */ + for (const InputSocketRef *origin_input_socket : + gather_input_socket_dependencies(field_dependency, origin_node)) { + if (!origin_input_socket->is_available()) { + continue; + } + if (!field_state_by_socket_id[origin_input_socket->id()].is_single) { + if (handled_sockets.add(origin_input_socket)) { + sockets_to_check.push(origin_input_socket); + } + } + } + } + } + } + return OutputFieldDependency::ForPartiallyDependentField(std::move(linked_input_indices)); +} + +static void propagate_data_requirements_from_right_to_left( + const NodeTreeRef &tree, const MutableSpan<SocketFieldState> field_state_by_socket_id) +{ + const NodeTreeRef::ToposortResult toposort_result = tree.toposort( + NodeTreeRef::ToposortDirection::RightToLeft); + + for (const NodeRef *node : toposort_result.sorted_nodes) { + const FieldInferencingInterface inferencing_interface = get_node_field_inferencing_interface( + *node); + + for (const OutputSocketRef *output_socket : node->outputs()) { + SocketFieldState &state = field_state_by_socket_id[output_socket->id()]; + + const OutputFieldDependency &field_dependency = + inferencing_interface.outputs[output_socket->index()]; + + if (field_dependency.field_type() == OutputSocketFieldType::FieldSource) { + continue; + } + if (field_dependency.field_type() == OutputSocketFieldType::None) { + state.requires_single = true; + state.is_always_single = true; + continue; + } + + /* The output is required to be a single value when it is connected to any input that does + * not support fields. */ + for (const InputSocketRef *target_socket : output_socket->directly_linked_sockets()) { + if (target_socket->is_available()) { + state.requires_single |= field_state_by_socket_id[target_socket->id()].requires_single; + } + } + + if (state.requires_single) { + bool any_input_is_field_implicitly = false; + const Vector<const InputSocketRef *> connected_inputs = gather_input_socket_dependencies( + field_dependency, *node); + for (const InputSocketRef *input_socket : connected_inputs) { + if (!input_socket->is_available()) { + continue; + } + if (inferencing_interface.inputs[input_socket->index()] == + InputSocketFieldType::Implicit) { + if (!input_socket->is_logically_linked()) { + any_input_is_field_implicitly = true; + break; + } + } + } + if (any_input_is_field_implicitly) { + /* This output isn't a single value actually. */ + state.requires_single = false; + } + else { + /* If the output is required to be a single value, the connected inputs in the same node + * must not be fields as well. */ + for (const InputSocketRef *input_socket : connected_inputs) { + field_state_by_socket_id[input_socket->id()].requires_single = true; + } + } + } + } + + /* Some inputs do not require fields independent of what the outputs are connected to. */ + for (const InputSocketRef *input_socket : node->inputs()) { + SocketFieldState &state = field_state_by_socket_id[input_socket->id()]; + if (inferencing_interface.inputs[input_socket->index()] == InputSocketFieldType::None) { + state.requires_single = true; + state.is_always_single = true; + } + } + } +} + +static void determine_group_input_states( + const NodeTreeRef &tree, + FieldInferencingInterface &new_inferencing_interface, + const MutableSpan<SocketFieldState> field_state_by_socket_id) +{ + { + /* Non-field inputs never support fields. */ + int index; + LISTBASE_FOREACH_INDEX (bNodeSocket *, group_input, &tree.btree()->inputs, index) { + if (!is_field_socket_type((eNodeSocketDatatype)group_input->type)) { + new_inferencing_interface.inputs[index] = InputSocketFieldType::None; + } + } + } + /* Check if group inputs are required to be single values, because they are (indirectly) + * connected to some socket that does not support fields. */ + for (const NodeRef *node : tree.nodes_by_type("NodeGroupInput")) { + for (const OutputSocketRef *output_socket : node->outputs().drop_back(1)) { + SocketFieldState &state = field_state_by_socket_id[output_socket->id()]; + if (state.requires_single) { + new_inferencing_interface.inputs[output_socket->index()] = InputSocketFieldType::None; + } + } + } + /* If an input does not support fields, this should be reflected in all Group Input nodes. */ + for (const NodeRef *node : tree.nodes_by_type("NodeGroupInput")) { + for (const OutputSocketRef *output_socket : node->outputs().drop_back(1)) { + SocketFieldState &state = field_state_by_socket_id[output_socket->id()]; + const bool supports_field = new_inferencing_interface.inputs[output_socket->index()] != + InputSocketFieldType::None; + if (supports_field) { + state.is_single = false; + state.is_field_source = true; + } + else { + state.requires_single = true; + } + } + SocketFieldState &dummy_socket_state = field_state_by_socket_id[node->outputs().last()->id()]; + dummy_socket_state.requires_single = true; + } +} + +static void propagate_field_status_from_left_to_right( + const NodeTreeRef &tree, const MutableSpan<SocketFieldState> field_state_by_socket_id) +{ + const NodeTreeRef::ToposortResult toposort_result = tree.toposort( + NodeTreeRef::ToposortDirection::LeftToRight); + + for (const NodeRef *node : toposort_result.sorted_nodes) { + if (node->is_group_input_node()) { + continue; + } + + const FieldInferencingInterface inferencing_interface = get_node_field_inferencing_interface( + *node); + + /* Update field state of input sockets, also taking into account linked origin sockets. */ + for (const InputSocketRef *input_socket : node->inputs()) { + SocketFieldState &state = field_state_by_socket_id[input_socket->id()]; + if (state.is_always_single) { + state.is_single = true; + continue; + } + state.is_single = true; + if (input_socket->directly_linked_sockets().is_empty()) { + if (inferencing_interface.inputs[input_socket->index()] == + InputSocketFieldType::Implicit) { + state.is_single = false; + } + } + else { + for (const OutputSocketRef *origin_socket : input_socket->directly_linked_sockets()) { + if (!field_state_by_socket_id[origin_socket->id()].is_single) { + state.is_single = false; + break; + } + } + } + } + + /* Update field state of output sockets, also taking into account input sockets. */ + for (const OutputSocketRef *output_socket : node->outputs()) { + SocketFieldState &state = field_state_by_socket_id[output_socket->id()]; + const OutputFieldDependency &field_dependency = + inferencing_interface.outputs[output_socket->index()]; + + switch (field_dependency.field_type()) { + case OutputSocketFieldType::None: { + state.is_single = true; + break; + } + case OutputSocketFieldType::FieldSource: { + state.is_single = false; + state.is_field_source = true; + break; + } + case OutputSocketFieldType::PartiallyDependent: + case OutputSocketFieldType::DependentField: { + for (const InputSocketRef *input_socket : + gather_input_socket_dependencies(field_dependency, *node)) { + if (!input_socket->is_available()) { + continue; + } + if (!field_state_by_socket_id[input_socket->id()].is_single) { + state.is_single = false; + break; + } + } + break; + } + } + } + } +} + +static void determine_group_output_states(const NodeTreeRef &tree, + FieldInferencingInterface &new_inferencing_interface, + const Span<SocketFieldState> field_state_by_socket_id) +{ + for (const NodeRef *group_output_node : tree.nodes_by_type("NodeGroupOutput")) { + /* Ignore inactive group output nodes. */ + if (!(group_output_node->bnode()->flag & NODE_DO_OUTPUT)) { + continue; + } + /* Determine dependencies of all group outputs. */ + for (const InputSocketRef *group_output_socket : group_output_node->inputs().drop_back(1)) { + OutputFieldDependency field_dependency = find_group_output_dependencies( + *group_output_socket, field_state_by_socket_id); + new_inferencing_interface.outputs[group_output_socket->index()] = std::move( + field_dependency); + } + break; + } +} + +static void update_socket_shapes(const NodeTreeRef &tree, + const Span<SocketFieldState> field_state_by_socket_id) +{ + const eNodeSocketDisplayShape requires_data_shape = SOCK_DISPLAY_SHAPE_CIRCLE; + const eNodeSocketDisplayShape data_but_can_be_field_shape = SOCK_DISPLAY_SHAPE_DIAMOND_DOT; + const eNodeSocketDisplayShape is_field_shape = SOCK_DISPLAY_SHAPE_DIAMOND; + + auto get_shape_for_state = [&](const SocketFieldState &state) { + if (state.is_always_single) { + return requires_data_shape; + } + if (!state.is_single) { + return is_field_shape; + } + if (state.requires_single) { + return requires_data_shape; + } + return data_but_can_be_field_shape; + }; + + for (const InputSocketRef *socket : tree.input_sockets()) { + bNodeSocket *bsocket = socket->bsocket(); + const SocketFieldState &state = field_state_by_socket_id[socket->id()]; + bsocket->display_shape = get_shape_for_state(state); + } + for (const OutputSocketRef *socket : tree.output_sockets()) { + bNodeSocket *bsocket = socket->bsocket(); + const SocketFieldState &state = field_state_by_socket_id[socket->id()]; + bsocket->display_shape = get_shape_for_state(state); + } +} + +static bool update_field_inferencing(const NodeTreeRef &tree) +{ + bNodeTree &btree = *tree.btree(); + + /* Create new inferencing interface for this node group. */ + FieldInferencingInterface *new_inferencing_interface = new FieldInferencingInterface(); + new_inferencing_interface->inputs.resize(BLI_listbase_count(&btree.inputs), + InputSocketFieldType::IsSupported); + new_inferencing_interface->outputs.resize(BLI_listbase_count(&btree.outputs), + OutputFieldDependency::ForDataSource()); + + /* Keep track of the state of all sockets. The index into this array is #SocketRef::id(). */ + Array<SocketFieldState> field_state_by_socket_id(tree.sockets().size()); + + propagate_data_requirements_from_right_to_left(tree, field_state_by_socket_id); + determine_group_input_states(tree, *new_inferencing_interface, field_state_by_socket_id); + propagate_field_status_from_left_to_right(tree, field_state_by_socket_id); + determine_group_output_states(tree, *new_inferencing_interface, field_state_by_socket_id); + update_socket_shapes(tree, field_state_by_socket_id); + + /* Update the previous group interface. */ + const bool group_interface_changed = btree.field_inferencing_interface == nullptr || + *btree.field_inferencing_interface != + *new_inferencing_interface; + delete btree.field_inferencing_interface; + btree.field_inferencing_interface = new_inferencing_interface; + + return group_interface_changed; +} + +} // namespace node_field_inferencing + +/** + * Common datatype priorities, works for compositor, shader and texture nodes alike + * defines priority of datatype connection based on output type (to): + * `< 0`: never connect these types. + * `>= 0`: priority of connection (higher values chosen first). + */ +static int get_internal_link_type_priority(const bNodeSocketType *from, const bNodeSocketType *to) +{ + switch (to->type) { + case SOCK_RGBA: + switch (from->type) { + case SOCK_RGBA: + return 4; + case SOCK_FLOAT: + return 3; + case SOCK_INT: + return 2; + case SOCK_BOOLEAN: + return 1; + } + return -1; + case SOCK_VECTOR: + switch (from->type) { + case SOCK_VECTOR: + return 4; + case SOCK_FLOAT: + return 3; + case SOCK_INT: + return 2; + case SOCK_BOOLEAN: + return 1; + } + return -1; + case SOCK_FLOAT: + switch (from->type) { + case SOCK_FLOAT: + return 5; + case SOCK_INT: + return 4; + case SOCK_BOOLEAN: + return 3; + case SOCK_RGBA: + return 2; + case SOCK_VECTOR: + return 1; + } + return -1; + case SOCK_INT: + switch (from->type) { + case SOCK_INT: + return 5; + case SOCK_FLOAT: + return 4; + case SOCK_BOOLEAN: + return 3; + case SOCK_RGBA: + return 2; + case SOCK_VECTOR: + return 1; + } + return -1; + case SOCK_BOOLEAN: + switch (from->type) { + case SOCK_BOOLEAN: + return 5; + case SOCK_INT: + return 4; + case SOCK_FLOAT: + return 3; + case SOCK_RGBA: + return 2; + case SOCK_VECTOR: + return 1; + } + return -1; + } + + /* The rest of the socket types only allow an internal link if both the input and output socket + * have the same type. If the sockets are custom, we check the idname instead. */ + if (to->type == from->type && (to->type != SOCK_CUSTOM || STREQ(to->idname, from->idname))) { + return 1; + } + + return -1; +} + +using TreeNodePair = std::pair<bNodeTree *, bNode *>; +using ObjectModifierPair = std::pair<Object *, ModifierData *>; +using NodeSocketPair = std::pair<bNode *, bNodeSocket *>; + +/** + * Cache common data about node trees from the #Main database that is expensive to retrieve on + * demand every time. + */ +struct NodeTreeRelations { + private: + Main *bmain_; + std::optional<Vector<bNodeTree *>> all_trees_; + std::optional<Map<bNodeTree *, ID *>> owner_ids_; + std::optional<MultiValueMap<bNodeTree *, TreeNodePair>> group_node_users_; + std::optional<MultiValueMap<bNodeTree *, ObjectModifierPair>> modifiers_users_; + + public: + NodeTreeRelations(Main *bmain) : bmain_(bmain) + { + } + + void ensure_all_trees() + { + if (all_trees_.has_value()) { + return; + } + all_trees_.emplace(); + owner_ids_.emplace(); + if (bmain_ == nullptr) { + return; + } + + FOREACH_NODETREE_BEGIN (bmain_, ntree, id) { + all_trees_->append(ntree); + if (&ntree->id != id) { + owner_ids_->add_new(ntree, id); + } + } + FOREACH_NODETREE_END; + } + + void ensure_owner_ids() + { + this->ensure_all_trees(); + } + + void ensure_group_node_users() + { + if (group_node_users_.has_value()) { + return; + } + group_node_users_.emplace(); + if (bmain_ == nullptr) { + return; + } + + this->ensure_all_trees(); + + for (bNodeTree *ntree : *all_trees_) { + LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { + if (node->id == nullptr) { + continue; + } + ID *id = node->id; + if (GS(id->name) == ID_NT) { + bNodeTree *group = (bNodeTree *)id; + group_node_users_->add(group, {ntree, node}); + } + } + } + } + + void ensure_modifier_users() + { + if (modifiers_users_.has_value()) { + return; + } + modifiers_users_.emplace(); + if (bmain_ == nullptr) { + return; + } + + LISTBASE_FOREACH (Object *, object, &bmain_->objects) { + LISTBASE_FOREACH (ModifierData *, md, &object->modifiers) { + if (md->type == eModifierType_Nodes) { + NodesModifierData *nmd = (NodesModifierData *)md; + if (nmd->node_group != nullptr) { + modifiers_users_->add(nmd->node_group, {object, md}); + } + } + } + } + } + + Span<ObjectModifierPair> get_modifier_users(bNodeTree *ntree) + { + BLI_assert(modifiers_users_.has_value()); + return modifiers_users_->lookup(ntree); + } + + Span<TreeNodePair> get_group_node_users(bNodeTree *ntree) + { + BLI_assert(group_node_users_.has_value()); + return group_node_users_->lookup(ntree); + } + + ID *get_owner_id(bNodeTree *ntree) + { + BLI_assert(owner_ids_.has_value()); + return owner_ids_->lookup_default(ntree, &ntree->id); + } +}; + +struct TreeUpdateResult { + bool interface_changed = false; + bool output_changed = false; +}; + +class NodeTreeMainUpdater { + private: + Main *bmain_; + NodeTreeUpdateExtraParams *params_; + Map<bNodeTree *, TreeUpdateResult> update_result_by_tree_; + NodeTreeRelations relations_; + + public: + NodeTreeMainUpdater(Main *bmain, NodeTreeUpdateExtraParams *params) + : bmain_(bmain), params_(params), relations_(bmain) + { + } + + void update() + { + Vector<bNodeTree *> changed_ntrees; + FOREACH_NODETREE_BEGIN (bmain_, ntree, id) { + if (ntree->changed_flag != NTREE_CHANGED_NOTHING) { + changed_ntrees.append(ntree); + } + } + FOREACH_NODETREE_END; + this->update_rooted(changed_ntrees); + } + + void update_rooted(Span<bNodeTree *> root_ntrees) + { + if (root_ntrees.is_empty()) { + return; + } + + bool is_single_tree_update = false; + + if (root_ntrees.size() == 1) { + bNodeTree *ntree = root_ntrees[0]; + if (ntree->changed_flag == NTREE_CHANGED_NOTHING) { + return; + } + const TreeUpdateResult result = this->update_tree(*ntree); + update_result_by_tree_.add_new(ntree, result); + if (!result.interface_changed && !result.output_changed) { + is_single_tree_update = true; + } + } + + if (!is_single_tree_update) { + Vector<bNodeTree *> ntrees_in_order = this->get_tree_update_order(root_ntrees); + for (bNodeTree *ntree : ntrees_in_order) { + if (ntree->changed_flag == NTREE_CHANGED_NOTHING) { + continue; + } + if (!update_result_by_tree_.contains(ntree)) { + const TreeUpdateResult result = this->update_tree(*ntree); + update_result_by_tree_.add_new(ntree, result); + } + const TreeUpdateResult result = update_result_by_tree_.lookup(ntree); + Span<TreeNodePair> dependent_trees = relations_.get_group_node_users(ntree); + if (result.output_changed) { + for (const TreeNodePair &pair : dependent_trees) { + add_node_tag(pair.first, pair.second, NTREE_CHANGED_NODE_OUTPUT); + } + } + if (result.interface_changed) { + for (const TreeNodePair &pair : dependent_trees) { + add_node_tag(pair.first, pair.second, NTREE_CHANGED_NODE_PROPERTY); + } + } + } + } + + for (const auto item : update_result_by_tree_.items()) { + bNodeTree *ntree = item.key; + const TreeUpdateResult &result = item.value; + + this->reset_changed_flags(*ntree); + + if (result.interface_changed) { + if (ntree->type == NTREE_GEOMETRY) { + relations_.ensure_modifier_users(); + for (const ObjectModifierPair &pair : relations_.get_modifier_users(ntree)) { + Object *object = pair.first; + ModifierData *md = pair.second; + + if (md->type == eModifierType_Nodes) { + MOD_nodes_update_interface(object, (NodesModifierData *)md); + } + } + } + } + + if (params_) { + relations_.ensure_owner_ids(); + ID *id = relations_.get_owner_id(ntree); + if (params_->tree_changed_fn) { + params_->tree_changed_fn(id, ntree, params_->user_data); + } + if (params_->tree_output_changed_fn && result.output_changed) { + params_->tree_output_changed_fn(id, ntree, params_->user_data); + } + } + } + } + + private: + enum class ToposortMark { + None, + Temporary, + Permanent, + }; + + using ToposortMarkMap = Map<bNodeTree *, ToposortMark>; + + /** + * Finds all trees that depend on the given trees (through node groups). Then those trees are + * ordered such that all trees used by one tree come before it. + */ + Vector<bNodeTree *> get_tree_update_order(Span<bNodeTree *> root_ntrees) + { + relations_.ensure_group_node_users(); + + Set<bNodeTree *> trees_to_update = get_trees_to_update(root_ntrees); + + Vector<bNodeTree *> sorted_ntrees; + + ToposortMarkMap marks; + for (bNodeTree *ntree : trees_to_update) { + marks.add_new(ntree, ToposortMark::None); + } + for (bNodeTree *ntree : trees_to_update) { + if (marks.lookup(ntree) == ToposortMark::None) { + const bool cycle_detected = !this->get_tree_update_order__visit_recursive( + ntree, marks, sorted_ntrees); + /* This should be prevented by higher level operators. */ + BLI_assert(!cycle_detected); + UNUSED_VARS_NDEBUG(cycle_detected); + } + } + + std::reverse(sorted_ntrees.begin(), sorted_ntrees.end()); + + return sorted_ntrees; + } + + bool get_tree_update_order__visit_recursive(bNodeTree *ntree, + ToposortMarkMap &marks, + Vector<bNodeTree *> &sorted_ntrees) + { + ToposortMark &mark = marks.lookup(ntree); + if (mark == ToposortMark::Permanent) { + return true; + } + if (mark == ToposortMark::Temporary) { + /* There is a dependency cycle. */ + return false; + } + + mark = ToposortMark::Temporary; + + for (const TreeNodePair &pair : relations_.get_group_node_users(ntree)) { + this->get_tree_update_order__visit_recursive(pair.first, marks, sorted_ntrees); + } + sorted_ntrees.append(ntree); + + mark = ToposortMark::Permanent; + return true; + } + + Set<bNodeTree *> get_trees_to_update(Span<bNodeTree *> root_ntrees) + { + relations_.ensure_group_node_users(); + + Set<bNodeTree *> reachable_trees; + VectorSet<bNodeTree *> trees_to_check = root_ntrees; + + while (!trees_to_check.is_empty()) { + bNodeTree *ntree = trees_to_check.pop(); + if (reachable_trees.add(ntree)) { + for (const TreeNodePair &pair : relations_.get_group_node_users(ntree)) { + trees_to_check.add(pair.first); + } + } + } + + return reachable_trees; + } + + TreeUpdateResult update_tree(bNodeTree &ntree) + { + TreeUpdateResult result; + + /* Use a #NodeTreeRef to speedup certain queries. It is rebuilt whenever the node tree topology + * changes, which typically happens zero or one times during the entire update of the node + * tree. */ + std::unique_ptr<NodeTreeRef> tree_ref; + this->ensure_tree_ref(ntree, tree_ref); + + this->update_socket_link_and_use(*tree_ref); + this->update_individual_nodes(ntree, tree_ref); + this->update_internal_links(ntree, tree_ref); + this->update_generic_callback(ntree, tree_ref); + this->remove_unused_previews_when_necessary(ntree); + + this->ensure_tree_ref(ntree, tree_ref); + if (ntree.type == NTREE_GEOMETRY) { + if (node_field_inferencing::update_field_inferencing(*tree_ref)) { + result.interface_changed = true; + } + } + + result.output_changed = this->check_if_output_changed(*tree_ref); + + this->update_socket_link_and_use(*tree_ref); + this->update_node_levels(ntree); + this->update_link_validation(ntree); + + if (ntree.type == NTREE_TEXTURE) { + ntreeTexCheckCyclics(&ntree); + } + + if (ntree.changed_flag & NTREE_CHANGED_INTERFACE || ntree.changed_flag & NTREE_CHANGED_ANY) { + result.interface_changed = true; + } + + if (result.interface_changed) { + ntreeInterfaceTypeUpdate(&ntree); + } + + return result; + } + + void ensure_tree_ref(bNodeTree &ntree, std::unique_ptr<NodeTreeRef> &tree_ref) + { + if (!tree_ref) { + tree_ref = std::make_unique<NodeTreeRef>(&ntree); + } + } + + void update_socket_link_and_use(const NodeTreeRef &tree) + { + for (const InputSocketRef *socket : tree.input_sockets()) { + bNodeSocket *bsocket = socket->bsocket(); + if (socket->directly_linked_links().is_empty()) { + bsocket->link = nullptr; + } + else { + bsocket->link = socket->directly_linked_links()[0]->blink(); + } + } + + this->update_socket_used_tags(tree); + } + + void update_socket_used_tags(const NodeTreeRef &tree) + { + for (const SocketRef *socket : tree.sockets()) { + bNodeSocket *bsocket = socket->bsocket(); + bsocket->flag &= ~SOCK_IN_USE; + for (const LinkRef *link : socket->directly_linked_links()) { + if (!link->is_muted()) { + bsocket->flag |= SOCK_IN_USE; + break; + } + } + } + } + + void update_individual_nodes(bNodeTree &ntree, std::unique_ptr<NodeTreeRef> &tree_ref) + { + /* Iterate over nodes instead of #NodeTreeRef, because the #tree_ref might be outdated after + * some update functions. */ + LISTBASE_FOREACH (bNode *, bnode, &ntree.nodes) { + this->ensure_tree_ref(ntree, tree_ref); + const NodeRef &node = *tree_ref->find_node(*bnode); + if (this->should_update_individual_node(node)) { + const uint32_t old_changed_flag = ntree.changed_flag; + ntree.changed_flag = NTREE_CHANGED_NOTHING; + + /* This may set #ntree.changed_flag which is detected below. */ + this->update_individual_node(node); + + if (ntree.changed_flag != NTREE_CHANGED_NOTHING) { + /* The tree ref is outdated and needs to be rebuilt. Generally, only very few update + * functions change the node. Typically zero or one nodes change after an update. */ + tree_ref.reset(); + } + ntree.changed_flag |= old_changed_flag; + } + } + } + + bool should_update_individual_node(const NodeRef &node) + { + bNodeTree &ntree = *node.btree(); + bNode &bnode = *node.bnode(); + if (ntree.changed_flag & NTREE_CHANGED_ANY) { + return true; + } + if (bnode.changed_flag & NTREE_CHANGED_NODE_PROPERTY) { + return true; + } + if (ntree.changed_flag & NTREE_CHANGED_LINK) { + /* Node groups currently always rebuilt their sockets when they are updated. + * So avoid calling the update method when no new link was added to it. */ + if (node.is_group_input_node()) { + if (node.outputs().last()->is_directly_linked()) { + return true; + } + } + else if (node.is_group_output_node()) { + if (node.inputs().last()->is_directly_linked()) { + return true; + } + } + else { + /* Currently we have no way to tell if a node needs to be updated when a link changed. */ + return true; + } + } + if (ntree.changed_flag & NTREE_CHANGED_INTERFACE) { + if (node.is_group_input_node() || node.is_group_output_node()) { + return true; + } + } + return false; + } + + void update_individual_node(const NodeRef &node) + { + bNodeTree &ntree = *node.btree(); + bNode &bnode = *node.bnode(); + bNodeType &ntype = *bnode.typeinfo; + if (ntype.group_update_func) { + ntype.group_update_func(&ntree, &bnode); + } + if (ntype.updatefunc) { + ntype.updatefunc(&ntree, &bnode); + } + } + + void update_internal_links(bNodeTree &ntree, std::unique_ptr<NodeTreeRef> &tree_ref) + { + bool any_internal_links_updated = false; + this->ensure_tree_ref(ntree, tree_ref); + for (const NodeRef *node : tree_ref->nodes()) { + if (!this->should_update_individual_node(*node)) { + continue; + } + /* Find all expected internal links. */ + Vector<std::pair<bNodeSocket *, bNodeSocket *>> expected_internal_links; + for (const OutputSocketRef *output_socket : node->outputs()) { + if (!output_socket->is_available()) { + continue; + } + if (!output_socket->is_directly_linked()) { + continue; + } + if (output_socket->bsocket()->flag & SOCK_NO_INTERNAL_LINK) { + continue; + } + const InputSocketRef *input_socket = this->find_internally_linked_input(output_socket); + if (input_socket != nullptr) { + expected_internal_links.append({input_socket->bsocket(), output_socket->bsocket()}); + } + } + /* rebuilt internal links if they have changed. */ + if (node->internal_links().size() != expected_internal_links.size()) { + this->update_internal_links_in_node(ntree, *node->bnode(), expected_internal_links); + any_internal_links_updated = true; + } + else { + for (auto &item : expected_internal_links) { + const bNodeSocket *from_socket = item.first; + const bNodeSocket *to_socket = item.second; + bool found = false; + for (const InternalLinkRef *internal_link : node->internal_links()) { + if (from_socket == internal_link->from().bsocket() && + to_socket == internal_link->to().bsocket()) { + found = true; + } + } + if (!found) { + this->update_internal_links_in_node(ntree, *node->bnode(), expected_internal_links); + any_internal_links_updated = true; + break; + } + } + } + } + + if (any_internal_links_updated) { + tree_ref.reset(); + } + } + + const InputSocketRef *find_internally_linked_input(const OutputSocketRef *output_socket) + { + const InputSocketRef *selected_socket = nullptr; + int selected_priority = -1; + bool selected_is_linked = false; + for (const InputSocketRef *input_socket : output_socket->node().inputs()) { + if (!input_socket->is_available()) { + continue; + } + if (input_socket->bsocket()->flag & SOCK_NO_INTERNAL_LINK) { + continue; + } + const int priority = get_internal_link_type_priority(input_socket->bsocket()->typeinfo, + output_socket->bsocket()->typeinfo); + if (priority < 0) { + continue; + } + const bool is_linked = input_socket->is_directly_linked(); + const bool is_preferred = priority > selected_priority || (is_linked && !selected_is_linked); + if (!is_preferred) { + continue; + } + selected_socket = input_socket; + selected_priority = priority; + selected_is_linked = is_linked; + } + return selected_socket; + } + + void update_internal_links_in_node(bNodeTree &ntree, + bNode &node, + Span<std::pair<bNodeSocket *, bNodeSocket *>> links) + { + BLI_freelistN(&node.internal_links); + for (const auto &item : links) { + bNodeSocket *from_socket = item.first; + bNodeSocket *to_socket = item.second; + bNodeLink *link = (bNodeLink *)MEM_callocN(sizeof(bNodeLink), __func__); + link->fromnode = &node; + link->fromsock = from_socket; + link->tonode = &node; + link->tosock = to_socket; + link->flag |= NODE_LINK_VALID; + BLI_addtail(&node.internal_links, link); + } + BKE_ntree_update_tag_node_internal_link(&ntree, &node); + } + + void update_generic_callback(bNodeTree &ntree, std::unique_ptr<NodeTreeRef> &tree_ref) + { + if (ntree.typeinfo->update == nullptr) { + return; + } + + /* Reset the changed_flag to allow detecting when the update callback changed the node tree. */ + const uint32_t old_changed_flag = ntree.changed_flag; + ntree.changed_flag = NTREE_CHANGED_NOTHING; + + ntree.typeinfo->update(&ntree); + + if (ntree.changed_flag != NTREE_CHANGED_NOTHING) { + /* The tree ref is outdated and needs to be rebuilt. */ + tree_ref.reset(); + } + ntree.changed_flag |= old_changed_flag; + } + + void remove_unused_previews_when_necessary(bNodeTree &ntree) + { + /* Don't trigger preview removal when only those flags are set. */ + const uint32_t allowed_flags = NTREE_CHANGED_LINK | NTREE_CHANGED_SOCKET_PROPERTY | + NTREE_CHANGED_NODE_PROPERTY | NTREE_CHANGED_NODE_OUTPUT | + NTREE_CHANGED_INTERFACE; + if ((ntree.changed_flag & allowed_flags) == ntree.changed_flag) { + return; + } + BKE_node_preview_remove_unused(&ntree); + } + + void update_node_levels(bNodeTree &ntree) + { + ntreeUpdateNodeLevels(&ntree); + } + + void update_link_validation(bNodeTree &ntree) + { + LISTBASE_FOREACH (bNodeLink *, link, &ntree.links) { + link->flag |= NODE_LINK_VALID; + if (link->fromnode && link->tonode && link->fromnode->level <= link->tonode->level) { + link->flag &= ~NODE_LINK_VALID; + } + else if (ntree.typeinfo->validate_link) { + const eNodeSocketDatatype from_type = static_cast<eNodeSocketDatatype>( + link->fromsock->type); + const eNodeSocketDatatype to_type = static_cast<eNodeSocketDatatype>(link->tosock->type); + if (!ntree.typeinfo->validate_link(from_type, to_type)) { + link->flag &= ~NODE_LINK_VALID; + } + } + } + } + + bool check_if_output_changed(const NodeTreeRef &tree) + { + bNodeTree &btree = *tree.btree(); + + /* Compute a hash that represents the node topology connected to the output. This always has to + * be updated even if it is not used to detect changes right now. Otherwise + * #btree.output_topology_hash will go out of date. */ + const Vector<const SocketRef *> tree_output_sockets = this->find_output_sockets(tree); + const uint32_t old_topology_hash = btree.output_topology_hash; + const uint32_t new_topology_hash = this->get_combined_socket_topology_hash( + tree, tree_output_sockets); + btree.output_topology_hash = new_topology_hash; + + if (const AnimData *adt = BKE_animdata_from_id(&btree.id)) { + /* Drivers may copy values in the node tree around arbitrarily and may cause the output to + * change even if it wouldn't without drivers. Only some special drivers like `frame/5` can + * be used without causing updates all the time currently. In the future we could try to + * handle other drivers better as well. + * Note that this optimization only works in practice when the depsgraph didn't also get a + * copy-on-write tag for the node tree (which happens when changing node properties). It does + * work in a few situations like adding reroutes and duplicating nodes though. */ + LISTBASE_FOREACH (const FCurve *, fcurve, &adt->drivers) { + const ChannelDriver *driver = fcurve->driver; + const StringRef expression = driver->expression; + if (expression.startswith("frame")) { + const StringRef remaining_expression = expression.drop_known_prefix("frame"); + if (remaining_expression.find_first_not_of(" */+-0123456789.") == StringRef::not_found) { + continue; + } + } + /* Unrecognized driver, assume that the output always changes. */ + return true; + } + } + + if (btree.changed_flag & NTREE_CHANGED_ANY) { + return true; + } + + if (old_topology_hash != new_topology_hash) { + return true; + } + + /* The topology hash can only be used when only topology-changing operations have been done. */ + if (btree.changed_flag == + (btree.changed_flag & (NTREE_CHANGED_LINK | NTREE_CHANGED_REMOVED_NODE))) { + if (old_topology_hash == new_topology_hash) { + return false; + } + } + + if (!this->check_if_socket_outputs_changed_based_on_flags(tree, tree_output_sockets)) { + return false; + } + + return true; + } + + Vector<const SocketRef *> find_output_sockets(const NodeTreeRef &tree) + { + Vector<const SocketRef *> sockets; + for (const NodeRef *node : tree.nodes()) { + const bNode *bnode = node->bnode(); + if (bnode->typeinfo->nclass != NODE_CLASS_OUTPUT && bnode->type != NODE_GROUP_OUTPUT) { + continue; + } + for (const InputSocketRef *socket : node->inputs()) { + if (socket->idname() != "NodeSocketVirtual") { + sockets.append(socket); + } + } + } + return sockets; + } + + /** + * Computes a hash that changes when the node tree topology connected to an output node changes. + * Adding reroutes does not have an effect on the hash. + */ + uint32_t get_combined_socket_topology_hash(const NodeTreeRef &tree, + Span<const SocketRef *> sockets) + { + Array<uint32_t> hashes = this->get_socket_topology_hashes(tree, sockets); + uint32_t combined_hash = 0; + for (uint32_t hash : hashes) { + combined_hash = noise::hash(combined_hash, hash); + } + return combined_hash; + } + + Array<uint32_t> get_socket_topology_hashes(const NodeTreeRef &tree, + Span<const SocketRef *> sockets) + { + Array<std::optional<uint32_t>> hash_by_socket_id(tree.sockets().size()); + Stack<const SocketRef *> sockets_to_check = sockets; + + while (!sockets_to_check.is_empty()) { + const SocketRef &in_out_socket = *sockets_to_check.peek(); + const NodeRef &node = in_out_socket.node(); + + if (hash_by_socket_id[in_out_socket.id()].has_value()) { + sockets_to_check.pop(); + /* Socket is handled already. */ + continue; + } + + if (in_out_socket.is_input()) { + /* For input sockets, first compute the hashes of all linked sockets. */ + const InputSocketRef &socket = in_out_socket.as_input(); + bool all_origins_computed = true; + for (const OutputSocketRef *origin_socket : socket.logically_linked_sockets()) { + if (!hash_by_socket_id[origin_socket->id()].has_value()) { + sockets_to_check.push(origin_socket); + all_origins_computed = false; + } + } + if (!all_origins_computed) { + continue; + } + /* When the hashes for the linked sockets are ready, combine them into a hash for the input + * socket. */ + const uint64_t socket_ptr = (uintptr_t)socket.bsocket(); + uint32_t socket_hash = noise::hash(socket_ptr, socket_ptr >> 32); + for (const OutputSocketRef *origin_socket : socket.logically_linked_sockets()) { + const uint32_t origin_socket_hash = *hash_by_socket_id[origin_socket->id()]; + socket_hash = noise::hash(socket_hash, origin_socket_hash); + } + hash_by_socket_id[socket.id()] = socket_hash; + sockets_to_check.pop(); + } + else { + /* For output sockets, first compute the hashes of all available input sockets. */ + const OutputSocketRef &socket = in_out_socket.as_output(); + bool all_available_inputs_computed = true; + for (const InputSocketRef *input_socket : node.inputs()) { + if (input_socket->is_available()) { + if (!hash_by_socket_id[input_socket->id()].has_value()) { + sockets_to_check.push(input_socket); + all_available_inputs_computed = false; + } + } + } + if (!all_available_inputs_computed) { + continue; + } + /* When all input socket hashes have been computed, combine them into a hash for the output + * socket. */ + const uint64_t socket_ptr = (uintptr_t)socket.bsocket(); + uint32_t socket_hash = noise::hash(socket_ptr, socket_ptr >> 32); + for (const InputSocketRef *input_socket : node.inputs()) { + if (input_socket->is_available()) { + const uint32_t input_socket_hash = *hash_by_socket_id[input_socket->id()]; + socket_hash = noise::hash(socket_hash, input_socket_hash); + } + } + hash_by_socket_id[socket.id()] = socket_hash; + sockets_to_check.pop(); + } + } + + /* Create output array. */ + Array<uint32_t> hashes(sockets.size()); + for (const int i : sockets.index_range()) { + hashes[i] = *hash_by_socket_id[sockets[i]->id()]; + } + return hashes; + } + + /** + * Returns true when any of the provided sockets changed its values. A change is detected by + * checking the #changed_flag on connected sockets and nodes. + */ + bool check_if_socket_outputs_changed_based_on_flags(const NodeTreeRef &tree, + Span<const SocketRef *> sockets) + { + /* Avoid visiting the same socket twice when multiple links point to the same socket. */ + Array<bool> pushed_by_socket_id(tree.sockets().size(), false); + Stack<const SocketRef *> sockets_to_check = sockets; + + for (const SocketRef *socket : sockets) { + pushed_by_socket_id[socket->id()] = true; + } + + while (!sockets_to_check.is_empty()) { + const SocketRef &in_out_socket = *sockets_to_check.pop(); + const bNode &bnode = *in_out_socket.node().bnode(); + const bNodeSocket &bsocket = *in_out_socket.bsocket(); + if (bsocket.changed_flag != NTREE_CHANGED_NOTHING) { + return true; + } + if (bnode.changed_flag != NTREE_CHANGED_NOTHING) { + const bool only_unused_internal_link_changed = (bnode.flag & NODE_MUTED) == 0 && + bnode.changed_flag == + NTREE_CHANGED_INTERNAL_LINK; + if (!only_unused_internal_link_changed) { + return true; + } + } + if (in_out_socket.is_input()) { + const InputSocketRef &socket = in_out_socket.as_input(); + for (const OutputSocketRef *origin_socket : socket.logically_linked_sockets()) { + bool &pushed = pushed_by_socket_id[origin_socket->id()]; + if (!pushed) { + sockets_to_check.push(origin_socket); + pushed = true; + } + } + } + else { + const OutputSocketRef &socket = in_out_socket.as_output(); + for (const InputSocketRef *input_socket : socket.node().inputs()) { + if (input_socket->is_available()) { + bool &pushed = pushed_by_socket_id[input_socket->id()]; + if (!pushed) { + sockets_to_check.push(input_socket); + pushed = true; + } + } + } + } + } + return false; + } + + void reset_changed_flags(bNodeTree &ntree) + { + ntree.changed_flag = NTREE_CHANGED_NOTHING; + LISTBASE_FOREACH (bNode *, node, &ntree.nodes) { + node->changed_flag = NTREE_CHANGED_NOTHING; + node->update = 0; + LISTBASE_FOREACH (bNodeSocket *, socket, &node->inputs) { + socket->changed_flag = NTREE_CHANGED_NOTHING; + } + LISTBASE_FOREACH (bNodeSocket *, socket, &node->outputs) { + socket->changed_flag = NTREE_CHANGED_NOTHING; + } + } + } +}; + +} // namespace blender::bke + +void BKE_ntree_update_tag_all(bNodeTree *ntree) +{ + add_tree_tag(ntree, NTREE_CHANGED_ANY); +} + +void BKE_ntree_update_tag_node_property(bNodeTree *ntree, bNode *node) +{ + add_node_tag(ntree, node, NTREE_CHANGED_NODE_PROPERTY); +} + +void BKE_ntree_update_tag_node_new(bNodeTree *ntree, bNode *node) +{ + add_node_tag(ntree, node, NTREE_CHANGED_NODE_PROPERTY); +} + +void BKE_ntree_update_tag_socket_property(bNodeTree *ntree, bNodeSocket *socket) +{ + add_socket_tag(ntree, socket, NTREE_CHANGED_SOCKET_PROPERTY); +} + +void BKE_ntree_update_tag_socket_new(bNodeTree *ntree, bNodeSocket *socket) +{ + add_socket_tag(ntree, socket, NTREE_CHANGED_SOCKET_PROPERTY); +} + +void BKE_ntree_update_tag_socket_removed(bNodeTree *ntree) +{ + add_tree_tag(ntree, NTREE_CHANGED_REMOVED_SOCKET); +} + +void BKE_ntree_update_tag_socket_type(bNodeTree *ntree, bNodeSocket *socket) +{ + add_socket_tag(ntree, socket, NTREE_CHANGED_SOCKET_PROPERTY); +} + +void BKE_ntree_update_tag_socket_availability(bNodeTree *ntree, bNodeSocket *socket) +{ + add_socket_tag(ntree, socket, NTREE_CHANGED_SOCKET_PROPERTY); +} + +void BKE_ntree_update_tag_node_removed(bNodeTree *ntree) +{ + add_tree_tag(ntree, NTREE_CHANGED_REMOVED_NODE); +} + +void BKE_ntree_update_tag_node_internal_link(bNodeTree *ntree, bNode *node) +{ + add_node_tag(ntree, node, NTREE_CHANGED_INTERNAL_LINK); +} + +void BKE_ntree_update_tag_link_changed(bNodeTree *ntree) +{ + add_tree_tag(ntree, NTREE_CHANGED_LINK); +} + +void BKE_ntree_update_tag_link_removed(bNodeTree *ntree) +{ + add_tree_tag(ntree, NTREE_CHANGED_LINK); +} + +void BKE_ntree_update_tag_link_added(bNodeTree *ntree, bNodeLink *UNUSED(link)) +{ + add_tree_tag(ntree, NTREE_CHANGED_LINK); +} + +void BKE_ntree_update_tag_link_mute(bNodeTree *ntree, bNodeLink *UNUSED(link)) +{ + add_tree_tag(ntree, NTREE_CHANGED_LINK); +} + +void BKE_ntree_update_tag_missing_runtime_data(bNodeTree *ntree) +{ + add_tree_tag(ntree, NTREE_CHANGED_ALL); +} + +void BKE_ntree_update_tag_interface(bNodeTree *ntree) +{ + add_tree_tag(ntree, NTREE_CHANGED_INTERFACE); +} + +void BKE_ntree_update_tag_id_changed(Main *bmain, ID *id) +{ + FOREACH_NODETREE_BEGIN (bmain, ntree, ntree_id) { + LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { + if (node->id == id) { + node->update |= NODE_UPDATE_ID; + add_node_tag(ntree, node, NTREE_CHANGED_NODE_PROPERTY); + } + } + } + FOREACH_NODETREE_END; +} + +/** + * Protect from recursive calls into the updating function. Some node update functions might + * trigger this from Python or in other cases. + * + * This could be added to #Main, but given that there is generally only one #Main, that's not + * really worth it now. + */ +static bool is_updating = false; + +void BKE_ntree_update_main(Main *bmain, NodeTreeUpdateExtraParams *params) +{ + if (is_updating) { + return; + } + + is_updating = true; + blender::bke::NodeTreeMainUpdater updater{bmain, params}; + updater.update(); + is_updating = false; +} + +void BKE_ntree_update_main_tree(Main *bmain, bNodeTree *ntree, NodeTreeUpdateExtraParams *params) +{ + if (ntree == nullptr) { + BKE_ntree_update_main(bmain, params); + return; + } + + if (is_updating) { + return; + } + + is_updating = true; + blender::bke::NodeTreeMainUpdater updater{bmain, params}; + updater.update_rooted({ntree}); + is_updating = false; +} diff --git a/source/blender/blenkernel/intern/spline_nurbs.cc b/source/blender/blenkernel/intern/spline_nurbs.cc index 69afb82baa8..e9d4ba7c7ef 100644 --- a/source/blender/blenkernel/intern/spline_nurbs.cc +++ b/source/blender/blenkernel/intern/spline_nurbs.cc @@ -257,13 +257,13 @@ void NURBSpline::calculate_knots() const Span<float> NURBSpline::knots() const { if (!knots_dirty_) { - BLI_assert(knots_.size() == this->size() + order_); + BLI_assert(knots_.size() == this->knots_size()); return knots_; } std::lock_guard lock{knots_mutex_}; if (!knots_dirty_) { - BLI_assert(knots_.size() == this->size() + order_); + BLI_assert(knots_.size() == this->knots_size()); return knots_; } diff --git a/source/blender/blenkernel/intern/volume.cc b/source/blender/blenkernel/intern/volume.cc index 130aa957491..63807f90334 100644 --- a/source/blender/blenkernel/intern/volume.cc +++ b/source/blender/blenkernel/intern/volume.cc @@ -537,7 +537,7 @@ static void volume_copy_data(Main *UNUSED(bmain), #ifdef WITH_OPENVDB if (volume_src->runtime.grids) { const VolumeGridVector &grids_src = *(volume_src->runtime.grids); - volume_dst->runtime.grids = OBJECT_GUARDED_NEW(VolumeGridVector, grids_src); + volume_dst->runtime.grids = MEM_new<VolumeGridVector>(__func__, grids_src); } #endif @@ -551,7 +551,8 @@ static void volume_free_data(ID *id) BKE_volume_batch_cache_free(volume); MEM_SAFE_FREE(volume->mat); #ifdef WITH_OPENVDB - OBJECT_GUARDED_SAFE_DELETE(volume->runtime.grids, VolumeGridVector); + MEM_delete(volume->runtime.grids); + volume->runtime.grids = nullptr; #endif } @@ -683,7 +684,7 @@ void BKE_volume_init_grids(Volume *volume) { #ifdef WITH_OPENVDB if (volume->runtime.grids == nullptr) { - volume->runtime.grids = OBJECT_GUARDED_NEW(VolumeGridVector); + volume->runtime.grids = MEM_new<VolumeGridVector>(__func__); } #else UNUSED_VARS(volume); @@ -1129,16 +1130,16 @@ void BKE_volume_grids_backup_restore(Volume *volume, VolumeGridVector *grids, co if (!grids->is_loaded()) { /* No grids loaded in CoW datablock, nothing lost by discarding. */ - OBJECT_GUARDED_DELETE(grids, VolumeGridVector); + MEM_delete(grids); } else if (!STREQ(volume->filepath, filepath)) { /* Filepath changed, discard grids from CoW datablock. */ - OBJECT_GUARDED_DELETE(grids, VolumeGridVector); + MEM_delete(grids); } else { /* Keep grids from CoW datablock. We might still unload them a little * later in BKE_volume_eval_geometry if the frame changes. */ - OBJECT_GUARDED_DELETE(volume->runtime.grids, VolumeGridVector); + MEM_delete(volume->runtime.grids); volume->runtime.grids = grids; } #else diff --git a/source/blender/blenlib/BLI_assert.h b/source/blender/blenlib/BLI_assert.h index 6019f0f3566..e32a19cfc25 100644 --- a/source/blender/blenlib/BLI_assert.h +++ b/source/blender/blenlib/BLI_assert.h @@ -30,6 +30,7 @@ extern "C" { #endif /* Utility functions. */ + void _BLI_assert_print_pos(const char *file, const int line, const char *function, const char *id); void _BLI_assert_print_extra(const char *str); void _BLI_assert_print_backtrace(void); diff --git a/source/blender/blenlib/BLI_color.hh b/source/blender/blenlib/BLI_color.hh index d93bd7f6f76..dce625777b9 100644 --- a/source/blender/blenlib/BLI_color.hh +++ b/source/blender/blenlib/BLI_color.hh @@ -73,27 +73,27 @@ namespace blender { * - Add non RGB spaces/storages ColorXyz. */ -/* Enumeration containing the different alpha modes. */ +/** Enumeration containing the different alpha modes. */ enum class eAlpha { - /* Color and alpha are unassociated. */ + /** Color and alpha are unassociated. */ Straight, - /* Color and alpha are associated. */ + /** Color and alpha are associated. */ Premultiplied, }; std::ostream &operator<<(std::ostream &stream, const eAlpha &space); -/* Enumeration containing internal spaces. */ +/** Enumeration containing internal spaces. */ enum class eSpace { - /* Blender theme color space (sRGB). */ + /** Blender theme color space (sRGB). */ Theme, - /* Blender internal scene linear color space (maps to SceneReference role in OCIO). */ + /** Blender internal scene linear color space (maps to SceneReference role in OCIO). */ SceneLinear, - /* Blender internal scene linear color space compressed to be stored in 4 uint8_t. */ + /** Blender internal scene linear color space compressed to be stored in 4 uint8_t. */ SceneLinearByteEncoded, }; std::ostream &operator<<(std::ostream &stream, const eSpace &space); -/* Template class to store RGBA values with different precision, space and alpha association. */ +/** Template class to store RGBA values with different precision, space and alpha association. */ template<typename ChannelStorageType, eSpace Space, eAlpha Alpha> class ColorRGBA { public: ChannelStorageType r, g, b, a; @@ -153,11 +153,13 @@ template<typename ChannelStorageType, eSpace Space, eAlpha Alpha> class ColorRGB }; /* Forward declarations of concrete color classes. */ + template<eAlpha Alpha> class ColorSceneLinear4f; template<eAlpha Alpha> class ColorSceneLinearByteEncoded4b; template<typename ChannelStorageType> class ColorTheme4; /* Forward declaration of precision conversion methods. */ + BLI_INLINE ColorTheme4<float> BLI_color_convert_to_theme4f(const ColorTheme4<uint8_t> &srgb4b); BLI_INLINE ColorTheme4<uint8_t> BLI_color_convert_to_theme4b(const ColorTheme4<float> &srgb4f); @@ -354,6 +356,7 @@ BLI_color_convert_to_theme4b(const ColorSceneLinear4f<eAlpha::Straight> &scene_l } /* Internal roles. For convenience to shorten the type names and hide complexity. */ + using ColorGeometry4f = ColorSceneLinear4f<eAlpha::Premultiplied>; using ColorGeometry4b = ColorSceneLinearByteEncoded4b<eAlpha::Premultiplied>; diff --git a/source/blender/blenlib/BLI_compiler_compat.h b/source/blender/blenlib/BLI_compiler_compat.h index bd8f84cedd6..023fea3853e 100644 --- a/source/blender/blenlib/BLI_compiler_compat.h +++ b/source/blender/blenlib/BLI_compiler_compat.h @@ -34,7 +34,7 @@ #if (defined(__GNUC__) || defined(__clang__)) && defined(__cplusplus) extern "C++" { -/* Some magic to be sure we don't have reference in the type. */ +/** Some magic to be sure we don't have reference in the type. */ template<typename T> static inline T decltype_helper(T x) { return x; diff --git a/source/blender/blenlib/BLI_delaunay_2d.h b/source/blender/blenlib/BLI_delaunay_2d.h index 658dcadadce..db0df95499f 100644 --- a/source/blender/blenlib/BLI_delaunay_2d.h +++ b/source/blender/blenlib/BLI_delaunay_2d.h @@ -222,7 +222,7 @@ void BLI_delaunay_2d_cdt_free(CDT_result *result); namespace blender::meshintersect { -/* vec2<Arith_t> is a 2d vector with Arith_t as the type for coordinates. */ +/** #vec2<Arith_t> is a 2d vector with #Arith_t as the type for coordinates. */ template<typename Arith_t> struct vec2_impl; template<> struct vec2_impl<double> { typedef double2 type; diff --git a/source/blender/blenlib/BLI_dlrbTree.h b/source/blender/blenlib/BLI_dlrbTree.h index 72f18244d5b..3cf849efaef 100644 --- a/source/blender/blenlib/BLI_dlrbTree.h +++ b/source/blender/blenlib/BLI_dlrbTree.h @@ -40,7 +40,7 @@ extern "C" { /** \name Base Structs * \{ */ -/* Basic Layout for a Node */ +/** Basic Layout for a Node. */ typedef struct DLRBT_Node { /* ListBase capabilities */ struct DLRBT_Node *next, *prev; @@ -53,7 +53,7 @@ typedef struct DLRBT_Node { /* ... for nice alignment, next item should usually be a char too... */ } DLRBT_Node; -/* Red/Black defines for tree_col */ +/** Red/Black defines for tree_col. */ typedef enum eDLRBT_Colors { DLRBT_BLACK = 0, DLRBT_RED, @@ -61,7 +61,7 @@ typedef enum eDLRBT_Colors { /* -------- */ -/* The Tree Data */ +/** The Tree Data. */ typedef struct DLRBT_Tree { /* ListBase capabilities */ void *first, *last; /* these should be based on DLRBT_Node-s */ diff --git a/source/blender/blenlib/BLI_endian_switch.h b/source/blender/blenlib/BLI_endian_switch.h index b512133b34c..e6ccbe4629a 100644 --- a/source/blender/blenlib/BLI_endian_switch.h +++ b/source/blender/blenlib/BLI_endian_switch.h @@ -30,6 +30,7 @@ extern "C" { #endif /* BLI_endian_switch_inline.h */ + BLI_INLINE void BLI_endian_switch_int16(short *val) ATTR_NONNULL(1); BLI_INLINE void BLI_endian_switch_uint16(unsigned short *val) ATTR_NONNULL(1); BLI_INLINE void BLI_endian_switch_int32(int *val) ATTR_NONNULL(1); @@ -40,6 +41,7 @@ BLI_INLINE void BLI_endian_switch_uint64(uint64_t *val) ATTR_NONNULL(1); BLI_INLINE void BLI_endian_switch_double(double *val) ATTR_NONNULL(1); /* endian_switch.c */ + void BLI_endian_switch_int16_array(short *val, const int size) ATTR_NONNULL(1); void BLI_endian_switch_uint16_array(unsigned short *val, const int size) ATTR_NONNULL(1); void BLI_endian_switch_int32_array(int *val, const int size) ATTR_NONNULL(1); diff --git a/source/blender/blenlib/BLI_endian_switch_inline.h b/source/blender/blenlib/BLI_endian_switch_inline.h index ec4cfe4801a..31be7fd47e4 100644 --- a/source/blender/blenlib/BLI_endian_switch_inline.h +++ b/source/blender/blenlib/BLI_endian_switch_inline.h @@ -33,6 +33,7 @@ extern "C" { * use bit shifting instead. */ /* *** 16 *** */ + BLI_INLINE void BLI_endian_switch_int16(short *val) { BLI_endian_switch_uint16((unsigned short *)val); @@ -48,6 +49,7 @@ BLI_INLINE void BLI_endian_switch_uint16(unsigned short *val) } /* *** 32 *** */ + BLI_INLINE void BLI_endian_switch_int32(int *val) { BLI_endian_switch_uint32((unsigned int *)val); @@ -67,6 +69,7 @@ BLI_INLINE void BLI_endian_switch_float(float *val) } /* *** 64 *** */ + BLI_INLINE void BLI_endian_switch_int64(int64_t *val) { BLI_endian_switch_uint64((uint64_t *)val); diff --git a/source/blender/blenlib/BLI_fileops.hh b/source/blender/blenlib/BLI_fileops.hh new file mode 100644 index 00000000000..c69b1983c59 --- /dev/null +++ b/source/blender/blenlib/BLI_fileops.hh @@ -0,0 +1,52 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup bli + * \brief File and directory operations. + */ + +#pragma once + +#ifndef __cplusplus +# error This is a C++ header +#endif + +#include "BLI_fileops.h" +#include "BLI_string_ref.hh" + +#include <fstream> +#include <string> + +namespace blender { + +/** + * std::fstream subclass that handles UTF-16 encoding on Windows. + * + * For documentation, see https://en.cppreference.com/w/cpp/io/basic_fstream + */ +class fstream : public std::fstream { + public: + fstream() = default; + explicit fstream(const char *filepath, + std::ios_base::openmode mode = ios_base::in | ios_base::out); + explicit fstream(const std::string &filepath, + std::ios_base::openmode mode = ios_base::in | ios_base::out); + + void open(StringRefNull filepath, ios_base::openmode mode = ios_base::in | ios_base::out); +}; + +} // namespace blender diff --git a/source/blender/blenlib/BLI_filereader.h b/source/blender/blenlib/BLI_filereader.h index da223cddf40..f232ad72cc4 100644 --- a/source/blender/blenlib/BLI_filereader.h +++ b/source/blender/blenlib/BLI_filereader.h @@ -47,7 +47,7 @@ typedef ssize_t (*FileReaderReadFn)(struct FileReader *reader, void *buffer, siz typedef off64_t (*FileReaderSeekFn)(struct FileReader *reader, off64_t offset, int whence); typedef void (*FileReaderCloseFn)(struct FileReader *reader); -/* General structure for all FileReaders, implementations add custom fields at the end. */ +/** General structure for all #FileReaders, implementations add custom fields at the end. */ typedef struct FileReader { FileReaderReadFn read; FileReaderSeekFn seek; @@ -64,16 +64,16 @@ typedef struct FileReader { * take over the base FileReader and will clean it up when their clean() is called. */ -/* Create FileReader from raw file descriptor. */ +/** Create #FileReader from raw file descriptor. */ FileReader *BLI_filereader_new_file(int filedes) ATTR_WARN_UNUSED_RESULT; -/* Create FileReader from raw file descriptor using memory-mapped IO. */ +/** Create #FileReader from raw file descriptor using memory-mapped IO. */ FileReader *BLI_filereader_new_mmap(int filedes) ATTR_WARN_UNUSED_RESULT; -/* Create FileReader from a region of memory. */ +/** Create #FileReader from a region of memory. */ FileReader *BLI_filereader_new_memory(const void *data, size_t len) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); -/* Create FileReader from applying `Zstd` decompression on an underlying file. */ +/** Create #FileReader from applying `Zstd` decompression on an underlying file. */ FileReader *BLI_filereader_new_zstd(FileReader *base) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); -/* Create FileReader from applying `Gzip` decompression on an underlying file. */ +/** Create #FileReader from applying `Gzip` decompression on an underlying file. */ FileReader *BLI_filereader_new_gzip(FileReader *base) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); #ifdef __cplusplus diff --git a/source/blender/blenlib/BLI_ghash.h b/source/blender/blenlib/BLI_ghash.h index 0194f9aad40..fca705535a3 100644 --- a/source/blender/blenlib/BLI_ghash.h +++ b/source/blender/blenlib/BLI_ghash.h @@ -450,7 +450,7 @@ void *BLI_gset_pop_key(GSet *gs, const void *key) ATTR_WARN_UNUSED_RESULT; /* rely on inline api for now */ -/* so we can cast but compiler sees as different */ +/** Use a GSet specific type so we can cast but compiler sees as different */ typedef struct GSetIterator { GHashIterator _ghi #if defined(__GNUC__) && !defined(__clang__) diff --git a/source/blender/blenlib/BLI_kdtree_impl.h b/source/blender/blenlib/BLI_kdtree_impl.h index 26a22fc2ac4..35dbf141c92 100644 --- a/source/blender/blenlib/BLI_kdtree_impl.h +++ b/source/blender/blenlib/BLI_kdtree_impl.h @@ -74,7 +74,7 @@ int BLI_kdtree_nd_(calc_duplicates_fast)(const KDTree *tree, int BLI_kdtree_nd_(deduplicate)(KDTree *tree); -/* Versions of find/range search that take a squared distance callback to support bias. */ +/** Versions of find/range search that take a squared distance callback to support bias. */ int BLI_kdtree_nd_(find_nearest_n_with_len_squared_cb)( const KDTree *tree, const float co[KD_DIMS], diff --git a/source/blender/blenlib/BLI_linklist_lockfree.h b/source/blender/blenlib/BLI_linklist_lockfree.h index 142fa1cf243..d2083bdd44e 100644 --- a/source/blender/blenlib/BLI_linklist_lockfree.h +++ b/source/blender/blenlib/BLI_linklist_lockfree.h @@ -48,18 +48,19 @@ typedef void (*LockfreeeLinkNodeFreeFP)(void *link); /* NOTE: These functions are NOT safe for use from threads. */ /* NOTE: !!! I REPEAT: DO NOT USE THEM WITHOUT EXTERNAL LOCK !!! */ -/* Make list ready for lock-free access. */ +/** Make list ready for lock-free access. */ void BLI_linklist_lockfree_init(LockfreeLinkList *list); -/* Completely free the whole list, it is NOT re-usable after this. */ +/** Completely free the whole list, it is NOT re-usable after this. */ void BLI_linklist_lockfree_free(LockfreeLinkList *list, LockfreeeLinkNodeFreeFP free_func); -/* Remove all the elements from the list, keep it usable for further - * inserts. +/** + * Remove all the elements from the list, keep it usable for further inserts. */ void BLI_linklist_lockfree_clear(LockfreeLinkList *list, LockfreeeLinkNodeFreeFP free_func); -/* Begin iteration of lock-free linked list, starting with a +/** + * Begin iteration of lock-free linked list, starting with a * first user=defined node. Will ignore the dummy node. */ LockfreeLinkNode *BLI_linklist_lockfree_begin(LockfreeLinkList *list); diff --git a/source/blender/blenlib/BLI_listbase.h b/source/blender/blenlib/BLI_listbase.h index 7d808d339e9..a2a6e958213 100644 --- a/source/blender/blenlib/BLI_listbase.h +++ b/source/blender/blenlib/BLI_listbase.h @@ -46,6 +46,11 @@ int BLI_findstringindex(const struct ListBase *listbase, const char *id, const int offset) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1); +/** + * Return a ListBase representing the entire list the given Link is in. + */ +ListBase BLI_listbase_from_link(struct Link *some_link); + /* Find forwards. */ /** @@ -279,6 +284,23 @@ BLI_INLINE void BLI_listbase_clear(struct ListBase *lb) } /** + * Equality check for ListBase. + * + * This only shallowly compares the ListBase itself (so the first/last + * pointers), and does not do any equality checks on the list items. + */ +BLI_INLINE bool BLI_listbase_equal(const struct ListBase *a, const struct ListBase *b) +{ + if (a == NULL) { + return b == NULL; + } + if (b == NULL) { + return false; + } + return a->first == b->first && a->last == b->last; +} + +/** * Create a generic list node containing link to provided data. */ struct LinkData *BLI_genericNodeN(void *data); @@ -353,3 +375,10 @@ struct LinkData *BLI_genericNodeN(void *data); #ifdef __cplusplus } #endif + +#ifdef __cplusplus +BLI_INLINE bool operator==(const ListBase &a, const ListBase &b) +{ + return BLI_listbase_equal(&a, &b); +} +#endif diff --git a/source/blender/blenlib/BLI_math_base.h b/source/blender/blenlib/BLI_math_base.h index 83822481112..6c82bd89e64 100644 --- a/source/blender/blenlib/BLI_math_base.h +++ b/source/blender/blenlib/BLI_math_base.h @@ -76,8 +76,7 @@ #if defined(__GNUC__) # define NAN_FLT __builtin_nanf("") -#else -/* evil quiet NaN definition */ +#else /* evil quiet NaN definition */ static const int NAN_INT = 0x7FC00000; # define NAN_FLT (*((float *)(&NAN_INT))) #endif @@ -97,7 +96,8 @@ extern "C" { /******************************* Float ******************************/ -/* powf is really slow for raising to integer powers. */ +/* `powf` is really slow for raising to integer powers. */ + MINLINE float pow2f(float x); MINLINE float pow3f(float x); MINLINE float pow4f(float x); diff --git a/source/blender/blenlib/BLI_math_bits.h b/source/blender/blenlib/BLI_math_bits.h index e881f1a0e4e..70a54879446 100644 --- a/source/blender/blenlib/BLI_math_bits.h +++ b/source/blender/blenlib/BLI_math_bits.h @@ -28,24 +28,29 @@ extern "C" { #endif /* Search the value from LSB to MSB for a set bit. Returns index of this bit. */ + MINLINE int bitscan_forward_i(int a); MINLINE unsigned int bitscan_forward_uint(unsigned int a); MINLINE unsigned int bitscan_forward_uint64(unsigned long long a); /* Similar to above, but also clears the bit. */ + MINLINE int bitscan_forward_clear_i(int *a); MINLINE unsigned int bitscan_forward_clear_uint(unsigned int *a); /* Search the value from MSB to LSB for a set bit. Returns index of this bit. */ + MINLINE int bitscan_reverse_i(int a); MINLINE unsigned int bitscan_reverse_uint(unsigned int a); MINLINE unsigned int bitscan_reverse_uint64(unsigned long long a); /* Similar to above, but also clears the bit. */ + MINLINE int bitscan_reverse_clear_i(int *a); MINLINE unsigned int bitscan_reverse_clear_uint(unsigned int *a); /* NOTE: Those functions returns 2 to the power of index of highest order bit. */ + MINLINE unsigned int highest_order_bit_uint(unsigned int n); MINLINE unsigned short highest_order_bit_s(unsigned short n); diff --git a/source/blender/blenlib/BLI_math_geom.h b/source/blender/blenlib/BLI_math_geom.h index 55d118d17de..c2a5ffafa64 100644 --- a/source/blender/blenlib/BLI_math_geom.h +++ b/source/blender/blenlib/BLI_math_geom.h @@ -441,7 +441,9 @@ void isect_seg_seg_v3(const float a0[3], float r_a[3], float r_b[3]); -/* intersect Line-Line, shorts */ +/** + * Intersect Line-Line, integer. + */ int isect_seg_seg_v2_int(const int v1[2], const int v2[2], const int v3[2], const int v4[2]); /** * Get intersection point of two 2D segments. @@ -929,7 +931,7 @@ void interp_weights_quad_v3(float w[4], void interp_weights_poly_v3(float w[], float v[][3], const int n, const float co[3]); void interp_weights_poly_v2(float w[], float v[][2], const int n, const float co[2]); -/* (x1, v1)(t1=0)------(x2, v2)(t2=1), 0<t<1 --> (x, v)(t) */ +/** `(x1, v1)(t1=0)------(x2, v2)(t2=1), 0<t<1 --> (x, v)(t)`. */ void interp_cubic_v3(float x[3], float v[3], const float x1[3], diff --git a/source/blender/blenlib/BLI_math_matrix.h b/source/blender/blenlib/BLI_math_matrix.h index 3d8fe6f564a..90b74e2f504 100644 --- a/source/blender/blenlib/BLI_math_matrix.h +++ b/source/blender/blenlib/BLI_math_matrix.h @@ -56,16 +56,18 @@ void copy_m4_m2(float m1[4][4], const float m2[2][2]); void copy_m4_m4_db(double m1[4][4], const double m2[4][4]); /* double->float */ + void copy_m3_m3d(float m1[3][3], const double m2[3][3]); /* float->double */ + void copy_m3d_m3(double m1[3][3], const float m2[3][3]); void copy_m4d_m4(double m1[4][4], const float m2[4][4]); void swap_m3m3(float m1[3][3], float m2[3][3]); void swap_m4m4(float m1[4][4], float m2[4][4]); -/* Build index shuffle matrix */ +/** Build index shuffle matrix. */ void shuffle_m4(float R[4][4], const int index[4]); /** \} */ @@ -112,7 +114,8 @@ void mul_m4db_m4db_m4fl_uniq(double R[4][4], const double A[4][4], const float B void mul_m4_m4_pre(float R[4][4], const float A[4][4]); void mul_m4_m4_post(float R[4][4], const float B[4][4]); -/* mul_m3_series */ +/* Implement #mul_m3_series macro. */ + void _va_mul_m3_series_3(float R[3][3], const float M1[3][3], const float M2[3][3]) ATTR_NONNULL(); void _va_mul_m3_series_4(float R[3][3], const float M1[3][3], @@ -153,7 +156,9 @@ void _va_mul_m3_series_9(float R[3][3], const float M6[3][3], const float M7[3][3], const float M8[3][3]) ATTR_NONNULL(); -/* mul_m4_series */ + +/* Implement #mul_m4_series macro. */ + void _va_mul_m4_series_3(float R[4][4], const float M1[4][4], const float M2[4][4]) ATTR_NONNULL(); void _va_mul_m4_series_4(float R[4][4], const float M1[4][4], @@ -266,11 +271,13 @@ bool invert_m4_m4(float R[4][4], const float A[4][4]); */ bool invert_m4_m4_fallback(float R[4][4], const float A[4][4]); -/* double arithmetic (mixed float/double) */ +/* Double arithmetic (mixed float/double). */ + void mul_m4_v4d(const float M[4][4], double r[4]); void mul_v4d_m4v4d(double r[4], const float M[4][4], const double v[4]); -/* double matrix functions (no mixing types) */ +/* Double matrix functions (no mixing types). */ + void mul_v3_m3v3_db(double r[3], const double M[3][3], const double a[3]); void mul_m3_v3_db(const double M[3][3], double r[3]); @@ -282,7 +289,9 @@ void mul_m3_v3_db(const double M[3][3], double r[3]); void transpose_m3(float R[3][3]); void transpose_m3_m3(float R[3][3], const float M[3][3]); -/* seems obscure but in-fact a common operation */ +/** + * \note Seems obscure but in-fact a common operation. + */ void transpose_m3_m4(float R[3][3], const float M[4][4]); void transpose_m4(float R[4][4]); void transpose_m4_m4(float R[4][4], const float M[4][4]); diff --git a/source/blender/blenlib/BLI_math_rotation.h b/source/blender/blenlib/BLI_math_rotation.h index 5e72d502262..8106251684d 100644 --- a/source/blender/blenlib/BLI_math_rotation.h +++ b/source/blender/blenlib/BLI_math_rotation.h @@ -57,7 +57,8 @@ void unit_axis_angle(float axis[3], float *angle); void unit_qt(float q[4]); void copy_qt_qt(float q[4], const float a[4]); -/* arithmetic */ +/* Arithmetic. */ + void mul_qt_qtqt(float q[4], const float a[4], const float b[4]); /** * \note @@ -106,7 +107,8 @@ float dot_qtqt(const float a[4], const float b[4]); float normalize_qt(float q[4]); float normalize_qt_qt(float r[4], const float q[4]); -/* comparison */ +/* Comparison. */ + bool is_zero_qt(const float q[4]); /* interpolation */ @@ -122,7 +124,8 @@ void interp_dot_slerp(const float t, const float cosom, float r_w[2]); void interp_qt_qtqt(float q[4], const float a[4], const float b[4], const float t); void add_qt_qtqt(float q[4], const float a[4], const float b[4], const float t); -/* conversion */ +/* Conversion. */ + void quat_to_mat3(float mat[3][3], const float q[4]); void quat_to_mat4(float mat[4][4], const float q[4]); @@ -188,7 +191,8 @@ float angle_signed_qtqt(const float q1[4], const float q2[4]); */ void mat3_to_quat_is_ok(float q[4], const float mat[3][3]); -/* other */ +/* Other. */ + void print_qt(const char *str, const float q[4]); #define print_qt_id(q) print_qt(STRINGIFY(q), q) @@ -199,7 +203,8 @@ void print_qt(const char *str, const float q[4]); /** \name Axis Angle * \{ */ -/* conversion */ +/* Conversion. */ + void axis_angle_normalized_to_quat(float r[4], const float axis[3], const float angle); void axis_angle_to_quat(float r[4], const float axis[3], const float angle); /** @@ -271,32 +276,25 @@ void expmap_to_quat(float r[4], const float expmap[3]); /** \name XYZ Eulers * \{ */ -/* XYZ order. */ void eul_to_quat(float quat[4], const float eul[3]); -/* XYZ order */ void eul_to_mat3(float mat[3][3], const float eul[3]); -/* XYZ order */ void eul_to_mat4(float mat[4][4], const float eul[3]); -/* XYZ order */ void mat3_normalized_to_eul(float eul[3], const float mat[3][3]); -/* XYZ order */ void mat4_normalized_to_eul(float eul[3], const float mat[4][4]); void mat3_to_eul(float eul[3], const float mat[3][3]); void mat4_to_eul(float eul[3], const float mat[4][4]); -/* XYZ order */ void quat_to_eul(float eul[3], const float quat[4]); -/* XYZ order */ void mat3_normalized_to_compatible_eul(float eul[3], const float old[3], float mat[3][3]); void mat3_to_compatible_eul(float eul[3], const float old[3], float mat[3][3]); void quat_to_compatible_eul(float eul[3], const float oldrot[3], const float quat[4]); -/* order independent! */ -void compatible_eul(float eul[3], const float old[3]); - -/* XYZ order */ void rotate_eul(float eul[3], const char axis, const float angle); +/* Order independent. */ + +void compatible_eul(float eul[3], const float old[3]); + void add_eul_euleul(float r_eul[3], float a[3], float b[3], const short order); void sub_eul_euleul(float r_eul[3], float a[3], float b[3], const short order); diff --git a/source/blender/blenlib/BLI_math_vector.h b/source/blender/blenlib/BLI_math_vector.h index 8f955076baf..61090e30f7b 100644 --- a/source/blender/blenlib/BLI_math_vector.h +++ b/source/blender/blenlib/BLI_math_vector.h @@ -79,7 +79,9 @@ MINLINE void copy_v4_v4_char(char r[4], const char a[4]); MINLINE void copy_v2_v2_short(short r[2], const short a[2]); MINLINE void copy_v3_v3_short(short r[3], const short a[3]); MINLINE void copy_v4_v4_short(short r[4], const short a[4]); + /* int */ + MINLINE void zero_v3_int(int r[3]); MINLINE void copy_v2_v2_int(int r[2], const int a[2]); MINLINE void copy_v3_v3_int(int r[3], const int a[3]); diff --git a/source/blender/blenlib/BLI_mempool.h b/source/blender/blenlib/BLI_mempool.h index d6abae36e00..a3cf2a77646 100644 --- a/source/blender/blenlib/BLI_mempool.h +++ b/source/blender/blenlib/BLI_mempool.h @@ -103,14 +103,14 @@ void BLI_mempool_set_memory_debug(void); * \note this may easy to produce bugs with. */ -/* Private structure. */ +/** \note Private structure. */ typedef struct BLI_mempool_iter { BLI_mempool *pool; struct BLI_mempool_chunk *curchunk; unsigned int curindex; } BLI_mempool_iter; -/* flag */ +/** #BLI_mempool.flag */ enum { BLI_MEMPOOL_NOP = 0, /** allow iterating on this mempool. diff --git a/source/blender/blenlib/BLI_quadric.h b/source/blender/blenlib/BLI_quadric.h index fdb7d1e67ac..450653a20ea 100644 --- a/source/blender/blenlib/BLI_quadric.h +++ b/source/blender/blenlib/BLI_quadric.h @@ -31,18 +31,21 @@ typedef struct Quadric { double a2, ab, ac, ad, b2, bc, bd, c2, cd, d2; } Quadric; -/* conversion */ +/* Conversion. */ + void BLI_quadric_from_plane(Quadric *q, const double v[4]); void BLI_quadric_to_vector_v3(const Quadric *q, double v[3]); void BLI_quadric_clear(Quadric *q); -/* math */ +/* Math operations. */ + void BLI_quadric_add_qu_qu(Quadric *a, const Quadric *b); void BLI_quadric_add_qu_ququ(Quadric *r, const Quadric *a, const Quadric *b); void BLI_quadric_mul(Quadric *a, const double scalar); -/* solve */ +/* Solve. */ + double BLI_quadric_evaluate(const Quadric *q, const double v[3]); bool BLI_quadric_optimize(const Quadric *q, double v[3], const double epsilon); diff --git a/source/blender/blenlib/BLI_scanfill.h b/source/blender/blenlib/BLI_scanfill.h index 744d30397d4..ec7fa8f27a0 100644 --- a/source/blender/blenlib/BLI_scanfill.h +++ b/source/blender/blenlib/BLI_scanfill.h @@ -91,6 +91,7 @@ typedef struct ScanFillFace { } ScanFillFace; /* scanfill.c */ + struct ScanFillVert *BLI_scanfill_vert_add(ScanFillContext *sf_ctx, const float vec[3]); struct ScanFillEdge *BLI_scanfill_edge_add(ScanFillContext *sf_ctx, struct ScanFillVert *v1, diff --git a/source/blender/blenlib/BLI_session_uuid.h b/source/blender/blenlib/BLI_session_uuid.h index 887044e9b54..29e291add5a 100644 --- a/source/blender/blenlib/BLI_session_uuid.h +++ b/source/blender/blenlib/BLI_session_uuid.h @@ -33,18 +33,19 @@ extern "C" { #include "DNA_session_uuid_types.h" -/* Generate new UUID which is unique throughout the Blender session. */ +/** Generate new UUID which is unique throughout the Blender session. */ SessionUUID BLI_session_uuid_generate(void); -/* Check whether the UUID is properly generated. */ +/** Check whether the UUID is properly generated. */ bool BLI_session_uuid_is_generated(const SessionUUID *uuid); -/* Check whether two UUIDs are identical. */ +/** Check whether two UUIDs are identical. */ bool BLI_session_uuid_is_equal(const SessionUUID *lhs, const SessionUUID *rhs); uint64_t BLI_session_uuid_hash_uint64(const SessionUUID *uuid); /* Utility functions to make it possible to create GHash/GSet with UUID as a key. */ + uint BLI_session_uuid_ghash_hash(const void *uuid_v); bool BLI_session_uuid_ghash_compare(const void *lhs_v, const void *rhs_v); diff --git a/source/blender/blenlib/BLI_sort.h b/source/blender/blenlib/BLI_sort.h index 969816086e2..31a052eb79d 100644 --- a/source/blender/blenlib/BLI_sort.h +++ b/source/blender/blenlib/BLI_sort.h @@ -30,7 +30,7 @@ # define BLI_qsort_r qsort_r #endif -/* Quick sort re-entrant */ +/** Quick sort (re-entrant). */ typedef int (*BLI_sort_cmp_t)(const void *a, const void *b, void *ctx); void BLI_qsort_r(void *a, size_t n, size_t es, BLI_sort_cmp_t cmp, void *thunk) diff --git a/source/blender/blenlib/BLI_sys_types.h b/source/blender/blenlib/BLI_sys_types.h index 2740e3740f2..95327ff33b8 100644 --- a/source/blender/blenlib/BLI_sys_types.h +++ b/source/blender/blenlib/BLI_sys_types.h @@ -72,8 +72,8 @@ typedef uint64_t u_int64_t; #include <stddef.h> /* size_t define */ #ifndef __cplusplus +/* The <uchar.h> standard header is missing on some systems. */ # if defined(__APPLE__) || defined(__NetBSD__) -/* The <uchar.h> standard header is missing on macOS. */ typedef unsigned int char32_t; # else # include <uchar.h> diff --git a/source/blender/blenlib/BLI_system.h b/source/blender/blenlib/BLI_system.h index 0d5b2e6e2df..2e5e07d8407 100644 --- a/source/blender/blenlib/BLI_system.h +++ b/source/blender/blenlib/BLI_system.h @@ -30,7 +30,7 @@ int BLI_cpu_support_sse2(void); int BLI_cpu_support_sse41(void); void BLI_system_backtrace(FILE *fp); -/* Get CPU brand, result is to be MEM_freeN()-ed. */ +/** Get CPU brand, result is to be MEM_freeN()-ed. */ char *BLI_cpu_brand_string(void); /** @@ -45,15 +45,19 @@ char *BLI_cpu_brand_string(void); */ void BLI_hostname_get(char *buffer, size_t bufsize); -/* Get maximum addressable memory in megabytes. */ +/** Get maximum addressable memory in megabytes. */ size_t BLI_system_memory_max_in_megabytes(void); +/** Get maximum addressable memory in megabytes (clamped to #INT_MAX). */ int BLI_system_memory_max_in_megabytes_int(void); /* For `getpid`. */ #ifdef WIN32 # define BLI_SYSTEM_PID_H <process.h> -/* void* since we really do not want to drag Windows.h in to get the proper typedef. */ +/** + * \note Use `void *` for `exception` since we really do not want to drag Windows.h + * in to get the proper `typedef`. + */ void BLI_windows_handle_exception(void *exception); #else diff --git a/source/blender/blenlib/BLI_task.h b/source/blender/blenlib/BLI_task.h index a67de2e2910..616237bc261 100644 --- a/source/blender/blenlib/BLI_task.h +++ b/source/blender/blenlib/BLI_task.h @@ -303,7 +303,7 @@ void BLI_task_parallel_mempool(struct BLI_mempool *mempool, TaskParallelMempoolFunc func, const TaskParallelSettings *settings); -/* TODO(sergey): Think of a better place for this. */ +/** TODO(sergey): Think of a better place for this. */ BLI_INLINE void BLI_parallel_range_settings_defaults(TaskParallelSettings *settings) { memset(settings, 0, sizeof(*settings)); diff --git a/source/blender/blenlib/BLI_threads.h b/source/blender/blenlib/BLI_threads.h index 6e60430ea38..7bae16f25ef 100644 --- a/source/blender/blenlib/BLI_threads.h +++ b/source/blender/blenlib/BLI_threads.h @@ -31,7 +31,7 @@ extern "C" { #endif -/* for tables, button in UI, etc */ +/** For tables, button in UI, etc. */ #define BLENDER_MAX_THREADS 1024 struct ListBase; diff --git a/source/blender/blenlib/BLI_timer.h b/source/blender/blenlib/BLI_timer.h index c219b5502f3..b1cc8d5514f 100644 --- a/source/blender/blenlib/BLI_timer.h +++ b/source/blender/blenlib/BLI_timer.h @@ -29,8 +29,11 @@ extern "C" { #endif -/* ret < 0: the timer will be removed. - * ret >= 0: the timer will be called again in ret seconds */ +/** + * \return A value of: + * - < 0: the timer will be removed. + * - >= 0: the timer will be called again in this number of seconds. + */ typedef double (*BLI_timer_func)(uintptr_t uuid, void *user_data); typedef void (*BLI_timer_data_free)(uintptr_t uuid, void *user_data); @@ -45,10 +48,10 @@ void BLI_timer_register(uintptr_t uuid, bool BLI_timer_is_registered(uintptr_t uuid); -/* Returns False when the timer does not exist (anymore). */ +/** Returns False when the timer does not exist (anymore). */ bool BLI_timer_unregister(uintptr_t uuid); -/* Execute all registered functions that are due. */ +/** Execute all registered functions that are due. */ void BLI_timer_execute(void); void BLI_timer_free(void); diff --git a/source/blender/blenlib/BLI_vector.hh b/source/blender/blenlib/BLI_vector.hh index d2b94a6d8ef..4ac48f259cf 100644 --- a/source/blender/blenlib/BLI_vector.hh +++ b/source/blender/blenlib/BLI_vector.hh @@ -637,7 +637,7 @@ class Vector { * Insert values at the beginning of the vector. The has to move all the other elements, so it * has a linear running time. */ - void prepend(const T &&value) + void prepend(const T &value) { this->insert(0, value); } diff --git a/source/blender/blenlib/BLI_virtual_array.hh b/source/blender/blenlib/BLI_virtual_array.hh index 5103ac4b668..3ed9f14712e 100644 --- a/source/blender/blenlib/BLI_virtual_array.hh +++ b/source/blender/blenlib/BLI_virtual_array.hh @@ -44,7 +44,7 @@ namespace blender { -/* Forward declarations for generic virtual arrays. */ +/** Forward declarations for generic virtual arrays. */ namespace fn { class GVArray; class GVMutableArray; @@ -194,7 +194,7 @@ template<typename T> class VArrayImpl { } }; -/* Similar to #VArrayImpl, but adds methods that allow modifying the referenced elements. */ +/** Similar to #VArrayImpl, but adds methods that allow modifying the referenced elements. */ template<typename T> class VMutableArrayImpl : public VArrayImpl<T> { public: using VArrayImpl<T>::VArrayImpl; diff --git a/source/blender/blenlib/BLI_virtual_vector_array.hh b/source/blender/blenlib/BLI_virtual_vector_array.hh index ab5afd2d80a..7672a76d5de 100644 --- a/source/blender/blenlib/BLI_virtual_vector_array.hh +++ b/source/blender/blenlib/BLI_virtual_vector_array.hh @@ -29,7 +29,7 @@ namespace blender { -/* A readonly virtual array of vectors. */ +/** A read-only virtual array of vectors. */ template<typename T> class VVectorArray { protected: int64_t size_; diff --git a/source/blender/blenlib/BLI_winstuff.h b/source/blender/blenlib/BLI_winstuff.h index cbf1716602a..568734ee8a7 100644 --- a/source/blender/blenlib/BLI_winstuff.h +++ b/source/blender/blenlib/BLI_winstuff.h @@ -88,7 +88,7 @@ typedef SSIZE_T ssize_t; # endif #endif -/* Directory reading compatibility with UNIX. */ +/** Directory reading compatibility with UNIX. */ struct dirent { int d_ino; int d_off; @@ -96,7 +96,7 @@ struct dirent { char *d_name; }; -/* intentionally opaque to users */ +/** Intentionally opaque to users. */ typedef struct __dirstream DIR; DIR *opendir(const char *path); @@ -105,6 +105,7 @@ int closedir(DIR *dp); const char *dirname(char *path); /* Windows utility functions. */ + bool BLI_windows_register_blend_extension(const bool background); void BLI_windows_get_default_root_dir(char root_dir[4]); int BLI_windows_get_executable_dir(char *str); diff --git a/source/blender/blenlib/CMakeLists.txt b/source/blender/blenlib/CMakeLists.txt index 29493c799b3..516d9d2fe84 100644 --- a/source/blender/blenlib/CMakeLists.txt +++ b/source/blender/blenlib/CMakeLists.txt @@ -76,6 +76,7 @@ set(SRC intern/endian_switch.c intern/expr_pylike_eval.c intern/fileops.c + intern/fileops.cc intern/filereader_file.c intern/filereader_gzip.c intern/filereader_memory.c @@ -204,6 +205,7 @@ set(SRC BLI_enumerable_thread_specific.hh BLI_expr_pylike_eval.h BLI_fileops.h + BLI_fileops.hh BLI_fileops_types.h BLI_filereader.h BLI_float2.hh @@ -422,6 +424,7 @@ if(WITH_GTESTS) tests/BLI_edgehash_test.cc tests/BLI_expr_pylike_eval_test.cc tests/BLI_function_ref_test.cc + tests/BLI_fileops_test.cc tests/BLI_ghash_test.cc tests/BLI_hash_mm2a_test.cc tests/BLI_heap_simple_test.cc diff --git a/source/blender/blenlib/intern/fileops.cc b/source/blender/blenlib/intern/fileops.cc new file mode 100644 index 00000000000..5ceedbd8cb5 --- /dev/null +++ b/source/blender/blenlib/intern/fileops.cc @@ -0,0 +1,51 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup bli + */ + +#include "BLI_fileops.hh" + +#ifdef WIN32 +# include "utfconv.h" +#endif + +namespace blender { +fstream::fstream(const char *filepath, std::ios_base::openmode mode) +{ + this->open(filepath, mode); +} + +fstream::fstream(const std::string &filepath, std::ios_base::openmode mode) +{ + this->open(filepath, mode); +} + +void fstream::open(StringRefNull filepath, ios_base::openmode mode) +{ +#ifdef WIN32 + const char *filepath_cstr = filepath.c_str(); + UTF16_ENCODE(filepath_cstr); + std::wstring filepath_wstr(filepath_cstr_16); + std::fstream::open(filepath_wstr.c_str(), mode); + UTF16_UN_ENCODE(filepath_cstr); +#else + std::fstream::open(filepath, mode); +#endif +} + +} // namespace blender diff --git a/source/blender/blenlib/intern/listbase.c b/source/blender/blenlib/intern/listbase.c index a166c846ea7..513b08a589d 100644 --- a/source/blender/blenlib/intern/listbase.c +++ b/source/blender/blenlib/intern/listbase.c @@ -749,6 +749,26 @@ int BLI_findstringindex(const ListBase *listbase, const char *id, const int offs return -1; } +ListBase BLI_listbase_from_link(Link *some_link) +{ + ListBase list = {some_link, some_link}; + if (some_link == NULL) { + return list; + } + + /* Find the first element. */ + while (((Link *)list.first)->prev != NULL) { + list.first = ((Link *)list.first)->prev; + } + + /* Find the last element. */ + while (((Link *)list.last)->next != NULL) { + list.last = ((Link *)list.last)->next; + } + + return list; +} + void BLI_duplicatelist(ListBase *dst, const ListBase *src) { struct Link *dst_link, *src_link; diff --git a/source/blender/blenlib/intern/task_scheduler.cc b/source/blender/blenlib/intern/task_scheduler.cc index 69117e9dc7e..5992e092f4d 100644 --- a/source/blender/blenlib/intern/task_scheduler.cc +++ b/source/blender/blenlib/intern/task_scheduler.cc @@ -50,8 +50,8 @@ void BLI_task_scheduler_init() if (num_threads_override > 0) { /* Override number of threads. This settings is used within the lifetime * of tbb::global_control, so we allocate it on the heap. */ - task_scheduler_global_control = OBJECT_GUARDED_NEW( - tbb::global_control, tbb::global_control::max_allowed_parallelism, num_threads_override); + task_scheduler_global_control = MEM_new<tbb::global_control>( + __func__, tbb::global_control::max_allowed_parallelism, num_threads_override); task_scheduler_num_threads = num_threads_override; } else { @@ -69,7 +69,7 @@ void BLI_task_scheduler_init() void BLI_task_scheduler_exit() { #ifdef WITH_TBB_GLOBAL_CONTROL - OBJECT_GUARDED_DELETE(task_scheduler_global_control, tbb::global_control); + MEM_delete(task_scheduler_global_control); #endif } diff --git a/source/blender/blenlib/tests/BLI_fileops_test.cc b/source/blender/blenlib/tests/BLI_fileops_test.cc new file mode 100644 index 00000000000..e2a792647dc --- /dev/null +++ b/source/blender/blenlib/tests/BLI_fileops_test.cc @@ -0,0 +1,40 @@ +/* Apache License, Version 2.0 */ + +#include "BLI_fileops.hh" + +#include "testing/testing.h" + +namespace blender::tests { + +TEST(fileops, fstream_open_string_filename) +{ + const std::string test_files_dir = blender::tests::flags_test_asset_dir(); + if (test_files_dir.empty()) { + FAIL(); + } + + const std::string filepath = test_files_dir + "/asset_library/новый/blender_assets.cats.txt"; + fstream in(filepath, std::ios_base::in); + ASSERT_TRUE(in.is_open()) << "could not open " << filepath; + in.close(); /* This should not crash. */ + + /* Reading the file not tested here. That's deferred to `std::fstream` anyway. */ +} + +TEST(fileops, fstream_open_charptr_filename) +{ + const std::string test_files_dir = blender::tests::flags_test_asset_dir(); + if (test_files_dir.empty()) { + FAIL(); + } + + const std::string filepath_str = test_files_dir + "/asset_library/новый/blender_assets.cats.txt"; + const char *filepath = filepath_str.c_str(); + fstream in(filepath, std::ios_base::in); + ASSERT_TRUE(in.is_open()) << "could not open " << filepath; + in.close(); /* This should not crash. */ + + /* Reading the file not tested here. That's deferred to `std::fstream` anyway. */ +} + +} // namespace blender::tests diff --git a/source/blender/blenlib/tests/BLI_listbase_test.cc b/source/blender/blenlib/tests/BLI_listbase_test.cc index d66eb214902..9e4d7c7dd36 100644 --- a/source/blender/blenlib/tests/BLI_listbase_test.cc +++ b/source/blender/blenlib/tests/BLI_listbase_test.cc @@ -154,6 +154,31 @@ TEST(listbase, FindLinkFromStringOrPointer) BLI_freelistN(&lb); } +TEST(listbase, FromLink) +{ + ListBase lb = {nullptr, nullptr}; + Link *link1 = static_cast<Link *>(MEM_callocN(sizeof(Link), "link1")); + Link *link2 = static_cast<Link *>(MEM_callocN(sizeof(Link), "link2")); + Link *link3 = static_cast<Link *>(MEM_callocN(sizeof(Link), "link3")); + + /* NULL safety. */ + EXPECT_EQ(lb, BLI_listbase_from_link(nullptr)); + + /* One link. */ + BLI_addtail(&lb, link1); + EXPECT_EQ(lb, BLI_listbase_from_link(link1)); + + /* Two links. */ + BLI_addtail(&lb, link2); + EXPECT_EQ(lb, BLI_listbase_from_link(link2)); + + /* Three links, search from middle. */ + BLI_addtail(&lb, link3); + EXPECT_EQ(lb, BLI_listbase_from_link(link2)); + + BLI_freelistN(&lb); +} + /* -------------------------------------------------------------------- */ /* Sort utilities & test */ diff --git a/source/blender/blenlib/tests/BLI_vector_test.cc b/source/blender/blenlib/tests/BLI_vector_test.cc index e8636168308..e9393a2b1e5 100644 --- a/source/blender/blenlib/tests/BLI_vector_test.cc +++ b/source/blender/blenlib/tests/BLI_vector_test.cc @@ -708,6 +708,17 @@ TEST(vector, Prepend) EXPECT_EQ_ARRAY(vec.data(), Span({7, 8, 1, 2, 3}).data(), 5); } +TEST(vector, PrependString) +{ + std::string s = "test"; + Vector<std::string> vec; + vec.prepend(s); + vec.prepend(std::move(s)); + EXPECT_EQ(vec.size(), 2); + EXPECT_EQ(vec[0], "test"); + EXPECT_EQ(vec[1], "test"); +} + TEST(vector, ReverseIterator) { Vector<int> vec = {4, 5, 6, 7}; diff --git a/source/blender/blenloader/intern/versioning_250.c b/source/blender/blenloader/intern/versioning_250.c index 54e673b51eb..8bcb99e41e4 100644 --- a/source/blender/blenloader/intern/versioning_250.c +++ b/source/blender/blenloader/intern/versioning_250.c @@ -70,6 +70,7 @@ #include "BKE_main.h" #include "BKE_modifier.h" #include "BKE_multires.h" +#include "BKE_node_tree_update.h" #include "BKE_particle.h" #include "BKE_pointcache.h" #include "BKE_screen.h" @@ -590,7 +591,7 @@ static bNodeSocket *do_versions_node_group_add_socket_2_56_2(bNodeTree *ngroup, BLI_addtail(in_out == SOCK_IN ? &ngroup->inputs : &ngroup->outputs, gsock); - ngroup->update |= (in_out == SOCK_IN ? NTREE_UPDATE_GROUP_IN : NTREE_UPDATE_GROUP_OUT); + BKE_ntree_update_tag_interface(ngroup); return gsock; } @@ -2019,7 +2020,7 @@ void blo_do_versions_250(FileData *fd, Library *lib, Main *bmain) link->fromsock = gsock; link->tonode = node; link->tosock = sock; - ntree->update |= NTREE_UPDATE_LINKS; + BKE_ntree_update_tag_link_added(ntree, link); sock->link = link; } @@ -2042,7 +2043,7 @@ void blo_do_versions_250(FileData *fd, Library *lib, Main *bmain) link->fromsock = sock; link->tonode = NULL; link->tosock = gsock; - ntree->update |= NTREE_UPDATE_LINKS; + BKE_ntree_update_tag_link_added(ntree, link); gsock->link = link; } @@ -2282,7 +2283,7 @@ void blo_do_versions_250(FileData *fd, Library *lib, Main *bmain) do_versions_socket_default_value_259(sock); } - ntree->update |= NTREE_UPDATE; + BKE_ntree_update_tag_all(ntree); } FOREACH_NODETREE_END; } diff --git a/source/blender/blenloader/intern/versioning_280.c b/source/blender/blenloader/intern/versioning_280.c index 4333bdb851c..d9052c6b1f7 100644 --- a/source/blender/blenloader/intern/versioning_280.c +++ b/source/blender/blenloader/intern/versioning_280.c @@ -88,6 +88,7 @@ #include "BKE_main.h" #include "BKE_mesh.h" #include "BKE_node.h" +#include "BKE_node_tree_update.h" #include "BKE_paint.h" #include "BKE_pointcache.h" #include "BKE_report.h" @@ -896,7 +897,7 @@ static void do_versions_material_convert_legacy_blend_mode(bNodeTree *ntree, cha } if (need_update) { - ntreeUpdateTree(NULL, ntree); + version_socket_update_is_used(ntree); } } diff --git a/source/blender/blenloader/intern/versioning_300.c b/source/blender/blenloader/intern/versioning_300.c index bc3cd7a09cb..5eb9b0a4c6a 100644 --- a/source/blender/blenloader/intern/versioning_300.c +++ b/source/blender/blenloader/intern/versioning_300.c @@ -30,6 +30,7 @@ #include "BLI_math_vector.h" #include "BLI_path_util.h" #include "BLI_string.h" +#include "BLI_string_utils.h" #include "BLI_utildefines.h" #include "DNA_anim_types.h" @@ -592,7 +593,7 @@ static bNodeTree *add_realize_node_tree(Main *bmain) nodeSetSelected(node, false); } - ntreeUpdateTree(bmain, node_tree); + version_socket_update_is_used(node_tree); return node_tree; } @@ -790,6 +791,32 @@ void do_versions_after_linking_300(Main *bmain, ReportList *UNUSED(reports)) */ { /* Keep this block, even when empty. */ + + { /* Ensure driver variable names are unique within the driver. */ + ID *id; + FOREACH_MAIN_ID_BEGIN (bmain, id) { + AnimData *adt = BKE_animdata_from_id(id); + if (adt == NULL) { + continue; + } + LISTBASE_FOREACH (FCurve *, fcu, &adt->drivers) { + ChannelDriver *driver = fcu->driver; + /* Ensure the uniqueness front to back. Given a list of identically + * named variables, the last one gets to keep its original name. This + * matches the evaluation order, and thus shouldn't change the evaluated + * value of the driver expression. */ + LISTBASE_FOREACH (DriverVar *, dvar, &driver->variables) { + BLI_uniquename(&driver->variables, + dvar, + dvar->name, + '_', + offsetof(DriverVar, name), + sizeof(dvar->name)); + } + } + } + FOREACH_MAIN_ID_END; + } } } @@ -2414,7 +2441,6 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) data->data_type = SOCK_FLOAT; data->operation = node->custom1; strcpy(node->idname, "FunctionNodeCompare"); - node->update = NODE_UPDATE; node->storage = data; } } @@ -2432,22 +2458,6 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) } } } - - /* Add node storage for map range node. */ - FOREACH_NODETREE_BEGIN (bmain, ntree, id) { - LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { - if (node->type == SH_NODE_MAP_RANGE) { - if (node->storage == NULL) { - NodeMapRange *data = MEM_callocN(sizeof(NodeMapRange), __func__); - data->clamp = node->custom1; - data->data_type = CD_PROP_FLOAT; - data->interpolation_type = node->custom2; - node->storage = data; - } - } - } - } - FOREACH_NODETREE_END; } if (!MAIN_VERSION_ATLEAST(bmain, 301, 5)) { @@ -2475,5 +2485,21 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) */ { /* Keep this block, even when empty. */ + + /* Add node storage for map range node. */ + FOREACH_NODETREE_BEGIN (bmain, ntree, id) { + LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { + if (node->type == SH_NODE_MAP_RANGE) { + if (node->storage == NULL) { + NodeMapRange *data = MEM_callocN(sizeof(NodeMapRange), __func__); + data->clamp = node->custom1; + data->data_type = CD_PROP_FLOAT; + data->interpolation_type = node->custom2; + node->storage = data; + } + } + } + } + FOREACH_NODETREE_END; } } diff --git a/source/blender/blenloader/intern/versioning_common.cc b/source/blender/blenloader/intern/versioning_common.cc index 3deaaa040d6..752157bffde 100644 --- a/source/blender/blenloader/intern/versioning_common.cc +++ b/source/blender/blenloader/intern/versioning_common.cc @@ -223,3 +223,23 @@ void version_node_socket_index_animdata(Main *bmain, FOREACH_NODETREE_END; } } + +/** + * The versioning code generally expects `SOCK_IN_USE` to be set correctly. This function updates + * the flag on all sockets after changes to the node tree. + */ +void version_socket_update_is_used(bNodeTree *ntree) +{ + LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { + LISTBASE_FOREACH (bNodeSocket *, socket, &node->inputs) { + socket->flag &= ~SOCK_IN_USE; + } + LISTBASE_FOREACH (bNodeSocket *, socket, &node->outputs) { + socket->flag &= ~SOCK_IN_USE; + } + } + LISTBASE_FOREACH (bNodeLink *, link, &ntree->links) { + link->fromsock->flag |= SOCK_IN_USE; + link->tosock->flag |= SOCK_IN_USE; + } +} diff --git a/source/blender/blenloader/intern/versioning_common.h b/source/blender/blenloader/intern/versioning_common.h index 0613484b754..dd1dd1f22f2 100644 --- a/source/blender/blenloader/intern/versioning_common.h +++ b/source/blender/blenloader/intern/versioning_common.h @@ -100,6 +100,8 @@ struct bNodeSocket *version_node_add_socket_if_not_exist(struct bNodeTree *ntree const char *identifier, const char *name); +void version_socket_update_is_used(bNodeTree *ntree); + #ifdef __cplusplus } #endif diff --git a/source/blender/blenloader/intern/versioning_cycles.c b/source/blender/blenloader/intern/versioning_cycles.c index afe2e1067ae..f1a2e678b2e 100644 --- a/source/blender/blenloader/intern/versioning_cycles.c +++ b/source/blender/blenloader/intern/versioning_cycles.c @@ -48,6 +48,7 @@ #include "BLO_readfile.h" #include "readfile.h" +#include "versioning_common.h" static bool socket_is_used(bNodeSocket *sock) { @@ -170,7 +171,7 @@ static void displacement_node_insert(bNodeTree *ntree) } if (need_update) { - ntreeUpdateTree(NULL, ntree); + version_socket_update_is_used(ntree); } } @@ -243,7 +244,7 @@ static void square_roughness_node_insert(bNodeTree *ntree) } if (need_update) { - ntreeUpdateTree(NULL, ntree); + version_socket_update_is_used(ntree); } } @@ -318,7 +319,7 @@ static void ambient_occlusion_node_relink(bNodeTree *ntree) } if (need_update) { - ntreeUpdateTree(NULL, ntree); + version_socket_update_is_used(ntree); } } @@ -466,7 +467,7 @@ static void update_math_node_single_operand_operators(bNodeTree *ntree) } if (need_update) { - ntreeUpdateTree(NULL, ntree); + version_socket_update_is_used(ntree); } } @@ -528,7 +529,7 @@ static void update_vector_math_node_add_and_subtract_operators(bNodeTree *ntree) } if (need_update) { - ntreeUpdateTree(NULL, ntree); + version_socket_update_is_used(ntree); } } @@ -566,7 +567,7 @@ static void update_vector_math_node_dot_product_operator(bNodeTree *ntree) } if (need_update) { - ntreeUpdateTree(NULL, ntree); + version_socket_update_is_used(ntree); } } @@ -631,7 +632,7 @@ static void update_vector_math_node_cross_product_operator(bNodeTree *ntree) } if (need_update) { - ntreeUpdateTree(NULL, ntree); + version_socket_update_is_used(ntree); } } @@ -683,7 +684,7 @@ static void update_vector_math_node_normalize_operator(bNodeTree *ntree) } } if (need_update) { - ntreeUpdateTree(NULL, ntree); + version_socket_update_is_used(ntree); } } @@ -785,7 +786,7 @@ static void update_vector_math_node_average_operator(bNodeTree *ntree) } if (need_update) { - ntreeUpdateTree(NULL, ntree); + version_socket_update_is_used(ntree); } } @@ -974,7 +975,7 @@ static void update_mapping_node_inputs_and_properties(bNodeTree *ntree) } if (need_update) { - ntreeUpdateTree(NULL, ntree); + version_socket_update_is_used(ntree); } } @@ -1149,7 +1150,7 @@ static void update_voronoi_node_crackle(bNodeTree *ntree) } if (need_update) { - ntreeUpdateTree(NULL, ntree); + version_socket_update_is_used(ntree); } } @@ -1191,7 +1192,7 @@ static void update_voronoi_node_coloring(bNodeTree *ntree) } if (need_update) { - ntreeUpdateTree(NULL, ntree); + version_socket_update_is_used(ntree); } } @@ -1234,7 +1235,7 @@ static void update_voronoi_node_square_distance(bNodeTree *ntree) } if (need_update) { - ntreeUpdateTree(NULL, ntree); + version_socket_update_is_used(ntree); } } @@ -1279,7 +1280,7 @@ static void update_noise_and_wave_distortion(bNodeTree *ntree) } if (need_update) { - ntreeUpdateTree(NULL, ntree); + version_socket_update_is_used(ntree); } } diff --git a/source/blender/blenloader/intern/versioning_defaults.c b/source/blender/blenloader/intern/versioning_defaults.c index a5c44ea711b..234951eac9a 100644 --- a/source/blender/blenloader/intern/versioning_defaults.c +++ b/source/blender/blenloader/intern/versioning_defaults.c @@ -61,6 +61,7 @@ #include "BKE_material.h" #include "BKE_mesh.h" #include "BKE_node.h" +#include "BKE_node_tree_update.h" #include "BKE_paint.h" #include "BKE_screen.h" #include "BKE_workspace.h" @@ -583,11 +584,11 @@ void BLO_update_defaults_startup_blend(Main *bmain, const char *app_template) bNodeSocketValueFloat *roughness_data = roughness_socket->default_value; roughness_data->value = 0.4f; node->custom2 = SHD_SUBSURFACE_RANDOM_WALK; - nodeUpdate(ma->nodetree, node); + BKE_ntree_update_tag_node_property(ma->nodetree, node); } else if (node->type == SH_NODE_SUBSURFACE_SCATTERING) { node->custom1 = SHD_SUBSURFACE_RANDOM_WALK; - nodeUpdate(ma->nodetree, node); + BKE_ntree_update_tag_node_property(ma->nodetree, node); } } } diff --git a/source/blender/blenloader/tests/blendfile_loading_base_test.cc b/source/blender/blenloader/tests/blendfile_loading_base_test.cc index a4a5ced070d..32d288f35e1 100644 --- a/source/blender/blenloader/tests/blendfile_loading_base_test.cc +++ b/source/blender/blenloader/tests/blendfile_loading_base_test.cc @@ -21,6 +21,7 @@ #include "BKE_appdir.h" #include "BKE_blender.h" +#include "BKE_callbacks.h" #include "BKE_context.h" #include "BKE_global.h" #include "BKE_idtype.h" @@ -71,6 +72,7 @@ void BlendfileLoadingBaseTest::SetUpTestCase() DEG_register_node_types(); RNA_init(); BKE_node_system_init(); + BKE_callback_global_init(); G.background = true; G.factory_startup = true; diff --git a/source/blender/compositor/intern/COM_compositor.cc b/source/blender/compositor/intern/COM_compositor.cc index be70ae792cb..5c9b34c4772 100644 --- a/source/blender/compositor/intern/COM_compositor.cc +++ b/source/blender/compositor/intern/COM_compositor.cc @@ -51,7 +51,7 @@ static void compositor_init_node_previews(const RenderData *render_data, bNodeTr preview_width = (int)(blender::compositor::COM_PREVIEW_SIZE / aspect); preview_height = blender::compositor::COM_PREVIEW_SIZE; } - BKE_node_preview_init_tree(node_tree, preview_width, preview_height, false); + BKE_node_preview_init_tree(node_tree, preview_width, preview_height); } static void compositor_reset_node_tree_status(bNodeTree *node_tree) diff --git a/source/blender/depsgraph/DEG_depsgraph_build.h b/source/blender/depsgraph/DEG_depsgraph_build.h index 94cba833096..8a69dbf3312 100644 --- a/source/blender/depsgraph/DEG_depsgraph_build.h +++ b/source/blender/depsgraph/DEG_depsgraph_build.h @@ -153,9 +153,9 @@ void DEG_add_collection_geometry_customdata_mask(struct DepsNodeHandle *node_han void DEG_add_simulation_relation(struct DepsNodeHandle *node_handle, struct Simulation *simulation, const char *description); -void DEG_add_node_tree_relation(struct DepsNodeHandle *node_handle, - struct bNodeTree *node_tree, - const char *description); +void DEG_add_node_tree_output_relation(struct DepsNodeHandle *node_handle, + struct bNodeTree *node_tree, + const char *description); void DEG_add_bone_relation(struct DepsNodeHandle *handle, struct Object *object, const char *bone_name, diff --git a/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc b/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc index 51582508b6f..16eacc735d4 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc @@ -1710,8 +1710,8 @@ void DepsgraphNodeBuilder::build_nodetree(bNodeTree *ntree) build_idproperties(ntree->id.properties); /* Animation, */ build_animdata(&ntree->id); - /* Shading update. */ - add_operation_node(&ntree->id, NodeType::SHADING, OperationCode::MATERIAL_UPDATE); + /* Output update. */ + add_operation_node(&ntree->id, NodeType::NTREE_OUTPUT, OperationCode::NTREE_OUTPUT); /* nodetree's nodes... */ LISTBASE_FOREACH (bNode *, bnode, &ntree->nodes) { build_idproperties(bnode->prop); diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc index b195b2d9e11..09263718677 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc @@ -1770,7 +1770,7 @@ void DepsgraphRelationBuilder::build_world(World *world) if (world->nodetree != nullptr) { build_nodetree(world->nodetree); OperationKey ntree_key( - &world->nodetree->id, NodeType::SHADING, OperationCode::MATERIAL_UPDATE); + &world->nodetree->id, NodeType::NTREE_OUTPUT, OperationCode::NTREE_OUTPUT); add_relation(ntree_key, world_key, "World's NTree"); build_nested_nodetree(&world->id, world->nodetree); } @@ -2381,17 +2381,17 @@ void DepsgraphRelationBuilder::build_light(Light *lamp) ComponentKey lamp_parameters_key(&lamp->id, NodeType::PARAMETERS); + /* For allowing drivers on lamp properties. */ + ComponentKey shading_key(&lamp->id, NodeType::SHADING); + add_relation(lamp_parameters_key, shading_key, "Light Shading Parameters"); + /* light's nodetree */ if (lamp->nodetree != nullptr) { build_nodetree(lamp->nodetree); - ComponentKey nodetree_key(&lamp->nodetree->id, NodeType::SHADING); - add_relation(nodetree_key, lamp_parameters_key, "NTree->Light Parameters"); + ComponentKey nodetree_key(&lamp->nodetree->id, NodeType::NTREE_OUTPUT); + add_relation(nodetree_key, shading_key, "NTree->Light Parameters"); build_nested_nodetree(&lamp->id, lamp->nodetree); } - - /* For allowing drivers on lamp properties. */ - ComponentKey shading_key(&lamp->id, NodeType::SHADING); - add_relation(lamp_parameters_key, shading_key, "Light Shading Parameters"); } void DepsgraphRelationBuilder::build_nodetree_socket(bNodeSocket *socket) @@ -2441,7 +2441,7 @@ void DepsgraphRelationBuilder::build_nodetree(bNodeTree *ntree) build_idproperties(ntree->id.properties); build_animdata(&ntree->id); build_parameters(&ntree->id); - ComponentKey shading_key(&ntree->id, NodeType::SHADING); + OperationKey ntree_output_key(&ntree->id, NodeType::NTREE_OUTPUT, OperationCode::NTREE_OUTPUT); /* nodetree's nodes... */ LISTBASE_FOREACH (bNode *, bnode, &ntree->nodes) { build_idproperties(bnode->prop); @@ -2460,25 +2460,25 @@ void DepsgraphRelationBuilder::build_nodetree(bNodeTree *ntree) if (id_type == ID_MA) { build_material((Material *)bnode->id); ComponentKey material_key(id, NodeType::SHADING); - add_relation(material_key, shading_key, "Material -> Node"); + add_relation(material_key, ntree_output_key, "Material -> Node"); } else if (id_type == ID_TE) { build_texture((Tex *)bnode->id); ComponentKey texture_key(id, NodeType::GENERIC_DATABLOCK); - add_relation(texture_key, shading_key, "Texture -> Node"); + add_relation(texture_key, ntree_output_key, "Texture -> Node"); } else if (id_type == ID_IM) { build_image((Image *)bnode->id); ComponentKey image_key(id, NodeType::GENERIC_DATABLOCK); - add_relation(image_key, shading_key, "Image -> Node"); + add_relation(image_key, ntree_output_key, "Image -> Node"); } else if (id_type == ID_OB) { build_object((Object *)id); ComponentKey object_transform_key(id, NodeType::TRANSFORM); - add_relation(object_transform_key, shading_key, "Object Transform -> Node"); + add_relation(object_transform_key, ntree_output_key, "Object Transform -> Node"); if (object_have_geometry_component(reinterpret_cast<Object *>(id))) { ComponentKey object_geometry_key(id, NodeType::GEOMETRY); - add_relation(object_geometry_key, shading_key, "Object Geometry -> Node"); + add_relation(object_geometry_key, ntree_output_key, "Object Geometry -> Node"); } } else if (id_type == ID_SCE) { @@ -2498,23 +2498,28 @@ void DepsgraphRelationBuilder::build_nodetree(bNodeTree *ntree) else if (id_type == ID_MSK) { build_mask((Mask *)id); OperationKey mask_key(id, NodeType::PARAMETERS, OperationCode::MASK_EVAL); - add_relation(mask_key, shading_key, "Mask -> Node"); + add_relation(mask_key, ntree_output_key, "Mask -> Node"); } else if (id_type == ID_MC) { build_movieclip((MovieClip *)id); OperationKey clip_key(id, NodeType::PARAMETERS, OperationCode::MOVIECLIP_EVAL); - add_relation(clip_key, shading_key, "Clip -> Node"); + add_relation(clip_key, ntree_output_key, "Clip -> Node"); } else if (id_type == ID_VF) { build_vfont((VFont *)id); ComponentKey vfont_key(id, NodeType::GENERIC_DATABLOCK); - add_relation(vfont_key, shading_key, "VFont -> Node"); + add_relation(vfont_key, ntree_output_key, "VFont -> Node"); } else if (ELEM(bnode->type, NODE_GROUP, NODE_CUSTOM_GROUP)) { bNodeTree *group_ntree = (bNodeTree *)id; build_nodetree(group_ntree); - ComponentKey group_shading_key(&group_ntree->id, NodeType::SHADING); - add_relation(group_shading_key, shading_key, "Group Node"); + ComponentKey group_output_key(&group_ntree->id, NodeType::NTREE_OUTPUT); + /* The output of the current tree does not necessarily change when the output of the group + * changed. The parent node group is currently explicitly tagged for update in + * #ED_node_tree_propagate_change. In the future we could move this relation to the + * depsgraph, but then the depsgraph has to do some more static analysis of the node tree to + * see which groups the output actually depends on. */ + add_relation(group_output_key, ntree_output_key, "Group Node", RELATION_FLAG_NO_FLUSH); } else { BLI_assert_msg(0, "Unknown ID type used for node"); @@ -2528,14 +2533,10 @@ void DepsgraphRelationBuilder::build_nodetree(bNodeTree *ntree) build_idproperties(socket->prop); } - OperationKey shading_update_key(&ntree->id, NodeType::SHADING, OperationCode::MATERIAL_UPDATE); - if (check_id_has_anim_component(&ntree->id)) { ComponentKey animation_key(&ntree->id, NodeType::ANIMATION); - add_relation(animation_key, shading_update_key, "NTree Shading Parameters"); + add_relation(animation_key, ntree_output_key, "NTree Shading Parameters"); } - ComponentKey parameters_key(&ntree->id, NodeType::PARAMETERS); - add_relation(parameters_key, shading_update_key, "NTree Shading Parameters"); } /* Recursively build graph for material */ @@ -2558,7 +2559,7 @@ void DepsgraphRelationBuilder::build_material(Material *material) if (material->nodetree != nullptr) { build_nodetree(material->nodetree); OperationKey ntree_key( - &material->nodetree->id, NodeType::SHADING, OperationCode::MATERIAL_UPDATE); + &material->nodetree->id, NodeType::NTREE_OUTPUT, OperationCode::NTREE_OUTPUT); add_relation(ntree_key, material_key, "Material's NTree"); build_nested_nodetree(&material->id, material->nodetree); } @@ -2587,8 +2588,13 @@ void DepsgraphRelationBuilder::build_texture(Tex *texture) build_parameters(&texture->id); /* texture's nodetree */ - build_nodetree(texture->nodetree); - build_nested_nodetree(&texture->id, texture->nodetree); + if (texture->nodetree) { + build_nodetree(texture->nodetree); + OperationKey ntree_key( + &texture->nodetree->id, NodeType::NTREE_OUTPUT, OperationCode::NTREE_OUTPUT); + add_relation(ntree_key, texture_key, "Texture's NTree"); + build_nested_nodetree(&texture->id, texture->nodetree); + } /* Special cases for different IDs which texture uses. */ if (texture->type == TEX_IMAGE) { @@ -2875,12 +2881,16 @@ void DepsgraphRelationBuilder::build_copy_on_write_relations() * * This is similar to what happens in ntree_hack_remap_pointers(). */ -void DepsgraphRelationBuilder::build_nested_datablock(ID *owner, ID *id) +void DepsgraphRelationBuilder::build_nested_datablock(ID *owner, ID *id, bool flush_cow_changes) { + int relation_flag = 0; + if (!flush_cow_changes) { + relation_flag |= RELATION_FLAG_NO_FLUSH; + } OperationKey owner_copy_on_write_key( owner, NodeType::COPY_ON_WRITE, OperationCode::COPY_ON_WRITE); OperationKey id_copy_on_write_key(id, NodeType::COPY_ON_WRITE, OperationCode::COPY_ON_WRITE); - add_relation(id_copy_on_write_key, owner_copy_on_write_key, "Eval Order"); + add_relation(id_copy_on_write_key, owner_copy_on_write_key, "Eval Order", relation_flag); } void DepsgraphRelationBuilder::build_nested_nodetree(ID *owner, bNodeTree *ntree) @@ -2888,7 +2898,10 @@ void DepsgraphRelationBuilder::build_nested_nodetree(ID *owner, bNodeTree *ntree if (ntree == nullptr) { return; } - build_nested_datablock(owner, &ntree->id); + /* Don't flush cow changes, because the node tree may change in ways that do not affect the + * owner data block (e.g. when a node is deleted that is not connected to any output). + * Data blocks owning node trees should add a relation to the `NTREE_OUTPUT` node instead. */ + build_nested_datablock(owner, &ntree->id, false); } void DepsgraphRelationBuilder::build_nested_shapekey(ID *owner, Key *key) @@ -2896,7 +2909,7 @@ void DepsgraphRelationBuilder::build_nested_shapekey(ID *owner, Key *key) if (key == nullptr) { return; } - build_nested_datablock(owner, &key->id); + build_nested_datablock(owner, &key->id, true); } void DepsgraphRelationBuilder::build_copy_on_write_relations(IDNode *id_node) diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations.h b/source/blender/depsgraph/intern/builder/deg_builder_relations.h index 09003de3ce4..787e3799029 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_relations.h +++ b/source/blender/depsgraph/intern/builder/deg_builder_relations.h @@ -299,7 +299,7 @@ class DepsgraphRelationBuilder : public DepsgraphBuilder { virtual void build_scene_speakers(Scene *scene, ViewLayer *view_layer); virtual void build_vfont(VFont *vfont); - virtual void build_nested_datablock(ID *owner, ID *id); + virtual void build_nested_datablock(ID *owner, ID *id, bool flush_cow_changes); virtual void build_nested_nodetree(ID *owner, bNodeTree *ntree); virtual void build_nested_shapekey(ID *owner, Key *key); diff --git a/source/blender/depsgraph/intern/builder/deg_builder_rna.cc b/source/blender/depsgraph/intern/builder/deg_builder_rna.cc index 40e59875832..43d24125dc2 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_rna.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_rna.cc @@ -351,7 +351,7 @@ RNANodeIdentifier RNANodeQuery::construct_node_identifier(const PointerRNA *ptr, return node_identifier; } else if (RNA_struct_is_a(ptr->type, &RNA_NodeSocket)) { - node_identifier.type = NodeType::SHADING; + node_identifier.type = NodeType::NTREE_OUTPUT; return node_identifier; } else if (RNA_struct_is_a(ptr->type, &RNA_ShaderNode)) { diff --git a/source/blender/depsgraph/intern/debug/deg_debug_relations_graphviz.cc b/source/blender/depsgraph/intern/debug/deg_debug_relations_graphviz.cc index 7be661d9668..36120ae76d1 100644 --- a/source/blender/depsgraph/intern/debug/deg_debug_relations_graphviz.cc +++ b/source/blender/depsgraph/intern/debug/deg_debug_relations_graphviz.cc @@ -425,6 +425,7 @@ static void deg_debug_graphviz_node(DotExportContext &ctx, case NodeType::ARMATURE: case NodeType::GENERIC_DATABLOCK: case NodeType::VISIBILITY: + case NodeType::NTREE_OUTPUT: case NodeType::SIMULATION: { ComponentNode *comp_node = (ComponentNode *)node; if (comp_node->operations.is_empty()) { diff --git a/source/blender/depsgraph/intern/depsgraph_build.cc b/source/blender/depsgraph/intern/depsgraph_build.cc index db00c595383..acb8fa8fe88 100644 --- a/source/blender/depsgraph/intern/depsgraph_build.cc +++ b/source/blender/depsgraph/intern/depsgraph_build.cc @@ -148,15 +148,15 @@ void DEG_add_simulation_relation(DepsNodeHandle *node_handle, deg_node_handle->builder->add_node_handle_relation(operation_key, deg_node_handle, description); } -void DEG_add_node_tree_relation(DepsNodeHandle *node_handle, - bNodeTree *node_tree, - const char *description) +void DEG_add_node_tree_output_relation(DepsNodeHandle *node_handle, + bNodeTree *node_tree, + const char *description) { - /* Using shading key, because that's the one that exists right now. Should use something else in - * the future. */ - deg::ComponentKey shading_key(&node_tree->id, deg::NodeType::SHADING); + deg::OperationKey ntree_output_key( + &node_tree->id, deg::NodeType::NTREE_OUTPUT, deg::OperationCode::NTREE_OUTPUT); deg::DepsNodeHandle *deg_node_handle = get_node_handle(node_handle); - deg_node_handle->builder->add_node_handle_relation(shading_key, deg_node_handle, description); + deg_node_handle->builder->add_node_handle_relation( + ntree_output_key, deg_node_handle, description); } void DEG_add_object_cache_relation(DepsNodeHandle *node_handle, diff --git a/source/blender/depsgraph/intern/depsgraph_query_iter.cc b/source/blender/depsgraph/intern/depsgraph_query_iter.cc index 7af3d03d478..c84adbcde00 100644 --- a/source/blender/depsgraph/intern/depsgraph_query_iter.cc +++ b/source/blender/depsgraph/intern/depsgraph_query_iter.cc @@ -334,10 +334,13 @@ static void DEG_iterator_ids_step(BLI_Iterator *iter, deg::IDNode *id_node, bool return; } if (only_updated && !(id_cow->recalc & ID_RECALC_ALL)) { - bNodeTree *ntree = ntreeFromID(id_cow); - /* Node-tree is considered part of the data-block. */ - if (!(ntree && (ntree->id.recalc & ID_RECALC_ALL))) { + bNodeTree *ntree = ntreeFromID(id_cow); + if (ntree == nullptr) { + iter->skip = true; + return; + } + if ((ntree->id.recalc & ID_RECALC_NTREE_OUTPUT) == 0) { iter->skip = true; return; } diff --git a/source/blender/depsgraph/intern/depsgraph_tag.cc b/source/blender/depsgraph/intern/depsgraph_tag.cc index 2b0d0e6e780..4bc9e0d2d14 100644 --- a/source/blender/depsgraph/intern/depsgraph_tag.cc +++ b/source/blender/depsgraph/intern/depsgraph_tag.cc @@ -232,6 +232,10 @@ void depsgraph_tag_to_component_opcode(const ID *id, break; case ID_RECALC_TAG_FOR_UNDO: break; /* Must be ignored by depsgraph. */ + case ID_RECALC_NTREE_OUTPUT: + *component_type = NodeType::NTREE_OUTPUT; + *operation_code = OperationCode::NTREE_OUTPUT; + break; } } @@ -749,6 +753,8 @@ const char *DEG_update_tag_as_string(IDRecalcFlag flag) return "ALL"; case ID_RECALC_TAG_FOR_UNDO: return "TAG_FOR_UNDO"; + case ID_RECALC_NTREE_OUTPUT: + return "ID_RECALC_NTREE_OUTPUT"; } return nullptr; } diff --git a/source/blender/depsgraph/intern/node/deg_node.cc b/source/blender/depsgraph/intern/node/deg_node.cc index 8bc03d8b736..075bfd35ec1 100644 --- a/source/blender/depsgraph/intern/node/deg_node.cc +++ b/source/blender/depsgraph/intern/node/deg_node.cc @@ -116,6 +116,8 @@ const char *nodeTypeAsString(NodeType type) return "VISIBILITY"; case NodeType::SIMULATION: return "SIMULATION"; + case NodeType::NTREE_OUTPUT: + return "NTREE_OUTPUT"; /* Total number of meaningful node types. */ case NodeType::NUM_TYPES: @@ -174,6 +176,7 @@ eDepsSceneComponentType nodeTypeToSceneComponent(NodeType type) case NodeType::CACHE: case NodeType::PROXY: case NodeType::SIMULATION: + case NodeType::NTREE_OUTPUT: return DEG_SCENE_COMP_PARAMETERS; case NodeType::VISIBILITY: @@ -251,6 +254,7 @@ eDepsObjectComponentType nodeTypeToObjectComponent(NodeType type) case NodeType::DUPLI: case NodeType::SYNCHRONIZATION: case NodeType::SIMULATION: + case NodeType::NTREE_OUTPUT: case NodeType::UNDEFINED: case NodeType::NUM_TYPES: return DEG_OB_COMP_PARAMETERS; diff --git a/source/blender/depsgraph/intern/node/deg_node.h b/source/blender/depsgraph/intern/node/deg_node.h index 7e093ab8765..ef58a35afb2 100644 --- a/source/blender/depsgraph/intern/node/deg_node.h +++ b/source/blender/depsgraph/intern/node/deg_node.h @@ -147,6 +147,8 @@ enum class NodeType { SYNCHRONIZATION, /* Simulation component. */ SIMULATION, + /* Node tree output component. */ + NTREE_OUTPUT, /* Total number of meaningful node types. */ NUM_TYPES, diff --git a/source/blender/depsgraph/intern/node/deg_node_component.cc b/source/blender/depsgraph/intern/node/deg_node_component.cc index b716877902c..4c430904e44 100644 --- a/source/blender/depsgraph/intern/node/deg_node_component.cc +++ b/source/blender/depsgraph/intern/node/deg_node_component.cc @@ -351,6 +351,7 @@ DEG_COMPONENT_NODE_DEFINE(Armature, ARMATURE, 0); DEG_COMPONENT_NODE_DEFINE(GenericDatablock, GENERIC_DATABLOCK, 0); DEG_COMPONENT_NODE_DEFINE(Visibility, VISIBILITY, 0); DEG_COMPONENT_NODE_DEFINE(Simulation, SIMULATION, 0); +DEG_COMPONENT_NODE_DEFINE(NTreeOutput, NTREE_OUTPUT, ID_RECALC_NTREE_OUTPUT); /** \} */ @@ -385,6 +386,7 @@ void deg_register_component_depsnodes() register_node_typeinfo(&DNTI_GENERIC_DATABLOCK); register_node_typeinfo(&DNTI_VISIBILITY); register_node_typeinfo(&DNTI_SIMULATION); + register_node_typeinfo(&DNTI_NTREE_OUTPUT); } /** \} */ diff --git a/source/blender/depsgraph/intern/node/deg_node_component.h b/source/blender/depsgraph/intern/node/deg_node_component.h index 9f108af8012..8fd28dfc497 100644 --- a/source/blender/depsgraph/intern/node/deg_node_component.h +++ b/source/blender/depsgraph/intern/node/deg_node_component.h @@ -222,6 +222,7 @@ DEG_COMPONENT_NODE_DECLARE_GENERIC(Armature); DEG_COMPONENT_NODE_DECLARE_GENERIC(GenericDatablock); DEG_COMPONENT_NODE_DECLARE_NO_COW(Visibility); DEG_COMPONENT_NODE_DECLARE_GENERIC(Simulation); +DEG_COMPONENT_NODE_DECLARE_GENERIC(NTreeOutput); /* Bone Component */ struct BoneComponentNode : public ComponentNode { diff --git a/source/blender/depsgraph/intern/node/deg_node_operation.cc b/source/blender/depsgraph/intern/node/deg_node_operation.cc index eaae5d2d5dc..2d0d6854851 100644 --- a/source/blender/depsgraph/intern/node/deg_node_operation.cc +++ b/source/blender/depsgraph/intern/node/deg_node_operation.cc @@ -182,6 +182,9 @@ const char *operationCodeAsString(OperationCode opcode) return "LIGHT_UPDATE"; case OperationCode::WORLD_UPDATE: return "WORLD_UPDATE"; + /* Node Tree. */ + case OperationCode::NTREE_OUTPUT: + return "NTREE_OUTPUT"; /* Movie clip. */ case OperationCode::MOVIECLIP_EVAL: return "MOVIECLIP_EVAL"; diff --git a/source/blender/depsgraph/intern/node/deg_node_operation.h b/source/blender/depsgraph/intern/node/deg_node_operation.h index 31cbb9702ba..4abdc014946 100644 --- a/source/blender/depsgraph/intern/node/deg_node_operation.h +++ b/source/blender/depsgraph/intern/node/deg_node_operation.h @@ -177,6 +177,9 @@ enum class OperationCode { LIGHT_UPDATE, WORLD_UPDATE, + /* Node Tree. ----------------------------------------------------------- */ + NTREE_OUTPUT, + /* Batch caches. -------------------------------------------------------- */ GEOMETRY_SELECT_UPDATE, diff --git a/source/blender/draw/engines/gpencil/gpencil_shader_fx.c b/source/blender/draw/engines/gpencil/gpencil_shader_fx.c index 315133186da..dfff45cbca5 100644 --- a/source/blender/draw/engines/gpencil/gpencil_shader_fx.c +++ b/source/blender/draw/engines/gpencil/gpencil_shader_fx.c @@ -99,6 +99,11 @@ static void gpencil_vfx_blur(BlurShaderFxData *fx, Object *ob, gpIterVfxData *it return; } + if ((fx->flag & FX_BLUR_DOF_MODE) && iter->pd->camera == NULL) { + /* No blur outside camera view (or when DOF is disabled on the camera). */ + return; + } + DRWShadingGroup *grp; const float s = sin(fx->rotation); const float c = cos(fx->rotation); @@ -108,7 +113,7 @@ static void gpencil_vfx_blur(BlurShaderFxData *fx, Object *ob, gpIterVfxData *it DRW_view_persmat_get(NULL, persmat, false); const float w = fabsf(mul_project_m4_v3_zfac(persmat, ob->obmat[3])); - if ((fx->flag & FX_BLUR_DOF_MODE) && iter->pd->camera != NULL) { + if ((fx->flag & FX_BLUR_DOF_MODE)) { /* Compute circle of confusion size. */ float coc = (iter->pd->dof_params[0] / -w) - iter->pd->dof_params[1]; copy_v2_fl(blur_size, fabsf(coc)); diff --git a/source/blender/editors/animation/drivers.c b/source/blender/editors/animation/drivers.c index f509898f2ee..e99e4a63786 100644 --- a/source/blender/editors/animation/drivers.c +++ b/source/blender/editors/animation/drivers.c @@ -879,10 +879,10 @@ EnumPropertyItem prop_driver_create_mapping_types[] = { }; /* Filtering callback for driver mapping types enum */ -static const EnumPropertyItem *driver_mapping_type_itemsf(bContext *C, - PointerRNA *UNUSED(owner_ptr), - PropertyRNA *UNUSED(owner_prop), - bool *r_free) +static const EnumPropertyItem *driver_mapping_type_itemf(bContext *C, + PointerRNA *UNUSED(owner_ptr), + PropertyRNA *UNUSED(owner_prop), + bool *r_free) { EnumPropertyItem *input = prop_driver_create_mapping_types; EnumPropertyItem *item = NULL; @@ -1039,7 +1039,7 @@ static void UNUSED_FUNCTION(ANIM_OT_driver_button_add_menu)(wmOperatorType *ot) 0, "Mapping Type", "Method used to match target and driven properties"); - RNA_def_enum_funcs(ot->prop, driver_mapping_type_itemsf); + RNA_def_enum_funcs(ot->prop, driver_mapping_type_itemf); } /* Add Driver Button Operator ------------------------ */ diff --git a/source/blender/editors/animation/keyframes_general.c b/source/blender/editors/animation/keyframes_general.c index dc5d71b5a1e..4fa5dee99a6 100644 --- a/source/blender/editors/animation/keyframes_general.c +++ b/source/blender/editors/animation/keyframes_general.c @@ -345,6 +345,28 @@ static bool find_fcurve_segment(FCurve *fcu, return in_segment; } +/* Return a list of FCurveSegment with a start index and a length. + * A segment is a continuous selection of keyframes. + * Keys that have BEZT_FLAG_IGNORE_TAG set are treated as unselected. + * The caller is responsible for freeing the memory. */ +ListBase find_fcurve_segments(FCurve *fcu) +{ + ListBase segments = {NULL, NULL}; + int segment_start_idx = 0; + int segment_len = 0; + int current_index = 0; + + while (find_fcurve_segment(fcu, current_index, &segment_start_idx, &segment_len)) { + FCurveSegment *segment; + segment = MEM_callocN(sizeof(*segment), "FCurveSegment"); + segment->start_index = segment_start_idx; + segment->length = segment_len; + BLI_addtail(&segments, segment); + current_index = segment_start_idx + segment_len; + } + return segments; +} + /* ---------------- */ /* Check if the keyframe interpolation type is supported */ @@ -440,15 +462,12 @@ bool decimate_fcurve(bAnimListElem *ale, float remove_ratio, float error_sq_max) fcu->bezt[i].f2 &= ~BEZT_FLAG_TEMP_TAG; } - /* Only decimate the individual selected curve segments. */ - int segment_start_idx = 0; - int segment_len = 0; - int current_index = 0; - - while (find_fcurve_segment(fcu, current_index, &segment_start_idx, &segment_len)) { - decimate_fcurve_segment(fcu, segment_start_idx, segment_len, remove_ratio, error_sq_max); - current_index = segment_start_idx + segment_len; + ListBase segments = find_fcurve_segments(fcu); + LISTBASE_FOREACH (FCurveSegment *, segment, &segments) { + decimate_fcurve_segment( + fcu, segment->start_index, segment->length, remove_ratio, error_sq_max); } + BLI_freelistN(&segments); uint old_totvert = fcu->totvert; fcu->bezt = NULL; diff --git a/source/blender/editors/armature/pose_edit.c b/source/blender/editors/armature/pose_edit.c index 772fe8f3196..8bd6c9f54fd 100644 --- a/source/blender/editors/armature/pose_edit.c +++ b/source/blender/editors/armature/pose_edit.c @@ -160,7 +160,7 @@ static bool pose_has_protected_selected(Object *ob, short warn) bArmature *arm = ob->data; for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) { - if (pchan->bone && (pchan->bone->layer & arm->layer)) { + if (pchan->bone && BKE_pose_is_layer_visible(arm, pchan)) { if (pchan->bone->layer & arm->layer_protected) { if (pchan->bone->flag & BONE_SELECTED) { break; diff --git a/source/blender/editors/armature/pose_lib.c b/source/blender/editors/armature/pose_lib.c index 646356e7a45..dc96c777be0 100644 --- a/source/blender/editors/armature/pose_lib.c +++ b/source/blender/editors/armature/pose_lib.c @@ -1070,7 +1070,7 @@ static void poselib_apply_pose(tPoseLib_PreviewData *pld, else if (pchan->bone) { /* only ok if bone is visible and selected */ if ((pchan->bone->flag & BONE_SELECTED) && (pchan->bone->flag & BONE_HIDDEN_P) == 0 && - (pchan->bone->layer & arm->layer)) { + BKE_pose_is_layer_visible(arm, pchan)) { ok = 1; } } diff --git a/source/blender/editors/armature/pose_select.c b/source/blender/editors/armature/pose_select.c index 17347aa57fe..0b889149f9d 100644 --- a/source/blender/editors/armature/pose_select.c +++ b/source/blender/editors/armature/pose_select.c @@ -746,7 +746,7 @@ static int pose_select_hierarchy_exec(bContext *C, wmOperator *op) const bool add_to_sel = RNA_boolean_get(op->ptr, "extend"); bool changed = false; - pchan_act = BKE_pose_channel_active(ob); + pchan_act = BKE_pose_channel_active_if_layer_visible(ob); if (pchan_act == NULL) { return OPERATOR_CANCELLED; } diff --git a/source/blender/editors/asset/intern/asset_indexer.cc b/source/blender/editors/asset/intern/asset_indexer.cc index 4107d28b5e3..fa55560d3c8 100644 --- a/source/blender/editors/asset/intern/asset_indexer.cc +++ b/source/blender/editors/asset/intern/asset_indexer.cc @@ -742,16 +742,15 @@ static void update_index(const char *filename, FileIndexerEntries *entries, void static void *init_user_data(const char *root_directory, size_t root_directory_maxlen) { - AssetLibraryIndex *library_index = OBJECT_GUARDED_NEW( - AssetLibraryIndex, - StringRef(root_directory, BLI_strnlen(root_directory, root_directory_maxlen))); + AssetLibraryIndex *library_index = MEM_new<AssetLibraryIndex>( + __func__, StringRef(root_directory, BLI_strnlen(root_directory, root_directory_maxlen))); library_index->init_unused_index_files(); return library_index; } static void free_user_data(void *user_data) { - OBJECT_GUARDED_DELETE(user_data, AssetLibraryIndex); + MEM_delete((AssetLibraryIndex *)user_data); } static void filelist_finished(void *user_data) diff --git a/source/blender/editors/asset/intern/asset_ops.cc b/source/blender/editors/asset/intern/asset_ops.cc index e4edff19a21..f7755aa9fea 100644 --- a/source/blender/editors/asset/intern/asset_ops.cc +++ b/source/blender/editors/asset/intern/asset_ops.cc @@ -934,10 +934,10 @@ static bool has_external_files(Main *bmain, struct ReportList *reports) BKE_reportf( callback_info.reports, RPT_ERROR, - "Unable to copy bundle due to %ld external dependencies; more details on the console", - callback_info.external_files.size()); - printf("Unable to copy bundle due to %ld external dependencies:\n", - callback_info.external_files.size()); + "Unable to copy bundle due to %zu external dependencies; more details on the console", + (size_t)callback_info.external_files.size()); + printf("Unable to copy bundle due to %zu external dependencies:\n", + (size_t)callback_info.external_files.size()); for (const std::string &path : callback_info.external_files) { printf(" \"%s\"\n", path.c_str()); } diff --git a/source/blender/editors/asset/intern/asset_temp_id_consumer.cc b/source/blender/editors/asset/intern/asset_temp_id_consumer.cc index f136c08f129..8790c907f05 100644 --- a/source/blender/editors/asset/intern/asset_temp_id_consumer.cc +++ b/source/blender/editors/asset/intern/asset_temp_id_consumer.cc @@ -88,12 +88,13 @@ AssetTempIDConsumer *ED_asset_temp_id_consumer_create(const AssetHandle *handle) } BLI_assert(handle->file_data->asset_data != nullptr); return reinterpret_cast<AssetTempIDConsumer *>( - OBJECT_GUARDED_NEW(AssetTemporaryIDConsumer, *handle)); + MEM_new<AssetTemporaryIDConsumer>(__func__, *handle)); } void ED_asset_temp_id_consumer_free(AssetTempIDConsumer **consumer) { - OBJECT_GUARDED_SAFE_DELETE(*consumer, AssetTemporaryIDConsumer); + MEM_delete(reinterpret_cast<AssetTemporaryIDConsumer *>(*consumer)); + *consumer = nullptr; } ID *ED_asset_temp_id_consumer_ensure_local_id(AssetTempIDConsumer *consumer_, diff --git a/source/blender/editors/gpencil/gpencil_paint.c b/source/blender/editors/gpencil/gpencil_paint.c index dabe2050b28..d1fbd3bc507 100644 --- a/source/blender/editors/gpencil/gpencil_paint.c +++ b/source/blender/editors/gpencil/gpencil_paint.c @@ -1567,6 +1567,12 @@ static void gpencil_stroke_eraser_dostroke(tGPsdata *p, } bGPDspoint npt; + gpencil_point_to_parent_space(pt1, p->diff_mat, &npt); + gpencil_point_to_xy(&p->gsc, gps, &npt, &pc1[0], &pc1[1]); + + gpencil_point_to_parent_space(pt2, p->diff_mat, &npt); + gpencil_point_to_xy(&p->gsc, gps, &npt, &pc2[0], &pc2[1]); + if (pt0) { gpencil_point_to_parent_space(pt0, p->diff_mat, &npt); gpencil_point_to_xy(&p->gsc, gps, &npt, &pc0[0], &pc0[1]); @@ -1576,12 +1582,6 @@ static void gpencil_stroke_eraser_dostroke(tGPsdata *p, copy_v2_v2_int(pc0, pc1); } - gpencil_point_to_parent_space(pt1, p->diff_mat, &npt); - gpencil_point_to_xy(&p->gsc, gps, &npt, &pc1[0], &pc1[1]); - - gpencil_point_to_parent_space(pt2, p->diff_mat, &npt); - gpencil_point_to_xy(&p->gsc, gps, &npt, &pc2[0], &pc2[1]); - /* Check that point segment of the boundbox of the eraser stroke */ if (((!ELEM(V2D_IS_CLIPPED, pc0[0], pc0[1])) && BLI_rcti_isect_pt(rect, pc0[0], pc0[1])) || ((!ELEM(V2D_IS_CLIPPED, pc1[0], pc1[1])) && BLI_rcti_isect_pt(rect, pc1[0], pc1[1])) || diff --git a/source/blender/editors/include/ED_keyframes_edit.h b/source/blender/editors/include/ED_keyframes_edit.h index bafe68bd28d..8a7831db0ea 100644 --- a/source/blender/editors/include/ED_keyframes_edit.h +++ b/source/blender/editors/include/ED_keyframes_edit.h @@ -380,6 +380,11 @@ bool delete_fcurve_keys(struct FCurve *fcu); void clear_fcurve_keys(struct FCurve *fcu); void duplicate_fcurve_keys(struct FCurve *fcu); +typedef struct FCurveSegment { + struct FCurveSegment *next, *prev; + int start_index, length; +} FCurveSegment; +ListBase find_fcurve_segments(struct FCurve *fcu); void clean_fcurve(struct bAnimContext *ac, struct bAnimListElem *ale, float thresh, diff --git a/source/blender/editors/include/ED_node.h b/source/blender/editors/include/ED_node.h index 5bac452c7c9..db1ba80dd3c 100644 --- a/source/blender/editors/include/ED_node.h +++ b/source/blender/editors/include/ED_node.h @@ -100,7 +100,7 @@ void ED_node_socket_draw(struct bNodeSocket *sock, float scale); void ED_node_tree_update(const struct bContext *C); void ED_node_tag_update_id(struct ID *id); -void ED_node_tag_update_nodetree(struct Main *bmain, struct bNodeTree *ntree, struct bNode *node); + /** * Sort nodes by selection: unselected nodes first, then selected, * then the active node at the very end. Relative order is kept intact. @@ -152,6 +152,26 @@ void ED_node_set_active(struct Main *bmain, bool *r_active_texture_changed); /** + * Call after one or more node trees have been changed and tagged accordingly. + * + * This function will make sure that other parts of Blender update accordingly. For example, if the + * node group interface changed, parent node groups have to be updated as well. + * + * Additionally, this will send notifiers and tag the depsgraph based on the changes. Depsgraph + * relation updates have to be triggered by the caller. + * + * \param C: Context if available. This can be null. + * \param bmain: Main whose data-blocks should be updated based on the changes. + * \param ntree: Under some circumstances the caller knows that only one node tree has + * changed since the last update. In this case the function may be able to skip scanning #bmain + * for other things that have to be changed. It may still scan #bmain if the interface of the + * node tree has changed. + */ +void ED_node_tree_propagate_change(const struct bContext *C, + struct Main *bmain, + struct bNodeTree *ntree); + +/** * \param scene_owner: is the owner of the job, * we don't use it for anything else currently so could also be a void pointer, * but for now keep it an 'Scene' for consistency. diff --git a/source/blender/editors/interface/interface_ops.c b/source/blender/editors/interface/interface_ops.c index 61869f3da41..35b621b0272 100644 --- a/source/blender/editors/interface/interface_ops.c +++ b/source/blender/editors/interface/interface_ops.c @@ -1023,8 +1023,8 @@ bool UI_context_copy_to_selected_check(PointerRNA *ptr, return false; } - /* Skip non-existing properties on link. This was previously covered with the lprop != prop check - * but we are now more permissive when it comes to ID properties, see below. */ + /* Skip non-existing properties on link. This was previously covered with the `lprop != prop` + * check but we are now more permissive when it comes to ID properties, see below. */ if (lprop == NULL) { return false; } @@ -1033,19 +1033,19 @@ bool UI_context_copy_to_selected_check(PointerRNA *ptr, return false; } - /* Check property pointers matching - * For ID properties, these pointers match - * - if the property is API defined on an existing class (and they are equally named) - * - never for ID properties on specific ID (even if they are equally named) - * - never for NodesModifierSettings properties (even if they are equally named) + /* Check property pointers matching. + * For ID properties, these pointers match: + * - If the property is API defined on an existing class (and they are equally named). + * - Never for ID properties on specific ID (even if they are equally named). + * - Never for NodesModifierSettings properties (even if they are equally named). * * Be permissive on ID properties in the following cases: - * - NodesModifierSettings properties - * - (special check: only if the nodegroup matches, since the 'Input_n' properties are name - * based and similar on potentionally very different nodegroups) + * - #NodesModifierSettings properties + * - (special check: only if the node-group matches, since the 'Input_n' properties are name + * based and similar on potentially very different node-groups). * - ID properties on specific ID - * - (no special check, copying seems OK [even if type does not match -- does not do anything - * then]) + * - (no special check, copying seems OK [even if type does not match -- does not do anything + * then]) */ bool ignore_prop_eq = RNA_property_is_idprop(lprop) && RNA_property_is_idprop(prop); if (RNA_struct_is_a(lptr.type, &RNA_NodesModifier) && diff --git a/source/blender/editors/interface/interface_templates.c b/source/blender/editors/interface/interface_templates.c index 166a8911ca1..0b57b6adcc6 100644 --- a/source/blender/editors/interface/interface_templates.c +++ b/source/blender/editors/interface/interface_templates.c @@ -2672,7 +2672,7 @@ static void constraint_ops_extra_draw(bContext *C, uiLayout *layout, void *con_v static void draw_constraint_header(uiLayout *layout, Object *ob, bConstraint *con) { - bPoseChannel *pchan = BKE_pose_channel_active(ob); + bPoseChannel *pchan = BKE_pose_channel_active_if_layer_visible(ob); short proxy_protected, xco = 0, yco = 0; // int rb_col; // UNUSED diff --git a/source/blender/editors/interface/interface_view.cc b/source/blender/editors/interface/interface_view.cc index 500834f4434..81b24c75020 100644 --- a/source/blender/editors/interface/interface_view.cc +++ b/source/blender/editors/interface/interface_view.cc @@ -60,7 +60,7 @@ AbstractTreeView *UI_block_add_view(uiBlock &block, StringRef idname, std::unique_ptr<AbstractTreeView> tree_view) { - ViewLink *view_link = OBJECT_GUARDED_NEW(ViewLink); + ViewLink *view_link = MEM_new<ViewLink>(__func__); BLI_addtail(&block.views, view_link); view_link->view = std::move(tree_view); @@ -72,7 +72,7 @@ AbstractTreeView *UI_block_add_view(uiBlock &block, void ui_block_free_views(uiBlock *block) { LISTBASE_FOREACH_MUTABLE (ViewLink *, link, &block->views) { - OBJECT_GUARDED_DELETE(link, ViewLink); + MEM_delete(link); } } diff --git a/source/blender/editors/object/object_constraint.c b/source/blender/editors/object/object_constraint.c index 5c3a8fc2277..91a512ae8e9 100644 --- a/source/blender/editors/object/object_constraint.c +++ b/source/blender/editors/object/object_constraint.c @@ -89,7 +89,7 @@ ListBase *ED_object_constraint_active_list(Object *ob) if (ob->mode & OB_MODE_POSE) { bPoseChannel *pchan; - pchan = BKE_pose_channel_active(ob); + pchan = BKE_pose_channel_active_if_layer_visible(ob); if (pchan) { return &pchan->constraints; } @@ -2215,7 +2215,7 @@ static bool get_new_constraint_target( bContext *C, int con_type, Object **tar_ob, bPoseChannel **tar_pchan, bool add) { Object *obact = ED_object_active_context(C); - bPoseChannel *pchanact = BKE_pose_channel_active(obact); + bPoseChannel *pchanact = BKE_pose_channel_active_if_layer_visible(obact); bool only_curve = false, only_mesh = false, only_ob = false; bool found = false; @@ -2370,7 +2370,7 @@ static int constraint_add_exec( pchan = NULL; } else { - pchan = BKE_pose_channel_active(ob); + pchan = BKE_pose_channel_active_if_layer_visible(ob); /* ensure not to confuse object/pose adding */ if (pchan == NULL) { @@ -2650,7 +2650,7 @@ void POSE_OT_constraint_add_with_targets(wmOperatorType *ot) static int pose_ik_add_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) { Object *ob = BKE_object_pose_armature_get(CTX_data_active_object(C)); - bPoseChannel *pchan = BKE_pose_channel_active(ob); + bPoseChannel *pchan = BKE_pose_channel_active_if_layer_visible(ob); bConstraint *con = NULL; uiPopupMenu *pup; diff --git a/source/blender/editors/object/object_edit.c b/source/blender/editors/object/object_edit.c index f06744068d5..38d0a044cb4 100644 --- a/source/blender/editors/object/object_edit.c +++ b/source/blender/editors/object/object_edit.c @@ -1638,10 +1638,10 @@ void OBJECT_OT_shade_smooth(wmOperatorType *ot) /** \name Object Mode Set Operator * \{ */ -static const EnumPropertyItem *object_mode_set_itemsf(bContext *C, - PointerRNA *UNUSED(ptr), - PropertyRNA *UNUSED(prop), - bool *r_free) +static const EnumPropertyItem *object_mode_set_itemf(bContext *C, + PointerRNA *UNUSED(ptr), + PropertyRNA *UNUSED(prop), + bool *r_free) { const EnumPropertyItem *input = rna_enum_object_mode_items; EnumPropertyItem *item = NULL; @@ -1790,7 +1790,7 @@ void OBJECT_OT_mode_set(wmOperatorType *ot) ot->prop = RNA_def_enum( ot->srna, "mode", rna_enum_object_mode_items, OB_MODE_OBJECT, "Mode", ""); - RNA_def_enum_funcs(ot->prop, object_mode_set_itemsf); + RNA_def_enum_funcs(ot->prop, object_mode_set_itemf); RNA_def_property_flag(ot->prop, PROP_SKIP_SAVE); prop = RNA_def_boolean(ot->srna, "toggle", 0, "Toggle", ""); diff --git a/source/blender/editors/object/object_hook.c b/source/blender/editors/object/object_hook.c index 5065a2c00f0..51967ff35c7 100644 --- a/source/blender/editors/object/object_hook.c +++ b/source/blender/editors/object/object_hook.c @@ -582,7 +582,7 @@ static int add_hook_object(const bContext *C, BLI_strncpy(hmd->subtarget, arm->act_bone->name, sizeof(hmd->subtarget)); - pchan_act = BKE_pose_channel_active(ob); + pchan_act = BKE_pose_channel_active_if_layer_visible(ob); if (LIKELY(pchan_act)) { invert_m4_m4(pose_mat, pchan_act->pose_mat); mul_v3_m4v3(cent, ob->obmat, pchan_act->pose_mat[3]); diff --git a/source/blender/editors/object/object_relations.c b/source/blender/editors/object/object_relations.c index 811f20e82be..71d9482597d 100644 --- a/source/blender/editors/object/object_relations.c +++ b/source/blender/editors/object/object_relations.c @@ -583,8 +583,8 @@ bool ED_object_parent_set(ReportList *reports, } case PAR_BONE: case PAR_BONE_RELATIVE: - pchan = BKE_pose_channel_active(par); - pchan_eval = BKE_pose_channel_active(parent_eval); + pchan = BKE_pose_channel_active_if_layer_visible(par); + pchan_eval = BKE_pose_channel_active_if_layer_visible(parent_eval); if (pchan == NULL) { BKE_report(reports, RPT_ERROR, "No active bone"); diff --git a/source/blender/editors/object/object_utils.c b/source/blender/editors/object/object_utils.c index 5f85f6ea0eb..df44d840ad3 100644 --- a/source/blender/editors/object/object_utils.c +++ b/source/blender/editors/object/object_utils.c @@ -115,7 +115,7 @@ bool ED_object_calc_active_center_for_posemode(Object *ob, const bool select_only, float r_center[3]) { - bPoseChannel *pchan = BKE_pose_channel_active(ob); + bPoseChannel *pchan = BKE_pose_channel_active_if_layer_visible(ob); if (pchan && (!select_only || (pchan->bone->flag & BONE_SELECTED))) { copy_v3_v3(r_center, pchan->pose_head); return true; diff --git a/source/blender/editors/render/render_internal.c b/source/blender/editors/render/render_internal.c index 29d829dc131..1a9b72c1fab 100644 --- a/source/blender/editors/render/render_internal.c +++ b/source/blender/editors/render/render_internal.c @@ -50,6 +50,7 @@ #include "BKE_lib_id.h" #include "BKE_main.h" #include "BKE_node.h" +#include "BKE_node_tree_update.h" #include "BKE_object.h" #include "BKE_report.h" #include "BKE_scene.h" @@ -725,7 +726,8 @@ static void render_endjob(void *rjv) rj->scene->r.scemode &= ~R_NO_FRAME_UPDATE; if (rj->single_layer) { - nodeUpdateID(rj->scene->nodetree, &rj->scene->id); + BKE_ntree_update_tag_id_changed(rj->main, &rj->scene->id); + BKE_ntree_update_main(rj->main, NULL); WM_main_add_notifier(NC_NODE | NA_EDITED, rj->scene); } diff --git a/source/blender/editors/screen/screen_context.c b/source/blender/editors/screen/screen_context.c index 304205d0cc4..04df90bf912 100644 --- a/source/blender/editors/screen/screen_context.c +++ b/source/blender/editors/screen/screen_context.c @@ -501,7 +501,7 @@ static eContextResult screen_ctx_active_pose_bone(const bContext *C, bContextDat Object *obact = view_layer->basact ? view_layer->basact->object : NULL; Object *obpose = BKE_object_pose_armature_get(obact); - bPoseChannel *pchan = BKE_pose_channel_active(obpose); + bPoseChannel *pchan = BKE_pose_channel_active_if_layer_visible(obpose); if (pchan) { CTX_data_pointer_set(result, &obpose->id, &RNA_PoseBone, pchan); return CTX_RESULT_OK; diff --git a/source/blender/editors/sculpt_paint/CMakeLists.txt b/source/blender/editors/sculpt_paint/CMakeLists.txt index b826ff8701d..517125f016e 100644 --- a/source/blender/editors/sculpt_paint/CMakeLists.txt +++ b/source/blender/editors/sculpt_paint/CMakeLists.txt @@ -59,6 +59,7 @@ set(SRC sculpt.c sculpt_automasking.c sculpt_boundary.c + sculpt_brushes.c sculpt_cloth.c sculpt_detail.c sculpt_dyntopo.c @@ -71,6 +72,7 @@ set(SRC sculpt_mask_expand.c sculpt_mask_init.c sculpt_multiplane_scrape.c + sculpt_ops.c sculpt_paint_color.c sculpt_pose.c sculpt_smooth.c diff --git a/source/blender/editors/sculpt_paint/paint_image_proj.c b/source/blender/editors/sculpt_paint/paint_image_proj.c index 7df5848e068..8a5d75d5f77 100644 --- a/source/blender/editors/sculpt_paint/paint_image_proj.c +++ b/source/blender/editors/sculpt_paint/paint_image_proj.c @@ -6623,7 +6623,7 @@ static bool proj_paint_add_slot(bContext *C, wmOperator *op) } } - ntreeUpdateTree(CTX_data_main(C), ntree); + ED_node_tree_propagate_change(C, bmain, ntree); /* In case we added more than one node, position them too. */ nodePositionPropagate(out_node); diff --git a/source/blender/editors/sculpt_paint/paint_intern.h b/source/blender/editors/sculpt_paint/paint_intern.h index 90887b9fc39..09013ea00f4 100644 --- a/source/blender/editors/sculpt_paint/paint_intern.h +++ b/source/blender/editors/sculpt_paint/paint_intern.h @@ -25,6 +25,8 @@ #include "BKE_paint.h" +#include "BLI_compiler_compat.h" +#include "BLI_math.h" #include "BLI_rect.h" #include "DNA_scene_types.h" @@ -404,8 +406,61 @@ bool facemask_paint_poll(struct bContext *C); /** * Uses symm to selectively flip any axis of a coordinate. */ -void flip_v3_v3(float out[3], const float in[3], const enum ePaintSymmetryFlags symm); -void flip_qt_qt(float out[4], const float in[4], const enum ePaintSymmetryFlags symm); + +BLI_INLINE void flip_v3_v3(float out[3], const float in[3], const ePaintSymmetryFlags symm) +{ + if (symm & PAINT_SYMM_X) { + out[0] = -in[0]; + } + else { + out[0] = in[0]; + } + if (symm & PAINT_SYMM_Y) { + out[1] = -in[1]; + } + else { + out[1] = in[1]; + } + if (symm & PAINT_SYMM_Z) { + out[2] = -in[2]; + } + else { + out[2] = in[2]; + } +} + +BLI_INLINE void flip_qt_qt(float out[4], const float in[4], const ePaintSymmetryFlags symm) +{ + float axis[3], angle; + + quat_to_axis_angle(axis, &angle, in); + normalize_v3(axis); + + if (symm & PAINT_SYMM_X) { + axis[0] *= -1.0f; + angle *= -1.0f; + } + if (symm & PAINT_SYMM_Y) { + axis[1] *= -1.0f; + angle *= -1.0f; + } + if (symm & PAINT_SYMM_Z) { + axis[2] *= -1.0f; + angle *= -1.0f; + } + + axis_angle_normalized_to_quat(out, axis, angle); +} + +BLI_INLINE void flip_v3(float v[3], const ePaintSymmetryFlags symm) +{ + flip_v3_v3(v, v, symm); +} + +BLI_INLINE void flip_qt(float quat[4], const ePaintSymmetryFlags symm) +{ + flip_qt_qt(quat, quat, symm); +} /* stroke operator */ typedef enum BrushStrokeMode { diff --git a/source/blender/editors/sculpt_paint/paint_utils.c b/source/blender/editors/sculpt_paint/paint_utils.c index 541893f7957..95a0aba1ffb 100644 --- a/source/blender/editors/sculpt_paint/paint_utils.c +++ b/source/blender/editors/sculpt_paint/paint_utils.c @@ -397,51 +397,6 @@ static Image *imapaint_face_image(Object *ob, Mesh *me, int face_index) return ima; } -void flip_v3_v3(float out[3], const float in[3], const ePaintSymmetryFlags symm) -{ - if (symm & PAINT_SYMM_X) { - out[0] = -in[0]; - } - else { - out[0] = in[0]; - } - if (symm & PAINT_SYMM_Y) { - out[1] = -in[1]; - } - else { - out[1] = in[1]; - } - if (symm & PAINT_SYMM_Z) { - out[2] = -in[2]; - } - else { - out[2] = in[2]; - } -} - -void flip_qt_qt(float out[4], const float in[4], const ePaintSymmetryFlags symm) -{ - float axis[3], angle; - - quat_to_axis_angle(axis, &angle, in); - normalize_v3(axis); - - if (symm & PAINT_SYMM_X) { - axis[0] *= -1.0f; - angle *= -1.0f; - } - if (symm & PAINT_SYMM_Y) { - axis[1] *= -1.0f; - angle *= -1.0f; - } - if (symm & PAINT_SYMM_Z) { - axis[2] *= -1.0f; - angle *= -1.0f; - } - - axis_angle_normalized_to_quat(out, axis, angle); -} - void paint_sample_color( bContext *C, ARegion *region, int x, int y, bool texpaint_proj, bool use_palette) { diff --git a/source/blender/editors/sculpt_paint/sculpt.c b/source/blender/editors/sculpt_paint/sculpt.c index b764d0e1b5b..ea13bb7adca 100644 --- a/source/blender/editors/sculpt_paint/sculpt.c +++ b/source/blender/editors/sculpt_paint/sculpt.c @@ -1329,103 +1329,6 @@ static void sculpt_rake_data_update(struct SculptRakeData *srd, const float co[3 } } -static void sculpt_rake_rotate(const SculptSession *ss, - const float sculpt_co[3], - const float v_co[3], - float factor, - float r_delta[3]) -{ - float vec_rot[3]; - -#if 0 - /* lerp */ - sub_v3_v3v3(vec_rot, v_co, sculpt_co); - mul_qt_v3(ss->cache->rake_rotation_symmetry, vec_rot); - add_v3_v3(vec_rot, sculpt_co); - sub_v3_v3v3(r_delta, vec_rot, v_co); - mul_v3_fl(r_delta, factor); -#else - /* slerp */ - float q_interp[4]; - sub_v3_v3v3(vec_rot, v_co, sculpt_co); - - copy_qt_qt(q_interp, ss->cache->rake_rotation_symmetry); - pow_qt_fl_normalized(q_interp, factor); - mul_qt_v3(q_interp, vec_rot); - - add_v3_v3(vec_rot, sculpt_co); - sub_v3_v3v3(r_delta, vec_rot, v_co); -#endif -} - -/** - * Align the grab delta to the brush normal. - * - * \param grab_delta: Typically from `ss->cache->grab_delta_symmetry`. - */ -static void sculpt_project_v3_normal_align(SculptSession *ss, - const float normal_weight, - float grab_delta[3]) -{ - /* Signed to support grabbing in (to make a hole) as well as out. */ - const float len_signed = dot_v3v3(ss->cache->sculpt_normal_symm, grab_delta); - - /* This scale effectively projects the offset so dragging follows the cursor, - * as the normal points towards the view, the scale increases. */ - float len_view_scale; - { - float view_aligned_normal[3]; - project_plane_v3_v3v3( - view_aligned_normal, ss->cache->sculpt_normal_symm, ss->cache->view_normal); - len_view_scale = fabsf(dot_v3v3(view_aligned_normal, ss->cache->sculpt_normal_symm)); - len_view_scale = (len_view_scale > FLT_EPSILON) ? 1.0f / len_view_scale : 1.0f; - } - - mul_v3_fl(grab_delta, 1.0f - normal_weight); - madd_v3_v3fl( - grab_delta, ss->cache->sculpt_normal_symm, (len_signed * normal_weight) * len_view_scale); -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name SculptProjectVector - * - * Fast-path for #project_plane_v3_v3v3 - * \{ */ - -typedef struct SculptProjectVector { - float plane[3]; - float len_sq; - float len_sq_inv_neg; - bool is_valid; - -} SculptProjectVector; - -/** - * \param plane: Direction, can be any length. - */ -static void sculpt_project_v3_cache_init(SculptProjectVector *spvc, const float plane[3]) -{ - copy_v3_v3(spvc->plane, plane); - spvc->len_sq = len_squared_v3(spvc->plane); - spvc->is_valid = (spvc->len_sq > FLT_EPSILON); - spvc->len_sq_inv_neg = (spvc->is_valid) ? -1.0f / spvc->len_sq : 0.0f; -} - -/** - * Calculate the projection. - */ -static void sculpt_project_v3(const SculptProjectVector *spvc, const float vec[3], float r_vec[3]) -{ -#if 0 - project_plane_v3_v3v3(r_vec, vec, spvc->plane); -#else - /* inline the projection, cache `-1.0 / dot_v3_v3(v_proj, v_proj)` */ - madd_v3_v3fl(r_vec, spvc->plane, dot_v3v3(vec, spvc->plane) * spvc->len_sq_inv_neg); -#endif -} - /** \} */ /* -------------------------------------------------------------------- */ @@ -1835,15 +1738,6 @@ static bool sculpt_brush_test_cyl(SculptBrushTest *test, /* ===== Sculpting ===== */ -static void flip_v3(float v[3], const ePaintSymmetryFlags symm) -{ - flip_v3_v3(v, v, symm); -} - -static void flip_qt(float quat[4], const ePaintSymmetryFlags symm) -{ - flip_qt_qt(quat, quat, symm); -} static float calc_overlap(StrokeCache *cache, const char symm, const char axis, const float angle) { @@ -1913,9 +1807,9 @@ static float calc_symmetry_feather(Sculpt *sd, StrokeCache *cache) * (optionally using original coordinates). * * Functions are: - * - #calc_area_center - * - #calc_area_normal - * - #calc_area_normal_and_center + * - #SCULPT_calc_area_center + * - #SCULPT_calc_area_normal + * - #SCULPT_calc_area_normal_and_center * * \note These are all _very_ similar, when changing one, check others. * \{ */ @@ -2137,7 +2031,7 @@ static void calc_area_normal_and_center_reduce(const void *__restrict UNUSED(use add_v2_v2_int(join->count_co, anctd->count_co); } -static void calc_area_center( +void SCULPT_calc_area_center( Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode, float r_area_co[3]) { const Brush *brush = BKE_paint_brush(&sd->paint); @@ -2238,7 +2132,7 @@ bool SCULPT_pbvh_calc_area_normal(const Brush *brush, * This calculates flatten center and area normal together, * amortizing the memory bandwidth and loop overhead to calculate both at the same time. */ -static void calc_area_normal_and_center( +void SCULPT_calc_area_normal_and_center( Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode, float r_area_no[3], float r_area_co[3]) { const Brush *brush = BKE_paint_brush(&sd->paint); @@ -2856,10 +2750,6 @@ static void update_brush_local_mat(Sculpt *sd, Object *ob) /** \} */ -/* -------------------------------------------------------------------- */ -/** \name Sculpt Topology Rake (Shared Utility) - * \{ */ - typedef struct { SculptSession *ss; const float *ray_start; @@ -2885,1295 +2775,6 @@ typedef struct { bool original; } SculptFindNearestToRayData; -static void do_topology_rake_bmesh_task_cb_ex(void *__restrict userdata, - const int n, - const TaskParallelTLS *__restrict tls) -{ - SculptThreadedTaskData *data = userdata; - SculptSession *ss = data->ob->sculpt; - Sculpt *sd = data->sd; - const Brush *brush = data->brush; - - float direction[3]; - copy_v3_v3(direction, ss->cache->grab_delta_symmetry); - - float tmp[3]; - mul_v3_v3fl( - tmp, ss->cache->sculpt_normal_symm, dot_v3v3(ss->cache->sculpt_normal_symm, direction)); - sub_v3_v3(direction, tmp); - normalize_v3(direction); - - /* Cancel if there's no grab data. */ - if (is_zero_v3(direction)) { - return; - } - - const float bstrength = clamp_f(data->strength, 0.0f, 1.0f); - - SculptBrushTest test; - SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( - ss, &test, data->brush->falloff_shape); - const int thread_id = BLI_task_parallel_thread_id(tls); - - PBVHVertexIter vd; - BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { - if (!sculpt_brush_test_sq_fn(&test, vd.co)) { - continue; - } - const float fade = - bstrength * - SCULPT_brush_strength_factor( - ss, brush, vd.co, sqrtf(test.dist), vd.no, vd.fno, *vd.mask, vd.index, thread_id) * - ss->cache->pressure; - - float avg[3], val[3]; - - SCULPT_bmesh_four_neighbor_average(avg, direction, vd.bm_vert); - - sub_v3_v3v3(val, avg, vd.co); - - madd_v3_v3v3fl(val, vd.co, val, fade); - - SCULPT_clip(sd, ss, vd.co, val); - - if (vd.mvert) { - vd.mvert->flag |= ME_VERT_PBVH_UPDATE; - } - } - BKE_pbvh_vertex_iter_end; -} - -static void bmesh_topology_rake( - Sculpt *sd, Object *ob, PBVHNode **nodes, const int totnode, float bstrength) -{ - Brush *brush = BKE_paint_brush(&sd->paint); - const float strength = clamp_f(bstrength, 0.0f, 1.0f); - - /* Interactions increase both strength and quality. */ - const int iterations = 3; - - int iteration; - const int count = iterations * strength + 1; - const float factor = iterations * strength / count; - - for (iteration = 0; iteration <= count; iteration++) { - - SculptThreadedTaskData data = { - .sd = sd, - .ob = ob, - .brush = brush, - .nodes = nodes, - .strength = factor, - }; - TaskParallelSettings settings; - BKE_pbvh_parallel_range_settings(&settings, true, totnode); - - BLI_task_parallel_range(0, totnode, &data, do_topology_rake_bmesh_task_cb_ex, &settings); - } -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Sculpt Mask Brush - * \{ */ - -static void do_mask_brush_draw_task_cb_ex(void *__restrict userdata, - const int n, - const TaskParallelTLS *__restrict tls) -{ - SculptThreadedTaskData *data = userdata; - SculptSession *ss = data->ob->sculpt; - const Brush *brush = data->brush; - const float bstrength = ss->cache->bstrength; - - PBVHVertexIter vd; - - SculptBrushTest test; - SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( - ss, &test, data->brush->falloff_shape); - const int thread_id = BLI_task_parallel_thread_id(tls); - - BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { - if (!sculpt_brush_test_sq_fn(&test, vd.co)) { - continue; - } - - const float fade = SCULPT_brush_strength_factor( - ss, brush, vd.co, sqrtf(test.dist), vd.no, vd.fno, 0.0f, vd.index, thread_id); - - if (bstrength > 0.0f) { - (*vd.mask) += fade * bstrength * (1.0f - *vd.mask); - } - else { - (*vd.mask) += fade * bstrength * (*vd.mask); - } - *vd.mask = clamp_f(*vd.mask, 0.0f, 1.0f); - - if (vd.mvert) { - vd.mvert->flag |= ME_VERT_PBVH_UPDATE; - } - BKE_pbvh_vertex_iter_end; - } -} - -static void do_mask_brush_draw(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) -{ - Brush *brush = BKE_paint_brush(&sd->paint); - - /* Threaded loop over nodes. */ - SculptThreadedTaskData data = { - .sd = sd, - .ob = ob, - .brush = brush, - .nodes = nodes, - }; - - TaskParallelSettings settings; - BKE_pbvh_parallel_range_settings(&settings, true, totnode); - BLI_task_parallel_range(0, totnode, &data, do_mask_brush_draw_task_cb_ex, &settings); -} - -static void do_mask_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) -{ - SculptSession *ss = ob->sculpt; - Brush *brush = BKE_paint_brush(&sd->paint); - - switch ((BrushMaskTool)brush->mask_tool) { - case BRUSH_MASK_DRAW: - do_mask_brush_draw(sd, ob, nodes, totnode); - break; - case BRUSH_MASK_SMOOTH: - SCULPT_smooth(sd, ob, nodes, totnode, ss->cache->bstrength, true); - break; - } -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Sculpt Multires Displacement Eraser Brush - * \{ */ - -static void do_displacement_eraser_brush_task_cb_ex(void *__restrict userdata, - const int n, - const TaskParallelTLS *__restrict tls) -{ - SculptThreadedTaskData *data = userdata; - SculptSession *ss = data->ob->sculpt; - const Brush *brush = data->brush; - const float bstrength = clamp_f(ss->cache->bstrength, 0.0f, 1.0f); - - float(*proxy)[3] = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co; - - SculptBrushTest test; - SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( - ss, &test, data->brush->falloff_shape); - const int thread_id = BLI_task_parallel_thread_id(tls); - - PBVHVertexIter vd; - BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { - if (!sculpt_brush_test_sq_fn(&test, vd.co)) { - continue; - } - const float fade = bstrength * SCULPT_brush_strength_factor(ss, - brush, - vd.co, - sqrtf(test.dist), - vd.no, - vd.fno, - vd.mask ? *vd.mask : 0.0f, - vd.index, - thread_id); - - float limit_co[3]; - float disp[3]; - SCULPT_vertex_limit_surface_get(ss, vd.index, limit_co); - sub_v3_v3v3(disp, limit_co, vd.co); - mul_v3_v3fl(proxy[vd.i], disp, fade); - - if (vd.mvert) { - vd.mvert->flag |= ME_VERT_PBVH_UPDATE; - } - } - BKE_pbvh_vertex_iter_end; -} - -static void do_displacement_eraser_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) -{ - Brush *brush = BKE_paint_brush(&sd->paint); - BKE_curvemapping_init(brush->curve); - - /* Threaded loop over nodes. */ - SculptThreadedTaskData data = { - .sd = sd, - .ob = ob, - .brush = brush, - .nodes = nodes, - }; - - TaskParallelSettings settings; - BKE_pbvh_parallel_range_settings(&settings, true, totnode); - BLI_task_parallel_range(0, totnode, &data, do_displacement_eraser_brush_task_cb_ex, &settings); -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Sculpt Multires Displacement Smear Brush - * \{ */ - -static void do_displacement_smear_brush_task_cb_ex(void *__restrict userdata, - const int n, - const TaskParallelTLS *__restrict tls) -{ - SculptThreadedTaskData *data = userdata; - SculptSession *ss = data->ob->sculpt; - const Brush *brush = data->brush; - const float bstrength = clamp_f(ss->cache->bstrength, 0.0f, 1.0f); - - SculptBrushTest test; - SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( - ss, &test, data->brush->falloff_shape); - const int thread_id = BLI_task_parallel_thread_id(tls); - - PBVHVertexIter vd; - BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { - if (!sculpt_brush_test_sq_fn(&test, vd.co)) { - continue; - } - const float fade = bstrength * SCULPT_brush_strength_factor(ss, - brush, - vd.co, - sqrtf(test.dist), - vd.no, - vd.fno, - vd.mask ? *vd.mask : 0.0f, - vd.index, - thread_id); - - float current_disp[3]; - float current_disp_norm[3]; - float interp_limit_surface_disp[3]; - - copy_v3_v3(interp_limit_surface_disp, ss->cache->prev_displacement[vd.index]); - - switch (brush->smear_deform_type) { - case BRUSH_SMEAR_DEFORM_DRAG: - sub_v3_v3v3(current_disp, ss->cache->location, ss->cache->last_location); - break; - case BRUSH_SMEAR_DEFORM_PINCH: - sub_v3_v3v3(current_disp, ss->cache->location, vd.co); - break; - case BRUSH_SMEAR_DEFORM_EXPAND: - sub_v3_v3v3(current_disp, vd.co, ss->cache->location); - break; - } - - normalize_v3_v3(current_disp_norm, current_disp); - mul_v3_v3fl(current_disp, current_disp_norm, ss->cache->bstrength); - - float weights_accum = 1.0f; - - SculptVertexNeighborIter ni; - SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.index, ni) { - float vertex_disp[3]; - float vertex_disp_norm[3]; - float neighbor_limit_co[3]; - SCULPT_vertex_limit_surface_get(ss, ni.index, neighbor_limit_co); - sub_v3_v3v3(vertex_disp, - ss->cache->limit_surface_co[ni.index], - ss->cache->limit_surface_co[vd.index]); - const float *neighbor_limit_surface_disp = ss->cache->prev_displacement[ni.index]; - normalize_v3_v3(vertex_disp_norm, vertex_disp); - - if (dot_v3v3(current_disp_norm, vertex_disp_norm) >= 0.0f) { - continue; - } - - const float disp_interp = clamp_f( - -dot_v3v3(current_disp_norm, vertex_disp_norm), 0.0f, 1.0f); - madd_v3_v3fl(interp_limit_surface_disp, neighbor_limit_surface_disp, disp_interp); - weights_accum += disp_interp; - } - SCULPT_VERTEX_NEIGHBORS_ITER_END(ni); - - mul_v3_fl(interp_limit_surface_disp, 1.0f / weights_accum); - - float new_co[3]; - add_v3_v3v3(new_co, ss->cache->limit_surface_co[vd.index], interp_limit_surface_disp); - interp_v3_v3v3(vd.co, vd.co, new_co, fade); - - if (vd.mvert) { - vd.mvert->flag |= ME_VERT_PBVH_UPDATE; - } - } - BKE_pbvh_vertex_iter_end; -} - -static void do_displacement_smear_store_prev_disp_task_cb_ex( - void *__restrict userdata, const int n, const TaskParallelTLS *__restrict UNUSED(tls)) -{ - SculptThreadedTaskData *data = userdata; - SculptSession *ss = data->ob->sculpt; - - PBVHVertexIter vd; - BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { - sub_v3_v3v3(ss->cache->prev_displacement[vd.index], - SCULPT_vertex_co_get(ss, vd.index), - ss->cache->limit_surface_co[vd.index]); - } - BKE_pbvh_vertex_iter_end; -} - -static void do_displacement_smear_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) -{ - Brush *brush = BKE_paint_brush(&sd->paint); - SculptSession *ss = ob->sculpt; - - BKE_curvemapping_init(brush->curve); - - const int totvert = SCULPT_vertex_count_get(ss); - if (!ss->cache->prev_displacement) { - ss->cache->prev_displacement = MEM_malloc_arrayN( - totvert, sizeof(float[3]), "prev displacement"); - ss->cache->limit_surface_co = MEM_malloc_arrayN(totvert, sizeof(float[3]), "limit surface co"); - for (int i = 0; i < totvert; i++) { - SCULPT_vertex_limit_surface_get(ss, i, ss->cache->limit_surface_co[i]); - sub_v3_v3v3(ss->cache->prev_displacement[i], - SCULPT_vertex_co_get(ss, i), - ss->cache->limit_surface_co[i]); - } - } - /* Threaded loop over nodes. */ - SculptThreadedTaskData data = { - .sd = sd, - .ob = ob, - .brush = brush, - .nodes = nodes, - }; - - TaskParallelSettings settings; - BKE_pbvh_parallel_range_settings(&settings, true, totnode); - BLI_task_parallel_range( - 0, totnode, &data, do_displacement_smear_store_prev_disp_task_cb_ex, &settings); - BLI_task_parallel_range(0, totnode, &data, do_displacement_smear_brush_task_cb_ex, &settings); -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Sculpt Draw Brush - * \{ */ - -static void do_draw_brush_task_cb_ex(void *__restrict userdata, - const int n, - const TaskParallelTLS *__restrict tls) -{ - SculptThreadedTaskData *data = userdata; - SculptSession *ss = data->ob->sculpt; - const Brush *brush = data->brush; - const float *offset = data->offset; - - PBVHVertexIter vd; - float(*proxy)[3]; - - proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co; - - SculptBrushTest test; - SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( - ss, &test, data->brush->falloff_shape); - const int thread_id = BLI_task_parallel_thread_id(tls); - - BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { - if (!sculpt_brush_test_sq_fn(&test, vd.co)) { - continue; - } - /* Offset vertex. */ - const float fade = SCULPT_brush_strength_factor(ss, - brush, - vd.co, - sqrtf(test.dist), - vd.no, - vd.fno, - vd.mask ? *vd.mask : 0.0f, - vd.index, - thread_id); - - mul_v3_v3fl(proxy[vd.i], offset, fade); - - if (vd.mvert) { - vd.mvert->flag |= ME_VERT_PBVH_UPDATE; - } - } - BKE_pbvh_vertex_iter_end; -} - -static void do_draw_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) -{ - SculptSession *ss = ob->sculpt; - Brush *brush = BKE_paint_brush(&sd->paint); - float offset[3]; - const float bstrength = ss->cache->bstrength; - - /* Offset with as much as possible factored in already. */ - float effective_normal[3]; - SCULPT_tilt_effective_normal_get(ss, brush, effective_normal); - mul_v3_v3fl(offset, effective_normal, ss->cache->radius); - mul_v3_v3(offset, ss->cache->scale); - mul_v3_fl(offset, bstrength); - - /* XXX: this shouldn't be necessary, but sculpting crashes in blender2.8 otherwise - * initialize before threads so they can do curve mapping. */ - BKE_curvemapping_init(brush->curve); - - /* Threaded loop over nodes. */ - SculptThreadedTaskData data = { - .sd = sd, - .ob = ob, - .brush = brush, - .nodes = nodes, - .offset = offset, - }; - - TaskParallelSettings settings; - BKE_pbvh_parallel_range_settings(&settings, true, totnode); - BLI_task_parallel_range(0, totnode, &data, do_draw_brush_task_cb_ex, &settings); -} - -static void do_draw_sharp_brush_task_cb_ex(void *__restrict userdata, - const int n, - const TaskParallelTLS *__restrict tls) -{ - SculptThreadedTaskData *data = userdata; - SculptSession *ss = data->ob->sculpt; - const Brush *brush = data->brush; - const float *offset = data->offset; - - PBVHVertexIter vd; - SculptOrigVertData orig_data; - float(*proxy)[3]; - - SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n]); - - proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co; - - SculptBrushTest test; - SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( - ss, &test, data->brush->falloff_shape); - const int thread_id = BLI_task_parallel_thread_id(tls); - - BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { - SCULPT_orig_vert_data_update(&orig_data, &vd); - if (!sculpt_brush_test_sq_fn(&test, orig_data.co)) { - continue; - } - /* Offset vertex. */ - const float fade = SCULPT_brush_strength_factor(ss, - brush, - orig_data.co, - sqrtf(test.dist), - orig_data.no, - NULL, - vd.mask ? *vd.mask : 0.0f, - vd.index, - thread_id); - - mul_v3_v3fl(proxy[vd.i], offset, fade); - - if (vd.mvert) { - vd.mvert->flag |= ME_VERT_PBVH_UPDATE; - } - } - BKE_pbvh_vertex_iter_end; -} - -static void do_draw_sharp_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) -{ - SculptSession *ss = ob->sculpt; - Brush *brush = BKE_paint_brush(&sd->paint); - float offset[3]; - const float bstrength = ss->cache->bstrength; - - /* Offset with as much as possible factored in already. */ - float effective_normal[3]; - SCULPT_tilt_effective_normal_get(ss, brush, effective_normal); - mul_v3_v3fl(offset, effective_normal, ss->cache->radius); - mul_v3_v3(offset, ss->cache->scale); - mul_v3_fl(offset, bstrength); - - /* XXX: this shouldn't be necessary, but sculpting crashes in blender2.8 otherwise - * initialize before threads so they can do curve mapping. */ - BKE_curvemapping_init(brush->curve); - - /* Threaded loop over nodes. */ - SculptThreadedTaskData data = { - .sd = sd, - .ob = ob, - .brush = brush, - .nodes = nodes, - .offset = offset, - }; - - TaskParallelSettings settings; - BKE_pbvh_parallel_range_settings(&settings, true, totnode); - BLI_task_parallel_range(0, totnode, &data, do_draw_sharp_brush_task_cb_ex, &settings); -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Sculpt Topology Brush - * \{ */ - -static void do_topology_slide_task_cb_ex(void *__restrict userdata, - const int n, - const TaskParallelTLS *__restrict tls) -{ - SculptThreadedTaskData *data = userdata; - SculptSession *ss = data->ob->sculpt; - const Brush *brush = data->brush; - - PBVHVertexIter vd; - SculptOrigVertData orig_data; - float(*proxy)[3]; - - SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n]); - - proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co; - - SculptBrushTest test; - SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( - ss, &test, data->brush->falloff_shape); - const int thread_id = BLI_task_parallel_thread_id(tls); - - BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { - SCULPT_orig_vert_data_update(&orig_data, &vd); - if (!sculpt_brush_test_sq_fn(&test, orig_data.co)) { - continue; - } - const float fade = SCULPT_brush_strength_factor(ss, - brush, - orig_data.co, - sqrtf(test.dist), - orig_data.no, - NULL, - vd.mask ? *vd.mask : 0.0f, - vd.index, - thread_id); - float current_disp[3]; - float current_disp_norm[3]; - float final_disp[3] = {0.0f, 0.0f, 0.0f}; - - switch (brush->slide_deform_type) { - case BRUSH_SLIDE_DEFORM_DRAG: - sub_v3_v3v3(current_disp, ss->cache->location, ss->cache->last_location); - break; - case BRUSH_SLIDE_DEFORM_PINCH: - sub_v3_v3v3(current_disp, ss->cache->location, vd.co); - break; - case BRUSH_SLIDE_DEFORM_EXPAND: - sub_v3_v3v3(current_disp, vd.co, ss->cache->location); - break; - } - - normalize_v3_v3(current_disp_norm, current_disp); - mul_v3_v3fl(current_disp, current_disp_norm, ss->cache->bstrength); - - SculptVertexNeighborIter ni; - SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.index, ni) { - float vertex_disp[3]; - float vertex_disp_norm[3]; - sub_v3_v3v3(vertex_disp, SCULPT_vertex_co_get(ss, ni.index), vd.co); - normalize_v3_v3(vertex_disp_norm, vertex_disp); - if (dot_v3v3(current_disp_norm, vertex_disp_norm) > 0.0f) { - madd_v3_v3fl(final_disp, vertex_disp_norm, dot_v3v3(current_disp, vertex_disp)); - } - } - SCULPT_VERTEX_NEIGHBORS_ITER_END(ni); - - mul_v3_v3fl(proxy[vd.i], final_disp, fade); - - if (vd.mvert) { - vd.mvert->flag |= ME_VERT_PBVH_UPDATE; - } - } - BKE_pbvh_vertex_iter_end; -} - -void SCULPT_relax_vertex(SculptSession *ss, - PBVHVertexIter *vd, - float factor, - bool filter_boundary_face_sets, - float *r_final_pos) -{ - float smooth_pos[3]; - float final_disp[3]; - float boundary_normal[3]; - int avg_count = 0; - int neighbor_count = 0; - zero_v3(smooth_pos); - zero_v3(boundary_normal); - const bool is_boundary = SCULPT_vertex_is_boundary(ss, vd->index); - - SculptVertexNeighborIter ni; - SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd->index, ni) { - neighbor_count++; - if (!filter_boundary_face_sets || - (filter_boundary_face_sets && !SCULPT_vertex_has_unique_face_set(ss, ni.index))) { - - /* When the vertex to relax is boundary, use only connected boundary vertices for the average - * position. */ - if (is_boundary) { - if (!SCULPT_vertex_is_boundary(ss, ni.index)) { - continue; - } - add_v3_v3(smooth_pos, SCULPT_vertex_co_get(ss, ni.index)); - avg_count++; - - /* Calculate a normal for the constraint plane using the edges of the boundary. */ - float to_neighbor[3]; - sub_v3_v3v3(to_neighbor, SCULPT_vertex_co_get(ss, ni.index), vd->co); - normalize_v3(to_neighbor); - add_v3_v3(boundary_normal, to_neighbor); - } - else { - add_v3_v3(smooth_pos, SCULPT_vertex_co_get(ss, ni.index)); - avg_count++; - } - } - } - SCULPT_VERTEX_NEIGHBORS_ITER_END(ni); - - /* Don't modify corner vertices. */ - if (neighbor_count <= 2) { - copy_v3_v3(r_final_pos, vd->co); - return; - } - - if (avg_count > 0) { - mul_v3_fl(smooth_pos, 1.0f / avg_count); - } - else { - copy_v3_v3(r_final_pos, vd->co); - return; - } - - float plane[4]; - float smooth_closest_plane[3]; - float vno[3]; - - if (is_boundary && avg_count == 2) { - normalize_v3_v3(vno, boundary_normal); - } - else { - SCULPT_vertex_normal_get(ss, vd->index, vno); - } - - if (is_zero_v3(vno)) { - copy_v3_v3(r_final_pos, vd->co); - return; - } - - plane_from_point_normal_v3(plane, vd->co, vno); - closest_to_plane_v3(smooth_closest_plane, plane, smooth_pos); - sub_v3_v3v3(final_disp, smooth_closest_plane, vd->co); - - mul_v3_fl(final_disp, factor); - add_v3_v3v3(r_final_pos, vd->co, final_disp); -} - -static void do_topology_relax_task_cb_ex(void *__restrict userdata, - const int n, - const TaskParallelTLS *__restrict tls) -{ - SculptThreadedTaskData *data = userdata; - SculptSession *ss = data->ob->sculpt; - const Brush *brush = data->brush; - const float bstrength = ss->cache->bstrength; - - PBVHVertexIter vd; - SculptOrigVertData orig_data; - - SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n]); - - BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n]); - - SculptBrushTest test; - SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( - ss, &test, data->brush->falloff_shape); - const int thread_id = BLI_task_parallel_thread_id(tls); - - BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { - SCULPT_orig_vert_data_update(&orig_data, &vd); - if (!sculpt_brush_test_sq_fn(&test, orig_data.co)) { - continue; - } - const float fade = SCULPT_brush_strength_factor(ss, - brush, - orig_data.co, - sqrtf(test.dist), - orig_data.no, - NULL, - vd.mask ? *vd.mask : 0.0f, - vd.index, - thread_id); - - SCULPT_relax_vertex(ss, &vd, fade * bstrength, false, vd.co); - if (vd.mvert) { - vd.mvert->flag |= ME_VERT_PBVH_UPDATE; - } - } - BKE_pbvh_vertex_iter_end; -} - -static void do_slide_relax_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) -{ - SculptSession *ss = ob->sculpt; - Brush *brush = BKE_paint_brush(&sd->paint); - - if (SCULPT_stroke_is_first_brush_step_of_symmetry_pass(ss->cache)) { - return; - } - - BKE_curvemapping_init(brush->curve); - - SculptThreadedTaskData data = { - .sd = sd, - .ob = ob, - .brush = brush, - .nodes = nodes, - }; - - TaskParallelSettings settings; - BKE_pbvh_parallel_range_settings(&settings, true, totnode); - if (ss->cache->alt_smooth) { - SCULPT_boundary_info_ensure(ob); - for (int i = 0; i < 4; i++) { - BLI_task_parallel_range(0, totnode, &data, do_topology_relax_task_cb_ex, &settings); - } - } - else { - BLI_task_parallel_range(0, totnode, &data, do_topology_slide_task_cb_ex, &settings); - } -} - -static void calc_sculpt_plane( - Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode, float r_area_no[3], float r_area_co[3]) -{ - SculptSession *ss = ob->sculpt; - Brush *brush = BKE_paint_brush(&sd->paint); - - if (SCULPT_stroke_is_main_symmetry_pass(ss->cache) && - (SCULPT_stroke_is_first_brush_step_of_symmetry_pass(ss->cache) || - !(brush->flag & BRUSH_ORIGINAL_PLANE) || !(brush->flag & BRUSH_ORIGINAL_NORMAL))) { - switch (brush->sculpt_plane) { - case SCULPT_DISP_DIR_VIEW: - copy_v3_v3(r_area_no, ss->cache->true_view_normal); - break; - - case SCULPT_DISP_DIR_X: - ARRAY_SET_ITEMS(r_area_no, 1.0f, 0.0f, 0.0f); - break; - - case SCULPT_DISP_DIR_Y: - ARRAY_SET_ITEMS(r_area_no, 0.0f, 1.0f, 0.0f); - break; - - case SCULPT_DISP_DIR_Z: - ARRAY_SET_ITEMS(r_area_no, 0.0f, 0.0f, 1.0f); - break; - - case SCULPT_DISP_DIR_AREA: - calc_area_normal_and_center(sd, ob, nodes, totnode, r_area_no, r_area_co); - if (brush->falloff_shape == PAINT_FALLOFF_SHAPE_TUBE) { - project_plane_v3_v3v3(r_area_no, r_area_no, ss->cache->view_normal); - normalize_v3(r_area_no); - } - break; - - default: - break; - } - - /* For flatten center. */ - /* Flatten center has not been calculated yet if we are not using the area normal. */ - if (brush->sculpt_plane != SCULPT_DISP_DIR_AREA) { - calc_area_center(sd, ob, nodes, totnode, r_area_co); - } - - /* For area normal. */ - if ((!SCULPT_stroke_is_first_brush_step_of_symmetry_pass(ss->cache)) && - (brush->flag & BRUSH_ORIGINAL_NORMAL)) { - copy_v3_v3(r_area_no, ss->cache->sculpt_normal); - } - else { - copy_v3_v3(ss->cache->sculpt_normal, r_area_no); - } - - /* For flatten center. */ - if ((!SCULPT_stroke_is_first_brush_step_of_symmetry_pass(ss->cache)) && - (brush->flag & BRUSH_ORIGINAL_PLANE)) { - copy_v3_v3(r_area_co, ss->cache->last_center); - } - else { - copy_v3_v3(ss->cache->last_center, r_area_co); - } - } - else { - /* For area normal. */ - copy_v3_v3(r_area_no, ss->cache->sculpt_normal); - - /* For flatten center. */ - copy_v3_v3(r_area_co, ss->cache->last_center); - - /* For area normal. */ - flip_v3(r_area_no, ss->cache->mirror_symmetry_pass); - - /* For flatten center. */ - flip_v3(r_area_co, ss->cache->mirror_symmetry_pass); - - /* For area normal. */ - mul_m4_v3(ss->cache->symm_rot_mat, r_area_no); - - /* For flatten center. */ - mul_m4_v3(ss->cache->symm_rot_mat, r_area_co); - - /* Shift the plane for the current tile. */ - add_v3_v3(r_area_co, ss->cache->plane_offset); - } -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Sculpt Crease & Blob Brush - * \{ */ - -/** - * Used for 'SCULPT_TOOL_CREASE' and 'SCULPT_TOOL_BLOB' - */ -static void do_crease_brush_task_cb_ex(void *__restrict userdata, - const int n, - const TaskParallelTLS *__restrict tls) -{ - SculptThreadedTaskData *data = userdata; - SculptSession *ss = data->ob->sculpt; - const Brush *brush = data->brush; - SculptProjectVector *spvc = data->spvc; - const float flippedbstrength = data->flippedbstrength; - const float *offset = data->offset; - - PBVHVertexIter vd; - float(*proxy)[3]; - - proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co; - - SculptBrushTest test; - SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( - ss, &test, data->brush->falloff_shape); - const int thread_id = BLI_task_parallel_thread_id(tls); - - BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { - if (!sculpt_brush_test_sq_fn(&test, vd.co)) { - continue; - } - /* Offset vertex. */ - const float fade = SCULPT_brush_strength_factor(ss, - brush, - vd.co, - sqrtf(test.dist), - vd.no, - vd.fno, - vd.mask ? *vd.mask : 0.0f, - vd.index, - thread_id); - float val1[3]; - float val2[3]; - - /* First we pinch. */ - sub_v3_v3v3(val1, test.location, vd.co); - if (brush->falloff_shape == PAINT_FALLOFF_SHAPE_TUBE) { - project_plane_v3_v3v3(val1, val1, ss->cache->view_normal); - } - - mul_v3_fl(val1, fade * flippedbstrength); - - sculpt_project_v3(spvc, val1, val1); - - /* Then we draw. */ - mul_v3_v3fl(val2, offset, fade); - - add_v3_v3v3(proxy[vd.i], val1, val2); - - if (vd.mvert) { - vd.mvert->flag |= ME_VERT_PBVH_UPDATE; - } - } - BKE_pbvh_vertex_iter_end; -} - -static void do_crease_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) -{ - SculptSession *ss = ob->sculpt; - const Scene *scene = ss->cache->vc->scene; - Brush *brush = BKE_paint_brush(&sd->paint); - float offset[3]; - float bstrength = ss->cache->bstrength; - float flippedbstrength, crease_correction; - float brush_alpha; - - SculptProjectVector spvc; - - /* Offset with as much as possible factored in already. */ - mul_v3_v3fl(offset, ss->cache->sculpt_normal_symm, ss->cache->radius); - mul_v3_v3(offset, ss->cache->scale); - mul_v3_fl(offset, bstrength); - - /* We divide out the squared alpha and multiply by the squared crease - * to give us the pinch strength. */ - crease_correction = brush->crease_pinch_factor * brush->crease_pinch_factor; - brush_alpha = BKE_brush_alpha_get(scene, brush); - if (brush_alpha > 0.0f) { - crease_correction /= brush_alpha * brush_alpha; - } - - /* We always want crease to pinch or blob to relax even when draw is negative. */ - flippedbstrength = (bstrength < 0.0f) ? -crease_correction * bstrength : - crease_correction * bstrength; - - if (brush->sculpt_tool == SCULPT_TOOL_BLOB) { - flippedbstrength *= -1.0f; - } - - /* Use surface normal for 'spvc', so the vertices are pinched towards a line instead of a single - * point. Without this we get a 'flat' surface surrounding the pinch. */ - sculpt_project_v3_cache_init(&spvc, ss->cache->sculpt_normal_symm); - - /* Threaded loop over nodes. */ - SculptThreadedTaskData data = { - .sd = sd, - .ob = ob, - .brush = brush, - .nodes = nodes, - .spvc = &spvc, - .offset = offset, - .flippedbstrength = flippedbstrength, - }; - - TaskParallelSettings settings; - BKE_pbvh_parallel_range_settings(&settings, true, totnode); - BLI_task_parallel_range(0, totnode, &data, do_crease_brush_task_cb_ex, &settings); -} - -static void do_pinch_brush_task_cb_ex(void *__restrict userdata, - const int n, - const TaskParallelTLS *__restrict tls) -{ - SculptThreadedTaskData *data = userdata; - SculptSession *ss = data->ob->sculpt; - const Brush *brush = data->brush; - float(*stroke_xz)[3] = data->stroke_xz; - - PBVHVertexIter vd; - float(*proxy)[3]; - const float bstrength = ss->cache->bstrength; - - proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co; - - SculptBrushTest test; - SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( - ss, &test, data->brush->falloff_shape); - const int thread_id = BLI_task_parallel_thread_id(tls); - - float x_object_space[3]; - float z_object_space[3]; - copy_v3_v3(x_object_space, stroke_xz[0]); - copy_v3_v3(z_object_space, stroke_xz[1]); - - BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { - if (!sculpt_brush_test_sq_fn(&test, vd.co)) { - continue; - } - const float fade = bstrength * SCULPT_brush_strength_factor(ss, - brush, - vd.co, - sqrtf(test.dist), - vd.no, - vd.fno, - vd.mask ? *vd.mask : 0.0f, - vd.index, - thread_id); - float disp_center[3]; - float x_disp[3]; - float z_disp[3]; - /* Calculate displacement from the vertex to the brush center. */ - sub_v3_v3v3(disp_center, test.location, vd.co); - - /* Project the displacement into the X vector (aligned to the stroke). */ - mul_v3_v3fl(x_disp, x_object_space, dot_v3v3(disp_center, x_object_space)); - - /* Project the displacement into the Z vector (aligned to the surface normal). */ - mul_v3_v3fl(z_disp, z_object_space, dot_v3v3(disp_center, z_object_space)); - - /* Add the two projected vectors to calculate the final displacement. - * The Y component is removed. */ - add_v3_v3v3(disp_center, x_disp, z_disp); - - if (brush->falloff_shape == PAINT_FALLOFF_SHAPE_TUBE) { - project_plane_v3_v3v3(disp_center, disp_center, ss->cache->view_normal); - } - mul_v3_v3fl(proxy[vd.i], disp_center, fade); - - if (vd.mvert) { - vd.mvert->flag |= ME_VERT_PBVH_UPDATE; - } - } - BKE_pbvh_vertex_iter_end; -} - -static void do_pinch_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) -{ - SculptSession *ss = ob->sculpt; - Brush *brush = BKE_paint_brush(&sd->paint); - - float area_no[3]; - float area_co[3]; - - float mat[4][4]; - calc_sculpt_plane(sd, ob, nodes, totnode, area_no, area_co); - - /* delay the first daub because grab delta is not setup */ - if (SCULPT_stroke_is_first_brush_step_of_symmetry_pass(ss->cache)) { - return; - } - - if (is_zero_v3(ss->cache->grab_delta_symmetry)) { - return; - } - - /* Initialize `mat`. */ - cross_v3_v3v3(mat[0], area_no, ss->cache->grab_delta_symmetry); - mat[0][3] = 0.0f; - cross_v3_v3v3(mat[1], area_no, mat[0]); - mat[1][3] = 0.0f; - copy_v3_v3(mat[2], area_no); - mat[2][3] = 0.0f; - copy_v3_v3(mat[3], ss->cache->location); - mat[3][3] = 1.0f; - normalize_m4(mat); - - float stroke_xz[2][3]; - normalize_v3_v3(stroke_xz[0], mat[0]); - normalize_v3_v3(stroke_xz[1], mat[2]); - - SculptThreadedTaskData data = { - .sd = sd, - .ob = ob, - .brush = brush, - .nodes = nodes, - .stroke_xz = stroke_xz, - }; - - TaskParallelSettings settings; - BKE_pbvh_parallel_range_settings(&settings, true, totnode); - BLI_task_parallel_range(0, totnode, &data, do_pinch_brush_task_cb_ex, &settings); -} - -static void do_grab_brush_task_cb_ex(void *__restrict userdata, - const int n, - const TaskParallelTLS *__restrict tls) -{ - SculptThreadedTaskData *data = userdata; - SculptSession *ss = data->ob->sculpt; - const Brush *brush = data->brush; - const float *grab_delta = data->grab_delta; - - PBVHVertexIter vd; - SculptOrigVertData orig_data; - float(*proxy)[3]; - const float bstrength = ss->cache->bstrength; - - SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n]); - - proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co; - - SculptBrushTest test; - SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( - ss, &test, data->brush->falloff_shape); - const int thread_id = BLI_task_parallel_thread_id(tls); - - const bool grab_silhouette = brush->flag2 & BRUSH_GRAB_SILHOUETTE; - - BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { - SCULPT_orig_vert_data_update(&orig_data, &vd); - - if (!sculpt_brush_test_sq_fn(&test, orig_data.co)) { - continue; - } - float fade = bstrength * SCULPT_brush_strength_factor(ss, - brush, - orig_data.co, - sqrtf(test.dist), - orig_data.no, - NULL, - vd.mask ? *vd.mask : 0.0f, - vd.index, - thread_id); - - if (grab_silhouette) { - float silhouette_test_dir[3]; - normalize_v3_v3(silhouette_test_dir, grab_delta); - if (dot_v3v3(ss->cache->initial_normal, ss->cache->grab_delta_symmetry) < 0.0f) { - mul_v3_fl(silhouette_test_dir, -1.0f); - } - float vno[3]; - normal_short_to_float_v3(vno, orig_data.no); - fade *= max_ff(dot_v3v3(vno, silhouette_test_dir), 0.0f); - } - - mul_v3_v3fl(proxy[vd.i], grab_delta, fade); - - if (vd.mvert) { - vd.mvert->flag |= ME_VERT_PBVH_UPDATE; - } - } - BKE_pbvh_vertex_iter_end; -} - -static void do_grab_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) -{ - SculptSession *ss = ob->sculpt; - Brush *brush = BKE_paint_brush(&sd->paint); - float grab_delta[3]; - - copy_v3_v3(grab_delta, ss->cache->grab_delta_symmetry); - - if (ss->cache->normal_weight > 0.0f) { - sculpt_project_v3_normal_align(ss, ss->cache->normal_weight, grab_delta); - } - - SculptThreadedTaskData data = { - .sd = sd, - .ob = ob, - .brush = brush, - .nodes = nodes, - .grab_delta = grab_delta, - }; - - TaskParallelSettings settings; - BKE_pbvh_parallel_range_settings(&settings, true, totnode); - BLI_task_parallel_range(0, totnode, &data, do_grab_brush_task_cb_ex, &settings); -} - -static void do_elastic_deform_brush_task_cb_ex(void *__restrict userdata, - const int n, - const TaskParallelTLS *__restrict UNUSED(tls)) -{ - SculptThreadedTaskData *data = userdata; - SculptSession *ss = data->ob->sculpt; - const Brush *brush = data->brush; - const float *grab_delta = data->grab_delta; - const float *location = ss->cache->location; - - PBVHVertexIter vd; - SculptOrigVertData orig_data; - float(*proxy)[3]; - - const float bstrength = ss->cache->bstrength; - - SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n]); - - proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co; - - float dir; - if (ss->cache->mouse[0] > ss->cache->initial_mouse[0]) { - dir = 1.0f; - } - else { - dir = -1.0f; - } - - if (brush->elastic_deform_type == BRUSH_ELASTIC_DEFORM_TWIST) { - int symm = ss->cache->mirror_symmetry_pass; - if (ELEM(symm, 1, 2, 4, 7)) { - dir = -dir; - } - } - - KelvinletParams params; - float force = len_v3(grab_delta) * dir * bstrength; - BKE_kelvinlet_init_params( - ¶ms, ss->cache->radius, force, 1.0f, brush->elastic_deform_volume_preservation); - - BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { - SCULPT_orig_vert_data_update(&orig_data, &vd); - float final_disp[3]; - switch (brush->elastic_deform_type) { - case BRUSH_ELASTIC_DEFORM_GRAB: - BKE_kelvinlet_grab(final_disp, ¶ms, orig_data.co, location, grab_delta); - mul_v3_fl(final_disp, bstrength * 20.0f); - break; - case BRUSH_ELASTIC_DEFORM_GRAB_BISCALE: { - BKE_kelvinlet_grab_biscale(final_disp, ¶ms, orig_data.co, location, grab_delta); - mul_v3_fl(final_disp, bstrength * 20.0f); - break; - } - case BRUSH_ELASTIC_DEFORM_GRAB_TRISCALE: { - BKE_kelvinlet_grab_triscale(final_disp, ¶ms, orig_data.co, location, grab_delta); - mul_v3_fl(final_disp, bstrength * 20.0f); - break; - } - case BRUSH_ELASTIC_DEFORM_SCALE: - BKE_kelvinlet_scale( - final_disp, ¶ms, orig_data.co, location, ss->cache->sculpt_normal_symm); - break; - case BRUSH_ELASTIC_DEFORM_TWIST: - BKE_kelvinlet_twist( - final_disp, ¶ms, orig_data.co, location, ss->cache->sculpt_normal_symm); - break; - } - - if (vd.mask) { - mul_v3_fl(final_disp, 1.0f - *vd.mask); - } - - mul_v3_fl(final_disp, SCULPT_automasking_factor_get(ss->cache->automasking, ss, vd.index)); - - copy_v3_v3(proxy[vd.i], final_disp); - - if (vd.mvert) { - vd.mvert->flag |= ME_VERT_PBVH_UPDATE; - } - } - BKE_pbvh_vertex_iter_end; -} - -static void do_elastic_deform_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) -{ - SculptSession *ss = ob->sculpt; - Brush *brush = BKE_paint_brush(&sd->paint); - float grab_delta[3]; - - copy_v3_v3(grab_delta, ss->cache->grab_delta_symmetry); - - if (ss->cache->normal_weight > 0.0f) { - sculpt_project_v3_normal_align(ss, ss->cache->normal_weight, grab_delta); - } - - SculptThreadedTaskData data = { - .sd = sd, - .ob = ob, - .brush = brush, - .nodes = nodes, - .grab_delta = grab_delta, - }; - - TaskParallelSettings settings; - BKE_pbvh_parallel_range_settings(&settings, true, totnode); - BLI_task_parallel_range(0, totnode, &data, do_elastic_deform_brush_task_cb_ex, &settings); -} - ePaintSymmetryAreas SCULPT_get_vertex_symm_area(const float co[3]) { ePaintSymmetryAreas symm_area = PAINT_SYMM_AREA_DEFAULT; @@ -4257,7 +2858,7 @@ void SCULPT_calc_brush_plane( break; case SCULPT_DISP_DIR_AREA: - calc_area_normal_and_center(sd, ob, nodes, totnode, r_area_no, r_area_co); + SCULPT_calc_area_normal_and_center(sd, ob, nodes, totnode, r_area_no, r_area_co); if (brush->falloff_shape == PAINT_FALLOFF_SHAPE_TUBE) { project_plane_v3_v3v3(r_area_no, r_area_no, ss->cache->view_normal); normalize_v3(r_area_no); @@ -4271,7 +2872,7 @@ void SCULPT_calc_brush_plane( /* For flatten center. */ /* Flatten center has not been calculated yet if we are not using the area normal. */ if (brush->sculpt_plane != SCULPT_DISP_DIR_AREA) { - calc_area_center(sd, ob, nodes, totnode, r_area_co); + SCULPT_calc_area_center(sd, ob, nodes, totnode, r_area_co); } /* For area normal. */ @@ -4316,564 +2917,12 @@ void SCULPT_calc_brush_plane( } } -static void do_nudge_brush_task_cb_ex(void *__restrict userdata, - const int n, - const TaskParallelTLS *__restrict tls) -{ - SculptThreadedTaskData *data = userdata; - SculptSession *ss = data->ob->sculpt; - const Brush *brush = data->brush; - const float *cono = data->cono; - - PBVHVertexIter vd; - float(*proxy)[3]; - const float bstrength = ss->cache->bstrength; - - proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co; - - SculptBrushTest test; - SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( - ss, &test, data->brush->falloff_shape); - const int thread_id = BLI_task_parallel_thread_id(tls); - - BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { - if (!sculpt_brush_test_sq_fn(&test, vd.co)) { - continue; - } - const float fade = bstrength * SCULPT_brush_strength_factor(ss, - brush, - vd.co, - sqrtf(test.dist), - vd.no, - vd.fno, - vd.mask ? *vd.mask : 0.0f, - vd.index, - thread_id); - - mul_v3_v3fl(proxy[vd.i], cono, fade); - - if (vd.mvert) { - vd.mvert->flag |= ME_VERT_PBVH_UPDATE; - } - } - BKE_pbvh_vertex_iter_end; -} - -static void do_nudge_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) -{ - SculptSession *ss = ob->sculpt; - Brush *brush = BKE_paint_brush(&sd->paint); - float grab_delta[3]; - float tmp[3], cono[3]; - - copy_v3_v3(grab_delta, ss->cache->grab_delta_symmetry); - - cross_v3_v3v3(tmp, ss->cache->sculpt_normal_symm, grab_delta); - cross_v3_v3v3(cono, tmp, ss->cache->sculpt_normal_symm); - - SculptThreadedTaskData data = { - .sd = sd, - .ob = ob, - .brush = brush, - .nodes = nodes, - .cono = cono, - }; - - TaskParallelSettings settings; - BKE_pbvh_parallel_range_settings(&settings, true, totnode); - BLI_task_parallel_range(0, totnode, &data, do_nudge_brush_task_cb_ex, &settings); -} - -static void do_snake_hook_brush_task_cb_ex(void *__restrict userdata, - const int n, - const TaskParallelTLS *__restrict tls) -{ - SculptThreadedTaskData *data = userdata; - SculptSession *ss = data->ob->sculpt; - const Brush *brush = data->brush; - SculptProjectVector *spvc = data->spvc; - const float *grab_delta = data->grab_delta; - - PBVHVertexIter vd; - float(*proxy)[3]; - const float bstrength = ss->cache->bstrength; - const bool do_rake_rotation = ss->cache->is_rake_rotation_valid; - const bool do_pinch = (brush->crease_pinch_factor != 0.5f); - const float pinch = do_pinch ? (2.0f * (0.5f - brush->crease_pinch_factor) * - (len_v3(grab_delta) / ss->cache->radius)) : - 0.0f; - - const bool do_elastic = brush->snake_hook_deform_type == BRUSH_SNAKE_HOOK_DEFORM_ELASTIC; - - proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co; - - SculptBrushTest test; - SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( - ss, &test, data->brush->falloff_shape); - const int thread_id = BLI_task_parallel_thread_id(tls); - - KelvinletParams params; - BKE_kelvinlet_init_params(¶ms, ss->cache->radius, bstrength, 1.0f, 0.4f); - - BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { - if (!do_elastic && !sculpt_brush_test_sq_fn(&test, vd.co)) { - continue; - } - - float fade; - if (do_elastic) { - fade = 1.0f; - } - else { - fade = bstrength * SCULPT_brush_strength_factor(ss, - brush, - vd.co, - sqrtf(test.dist), - vd.no, - vd.fno, - vd.mask ? *vd.mask : 0.0f, - vd.index, - thread_id); - } - - mul_v3_v3fl(proxy[vd.i], grab_delta, fade); - - /* Negative pinch will inflate, helps maintain volume. */ - if (do_pinch) { - float delta_pinch_init[3], delta_pinch[3]; - - sub_v3_v3v3(delta_pinch, vd.co, test.location); - if (brush->falloff_shape == PAINT_FALLOFF_SHAPE_TUBE) { - project_plane_v3_v3v3(delta_pinch, delta_pinch, ss->cache->true_view_normal); - } - - /* Important to calculate based on the grabbed location - * (intentionally ignore fade here). */ - add_v3_v3(delta_pinch, grab_delta); - - sculpt_project_v3(spvc, delta_pinch, delta_pinch); - - copy_v3_v3(delta_pinch_init, delta_pinch); - - float pinch_fade = pinch * fade; - /* When reducing, scale reduction back by how close to the center we are, - * so we don't pinch into nothingness. */ - if (pinch > 0.0f) { - /* Square to have even less impact for close vertices. */ - pinch_fade *= pow2f(min_ff(1.0f, len_v3(delta_pinch) / ss->cache->radius)); - } - mul_v3_fl(delta_pinch, 1.0f + pinch_fade); - sub_v3_v3v3(delta_pinch, delta_pinch_init, delta_pinch); - add_v3_v3(proxy[vd.i], delta_pinch); - } - - if (do_rake_rotation) { - float delta_rotate[3]; - sculpt_rake_rotate(ss, test.location, vd.co, fade, delta_rotate); - add_v3_v3(proxy[vd.i], delta_rotate); - } - - if (do_elastic) { - float disp[3]; - BKE_kelvinlet_grab_triscale(disp, ¶ms, vd.co, ss->cache->location, proxy[vd.i]); - mul_v3_fl(disp, bstrength * 20.0f); - if (vd.mask) { - mul_v3_fl(disp, 1.0f - *vd.mask); - } - mul_v3_fl(disp, SCULPT_automasking_factor_get(ss->cache->automasking, ss, vd.index)); - copy_v3_v3(proxy[vd.i], disp); - } - - if (vd.mvert) { - vd.mvert->flag |= ME_VERT_PBVH_UPDATE; - } - } - BKE_pbvh_vertex_iter_end; -} - -static void do_snake_hook_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) -{ - SculptSession *ss = ob->sculpt; - Brush *brush = BKE_paint_brush(&sd->paint); - const float bstrength = ss->cache->bstrength; - float grab_delta[3]; - - SculptProjectVector spvc; - - copy_v3_v3(grab_delta, ss->cache->grab_delta_symmetry); - - if (bstrength < 0.0f) { - negate_v3(grab_delta); - } - - if (ss->cache->normal_weight > 0.0f) { - sculpt_project_v3_normal_align(ss, ss->cache->normal_weight, grab_delta); - } - - /* Optionally pinch while painting. */ - if (brush->crease_pinch_factor != 0.5f) { - sculpt_project_v3_cache_init(&spvc, grab_delta); - } - - SculptThreadedTaskData data = { - .sd = sd, - .ob = ob, - .brush = brush, - .nodes = nodes, - .spvc = &spvc, - .grab_delta = grab_delta, - }; - - TaskParallelSettings settings; - BKE_pbvh_parallel_range_settings(&settings, true, totnode); - BLI_task_parallel_range(0, totnode, &data, do_snake_hook_brush_task_cb_ex, &settings); -} - -static void do_thumb_brush_task_cb_ex(void *__restrict userdata, - const int n, - const TaskParallelTLS *__restrict tls) -{ - SculptThreadedTaskData *data = userdata; - SculptSession *ss = data->ob->sculpt; - const Brush *brush = data->brush; - const float *cono = data->cono; - - PBVHVertexIter vd; - SculptOrigVertData orig_data; - float(*proxy)[3]; - const float bstrength = ss->cache->bstrength; - - SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n]); - - proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co; - - SculptBrushTest test; - SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( - ss, &test, data->brush->falloff_shape); - const int thread_id = BLI_task_parallel_thread_id(tls); - - BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { - SCULPT_orig_vert_data_update(&orig_data, &vd); - - if (!sculpt_brush_test_sq_fn(&test, orig_data.co)) { - continue; - } - const float fade = bstrength * SCULPT_brush_strength_factor(ss, - brush, - orig_data.co, - sqrtf(test.dist), - orig_data.no, - NULL, - vd.mask ? *vd.mask : 0.0f, - vd.index, - thread_id); - - mul_v3_v3fl(proxy[vd.i], cono, fade); - - if (vd.mvert) { - vd.mvert->flag |= ME_VERT_PBVH_UPDATE; - } - } - BKE_pbvh_vertex_iter_end; -} - -static void do_thumb_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) -{ - SculptSession *ss = ob->sculpt; - Brush *brush = BKE_paint_brush(&sd->paint); - float grab_delta[3]; - float tmp[3], cono[3]; - - copy_v3_v3(grab_delta, ss->cache->grab_delta_symmetry); - - cross_v3_v3v3(tmp, ss->cache->sculpt_normal_symm, grab_delta); - cross_v3_v3v3(cono, tmp, ss->cache->sculpt_normal_symm); - - SculptThreadedTaskData data = { - .sd = sd, - .ob = ob, - .brush = brush, - .nodes = nodes, - .cono = cono, - }; - - TaskParallelSettings settings; - BKE_pbvh_parallel_range_settings(&settings, true, totnode); - BLI_task_parallel_range(0, totnode, &data, do_thumb_brush_task_cb_ex, &settings); -} - -static void do_rotate_brush_task_cb_ex(void *__restrict userdata, - const int n, - const TaskParallelTLS *__restrict tls) -{ - SculptThreadedTaskData *data = userdata; - SculptSession *ss = data->ob->sculpt; - const Brush *brush = data->brush; - const float angle = data->angle; - - PBVHVertexIter vd; - SculptOrigVertData orig_data; - float(*proxy)[3]; - const float bstrength = ss->cache->bstrength; - - SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n]); - - proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co; - - SculptBrushTest test; - SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( - ss, &test, data->brush->falloff_shape); - const int thread_id = BLI_task_parallel_thread_id(tls); - - BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { - SCULPT_orig_vert_data_update(&orig_data, &vd); - - if (!sculpt_brush_test_sq_fn(&test, orig_data.co)) { - continue; - } - float vec[3], rot[3][3]; - const float fade = bstrength * SCULPT_brush_strength_factor(ss, - brush, - orig_data.co, - sqrtf(test.dist), - orig_data.no, - NULL, - vd.mask ? *vd.mask : 0.0f, - vd.index, - thread_id); - - sub_v3_v3v3(vec, orig_data.co, ss->cache->location); - axis_angle_normalized_to_mat3(rot, ss->cache->sculpt_normal_symm, angle * fade); - mul_v3_m3v3(proxy[vd.i], rot, vec); - add_v3_v3(proxy[vd.i], ss->cache->location); - sub_v3_v3(proxy[vd.i], orig_data.co); - - if (vd.mvert) { - vd.mvert->flag |= ME_VERT_PBVH_UPDATE; - } - } - BKE_pbvh_vertex_iter_end; -} - -static void do_rotate_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) -{ - SculptSession *ss = ob->sculpt; - Brush *brush = BKE_paint_brush(&sd->paint); - - static const int flip[8] = {1, -1, -1, 1, -1, 1, 1, -1}; - const float angle = ss->cache->vertex_rotation * flip[ss->cache->mirror_symmetry_pass]; - - SculptThreadedTaskData data = { - .sd = sd, - .ob = ob, - .brush = brush, - .nodes = nodes, - .angle = angle, - }; - - TaskParallelSettings settings; - BKE_pbvh_parallel_range_settings(&settings, true, totnode); - BLI_task_parallel_range(0, totnode, &data, do_rotate_brush_task_cb_ex, &settings); -} - -static void do_layer_brush_task_cb_ex(void *__restrict userdata, - const int n, - const TaskParallelTLS *__restrict tls) -{ - SculptThreadedTaskData *data = userdata; - SculptSession *ss = data->ob->sculpt; - Sculpt *sd = data->sd; - const Brush *brush = data->brush; - - const bool use_persistent_base = ss->persistent_base && brush->flag & BRUSH_PERSISTENT; - - PBVHVertexIter vd; - SculptOrigVertData orig_data; - const float bstrength = ss->cache->bstrength; - SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n]); - - SculptBrushTest test; - SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( - ss, &test, data->brush->falloff_shape); - const int thread_id = BLI_task_parallel_thread_id(tls); - - BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { - SCULPT_orig_vert_data_update(&orig_data, &vd); - - if (!sculpt_brush_test_sq_fn(&test, orig_data.co)) { - continue; - } - const float fade = SCULPT_brush_strength_factor(ss, - brush, - vd.co, - sqrtf(test.dist), - vd.no, - vd.fno, - vd.mask ? *vd.mask : 0.0f, - vd.index, - thread_id); - - const int vi = vd.index; - float *disp_factor; - if (use_persistent_base) { - disp_factor = &ss->persistent_base[vi].disp; - } - else { - disp_factor = &ss->cache->layer_displacement_factor[vi]; - } - - /* When using persistent base, the layer brush (holding Control) invert mode resets the - * height of the layer to 0. This makes possible to clean edges of previously added layers - * on top of the base. */ - /* The main direction of the layers is inverted using the regular brush strength with the - * brush direction property. */ - if (use_persistent_base && ss->cache->invert) { - (*disp_factor) += fabsf(fade * bstrength * (*disp_factor)) * - ((*disp_factor) > 0.0f ? -1.0f : 1.0f); - } - else { - (*disp_factor) += fade * bstrength * (1.05f - fabsf(*disp_factor)); - } - if (vd.mask) { - const float clamp_mask = 1.0f - *vd.mask; - *disp_factor = clamp_f(*disp_factor, -clamp_mask, clamp_mask); - } - else { - *disp_factor = clamp_f(*disp_factor, -1.0f, 1.0f); - } - - float final_co[3]; - float normal[3]; - - if (use_persistent_base) { - SCULPT_vertex_persistent_normal_get(ss, vi, normal); - mul_v3_fl(normal, brush->height); - madd_v3_v3v3fl(final_co, SCULPT_vertex_persistent_co_get(ss, vi), normal, *disp_factor); - } - else { - normal_short_to_float_v3(normal, orig_data.no); - mul_v3_fl(normal, brush->height); - madd_v3_v3v3fl(final_co, orig_data.co, normal, *disp_factor); - } - - float vdisp[3]; - sub_v3_v3v3(vdisp, final_co, vd.co); - mul_v3_fl(vdisp, fabsf(fade)); - add_v3_v3v3(final_co, vd.co, vdisp); - - SCULPT_clip(sd, ss, vd.co, final_co); - - if (vd.mvert) { - vd.mvert->flag |= ME_VERT_PBVH_UPDATE; - } - } - BKE_pbvh_vertex_iter_end; -} - -static void do_layer_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) -{ - SculptSession *ss = ob->sculpt; - Brush *brush = BKE_paint_brush(&sd->paint); - - if (ss->cache->layer_displacement_factor == NULL) { - ss->cache->layer_displacement_factor = MEM_callocN(sizeof(float) * SCULPT_vertex_count_get(ss), - "layer displacement factor"); - } - - SculptThreadedTaskData data = { - .sd = sd, - .ob = ob, - .brush = brush, - .nodes = nodes, - }; - - TaskParallelSettings settings; - BKE_pbvh_parallel_range_settings(&settings, true, totnode); - BLI_task_parallel_range(0, totnode, &data, do_layer_brush_task_cb_ex, &settings); -} - -static void do_inflate_brush_task_cb_ex(void *__restrict userdata, - const int n, - const TaskParallelTLS *__restrict tls) -{ - SculptThreadedTaskData *data = userdata; - SculptSession *ss = data->ob->sculpt; - const Brush *brush = data->brush; - - PBVHVertexIter vd; - float(*proxy)[3]; - const float bstrength = ss->cache->bstrength; - - proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co; - - SculptBrushTest test; - SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( - ss, &test, data->brush->falloff_shape); - const int thread_id = BLI_task_parallel_thread_id(tls); - - BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { - if (!sculpt_brush_test_sq_fn(&test, vd.co)) { - continue; - } - const float fade = bstrength * SCULPT_brush_strength_factor(ss, - brush, - vd.co, - sqrtf(test.dist), - vd.no, - vd.fno, - vd.mask ? *vd.mask : 0.0f, - vd.index, - thread_id); - float val[3]; - - if (vd.fno) { - copy_v3_v3(val, vd.fno); - } - else { - normal_short_to_float_v3(val, vd.no); - } - - mul_v3_fl(val, fade * ss->cache->radius); - mul_v3_v3v3(proxy[vd.i], val, ss->cache->scale); - - if (vd.mvert) { - vd.mvert->flag |= ME_VERT_PBVH_UPDATE; - } - } - BKE_pbvh_vertex_iter_end; -} - -static void do_inflate_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) -{ - Brush *brush = BKE_paint_brush(&sd->paint); - - SculptThreadedTaskData data = { - .sd = sd, - .ob = ob, - .brush = brush, - .nodes = nodes, - }; - - TaskParallelSettings settings; - BKE_pbvh_parallel_range_settings(&settings, true, totnode); - BLI_task_parallel_range(0, totnode, &data, do_inflate_brush_task_cb_ex, &settings); -} - int SCULPT_plane_trim(const StrokeCache *cache, const Brush *brush, const float val[3]) { return (!(brush->flag & BRUSH_PLANE_TRIM) || ((dot_v3v3(val, val) <= cache->radius_squared * cache->plane_trim_squared))); } -static bool plane_point_side_flip(const float co[3], const float plane[4], const bool flip) -{ - float d = plane_point_side_v3(plane, co); - if (flip) { - d = -d; - } - return d <= 0.0f; -} - int SCULPT_plane_point_side(const float co[3], const float plane[4]) { float d = plane_point_side_v3(plane, co); @@ -4893,807 +2942,6 @@ float SCULPT_brush_plane_offset_get(Sculpt *sd, SculptSession *ss) return rv; } -static void do_flatten_brush_task_cb_ex(void *__restrict userdata, - const int n, - const TaskParallelTLS *__restrict tls) -{ - SculptThreadedTaskData *data = userdata; - SculptSession *ss = data->ob->sculpt; - const Brush *brush = data->brush; - const float *area_no = data->area_no; - const float *area_co = data->area_co; - - PBVHVertexIter vd; - float(*proxy)[3]; - const float bstrength = ss->cache->bstrength; - - proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co; - - SculptBrushTest test; - SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( - ss, &test, data->brush->falloff_shape); - const int thread_id = BLI_task_parallel_thread_id(tls); - - plane_from_point_normal_v3(test.plane_tool, area_co, area_no); - - BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { - if (!sculpt_brush_test_sq_fn(&test, vd.co)) { - continue; - } - float intr[3]; - float val[3]; - - closest_to_plane_normalized_v3(intr, test.plane_tool, vd.co); - - sub_v3_v3v3(val, intr, vd.co); - - if (SCULPT_plane_trim(ss->cache, brush, val)) { - const float fade = bstrength * SCULPT_brush_strength_factor(ss, - brush, - vd.co, - sqrtf(test.dist), - vd.no, - vd.fno, - vd.mask ? *vd.mask : 0.0f, - vd.index, - thread_id); - - mul_v3_v3fl(proxy[vd.i], val, fade); - - if (vd.mvert) { - vd.mvert->flag |= ME_VERT_PBVH_UPDATE; - } - } - } - BKE_pbvh_vertex_iter_end; -} - -static void do_flatten_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) -{ - SculptSession *ss = ob->sculpt; - Brush *brush = BKE_paint_brush(&sd->paint); - - const float radius = ss->cache->radius; - - float area_no[3]; - float area_co[3]; - - float offset = SCULPT_brush_plane_offset_get(sd, ss); - float displace; - float temp[3]; - - SCULPT_calc_brush_plane(sd, ob, nodes, totnode, area_no, area_co); - - SCULPT_tilt_apply_to_normal(area_no, ss->cache, brush->tilt_strength_factor); - - displace = radius * offset; - - mul_v3_v3v3(temp, area_no, ss->cache->scale); - mul_v3_fl(temp, displace); - add_v3_v3(area_co, temp); - - SculptThreadedTaskData data = { - .sd = sd, - .ob = ob, - .brush = brush, - .nodes = nodes, - .area_no = area_no, - .area_co = area_co, - }; - - TaskParallelSettings settings; - BKE_pbvh_parallel_range_settings(&settings, true, totnode); - BLI_task_parallel_range(0, totnode, &data, do_flatten_brush_task_cb_ex, &settings); -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Sculpt Clay Brush - * \{ */ - -typedef struct ClaySampleData { - float plane_dist[2]; -} ClaySampleData; - -static void calc_clay_surface_task_cb(void *__restrict userdata, - const int n, - const TaskParallelTLS *__restrict tls) -{ - SculptThreadedTaskData *data = userdata; - SculptSession *ss = data->ob->sculpt; - const Brush *brush = data->brush; - ClaySampleData *csd = tls->userdata_chunk; - const float *area_no = data->area_no; - const float *area_co = data->area_co; - float plane[4]; - - PBVHVertexIter vd; - - SculptBrushTest test; - SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( - ss, &test, brush->falloff_shape); - - /* Apply the brush normal radius to the test before sampling. */ - float test_radius = sqrtf(test.radius_squared); - test_radius *= brush->normal_radius_factor; - test.radius_squared = test_radius * test_radius; - plane_from_point_normal_v3(plane, area_co, area_no); - - if (is_zero_v4(plane)) { - return; - } - - BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { - if (!sculpt_brush_test_sq_fn(&test, vd.co)) { - continue; - } - - float plane_dist = dist_signed_to_plane_v3(vd.co, plane); - float plane_dist_abs = fabsf(plane_dist); - if (plane_dist > 0.0f) { - csd->plane_dist[0] = MIN2(csd->plane_dist[0], plane_dist_abs); - } - else { - csd->plane_dist[1] = MIN2(csd->plane_dist[1], plane_dist_abs); - } - BKE_pbvh_vertex_iter_end; - } -} - -static void calc_clay_surface_reduce(const void *__restrict UNUSED(userdata), - void *__restrict chunk_join, - void *__restrict chunk) -{ - ClaySampleData *join = chunk_join; - ClaySampleData *csd = chunk; - join->plane_dist[0] = MIN2(csd->plane_dist[0], join->plane_dist[0]); - join->plane_dist[1] = MIN2(csd->plane_dist[1], join->plane_dist[1]); -} - -static void do_clay_brush_task_cb_ex(void *__restrict userdata, - const int n, - const TaskParallelTLS *__restrict tls) -{ - SculptThreadedTaskData *data = userdata; - SculptSession *ss = data->ob->sculpt; - const Brush *brush = data->brush; - const float *area_no = data->area_no; - const float *area_co = data->area_co; - - PBVHVertexIter vd; - float(*proxy)[3]; - const float bstrength = fabsf(ss->cache->bstrength); - - proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co; - - SculptBrushTest test; - SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( - ss, &test, data->brush->falloff_shape); - const int thread_id = BLI_task_parallel_thread_id(tls); - - plane_from_point_normal_v3(test.plane_tool, area_co, area_no); - - BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { - if (!sculpt_brush_test_sq_fn(&test, vd.co)) { - continue; - } - - float intr[3]; - float val[3]; - closest_to_plane_normalized_v3(intr, test.plane_tool, vd.co); - - sub_v3_v3v3(val, intr, vd.co); - - const float fade = bstrength * SCULPT_brush_strength_factor(ss, - brush, - vd.co, - sqrtf(test.dist), - vd.no, - vd.fno, - vd.mask ? *vd.mask : 0.0f, - vd.index, - thread_id); - - mul_v3_v3fl(proxy[vd.i], val, fade); - - if (vd.mvert) { - vd.mvert->flag |= ME_VERT_PBVH_UPDATE; - } - } - BKE_pbvh_vertex_iter_end; -} - -static void do_clay_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) -{ - SculptSession *ss = ob->sculpt; - Brush *brush = BKE_paint_brush(&sd->paint); - - const float radius = fabsf(ss->cache->radius); - const float initial_radius = fabsf(ss->cache->initial_radius); - bool flip = ss->cache->bstrength < 0.0f; - - float offset = SCULPT_brush_plane_offset_get(sd, ss); - float displace; - - float area_no[3]; - float area_co[3]; - float temp[3]; - - SCULPT_calc_brush_plane(sd, ob, nodes, totnode, area_no, area_co); - - SculptThreadedTaskData sample_data = { - .sd = NULL, - .ob = ob, - .brush = brush, - .nodes = nodes, - .totnode = totnode, - .area_no = area_no, - .area_co = ss->cache->location, - }; - - ClaySampleData csd = {{0}}; - - TaskParallelSettings sample_settings; - BKE_pbvh_parallel_range_settings(&sample_settings, true, totnode); - sample_settings.func_reduce = calc_clay_surface_reduce; - sample_settings.userdata_chunk = &csd; - sample_settings.userdata_chunk_size = sizeof(ClaySampleData); - - BLI_task_parallel_range(0, totnode, &sample_data, calc_clay_surface_task_cb, &sample_settings); - - float d_offset = (csd.plane_dist[0] + csd.plane_dist[1]); - d_offset = min_ff(radius, d_offset); - d_offset = d_offset / radius; - d_offset = 1.0f - d_offset; - displace = fabsf(initial_radius * (0.25f + offset + (d_offset * 0.15f))); - if (flip) { - displace = -displace; - } - - mul_v3_v3v3(temp, area_no, ss->cache->scale); - mul_v3_fl(temp, displace); - copy_v3_v3(area_co, ss->cache->location); - add_v3_v3(area_co, temp); - - SculptThreadedTaskData data = { - .sd = sd, - .ob = ob, - .brush = brush, - .nodes = nodes, - .area_no = area_no, - .area_co = area_co, - }; - - TaskParallelSettings settings; - BKE_pbvh_parallel_range_settings(&settings, true, totnode); - BLI_task_parallel_range(0, totnode, &data, do_clay_brush_task_cb_ex, &settings); -} - -static void do_clay_strips_brush_task_cb_ex(void *__restrict userdata, - const int n, - const TaskParallelTLS *__restrict tls) -{ - SculptThreadedTaskData *data = userdata; - SculptSession *ss = data->ob->sculpt; - const Brush *brush = data->brush; - float(*mat)[4] = data->mat; - const float *area_no_sp = data->area_no_sp; - const float *area_co = data->area_co; - - PBVHVertexIter vd; - SculptBrushTest test; - float(*proxy)[3]; - const bool flip = (ss->cache->bstrength < 0.0f); - const float bstrength = flip ? -ss->cache->bstrength : ss->cache->bstrength; - - proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co; - - SCULPT_brush_test_init(ss, &test); - plane_from_point_normal_v3(test.plane_tool, area_co, area_no_sp); - const int thread_id = BLI_task_parallel_thread_id(tls); - - BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { - if (!SCULPT_brush_test_cube(&test, vd.co, mat, brush->tip_roundness)) { - continue; - } - - if (!plane_point_side_flip(vd.co, test.plane_tool, flip)) { - continue; - } - - float intr[3]; - float val[3]; - closest_to_plane_normalized_v3(intr, test.plane_tool, vd.co); - sub_v3_v3v3(val, intr, vd.co); - - if (!SCULPT_plane_trim(ss->cache, brush, val)) { - continue; - } - /* The normal from the vertices is ignored, it causes glitch with planes, see: T44390. */ - const float fade = bstrength * SCULPT_brush_strength_factor(ss, - brush, - vd.co, - ss->cache->radius * test.dist, - vd.no, - vd.fno, - vd.mask ? *vd.mask : 0.0f, - vd.index, - thread_id); - - mul_v3_v3fl(proxy[vd.i], val, fade); - - if (vd.mvert) { - vd.mvert->flag |= ME_VERT_PBVH_UPDATE; - } - } - BKE_pbvh_vertex_iter_end; -} - -static void do_clay_strips_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) -{ - SculptSession *ss = ob->sculpt; - Brush *brush = BKE_paint_brush(&sd->paint); - - const bool flip = (ss->cache->bstrength < 0.0f); - const float radius = flip ? -ss->cache->radius : ss->cache->radius; - const float offset = SCULPT_brush_plane_offset_get(sd, ss); - const float displace = radius * (0.18f + offset); - - /* The sculpt-plane normal (whatever its set to). */ - float area_no_sp[3]; - - /* Geometry normal */ - float area_no[3]; - float area_co[3]; - - float temp[3]; - float mat[4][4]; - float scale[4][4]; - float tmat[4][4]; - - SCULPT_calc_brush_plane(sd, ob, nodes, totnode, area_no_sp, area_co); - SCULPT_tilt_apply_to_normal(area_no_sp, ss->cache, brush->tilt_strength_factor); - - if (brush->sculpt_plane != SCULPT_DISP_DIR_AREA || (brush->flag & BRUSH_ORIGINAL_NORMAL)) { - SCULPT_calc_area_normal(sd, ob, nodes, totnode, area_no); - } - else { - copy_v3_v3(area_no, area_no_sp); - } - - /* Delay the first daub because grab delta is not setup. */ - if (SCULPT_stroke_is_first_brush_step_of_symmetry_pass(ss->cache)) { - return; - } - - if (is_zero_v3(ss->cache->grab_delta_symmetry)) { - return; - } - - mul_v3_v3v3(temp, area_no_sp, ss->cache->scale); - mul_v3_fl(temp, displace); - add_v3_v3(area_co, temp); - - /* Clay Strips uses a cube test with falloff in the XY axis (not in Z) and a plane to deform the - * vertices. When in Add mode, vertices that are below the plane and inside the cube are move - * towards the plane. In this situation, there may be cases where a vertex is outside the cube - * but below the plane, so won't be deformed, causing artifacts. In order to prevent these - * artifacts, this displaces the test cube space in relation to the plane in order to - * deform more vertices that may be below it. */ - /* The 0.7 and 1.25 factors are arbitrary and don't have any relation between them, they were set - * by doing multiple tests using the default "Clay Strips" brush preset. */ - float area_co_displaced[3]; - madd_v3_v3v3fl(area_co_displaced, area_co, area_no, -radius * 0.7f); - - /* Initialize brush local-space matrix. */ - cross_v3_v3v3(mat[0], area_no, ss->cache->grab_delta_symmetry); - mat[0][3] = 0.0f; - cross_v3_v3v3(mat[1], area_no, mat[0]); - mat[1][3] = 0.0f; - copy_v3_v3(mat[2], area_no); - mat[2][3] = 0.0f; - copy_v3_v3(mat[3], area_co_displaced); - mat[3][3] = 1.0f; - normalize_m4(mat); - - /* Scale brush local space matrix. */ - scale_m4_fl(scale, ss->cache->radius); - mul_m4_m4m4(tmat, mat, scale); - - /* Deform the local space in Z to scale the test cube. As the test cube does not have falloff in - * Z this does not produce artifacts in the falloff cube and allows to deform extra vertices - * during big deformation while keeping the surface as uniform as possible. */ - mul_v3_fl(tmat[2], 1.25f); - - invert_m4_m4(mat, tmat); - - SculptThreadedTaskData data = { - .sd = sd, - .ob = ob, - .brush = brush, - .nodes = nodes, - .area_no_sp = area_no_sp, - .area_co = area_co, - .mat = mat, - }; - - TaskParallelSettings settings; - BKE_pbvh_parallel_range_settings(&settings, true, totnode); - BLI_task_parallel_range(0, totnode, &data, do_clay_strips_brush_task_cb_ex, &settings); -} - -static void do_fill_brush_task_cb_ex(void *__restrict userdata, - const int n, - const TaskParallelTLS *__restrict tls) -{ - SculptThreadedTaskData *data = userdata; - SculptSession *ss = data->ob->sculpt; - const Brush *brush = data->brush; - const float *area_no = data->area_no; - const float *area_co = data->area_co; - - PBVHVertexIter vd; - float(*proxy)[3]; - const float bstrength = ss->cache->bstrength; - - proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co; - - SculptBrushTest test; - SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( - ss, &test, data->brush->falloff_shape); - const int thread_id = BLI_task_parallel_thread_id(tls); - - plane_from_point_normal_v3(test.plane_tool, area_co, area_no); - - BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { - if (!sculpt_brush_test_sq_fn(&test, vd.co)) { - continue; - } - - if (!SCULPT_plane_point_side(vd.co, test.plane_tool)) { - continue; - } - - float intr[3]; - float val[3]; - closest_to_plane_normalized_v3(intr, test.plane_tool, vd.co); - sub_v3_v3v3(val, intr, vd.co); - - if (!SCULPT_plane_trim(ss->cache, brush, val)) { - continue; - } - - const float fade = bstrength * SCULPT_brush_strength_factor(ss, - brush, - vd.co, - sqrtf(test.dist), - vd.no, - vd.fno, - vd.mask ? *vd.mask : 0.0f, - vd.index, - thread_id); - - mul_v3_v3fl(proxy[vd.i], val, fade); - - if (vd.mvert) { - vd.mvert->flag |= ME_VERT_PBVH_UPDATE; - } - } - BKE_pbvh_vertex_iter_end; -} - -static void do_fill_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) -{ - SculptSession *ss = ob->sculpt; - Brush *brush = BKE_paint_brush(&sd->paint); - - const float radius = ss->cache->radius; - - float area_no[3]; - float area_co[3]; - float offset = SCULPT_brush_plane_offset_get(sd, ss); - - float displace; - - float temp[3]; - - SCULPT_calc_brush_plane(sd, ob, nodes, totnode, area_no, area_co); - - SCULPT_tilt_apply_to_normal(area_no, ss->cache, brush->tilt_strength_factor); - - displace = radius * offset; - - mul_v3_v3v3(temp, area_no, ss->cache->scale); - mul_v3_fl(temp, displace); - add_v3_v3(area_co, temp); - - SculptThreadedTaskData data = { - .sd = sd, - .ob = ob, - .brush = brush, - .nodes = nodes, - .area_no = area_no, - .area_co = area_co, - }; - - TaskParallelSettings settings; - BKE_pbvh_parallel_range_settings(&settings, true, totnode); - BLI_task_parallel_range(0, totnode, &data, do_fill_brush_task_cb_ex, &settings); -} - -static void do_scrape_brush_task_cb_ex(void *__restrict userdata, - const int n, - const TaskParallelTLS *__restrict tls) -{ - SculptThreadedTaskData *data = userdata; - SculptSession *ss = data->ob->sculpt; - const Brush *brush = data->brush; - const float *area_no = data->area_no; - const float *area_co = data->area_co; - - PBVHVertexIter vd; - float(*proxy)[3]; - const float bstrength = ss->cache->bstrength; - - proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co; - - SculptBrushTest test; - SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( - ss, &test, data->brush->falloff_shape); - const int thread_id = BLI_task_parallel_thread_id(tls); - plane_from_point_normal_v3(test.plane_tool, area_co, area_no); - - BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { - if (!sculpt_brush_test_sq_fn(&test, vd.co)) { - continue; - } - - if (SCULPT_plane_point_side(vd.co, test.plane_tool)) { - continue; - } - - float intr[3]; - float val[3]; - closest_to_plane_normalized_v3(intr, test.plane_tool, vd.co); - sub_v3_v3v3(val, intr, vd.co); - - if (!SCULPT_plane_trim(ss->cache, brush, val)) { - continue; - } - - const float fade = bstrength * SCULPT_brush_strength_factor(ss, - brush, - vd.co, - sqrtf(test.dist), - vd.no, - vd.fno, - vd.mask ? *vd.mask : 0.0f, - vd.index, - thread_id); - - mul_v3_v3fl(proxy[vd.i], val, fade); - - if (vd.mvert) { - vd.mvert->flag |= ME_VERT_PBVH_UPDATE; - } - } - BKE_pbvh_vertex_iter_end; -} - -static void do_scrape_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) -{ - SculptSession *ss = ob->sculpt; - Brush *brush = BKE_paint_brush(&sd->paint); - - const float radius = ss->cache->radius; - - float area_no[3]; - float area_co[3]; - float offset = SCULPT_brush_plane_offset_get(sd, ss); - - float displace; - - float temp[3]; - - SCULPT_calc_brush_plane(sd, ob, nodes, totnode, area_no, area_co); - - SCULPT_tilt_apply_to_normal(area_no, ss->cache, brush->tilt_strength_factor); - - displace = -radius * offset; - - mul_v3_v3v3(temp, area_no, ss->cache->scale); - mul_v3_fl(temp, displace); - add_v3_v3(area_co, temp); - - SculptThreadedTaskData data = { - .sd = sd, - .ob = ob, - .brush = brush, - .nodes = nodes, - .area_no = area_no, - .area_co = area_co, - }; - - TaskParallelSettings settings; - BKE_pbvh_parallel_range_settings(&settings, true, totnode); - BLI_task_parallel_range(0, totnode, &data, do_scrape_brush_task_cb_ex, &settings); -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Sculpt Clay Thumb Brush - * \{ */ - -static void do_clay_thumb_brush_task_cb_ex(void *__restrict userdata, - const int n, - const TaskParallelTLS *__restrict tls) -{ - SculptThreadedTaskData *data = userdata; - SculptSession *ss = data->ob->sculpt; - const Brush *brush = data->brush; - float(*mat)[4] = data->mat; - const float *area_no_sp = data->area_no_sp; - const float *area_co = data->area_co; - - PBVHVertexIter vd; - float(*proxy)[3]; - const float bstrength = data->clay_strength; - - proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co; - - SculptBrushTest test; - SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( - ss, &test, data->brush->falloff_shape); - const int thread_id = BLI_task_parallel_thread_id(tls); - - float plane_tilt[4]; - float normal_tilt[3]; - float imat[4][4]; - - invert_m4_m4(imat, mat); - rotate_v3_v3v3fl(normal_tilt, area_no_sp, imat[0], DEG2RADF(-ss->cache->clay_thumb_front_angle)); - - /* Plane aligned to the geometry normal (back part of the brush). */ - plane_from_point_normal_v3(test.plane_tool, area_co, area_no_sp); - /* Tilted plane (front part of the brush). */ - plane_from_point_normal_v3(plane_tilt, area_co, normal_tilt); - - BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { - if (!sculpt_brush_test_sq_fn(&test, vd.co)) { - continue; - } - float local_co[3]; - mul_v3_m4v3(local_co, mat, vd.co); - float intr[3], intr_tilt[3]; - float val[3]; - - closest_to_plane_normalized_v3(intr, test.plane_tool, vd.co); - closest_to_plane_normalized_v3(intr_tilt, plane_tilt, vd.co); - - /* Mix the deformation of the aligned and the tilted plane based on the brush space vertex - * coordinates. */ - /* We can also control the mix with a curve if it produces noticeable artifacts in the center - * of the brush. */ - const float tilt_mix = local_co[1] > 0.0f ? 0.0f : 1.0f; - interp_v3_v3v3(intr, intr, intr_tilt, tilt_mix); - sub_v3_v3v3(val, intr_tilt, vd.co); - - const float fade = bstrength * SCULPT_brush_strength_factor(ss, - brush, - vd.co, - sqrtf(test.dist), - vd.no, - vd.fno, - vd.mask ? *vd.mask : 0.0f, - vd.index, - thread_id); - - mul_v3_v3fl(proxy[vd.i], val, fade); - - if (vd.mvert) { - vd.mvert->flag |= ME_VERT_PBVH_UPDATE; - } - } - BKE_pbvh_vertex_iter_end; -} - -static float sculpt_clay_thumb_get_stabilized_pressure(StrokeCache *cache) -{ - float final_pressure = 0.0f; - for (int i = 0; i < SCULPT_CLAY_STABILIZER_LEN; i++) { - final_pressure += cache->clay_pressure_stabilizer[i]; - } - return final_pressure / SCULPT_CLAY_STABILIZER_LEN; -} - -static void do_clay_thumb_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) -{ - SculptSession *ss = ob->sculpt; - Brush *brush = BKE_paint_brush(&sd->paint); - - const float radius = ss->cache->radius; - const float offset = SCULPT_brush_plane_offset_get(sd, ss); - const float displace = radius * (0.25f + offset); - - /* Sampled geometry normal and area center. */ - float area_no_sp[3]; - float area_no[3]; - float area_co[3]; - - float temp[3]; - float mat[4][4]; - float scale[4][4]; - float tmat[4][4]; - - SCULPT_calc_brush_plane(sd, ob, nodes, totnode, area_no_sp, area_co); - - if (brush->sculpt_plane != SCULPT_DISP_DIR_AREA || (brush->flag & BRUSH_ORIGINAL_NORMAL)) { - SCULPT_calc_area_normal(sd, ob, nodes, totnode, area_no); - } - else { - copy_v3_v3(area_no, area_no_sp); - } - - /* Delay the first daub because grab delta is not setup. */ - if (SCULPT_stroke_is_first_brush_step_of_symmetry_pass(ss->cache)) { - ss->cache->clay_thumb_front_angle = 0.0f; - return; - } - - /* Simulate the clay accumulation by increasing the plane angle as more samples are added to the - * stroke. */ - if (SCULPT_stroke_is_main_symmetry_pass(ss->cache)) { - ss->cache->clay_thumb_front_angle += 0.8f; - ss->cache->clay_thumb_front_angle = clamp_f(ss->cache->clay_thumb_front_angle, 0.0f, 60.0f); - } - - if (is_zero_v3(ss->cache->grab_delta_symmetry)) { - return; - } - - /* Displace the brush planes. */ - copy_v3_v3(area_co, ss->cache->location); - mul_v3_v3v3(temp, area_no_sp, ss->cache->scale); - mul_v3_fl(temp, displace); - add_v3_v3(area_co, temp); - - /* Initialize brush local-space matrix. */ - cross_v3_v3v3(mat[0], area_no, ss->cache->grab_delta_symmetry); - mat[0][3] = 0.0f; - cross_v3_v3v3(mat[1], area_no, mat[0]); - mat[1][3] = 0.0f; - copy_v3_v3(mat[2], area_no); - mat[2][3] = 0.0f; - copy_v3_v3(mat[3], ss->cache->location); - mat[3][3] = 1.0f; - normalize_m4(mat); - - /* Scale brush local space matrix. */ - scale_m4_fl(scale, ss->cache->radius); - mul_m4_m4m4(tmat, mat, scale); - invert_m4_m4(mat, tmat); - - float clay_strength = ss->cache->bstrength * - sculpt_clay_thumb_get_stabilized_pressure(ss->cache); - - SculptThreadedTaskData data = { - .sd = sd, - .ob = ob, - .brush = brush, - .nodes = nodes, - .area_no_sp = area_no_sp, - .area_co = ss->cache->location, - .mat = mat, - .clay_strength = clay_strength, - }; - - TaskParallelSettings settings; - BKE_pbvh_parallel_range_settings(&settings, true, totnode); - BLI_task_parallel_range(0, totnode, &data, do_clay_thumb_brush_task_cb_ex, &settings); -} - /** \} */ /* -------------------------------------------------------------------- */ @@ -6036,7 +3284,7 @@ static void do_brush_action(Sculpt *sd, Object *ob, Brush *brush, UnifiedPaintSe /* Apply one type of brush action. */ switch (brush->sculpt_tool) { case SCULPT_TOOL_DRAW: - do_draw_brush(sd, ob, nodes, totnode); + SCULPT_do_draw_brush(sd, ob, nodes, totnode); break; case SCULPT_TOOL_SMOOTH: if (brush->smooth_deform_type == BRUSH_SMOOTH_DEFORM_LAPLACIAN) { @@ -6047,80 +3295,80 @@ static void do_brush_action(Sculpt *sd, Object *ob, Brush *brush, UnifiedPaintSe } break; case SCULPT_TOOL_CREASE: - do_crease_brush(sd, ob, nodes, totnode); + SCULPT_do_crease_brush(sd, ob, nodes, totnode); break; case SCULPT_TOOL_BLOB: - do_crease_brush(sd, ob, nodes, totnode); + SCULPT_do_crease_brush(sd, ob, nodes, totnode); break; case SCULPT_TOOL_PINCH: - do_pinch_brush(sd, ob, nodes, totnode); + SCULPT_do_pinch_brush(sd, ob, nodes, totnode); break; case SCULPT_TOOL_INFLATE: - do_inflate_brush(sd, ob, nodes, totnode); + SCULPT_do_inflate_brush(sd, ob, nodes, totnode); break; case SCULPT_TOOL_GRAB: - do_grab_brush(sd, ob, nodes, totnode); + SCULPT_do_grab_brush(sd, ob, nodes, totnode); break; case SCULPT_TOOL_ROTATE: - do_rotate_brush(sd, ob, nodes, totnode); + SCULPT_do_rotate_brush(sd, ob, nodes, totnode); break; case SCULPT_TOOL_SNAKE_HOOK: - do_snake_hook_brush(sd, ob, nodes, totnode); + SCULPT_do_snake_hook_brush(sd, ob, nodes, totnode); break; case SCULPT_TOOL_NUDGE: - do_nudge_brush(sd, ob, nodes, totnode); + SCULPT_do_nudge_brush(sd, ob, nodes, totnode); break; case SCULPT_TOOL_THUMB: - do_thumb_brush(sd, ob, nodes, totnode); + SCULPT_do_thumb_brush(sd, ob, nodes, totnode); break; case SCULPT_TOOL_LAYER: - do_layer_brush(sd, ob, nodes, totnode); + SCULPT_do_layer_brush(sd, ob, nodes, totnode); break; case SCULPT_TOOL_FLATTEN: - do_flatten_brush(sd, ob, nodes, totnode); + SCULPT_do_flatten_brush(sd, ob, nodes, totnode); break; case SCULPT_TOOL_CLAY: - do_clay_brush(sd, ob, nodes, totnode); + SCULPT_do_clay_brush(sd, ob, nodes, totnode); break; case SCULPT_TOOL_CLAY_STRIPS: - do_clay_strips_brush(sd, ob, nodes, totnode); + SCULPT_do_clay_strips_brush(sd, ob, nodes, totnode); break; case SCULPT_TOOL_MULTIPLANE_SCRAPE: SCULPT_do_multiplane_scrape_brush(sd, ob, nodes, totnode); break; case SCULPT_TOOL_CLAY_THUMB: - do_clay_thumb_brush(sd, ob, nodes, totnode); + SCULPT_do_clay_thumb_brush(sd, ob, nodes, totnode); break; case SCULPT_TOOL_FILL: if (invert && brush->flag & BRUSH_INVERT_TO_SCRAPE_FILL) { - do_scrape_brush(sd, ob, nodes, totnode); + SCULPT_do_scrape_brush(sd, ob, nodes, totnode); } else { - do_fill_brush(sd, ob, nodes, totnode); + SCULPT_do_fill_brush(sd, ob, nodes, totnode); } break; case SCULPT_TOOL_SCRAPE: if (invert && brush->flag & BRUSH_INVERT_TO_SCRAPE_FILL) { - do_fill_brush(sd, ob, nodes, totnode); + SCULPT_do_fill_brush(sd, ob, nodes, totnode); } else { - do_scrape_brush(sd, ob, nodes, totnode); + SCULPT_do_scrape_brush(sd, ob, nodes, totnode); } break; case SCULPT_TOOL_MASK: - do_mask_brush(sd, ob, nodes, totnode); + SCULPT_do_mask_brush(sd, ob, nodes, totnode); break; case SCULPT_TOOL_POSE: SCULPT_do_pose_brush(sd, ob, nodes, totnode); break; case SCULPT_TOOL_DRAW_SHARP: - do_draw_sharp_brush(sd, ob, nodes, totnode); + SCULPT_do_draw_sharp_brush(sd, ob, nodes, totnode); break; case SCULPT_TOOL_ELASTIC_DEFORM: - do_elastic_deform_brush(sd, ob, nodes, totnode); + SCULPT_do_elastic_deform_brush(sd, ob, nodes, totnode); break; case SCULPT_TOOL_SLIDE_RELAX: - do_slide_relax_brush(sd, ob, nodes, totnode); + SCULPT_do_slide_relax_brush(sd, ob, nodes, totnode); break; case SCULPT_TOOL_BOUNDARY: SCULPT_do_boundary_brush(sd, ob, nodes, totnode); @@ -6132,10 +3380,10 @@ static void do_brush_action(Sculpt *sd, Object *ob, Brush *brush, UnifiedPaintSe SCULPT_do_draw_face_sets_brush(sd, ob, nodes, totnode); break; case SCULPT_TOOL_DISPLACEMENT_ERASER: - do_displacement_eraser_brush(sd, ob, nodes, totnode); + SCULPT_do_displacement_eraser_brush(sd, ob, nodes, totnode); break; case SCULPT_TOOL_DISPLACEMENT_SMEAR: - do_displacement_smear_brush(sd, ob, nodes, totnode); + SCULPT_do_displacement_smear_brush(sd, ob, nodes, totnode); break; case SCULPT_TOOL_PAINT: SCULPT_do_paint_brush(sd, ob, nodes, totnode); @@ -6157,7 +3405,7 @@ static void do_brush_action(Sculpt *sd, Object *ob, Brush *brush, UnifiedPaintSe } if (sculpt_brush_use_topology_rake(ss, brush)) { - bmesh_topology_rake(sd, ob, nodes, totnode, brush->topology_rake_factor); + SCULPT_bmesh_topology_rake(sd, ob, nodes, totnode, brush->topology_rake_factor); } /* The cloth brush adds the gravity as a regular force and it is processed in the solver. */ @@ -6946,7 +4194,7 @@ static float sculpt_brush_dynamic_size_get(Brush *brush, StrokeCache *cache, flo case SCULPT_TOOL_CLAY_STRIPS: return max_ff(initial_size * 0.30f, initial_size * powf(cache->pressure, 1.5f)); case SCULPT_TOOL_CLAY_THUMB: { - float clay_stabilized_pressure = sculpt_clay_thumb_get_stabilized_pressure(cache); + float clay_stabilized_pressure = SCULPT_clay_thumb_get_stabilized_pressure(cache); return initial_size * clay_stabilized_pressure; } default: @@ -8131,7 +5379,7 @@ static void sculpt_brush_stroke_cancel(bContext *C, wmOperator *op) sculpt_brush_exit_tex(sd); } -static void SCULPT_OT_brush_stroke(wmOperatorType *ot) +void SCULPT_OT_brush_stroke(wmOperatorType *ot) { /* Identifiers. */ ot->name = "Sculpt"; @@ -8159,677 +5407,6 @@ static void SCULPT_OT_brush_stroke(wmOperatorType *ot) "Clicks on the background do not start the stroke"); } -/* Reset the copy of the mesh that is being sculpted on (currently just for the layer brush). */ - -static int sculpt_set_persistent_base_exec(bContext *C, wmOperator *UNUSED(op)) -{ - Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C); - Object *ob = CTX_data_active_object(C); - SculptSession *ss = ob->sculpt; - - if (!ss) { - return OPERATOR_FINISHED; - } - SCULPT_vertex_random_access_ensure(ss); - BKE_sculpt_update_object_for_edit(depsgraph, ob, false, false, false); - - MEM_SAFE_FREE(ss->persistent_base); - - const int totvert = SCULPT_vertex_count_get(ss); - ss->persistent_base = MEM_mallocN(sizeof(SculptPersistentBase) * totvert, - "layer persistent base"); - - for (int i = 0; i < totvert; i++) { - copy_v3_v3(ss->persistent_base[i].co, SCULPT_vertex_co_get(ss, i)); - SCULPT_vertex_normal_get(ss, i, ss->persistent_base[i].no); - ss->persistent_base[i].disp = 0.0f; - } - - return OPERATOR_FINISHED; -} - -static void SCULPT_OT_set_persistent_base(wmOperatorType *ot) -{ - /* Identifiers. */ - ot->name = "Set Persistent Base"; - ot->idname = "SCULPT_OT_set_persistent_base"; - ot->description = "Reset the copy of the mesh that is being sculpted on"; - - /* API callbacks. */ - ot->exec = sculpt_set_persistent_base_exec; - ot->poll = SCULPT_mode_poll; - - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; -} - -/************************* SCULPT_OT_optimize *************************/ - -static int sculpt_optimize_exec(bContext *C, wmOperator *UNUSED(op)) -{ - Object *ob = CTX_data_active_object(C); - - SCULPT_pbvh_clear(ob); - WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob); - - return OPERATOR_FINISHED; -} - -/* The BVH gets less optimal more quickly with dynamic topology than - * regular sculpting. There is no doubt more clever stuff we can do to - * optimize it on the fly, but for now this gives the user a nicer way - * to recalculate it than toggling modes. */ -static void SCULPT_OT_optimize(wmOperatorType *ot) -{ - /* Identifiers. */ - ot->name = "Rebuild BVH"; - ot->idname = "SCULPT_OT_optimize"; - ot->description = "Recalculate the sculpt BVH to improve performance"; - - /* API callbacks. */ - ot->exec = sculpt_optimize_exec; - ot->poll = SCULPT_mode_poll; - - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; -} - -/********************* Dynamic topology symmetrize ********************/ - -static bool sculpt_no_multires_poll(bContext *C) -{ - Object *ob = CTX_data_active_object(C); - if (SCULPT_mode_poll(C) && ob->sculpt && ob->sculpt->pbvh) { - return BKE_pbvh_type(ob->sculpt->pbvh) != PBVH_GRIDS; - } - return false; -} - -static int sculpt_symmetrize_exec(bContext *C, wmOperator *op) -{ - Main *bmain = CTX_data_main(C); - Object *ob = CTX_data_active_object(C); - const Sculpt *sd = CTX_data_tool_settings(C)->sculpt; - SculptSession *ss = ob->sculpt; - PBVH *pbvh = ss->pbvh; - const float dist = RNA_float_get(op->ptr, "merge_tolerance"); - - if (!pbvh) { - return OPERATOR_CANCELLED; - } - - switch (BKE_pbvh_type(pbvh)) { - case PBVH_BMESH: - /* Dyntopo Symmetrize. */ - - /* To simplify undo for symmetrize, all BMesh elements are logged - * as deleted, then after symmetrize operation all BMesh elements - * are logged as added (as opposed to attempting to store just the - * parts that symmetrize modifies). */ - SCULPT_undo_push_begin(ob, "Dynamic topology symmetrize"); - SCULPT_undo_push_node(ob, NULL, SCULPT_UNDO_DYNTOPO_SYMMETRIZE); - BM_log_before_all_removed(ss->bm, ss->bm_log); - - BM_mesh_toolflags_set(ss->bm, true); - - /* Symmetrize and re-triangulate. */ - BMO_op_callf(ss->bm, - (BMO_FLAG_DEFAULTS & ~BMO_FLAG_RESPECT_HIDE), - "symmetrize input=%avef direction=%i dist=%f use_shapekey=%b", - sd->symmetrize_direction, - dist, - true); - SCULPT_dynamic_topology_triangulate(ss->bm); - - /* Bisect operator flags edges (keep tags clean for edge queue). */ - BM_mesh_elem_hflag_disable_all(ss->bm, BM_EDGE, BM_ELEM_TAG, false); - - BM_mesh_toolflags_set(ss->bm, false); - - /* Finish undo. */ - BM_log_all_added(ss->bm, ss->bm_log); - SCULPT_undo_push_end(); - - break; - case PBVH_FACES: - /* Mesh Symmetrize. */ - ED_sculpt_undo_geometry_begin(ob, "mesh symmetrize"); - Mesh *mesh = ob->data; - - BKE_mesh_mirror_apply_mirror_on_axis(bmain, mesh, sd->symmetrize_direction, dist); - - ED_sculpt_undo_geometry_end(ob); - BKE_mesh_calc_normals(ob->data); - BKE_mesh_batch_cache_dirty_tag(ob->data, BKE_MESH_BATCH_DIRTY_ALL); - - break; - case PBVH_GRIDS: - return OPERATOR_CANCELLED; - } - - /* Redraw. */ - SCULPT_pbvh_clear(ob); - WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob); - - return OPERATOR_FINISHED; -} - -static void SCULPT_OT_symmetrize(wmOperatorType *ot) -{ - /* Identifiers. */ - ot->name = "Symmetrize"; - ot->idname = "SCULPT_OT_symmetrize"; - ot->description = "Symmetrize the topology modifications"; - - /* API callbacks. */ - ot->exec = sculpt_symmetrize_exec; - ot->poll = sculpt_no_multires_poll; - - RNA_def_float(ot->srna, - "merge_tolerance", - 0.001f, - 0.0f, - FLT_MAX, - "Merge Distance", - "Distance within which symmetrical vertices are merged", - 0.0f, - 1.0f); -} - -/**** Toggle operator for turning sculpt mode on or off ****/ - -static void sculpt_init_session(Main *bmain, Depsgraph *depsgraph, Scene *scene, Object *ob) -{ - /* Create persistent sculpt mode data. */ - BKE_sculpt_toolsettings_data_ensure(scene); - - /* Create sculpt mode session data. */ - if (ob->sculpt != NULL) { - BKE_sculptsession_free(ob); - } - ob->sculpt = MEM_callocN(sizeof(SculptSession), "sculpt session"); - ob->sculpt->mode_type = OB_MODE_SCULPT; - - BKE_sculpt_ensure_orig_mesh_data(scene, ob); - - BKE_scene_graph_evaluated_ensure(depsgraph, bmain); - - /* This function expects a fully evaluated depsgraph. */ - BKE_sculpt_update_object_for_edit(depsgraph, ob, false, false, false); - - /* Here we can detect geometry that was just added to Sculpt Mode as it has the - * SCULPT_FACE_SET_NONE assigned, so we can create a new Face Set for it. */ - /* In sculpt mode all geometry that is assigned to SCULPT_FACE_SET_NONE is considered as not - * initialized, which is used is some operators that modify the mesh topology to perform certain - * actions in the new polys. After these operations are finished, all polys should have a valid - * face set ID assigned (different from SCULPT_FACE_SET_NONE) to manage their visibility - * correctly. */ - /* TODO(pablodp606): Based on this we can improve the UX in future tools for creating new - * objects, like moving the transform pivot position to the new area or masking existing - * geometry. */ - SculptSession *ss = ob->sculpt; - const int new_face_set = SCULPT_face_set_next_available_get(ss); - for (int i = 0; i < ss->totfaces; i++) { - if (ss->face_sets[i] == SCULPT_FACE_SET_NONE) { - ss->face_sets[i] = new_face_set; - } - } -} - -void ED_object_sculptmode_enter_ex(Main *bmain, - Depsgraph *depsgraph, - Scene *scene, - Object *ob, - const bool force_dyntopo, - ReportList *reports) -{ - const int mode_flag = OB_MODE_SCULPT; - Mesh *me = BKE_mesh_from_object(ob); - - /* Enter sculpt mode. */ - ob->mode |= mode_flag; - - sculpt_init_session(bmain, depsgraph, scene, ob); - - if (!(fabsf(ob->scale[0] - ob->scale[1]) < 1e-4f && - fabsf(ob->scale[1] - ob->scale[2]) < 1e-4f)) { - BKE_report( - reports, RPT_WARNING, "Object has non-uniform scale, sculpting may be unpredictable"); - } - else if (is_negative_m4(ob->obmat)) { - BKE_report(reports, RPT_WARNING, "Object has negative scale, sculpting may be unpredictable"); - } - - Paint *paint = BKE_paint_get_active_from_paintmode(scene, PAINT_MODE_SCULPT); - BKE_paint_init(bmain, scene, PAINT_MODE_SCULPT, PAINT_CURSOR_SCULPT); - - paint_cursor_start(paint, SCULPT_mode_poll_view3d); - - /* Check dynamic-topology flag; re-enter dynamic-topology mode when changing modes, - * As long as no data was added that is not supported. */ - if (me->flag & ME_SCULPT_DYNAMIC_TOPOLOGY) { - MultiresModifierData *mmd = BKE_sculpt_multires_active(scene, ob); - - const char *message_unsupported = NULL; - if (me->totloop != me->totpoly * 3) { - message_unsupported = TIP_("non-triangle face"); - } - else if (mmd != NULL) { - message_unsupported = TIP_("multi-res modifier"); - } - else { - enum eDynTopoWarnFlag flag = SCULPT_dynamic_topology_check(scene, ob); - if (flag == 0) { - /* pass */ - } - else if (flag & DYNTOPO_WARN_VDATA) { - message_unsupported = TIP_("vertex data"); - } - else if (flag & DYNTOPO_WARN_EDATA) { - message_unsupported = TIP_("edge data"); - } - else if (flag & DYNTOPO_WARN_LDATA) { - message_unsupported = TIP_("face data"); - } - else if (flag & DYNTOPO_WARN_MODIFIER) { - message_unsupported = TIP_("constructive modifier"); - } - else { - BLI_assert(0); - } - } - - if ((message_unsupported == NULL) || force_dyntopo) { - /* Needed because we may be entering this mode before the undo system loads. */ - wmWindowManager *wm = bmain->wm.first; - bool has_undo = wm->undo_stack != NULL; - /* Undo push is needed to prevent memory leak. */ - if (has_undo) { - SCULPT_undo_push_begin(ob, "Dynamic topology enable"); - } - SCULPT_dynamic_topology_enable_ex(bmain, depsgraph, scene, ob); - if (has_undo) { - SCULPT_undo_push_node(ob, NULL, SCULPT_UNDO_DYNTOPO_BEGIN); - SCULPT_undo_push_end(); - } - } - else { - BKE_reportf( - reports, RPT_WARNING, "Dynamic Topology found: %s, disabled", message_unsupported); - me->flag &= ~ME_SCULPT_DYNAMIC_TOPOLOGY; - } - } - - /* Flush object mode. */ - DEG_id_tag_update(&ob->id, ID_RECALC_COPY_ON_WRITE); -} - -void ED_object_sculptmode_enter(struct bContext *C, Depsgraph *depsgraph, ReportList *reports) -{ - Main *bmain = CTX_data_main(C); - Scene *scene = CTX_data_scene(C); - ViewLayer *view_layer = CTX_data_view_layer(C); - Object *ob = OBACT(view_layer); - ED_object_sculptmode_enter_ex(bmain, depsgraph, scene, ob, false, reports); -} - -void ED_object_sculptmode_exit_ex(Main *bmain, Depsgraph *depsgraph, Scene *scene, Object *ob) -{ - const int mode_flag = OB_MODE_SCULPT; - Mesh *me = BKE_mesh_from_object(ob); - - multires_flush_sculpt_updates(ob); - - /* Not needed for now. */ -#if 0 - MultiresModifierData *mmd = BKE_sculpt_multires_active(scene, ob); - const int flush_recalc = ed_object_sculptmode_flush_recalc_flag(scene, ob, mmd); -#endif - - /* Always for now, so leaving sculpt mode always ensures scene is in - * a consistent state. */ - if (true || /* flush_recalc || */ (ob->sculpt && ob->sculpt->bm)) { - DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); - } - - if (me->flag & ME_SCULPT_DYNAMIC_TOPOLOGY) { - /* Dynamic topology must be disabled before exiting sculpt - * mode to ensure the undo stack stays in a consistent - * state. */ - sculpt_dynamic_topology_disable_with_undo(bmain, depsgraph, scene, ob); - - /* Store so we know to re-enable when entering sculpt mode. */ - me->flag |= ME_SCULPT_DYNAMIC_TOPOLOGY; - } - - /* Leave sculpt mode. */ - ob->mode &= ~mode_flag; - - BKE_sculptsession_free(ob); - - paint_cursor_delete_textures(); - - /* Never leave derived meshes behind. */ - BKE_object_free_derived_caches(ob); - - /* Flush object mode. */ - DEG_id_tag_update(&ob->id, ID_RECALC_COPY_ON_WRITE); -} - -void ED_object_sculptmode_exit(bContext *C, Depsgraph *depsgraph) -{ - Main *bmain = CTX_data_main(C); - Scene *scene = CTX_data_scene(C); - ViewLayer *view_layer = CTX_data_view_layer(C); - Object *ob = OBACT(view_layer); - ED_object_sculptmode_exit_ex(bmain, depsgraph, scene, ob); -} - -static int sculpt_mode_toggle_exec(bContext *C, wmOperator *op) -{ - struct wmMsgBus *mbus = CTX_wm_message_bus(C); - Main *bmain = CTX_data_main(C); - Depsgraph *depsgraph = CTX_data_depsgraph_on_load(C); - Scene *scene = CTX_data_scene(C); - ToolSettings *ts = scene->toolsettings; - ViewLayer *view_layer = CTX_data_view_layer(C); - Object *ob = OBACT(view_layer); - const int mode_flag = OB_MODE_SCULPT; - const bool is_mode_set = (ob->mode & mode_flag) != 0; - - if (!is_mode_set) { - if (!ED_object_mode_compat_set(C, ob, mode_flag, op->reports)) { - return OPERATOR_CANCELLED; - } - } - - if (is_mode_set) { - ED_object_sculptmode_exit_ex(bmain, depsgraph, scene, ob); - } - else { - if (depsgraph) { - depsgraph = CTX_data_ensure_evaluated_depsgraph(C); - } - ED_object_sculptmode_enter_ex(bmain, depsgraph, scene, ob, false, op->reports); - BKE_paint_toolslots_brush_validate(bmain, &ts->sculpt->paint); - - if (ob->mode & mode_flag) { - Mesh *me = ob->data; - /* Dyntopo adds its own undo step. */ - if ((me->flag & ME_SCULPT_DYNAMIC_TOPOLOGY) == 0) { - /* Without this the memfile undo step is used, - * while it works it causes lag when undoing the first undo step, see T71564. */ - wmWindowManager *wm = CTX_wm_manager(C); - if (wm->op_undo_depth <= 1) { - SCULPT_undo_push_begin(ob, op->type->name); - } - } - } - } - - WM_event_add_notifier(C, NC_SCENE | ND_MODE, scene); - - WM_msg_publish_rna_prop(mbus, &ob->id, ob, Object, mode); - - WM_toolsystem_update_from_context_view3d(C); - - return OPERATOR_FINISHED; -} - -static void SCULPT_OT_sculptmode_toggle(wmOperatorType *ot) -{ - /* Identifiers. */ - ot->name = "Sculpt Mode"; - ot->idname = "SCULPT_OT_sculptmode_toggle"; - ot->description = "Toggle sculpt mode in 3D view"; - - /* API callbacks. */ - ot->exec = sculpt_mode_toggle_exec; - ot->poll = ED_operator_object_active_editable_mesh; - - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; -} - -void SCULPT_geometry_preview_lines_update(bContext *C, SculptSession *ss, float radius) -{ - Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C); - Object *ob = CTX_data_active_object(C); - - ss->preview_vert_index_count = 0; - int totpoints = 0; - - /* This function is called from the cursor drawing code, so the PBVH may not be build yet. */ - if (!ss->pbvh) { - return; - } - - if (!ss->deform_modifiers_active) { - return; - } - - if (BKE_pbvh_type(ss->pbvh) == PBVH_GRIDS) { - return; - } - - BKE_sculpt_update_object_for_edit(depsgraph, ob, true, true, false); - - if (!ss->pmap) { - return; - } - - float brush_co[3]; - copy_v3_v3(brush_co, SCULPT_active_vertex_co_get(ss)); - - BLI_bitmap *visited_vertices = BLI_BITMAP_NEW(SCULPT_vertex_count_get(ss), "visited_vertices"); - - /* Assuming an average of 6 edges per vertex in a triangulated mesh. */ - const int max_preview_vertices = SCULPT_vertex_count_get(ss) * 3 * 2; - - if (ss->preview_vert_index_list == NULL) { - ss->preview_vert_index_list = MEM_callocN(max_preview_vertices * sizeof(int), "preview lines"); - } - - GSQueue *not_visited_vertices = BLI_gsqueue_new(sizeof(int)); - int active_v = SCULPT_active_vertex_get(ss); - BLI_gsqueue_push(not_visited_vertices, &active_v); - - while (!BLI_gsqueue_is_empty(not_visited_vertices)) { - int from_v; - BLI_gsqueue_pop(not_visited_vertices, &from_v); - SculptVertexNeighborIter ni; - SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, from_v, ni) { - if (totpoints + (ni.size * 2) < max_preview_vertices) { - int to_v = ni.index; - ss->preview_vert_index_list[totpoints] = from_v; - totpoints++; - ss->preview_vert_index_list[totpoints] = to_v; - totpoints++; - if (BLI_BITMAP_TEST(visited_vertices, to_v)) { - continue; - } - BLI_BITMAP_ENABLE(visited_vertices, to_v); - const float *co = SCULPT_vertex_co_for_grab_active_get(ss, to_v); - if (len_squared_v3v3(brush_co, co) < radius * radius) { - BLI_gsqueue_push(not_visited_vertices, &to_v); - } - } - } - SCULPT_VERTEX_NEIGHBORS_ITER_END(ni); - } - - BLI_gsqueue_free(not_visited_vertices); - - MEM_freeN(visited_vertices); - - ss->preview_vert_index_count = totpoints; -} - -static int vertex_to_loop_colors_exec(bContext *C, wmOperator *UNUSED(op)) -{ - Object *ob = CTX_data_active_object(C); - - ID *data; - data = ob->data; - if (data && ID_IS_LINKED(data)) { - return OPERATOR_CANCELLED; - } - - if (ob->type != OB_MESH) { - return OPERATOR_CANCELLED; - } - - Mesh *mesh = ob->data; - - const int mloopcol_layer_n = CustomData_get_active_layer(&mesh->ldata, CD_MLOOPCOL); - if (mloopcol_layer_n == -1) { - return OPERATOR_CANCELLED; - } - MLoopCol *loopcols = CustomData_get_layer_n(&mesh->ldata, CD_MLOOPCOL, mloopcol_layer_n); - - const int MPropCol_layer_n = CustomData_get_active_layer(&mesh->vdata, CD_PROP_COLOR); - if (MPropCol_layer_n == -1) { - return OPERATOR_CANCELLED; - } - MPropCol *vertcols = CustomData_get_layer_n(&mesh->vdata, CD_PROP_COLOR, MPropCol_layer_n); - - MLoop *loops = CustomData_get_layer(&mesh->ldata, CD_MLOOP); - MPoly *polys = CustomData_get_layer(&mesh->pdata, CD_MPOLY); - - for (int i = 0; i < mesh->totpoly; i++) { - MPoly *c_poly = &polys[i]; - for (int j = 0; j < c_poly->totloop; j++) { - int loop_index = c_poly->loopstart + j; - MLoop *c_loop = &loops[c_poly->loopstart + j]; - float srgb_color[4]; - linearrgb_to_srgb_v4(srgb_color, vertcols[c_loop->v].color); - loopcols[loop_index].r = (char)(srgb_color[0] * 255); - loopcols[loop_index].g = (char)(srgb_color[1] * 255); - loopcols[loop_index].b = (char)(srgb_color[2] * 255); - loopcols[loop_index].a = (char)(srgb_color[3] * 255); - } - } - - DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); - WM_event_add_notifier(C, NC_GEOM | ND_DATA, ob->data); - - return OPERATOR_FINISHED; -} - -static void SCULPT_OT_vertex_to_loop_colors(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Sculpt Vertex Color to Vertex Color"; - ot->description = "Copy the Sculpt Vertex Color to a regular color layer"; - ot->idname = "SCULPT_OT_vertex_to_loop_colors"; - - /* api callbacks */ - ot->poll = SCULPT_vertex_colors_poll; - ot->exec = vertex_to_loop_colors_exec; - - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; -} - -static int loop_to_vertex_colors_exec(bContext *C, wmOperator *UNUSED(op)) -{ - Object *ob = CTX_data_active_object(C); - - ID *data; - data = ob->data; - if (data && ID_IS_LINKED(data)) { - return OPERATOR_CANCELLED; - } - - if (ob->type != OB_MESH) { - return OPERATOR_CANCELLED; - } - - Mesh *mesh = ob->data; - - const int mloopcol_layer_n = CustomData_get_active_layer(&mesh->ldata, CD_MLOOPCOL); - if (mloopcol_layer_n == -1) { - return OPERATOR_CANCELLED; - } - MLoopCol *loopcols = CustomData_get_layer_n(&mesh->ldata, CD_MLOOPCOL, mloopcol_layer_n); - - const int MPropCol_layer_n = CustomData_get_active_layer(&mesh->vdata, CD_PROP_COLOR); - if (MPropCol_layer_n == -1) { - return OPERATOR_CANCELLED; - } - MPropCol *vertcols = CustomData_get_layer_n(&mesh->vdata, CD_PROP_COLOR, MPropCol_layer_n); - - MLoop *loops = CustomData_get_layer(&mesh->ldata, CD_MLOOP); - MPoly *polys = CustomData_get_layer(&mesh->pdata, CD_MPOLY); - - for (int i = 0; i < mesh->totpoly; i++) { - MPoly *c_poly = &polys[i]; - for (int j = 0; j < c_poly->totloop; j++) { - int loop_index = c_poly->loopstart + j; - MLoop *c_loop = &loops[c_poly->loopstart + j]; - vertcols[c_loop->v].color[0] = (loopcols[loop_index].r / 255.0f); - vertcols[c_loop->v].color[1] = (loopcols[loop_index].g / 255.0f); - vertcols[c_loop->v].color[2] = (loopcols[loop_index].b / 255.0f); - vertcols[c_loop->v].color[3] = (loopcols[loop_index].a / 255.0f); - srgb_to_linearrgb_v4(vertcols[c_loop->v].color, vertcols[c_loop->v].color); - } - } - - DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); - WM_event_add_notifier(C, NC_GEOM | ND_DATA, ob->data); - - return OPERATOR_FINISHED; -} - -static void SCULPT_OT_loop_to_vertex_colors(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Vertex Color to Sculpt Vertex Color"; - ot->description = "Copy the active loop color layer to the vertex color"; - ot->idname = "SCULPT_OT_loop_to_vertex_colors"; - - /* api callbacks */ - ot->poll = SCULPT_vertex_colors_poll; - ot->exec = loop_to_vertex_colors_exec; - - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; -} - -static int sculpt_sample_color_invoke(bContext *C, - wmOperator *UNUSED(op), - const wmEvent *UNUSED(e)) -{ - Sculpt *sd = CTX_data_tool_settings(C)->sculpt; - Scene *scene = CTX_data_scene(C); - Object *ob = CTX_data_active_object(C); - Brush *brush = BKE_paint_brush(&sd->paint); - SculptSession *ss = ob->sculpt; - int active_vertex = SCULPT_active_vertex_get(ss); - const float *active_vertex_color = SCULPT_vertex_color_get(ss, active_vertex); - if (!active_vertex_color) { - return OPERATOR_CANCELLED; - } - - float color_srgb[3]; - copy_v3_v3(color_srgb, active_vertex_color); - IMB_colormanagement_scene_linear_to_srgb_v3(color_srgb); - BKE_brush_color_set(scene, brush, color_srgb); - - WM_event_add_notifier(C, NC_BRUSH | NA_EDITED, brush); - - return OPERATOR_FINISHED; -} - -static void SCULPT_OT_sample_color(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Sample Color"; - ot->idname = "SCULPT_OT_sample_color"; - ot->description = "Sample the vertex color of the active vertex"; - - /* api callbacks */ - ot->invoke = sculpt_sample_color_invoke; - ot->poll = SCULPT_vertex_colors_poll; - - ot->flag = OPTYPE_REGISTER; -} - /* Fake Neighbors. */ /* This allows the sculpt tools to work on meshes with multiple connected components as they had * only one connected component. When initialized and enabled, the sculpt API will return extra @@ -9105,356 +5682,10 @@ void SCULPT_fake_neighbors_free(Object *ob) sculpt_pose_fake_neighbors_free(ss); } -/** - * #sculpt_mask_by_color_delta_get returns values in the (0,1) range that are used to generate the - * mask based on the difference between two colors (the active color and the color of any other - * vertex). Ideally, a threshold of 0 should mask only the colors that are equal to the active - * color and threshold of 1 should mask all colors. In order to avoid artifacts and produce softer - * falloffs in the mask, the MASK_BY_COLOR_SLOPE defines the size of the transition values between - * masked and unmasked vertices. The smaller this value is, the sharper the generated mask is going - * to be. - */ -#define MASK_BY_COLOR_SLOPE 0.25f - -static float sculpt_mask_by_color_delta_get(const float *color_a, - const float *color_b, - const float threshold, - const bool invert) -{ - float len = len_v3v3(color_a, color_b); - /* Normalize len to the (0, 1) range. */ - len = len / M_SQRT3; - - if (len < threshold - MASK_BY_COLOR_SLOPE) { - len = 1.0f; - } - else if (len >= threshold) { - len = 0.0f; - } - else { - len = (-len + threshold) / MASK_BY_COLOR_SLOPE; - } - - if (invert) { - return 1.0f - len; - } - return len; -} - -static float sculpt_mask_by_color_final_mask_get(const float current_mask, - const float new_mask, - const bool invert, - const bool preserve_mask) -{ - if (preserve_mask) { - if (invert) { - return min_ff(current_mask, new_mask); - } - return max_ff(current_mask, new_mask); - } - return new_mask; -} - -typedef struct MaskByColorContiguousFloodFillData { - float threshold; - bool invert; - float *new_mask; - float initial_color[3]; -} MaskByColorContiguousFloodFillData; - -static void do_mask_by_color_contiguous_update_nodes_cb( - void *__restrict userdata, const int n, const TaskParallelTLS *__restrict UNUSED(tls)) -{ - SculptThreadedTaskData *data = userdata; - SculptSession *ss = data->ob->sculpt; - - SCULPT_undo_push_node(data->ob, data->nodes[n], SCULPT_UNDO_MASK); - bool update_node = false; - - const bool invert = data->mask_by_color_invert; - const bool preserve_mask = data->mask_by_color_preserve_mask; - - PBVHVertexIter vd; - BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { - const float current_mask = *vd.mask; - const float new_mask = data->mask_by_color_floodfill[vd.index]; - *vd.mask = sculpt_mask_by_color_final_mask_get(current_mask, new_mask, invert, preserve_mask); - if (current_mask == *vd.mask) { - continue; - } - update_node = true; - if (vd.mvert) { - vd.mvert->flag |= ME_VERT_PBVH_UPDATE; - } - } - BKE_pbvh_vertex_iter_end; - if (update_node) { - BKE_pbvh_node_mark_redraw(data->nodes[n]); - } -} - -static bool sculpt_mask_by_color_contiguous_floodfill_cb( - SculptSession *ss, int from_v, int to_v, bool is_duplicate, void *userdata) -{ - MaskByColorContiguousFloodFillData *data = userdata; - const float *current_color = SCULPT_vertex_color_get(ss, to_v); - float new_vertex_mask = sculpt_mask_by_color_delta_get( - current_color, data->initial_color, data->threshold, data->invert); - data->new_mask[to_v] = new_vertex_mask; - - if (is_duplicate) { - data->new_mask[to_v] = data->new_mask[from_v]; - } - - float len = len_v3v3(current_color, data->initial_color); - len = len / M_SQRT3; - return len <= data->threshold; -} - -static void sculpt_mask_by_color_contiguous(Object *object, - const int vertex, - const float threshold, - const bool invert, - const bool preserve_mask) -{ - SculptSession *ss = object->sculpt; - const int totvert = SCULPT_vertex_count_get(ss); - - float *new_mask = MEM_calloc_arrayN(totvert, sizeof(float), "new mask"); - - if (invert) { - for (int i = 0; i < totvert; i++) { - new_mask[i] = 1.0f; - } - } - - SculptFloodFill flood; - SCULPT_floodfill_init(ss, &flood); - SCULPT_floodfill_add_initial(&flood, vertex); - - MaskByColorContiguousFloodFillData ffd; - ffd.threshold = threshold; - ffd.invert = invert; - ffd.new_mask = new_mask; - copy_v3_v3(ffd.initial_color, SCULPT_vertex_color_get(ss, vertex)); - - SCULPT_floodfill_execute(ss, &flood, sculpt_mask_by_color_contiguous_floodfill_cb, &ffd); - SCULPT_floodfill_free(&flood); - - int totnode; - PBVHNode **nodes; - BKE_pbvh_search_gather(ss->pbvh, NULL, NULL, &nodes, &totnode); - - SculptThreadedTaskData data = { - .ob = object, - .nodes = nodes, - .mask_by_color_floodfill = new_mask, - .mask_by_color_vertex = vertex, - .mask_by_color_threshold = threshold, - .mask_by_color_invert = invert, - .mask_by_color_preserve_mask = preserve_mask, - }; - - TaskParallelSettings settings; - BKE_pbvh_parallel_range_settings(&settings, true, totnode); - BLI_task_parallel_range( - 0, totnode, &data, do_mask_by_color_contiguous_update_nodes_cb, &settings); - - MEM_SAFE_FREE(nodes); - - MEM_freeN(new_mask); -} - -static void do_mask_by_color_task_cb(void *__restrict userdata, - const int n, - const TaskParallelTLS *__restrict UNUSED(tls)) -{ - SculptThreadedTaskData *data = userdata; - SculptSession *ss = data->ob->sculpt; - - SCULPT_undo_push_node(data->ob, data->nodes[n], SCULPT_UNDO_MASK); - bool update_node = false; - - const float threshold = data->mask_by_color_threshold; - const bool invert = data->mask_by_color_invert; - const bool preserve_mask = data->mask_by_color_preserve_mask; - const float *active_color = SCULPT_vertex_color_get(ss, data->mask_by_color_vertex); - - PBVHVertexIter vd; - BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { - const float current_mask = *vd.mask; - const float new_mask = sculpt_mask_by_color_delta_get(active_color, vd.col, threshold, invert); - *vd.mask = sculpt_mask_by_color_final_mask_get(current_mask, new_mask, invert, preserve_mask); - - if (current_mask == *vd.mask) { - continue; - } - update_node = true; - if (vd.mvert) { - vd.mvert->flag |= ME_VERT_PBVH_UPDATE; - } - } - BKE_pbvh_vertex_iter_end; - if (update_node) { - BKE_pbvh_node_mark_redraw(data->nodes[n]); - } -} - -static void sculpt_mask_by_color_full_mesh(Object *object, - const int vertex, - const float threshold, - const bool invert, - const bool preserve_mask) -{ - SculptSession *ss = object->sculpt; - - int totnode; - PBVHNode **nodes; - BKE_pbvh_search_gather(ss->pbvh, NULL, NULL, &nodes, &totnode); - - SculptThreadedTaskData data = { - .ob = object, - .nodes = nodes, - .mask_by_color_vertex = vertex, - .mask_by_color_threshold = threshold, - .mask_by_color_invert = invert, - .mask_by_color_preserve_mask = preserve_mask, - }; - - TaskParallelSettings settings; - BKE_pbvh_parallel_range_settings(&settings, true, totnode); - BLI_task_parallel_range(0, totnode, &data, do_mask_by_color_task_cb, &settings); - - MEM_SAFE_FREE(nodes); -} - -static int sculpt_mask_by_color_invoke(bContext *C, wmOperator *op, const wmEvent *event) -{ - Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C); - Object *ob = CTX_data_active_object(C); - SculptSession *ss = ob->sculpt; - - BKE_sculpt_update_object_for_edit(depsgraph, ob, true, true, false); - - /* Color data is not available in Multires. */ - if (BKE_pbvh_type(ss->pbvh) != PBVH_FACES) { - return OPERATOR_CANCELLED; - } - - if (!ss->vcol) { - return OPERATOR_CANCELLED; - } - - SCULPT_vertex_random_access_ensure(ss); - - /* Tools that are not brushes do not have the brush gizmo to update the vertex as the mouse move, - * so it needs to be updated here. */ - SculptCursorGeometryInfo sgi; - float mouse[2]; - mouse[0] = event->mval[0]; - mouse[1] = event->mval[1]; - SCULPT_cursor_geometry_info_update(C, &sgi, mouse, false); - - SCULPT_undo_push_begin(ob, "Mask by color"); - - const int active_vertex = SCULPT_active_vertex_get(ss); - const float threshold = RNA_float_get(op->ptr, "threshold"); - const bool invert = RNA_boolean_get(op->ptr, "invert"); - const bool preserve_mask = RNA_boolean_get(op->ptr, "preserve_previous_mask"); - - if (RNA_boolean_get(op->ptr, "contiguous")) { - sculpt_mask_by_color_contiguous(ob, active_vertex, threshold, invert, preserve_mask); - } - else { - sculpt_mask_by_color_full_mesh(ob, active_vertex, threshold, invert, preserve_mask); - } - - BKE_pbvh_update_vertex_data(ss->pbvh, PBVH_UpdateMask); - SCULPT_undo_push_end(); - - SCULPT_flush_update_done(C, ob, SCULPT_UPDATE_MASK); - - return OPERATOR_FINISHED; -} - -static void SCULPT_OT_mask_by_color(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Mask by Color"; - ot->idname = "SCULPT_OT_mask_by_color"; - ot->description = "Creates a mask based on the sculpt vertex colors"; - - /* api callbacks */ - ot->invoke = sculpt_mask_by_color_invoke; - ot->poll = SCULPT_vertex_colors_poll; - - ot->flag = OPTYPE_REGISTER; - - ot->prop = RNA_def_boolean( - ot->srna, "contiguous", false, "Contiguous", "Mask only contiguous color areas"); - - ot->prop = RNA_def_boolean(ot->srna, "invert", false, "Invert", "Invert the generated mask"); - ot->prop = RNA_def_boolean( - ot->srna, - "preserve_previous_mask", - false, - "Preserve Previous Mask", - "Preserve the previous mask and add or subtract the new one generated by the colors"); - - RNA_def_float(ot->srna, - "threshold", - 0.35f, - 0.0f, - 1.0f, - "Threshold", - "How much changes in color affect the mask generation", - 0.0f, - 1.0f); -} - /** \} */ /* -------------------------------------------------------------------- */ /** \name Operator Registration * \{ */ -void ED_operatortypes_sculpt(void) -{ - WM_operatortype_append(SCULPT_OT_brush_stroke); - WM_operatortype_append(SCULPT_OT_sculptmode_toggle); - WM_operatortype_append(SCULPT_OT_set_persistent_base); - WM_operatortype_append(SCULPT_OT_dynamic_topology_toggle); - WM_operatortype_append(SCULPT_OT_optimize); - WM_operatortype_append(SCULPT_OT_symmetrize); - WM_operatortype_append(SCULPT_OT_detail_flood_fill); - WM_operatortype_append(SCULPT_OT_sample_detail_size); - WM_operatortype_append(SCULPT_OT_set_detail_size); - WM_operatortype_append(SCULPT_OT_mesh_filter); - WM_operatortype_append(SCULPT_OT_mask_filter); - WM_operatortype_append(SCULPT_OT_dirty_mask); - WM_operatortype_append(SCULPT_OT_mask_expand); - WM_operatortype_append(SCULPT_OT_set_pivot_position); - WM_operatortype_append(SCULPT_OT_face_sets_create); - WM_operatortype_append(SCULPT_OT_face_sets_change_visibility); - WM_operatortype_append(SCULPT_OT_face_sets_randomize_colors); - WM_operatortype_append(SCULPT_OT_face_sets_init); - WM_operatortype_append(SCULPT_OT_cloth_filter); - WM_operatortype_append(SCULPT_OT_face_sets_edit); - WM_operatortype_append(SCULPT_OT_face_set_lasso_gesture); - WM_operatortype_append(SCULPT_OT_face_set_box_gesture); - WM_operatortype_append(SCULPT_OT_trim_box_gesture); - WM_operatortype_append(SCULPT_OT_trim_lasso_gesture); - WM_operatortype_append(SCULPT_OT_project_line_gesture); - - WM_operatortype_append(SCULPT_OT_sample_color); - WM_operatortype_append(SCULPT_OT_loop_to_vertex_colors); - WM_operatortype_append(SCULPT_OT_vertex_to_loop_colors); - WM_operatortype_append(SCULPT_OT_color_filter); - WM_operatortype_append(SCULPT_OT_mask_by_color); - WM_operatortype_append(SCULPT_OT_dyntopo_detail_size_edit); - WM_operatortype_append(SCULPT_OT_mask_init); - - WM_operatortype_append(SCULPT_OT_expand); -} - /** \} */ diff --git a/source/blender/editors/sculpt_paint/sculpt_brushes.c b/source/blender/editors/sculpt_paint/sculpt_brushes.c new file mode 100644 index 00000000000..8842d93410c --- /dev/null +++ b/source/blender/editors/sculpt_paint/sculpt_brushes.c @@ -0,0 +1,2847 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2006 by Nicholas Bishop + * All rights reserved. + * Implements the Sculpt Mode tools + */ + +/** \file + * \ingroup edsculpt + */ + +#include "MEM_guardedalloc.h" + +#include "BLI_blenlib.h" +#include "BLI_dial_2d.h" +#include "BLI_ghash.h" +#include "BLI_gsqueue.h" +#include "BLI_hash.h" +#include "BLI_math.h" +#include "BLI_math_color.h" +#include "BLI_math_color_blend.h" +#include "BLI_task.h" +#include "BLI_utildefines.h" + +#include "BLT_translation.h" + +#include "PIL_time.h" + +#include "DNA_brush_types.h" +#include "DNA_customdata_types.h" +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" +#include "DNA_node_types.h" +#include "DNA_object_types.h" +#include "DNA_scene_types.h" + +#include "BKE_brush.h" +#include "BKE_ccg.h" +#include "BKE_colortools.h" +#include "BKE_context.h" +#include "BKE_image.h" +#include "BKE_kelvinlet.h" +#include "BKE_key.h" +#include "BKE_lib_id.h" +#include "BKE_main.h" +#include "BKE_mesh.h" +#include "BKE_mesh_mapping.h" +#include "BKE_mesh_mirror.h" +#include "BKE_modifier.h" +#include "BKE_multires.h" +#include "BKE_node.h" +#include "BKE_object.h" +#include "BKE_paint.h" +#include "BKE_particle.h" +#include "BKE_pbvh.h" +#include "BKE_pointcache.h" +#include "BKE_report.h" +#include "BKE_scene.h" +#include "BKE_screen.h" +#include "BKE_subdiv_ccg.h" +#include "BKE_subsurf.h" + +#include "DEG_depsgraph.h" + +#include "IMB_colormanagement.h" + +#include "WM_api.h" +#include "WM_message.h" +#include "WM_toolsystem.h" +#include "WM_types.h" + +#include "ED_object.h" +#include "ED_screen.h" +#include "ED_sculpt.h" +#include "ED_view3d.h" + +#include "paint_intern.h" +#include "sculpt_intern.h" + +#include "RNA_access.h" +#include "RNA_define.h" + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "bmesh.h" +#include "bmesh_tools.h" + +#include <math.h> +#include <stdlib.h> +#include <string.h> + +/* -------------------------------------------------------------------- */ +/** \name SculptProjectVector + * + * Fast-path for #project_plane_v3_v3v3 + * \{ */ + +typedef struct SculptProjectVector { + float plane[3]; + float len_sq; + float len_sq_inv_neg; + bool is_valid; + +} SculptProjectVector; + +static bool plane_point_side_flip(const float co[3], const float plane[4], const bool flip) +{ + float d = plane_point_side_v3(plane, co); + if (flip) { + d = -d; + } + return d <= 0.0f; +} + +/** + * \param plane: Direction, can be any length. + */ +static void sculpt_project_v3_cache_init(SculptProjectVector *spvc, const float plane[3]) +{ + copy_v3_v3(spvc->plane, plane); + spvc->len_sq = len_squared_v3(spvc->plane); + spvc->is_valid = (spvc->len_sq > FLT_EPSILON); + spvc->len_sq_inv_neg = (spvc->is_valid) ? -1.0f / spvc->len_sq : 0.0f; +} + +/** + * Calculate the projection. + */ +static void sculpt_project_v3(const SculptProjectVector *spvc, const float vec[3], float r_vec[3]) +{ +#if 0 + project_plane_v3_v3v3(r_vec, vec, spvc->plane); +#else + /* inline the projection, cache `-1.0 / dot_v3_v3(v_proj, v_proj)` */ + madd_v3_v3fl(r_vec, spvc->plane, dot_v3v3(vec, spvc->plane) * spvc->len_sq_inv_neg); +#endif +} + +static void calc_sculpt_plane( + Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode, float r_area_no[3], float r_area_co[3]) +{ + SculptSession *ss = ob->sculpt; + Brush *brush = BKE_paint_brush(&sd->paint); + + if (SCULPT_stroke_is_main_symmetry_pass(ss->cache) && + (SCULPT_stroke_is_first_brush_step_of_symmetry_pass(ss->cache) || + !(brush->flag & BRUSH_ORIGINAL_PLANE) || !(brush->flag & BRUSH_ORIGINAL_NORMAL))) { + switch (brush->sculpt_plane) { + case SCULPT_DISP_DIR_VIEW: + copy_v3_v3(r_area_no, ss->cache->true_view_normal); + break; + + case SCULPT_DISP_DIR_X: + ARRAY_SET_ITEMS(r_area_no, 1.0f, 0.0f, 0.0f); + break; + + case SCULPT_DISP_DIR_Y: + ARRAY_SET_ITEMS(r_area_no, 0.0f, 1.0f, 0.0f); + break; + + case SCULPT_DISP_DIR_Z: + ARRAY_SET_ITEMS(r_area_no, 0.0f, 0.0f, 1.0f); + break; + + case SCULPT_DISP_DIR_AREA: + SCULPT_calc_area_normal_and_center(sd, ob, nodes, totnode, r_area_no, r_area_co); + if (brush->falloff_shape == PAINT_FALLOFF_SHAPE_TUBE) { + project_plane_v3_v3v3(r_area_no, r_area_no, ss->cache->view_normal); + normalize_v3(r_area_no); + } + break; + + default: + break; + } + + /* For flatten center. */ + /* Flatten center has not been calculated yet if we are not using the area normal. */ + if (brush->sculpt_plane != SCULPT_DISP_DIR_AREA) { + SCULPT_calc_area_center(sd, ob, nodes, totnode, r_area_co); + } + + /* For area normal. */ + if ((!SCULPT_stroke_is_first_brush_step_of_symmetry_pass(ss->cache)) && + (brush->flag & BRUSH_ORIGINAL_NORMAL)) { + copy_v3_v3(r_area_no, ss->cache->sculpt_normal); + } + else { + copy_v3_v3(ss->cache->sculpt_normal, r_area_no); + } + + /* For flatten center. */ + if ((!SCULPT_stroke_is_first_brush_step_of_symmetry_pass(ss->cache)) && + (brush->flag & BRUSH_ORIGINAL_PLANE)) { + copy_v3_v3(r_area_co, ss->cache->last_center); + } + else { + copy_v3_v3(ss->cache->last_center, r_area_co); + } + } + else { + /* For area normal. */ + copy_v3_v3(r_area_no, ss->cache->sculpt_normal); + + /* For flatten center. */ + copy_v3_v3(r_area_co, ss->cache->last_center); + + /* For area normal. */ + flip_v3(r_area_no, ss->cache->mirror_symmetry_pass); + + /* For flatten center. */ + flip_v3(r_area_co, ss->cache->mirror_symmetry_pass); + + /* For area normal. */ + mul_m4_v3(ss->cache->symm_rot_mat, r_area_no); + + /* For flatten center. */ + mul_m4_v3(ss->cache->symm_rot_mat, r_area_co); + + /* Shift the plane for the current tile. */ + add_v3_v3(r_area_co, ss->cache->plane_offset); + } +} + +static void sculpt_rake_rotate(const SculptSession *ss, + const float sculpt_co[3], + const float v_co[3], + float factor, + float r_delta[3]) +{ + float vec_rot[3]; + +#if 0 + /* lerp */ + sub_v3_v3v3(vec_rot, v_co, sculpt_co); + mul_qt_v3(ss->cache->rake_rotation_symmetry, vec_rot); + add_v3_v3(vec_rot, sculpt_co); + sub_v3_v3v3(r_delta, vec_rot, v_co); + mul_v3_fl(r_delta, factor); +#else + /* slerp */ + float q_interp[4]; + sub_v3_v3v3(vec_rot, v_co, sculpt_co); + + copy_qt_qt(q_interp, ss->cache->rake_rotation_symmetry); + pow_qt_fl_normalized(q_interp, factor); + mul_qt_v3(q_interp, vec_rot); + + add_v3_v3(vec_rot, sculpt_co); + sub_v3_v3v3(r_delta, vec_rot, v_co); +#endif +} + +/** + * Align the grab delta to the brush normal. + * + * \param grab_delta: Typically from `ss->cache->grab_delta_symmetry`. + */ +static void sculpt_project_v3_normal_align(SculptSession *ss, + const float normal_weight, + float grab_delta[3]) +{ + /* Signed to support grabbing in (to make a hole) as well as out. */ + const float len_signed = dot_v3v3(ss->cache->sculpt_normal_symm, grab_delta); + + /* This scale effectively projects the offset so dragging follows the cursor, + * as the normal points towards the view, the scale increases. */ + float len_view_scale; + { + float view_aligned_normal[3]; + project_plane_v3_v3v3( + view_aligned_normal, ss->cache->sculpt_normal_symm, ss->cache->view_normal); + len_view_scale = fabsf(dot_v3v3(view_aligned_normal, ss->cache->sculpt_normal_symm)); + len_view_scale = (len_view_scale > FLT_EPSILON) ? 1.0f / len_view_scale : 1.0f; + } + + mul_v3_fl(grab_delta, 1.0f - normal_weight); + madd_v3_v3fl( + grab_delta, ss->cache->sculpt_normal_symm, (len_signed * normal_weight) * len_view_scale); +} + +/* -------------------------------------------------------------------- */ +/** \name Sculpt Draw Brush + * \{ */ + +static void do_draw_brush_task_cb_ex(void *__restrict userdata, + const int n, + const TaskParallelTLS *__restrict tls) +{ + SculptThreadedTaskData *data = userdata; + SculptSession *ss = data->ob->sculpt; + const Brush *brush = data->brush; + const float *offset = data->offset; + + PBVHVertexIter vd; + float(*proxy)[3]; + + proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co; + + SculptBrushTest test; + SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( + ss, &test, data->brush->falloff_shape); + const int thread_id = BLI_task_parallel_thread_id(tls); + + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { + if (!sculpt_brush_test_sq_fn(&test, vd.co)) { + continue; + } + /* Offset vertex. */ + const float fade = SCULPT_brush_strength_factor(ss, + brush, + vd.co, + sqrtf(test.dist), + vd.no, + vd.fno, + vd.mask ? *vd.mask : 0.0f, + vd.index, + thread_id); + + mul_v3_v3fl(proxy[vd.i], offset, fade); + + if (vd.mvert) { + vd.mvert->flag |= ME_VERT_PBVH_UPDATE; + } + } + BKE_pbvh_vertex_iter_end; +} + +void SCULPT_do_draw_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) +{ + SculptSession *ss = ob->sculpt; + Brush *brush = BKE_paint_brush(&sd->paint); + float offset[3]; + const float bstrength = ss->cache->bstrength; + + /* Offset with as much as possible factored in already. */ + float effective_normal[3]; + SCULPT_tilt_effective_normal_get(ss, brush, effective_normal); + mul_v3_v3fl(offset, effective_normal, ss->cache->radius); + mul_v3_v3(offset, ss->cache->scale); + mul_v3_fl(offset, bstrength); + + /* XXX: this shouldn't be necessary, but sculpting crashes in blender2.8 otherwise + * initialize before threads so they can do curve mapping. */ + BKE_curvemapping_init(brush->curve); + + /* Threaded loop over nodes. */ + SculptThreadedTaskData data = { + .sd = sd, + .ob = ob, + .brush = brush, + .nodes = nodes, + .offset = offset, + }; + + TaskParallelSettings settings; + BKE_pbvh_parallel_range_settings(&settings, true, totnode); + BLI_task_parallel_range(0, totnode, &data, do_draw_brush_task_cb_ex, &settings); +} + +/** \} */ + +static void do_fill_brush_task_cb_ex(void *__restrict userdata, + const int n, + const TaskParallelTLS *__restrict tls) +{ + SculptThreadedTaskData *data = userdata; + SculptSession *ss = data->ob->sculpt; + const Brush *brush = data->brush; + const float *area_no = data->area_no; + const float *area_co = data->area_co; + + PBVHVertexIter vd; + float(*proxy)[3]; + const float bstrength = ss->cache->bstrength; + + proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co; + + SculptBrushTest test; + SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( + ss, &test, data->brush->falloff_shape); + const int thread_id = BLI_task_parallel_thread_id(tls); + + plane_from_point_normal_v3(test.plane_tool, area_co, area_no); + + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { + if (!sculpt_brush_test_sq_fn(&test, vd.co)) { + continue; + } + + if (!SCULPT_plane_point_side(vd.co, test.plane_tool)) { + continue; + } + + float intr[3]; + float val[3]; + closest_to_plane_normalized_v3(intr, test.plane_tool, vd.co); + sub_v3_v3v3(val, intr, vd.co); + + if (!SCULPT_plane_trim(ss->cache, brush, val)) { + continue; + } + + const float fade = bstrength * SCULPT_brush_strength_factor(ss, + brush, + vd.co, + sqrtf(test.dist), + vd.no, + vd.fno, + vd.mask ? *vd.mask : 0.0f, + vd.index, + thread_id); + + mul_v3_v3fl(proxy[vd.i], val, fade); + + if (vd.mvert) { + vd.mvert->flag |= ME_VERT_PBVH_UPDATE; + } + } + BKE_pbvh_vertex_iter_end; +} + +void SCULPT_do_fill_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) +{ + SculptSession *ss = ob->sculpt; + Brush *brush = BKE_paint_brush(&sd->paint); + + const float radius = ss->cache->radius; + + float area_no[3]; + float area_co[3]; + float offset = SCULPT_brush_plane_offset_get(sd, ss); + + float displace; + + float temp[3]; + + SCULPT_calc_brush_plane(sd, ob, nodes, totnode, area_no, area_co); + + SCULPT_tilt_apply_to_normal(area_no, ss->cache, brush->tilt_strength_factor); + + displace = radius * offset; + + mul_v3_v3v3(temp, area_no, ss->cache->scale); + mul_v3_fl(temp, displace); + add_v3_v3(area_co, temp); + + SculptThreadedTaskData data = { + .sd = sd, + .ob = ob, + .brush = brush, + .nodes = nodes, + .area_no = area_no, + .area_co = area_co, + }; + + TaskParallelSettings settings; + BKE_pbvh_parallel_range_settings(&settings, true, totnode); + BLI_task_parallel_range(0, totnode, &data, do_fill_brush_task_cb_ex, &settings); +} + +static void do_scrape_brush_task_cb_ex(void *__restrict userdata, + const int n, + const TaskParallelTLS *__restrict tls) +{ + SculptThreadedTaskData *data = userdata; + SculptSession *ss = data->ob->sculpt; + const Brush *brush = data->brush; + const float *area_no = data->area_no; + const float *area_co = data->area_co; + + PBVHVertexIter vd; + float(*proxy)[3]; + const float bstrength = ss->cache->bstrength; + + proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co; + + SculptBrushTest test; + SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( + ss, &test, data->brush->falloff_shape); + const int thread_id = BLI_task_parallel_thread_id(tls); + plane_from_point_normal_v3(test.plane_tool, area_co, area_no); + + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { + if (!sculpt_brush_test_sq_fn(&test, vd.co)) { + continue; + } + + if (SCULPT_plane_point_side(vd.co, test.plane_tool)) { + continue; + } + + float intr[3]; + float val[3]; + closest_to_plane_normalized_v3(intr, test.plane_tool, vd.co); + sub_v3_v3v3(val, intr, vd.co); + + if (!SCULPT_plane_trim(ss->cache, brush, val)) { + continue; + } + + const float fade = bstrength * SCULPT_brush_strength_factor(ss, + brush, + vd.co, + sqrtf(test.dist), + vd.no, + vd.fno, + vd.mask ? *vd.mask : 0.0f, + vd.index, + thread_id); + + mul_v3_v3fl(proxy[vd.i], val, fade); + + if (vd.mvert) { + vd.mvert->flag |= ME_VERT_PBVH_UPDATE; + } + } + BKE_pbvh_vertex_iter_end; +} + +void SCULPT_do_scrape_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) +{ + SculptSession *ss = ob->sculpt; + Brush *brush = BKE_paint_brush(&sd->paint); + + const float radius = ss->cache->radius; + + float area_no[3]; + float area_co[3]; + float offset = SCULPT_brush_plane_offset_get(sd, ss); + + float displace; + + float temp[3]; + + SCULPT_calc_brush_plane(sd, ob, nodes, totnode, area_no, area_co); + + SCULPT_tilt_apply_to_normal(area_no, ss->cache, brush->tilt_strength_factor); + + displace = -radius * offset; + + mul_v3_v3v3(temp, area_no, ss->cache->scale); + mul_v3_fl(temp, displace); + add_v3_v3(area_co, temp); + + SculptThreadedTaskData data = { + .sd = sd, + .ob = ob, + .brush = brush, + .nodes = nodes, + .area_no = area_no, + .area_co = area_co, + }; + + TaskParallelSettings settings; + BKE_pbvh_parallel_range_settings(&settings, true, totnode); + BLI_task_parallel_range(0, totnode, &data, do_scrape_brush_task_cb_ex, &settings); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Sculpt Clay Thumb Brush + * \{ */ + +static void do_clay_thumb_brush_task_cb_ex(void *__restrict userdata, + const int n, + const TaskParallelTLS *__restrict tls) +{ + SculptThreadedTaskData *data = userdata; + SculptSession *ss = data->ob->sculpt; + const Brush *brush = data->brush; + float(*mat)[4] = data->mat; + const float *area_no_sp = data->area_no_sp; + const float *area_co = data->area_co; + + PBVHVertexIter vd; + float(*proxy)[3]; + const float bstrength = data->clay_strength; + + proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co; + + SculptBrushTest test; + SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( + ss, &test, data->brush->falloff_shape); + const int thread_id = BLI_task_parallel_thread_id(tls); + + float plane_tilt[4]; + float normal_tilt[3]; + float imat[4][4]; + + invert_m4_m4(imat, mat); + rotate_v3_v3v3fl(normal_tilt, area_no_sp, imat[0], DEG2RADF(-ss->cache->clay_thumb_front_angle)); + + /* Plane aligned to the geometry normal (back part of the brush). */ + plane_from_point_normal_v3(test.plane_tool, area_co, area_no_sp); + /* Tilted plane (front part of the brush). */ + plane_from_point_normal_v3(plane_tilt, area_co, normal_tilt); + + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { + if (!sculpt_brush_test_sq_fn(&test, vd.co)) { + continue; + } + float local_co[3]; + mul_v3_m4v3(local_co, mat, vd.co); + float intr[3], intr_tilt[3]; + float val[3]; + + closest_to_plane_normalized_v3(intr, test.plane_tool, vd.co); + closest_to_plane_normalized_v3(intr_tilt, plane_tilt, vd.co); + + /* Mix the deformation of the aligned and the tilted plane based on the brush space vertex + * coordinates. */ + /* We can also control the mix with a curve if it produces noticeable artifacts in the center + * of the brush. */ + const float tilt_mix = local_co[1] > 0.0f ? 0.0f : 1.0f; + interp_v3_v3v3(intr, intr, intr_tilt, tilt_mix); + sub_v3_v3v3(val, intr_tilt, vd.co); + + const float fade = bstrength * SCULPT_brush_strength_factor(ss, + brush, + vd.co, + sqrtf(test.dist), + vd.no, + vd.fno, + vd.mask ? *vd.mask : 0.0f, + vd.index, + thread_id); + + mul_v3_v3fl(proxy[vd.i], val, fade); + + if (vd.mvert) { + vd.mvert->flag |= ME_VERT_PBVH_UPDATE; + } + } + BKE_pbvh_vertex_iter_end; +} + +float SCULPT_clay_thumb_get_stabilized_pressure(StrokeCache *cache) +{ + float final_pressure = 0.0f; + for (int i = 0; i < SCULPT_CLAY_STABILIZER_LEN; i++) { + final_pressure += cache->clay_pressure_stabilizer[i]; + } + return final_pressure / SCULPT_CLAY_STABILIZER_LEN; +} + +void SCULPT_do_clay_thumb_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) +{ + SculptSession *ss = ob->sculpt; + Brush *brush = BKE_paint_brush(&sd->paint); + + const float radius = ss->cache->radius; + const float offset = SCULPT_brush_plane_offset_get(sd, ss); + const float displace = radius * (0.25f + offset); + + /* Sampled geometry normal and area center. */ + float area_no_sp[3]; + float area_no[3]; + float area_co[3]; + + float temp[3]; + float mat[4][4]; + float scale[4][4]; + float tmat[4][4]; + + SCULPT_calc_brush_plane(sd, ob, nodes, totnode, area_no_sp, area_co); + + if (brush->sculpt_plane != SCULPT_DISP_DIR_AREA || (brush->flag & BRUSH_ORIGINAL_NORMAL)) { + SCULPT_calc_area_normal(sd, ob, nodes, totnode, area_no); + } + else { + copy_v3_v3(area_no, area_no_sp); + } + + /* Delay the first daub because grab delta is not setup. */ + if (SCULPT_stroke_is_first_brush_step_of_symmetry_pass(ss->cache)) { + ss->cache->clay_thumb_front_angle = 0.0f; + return; + } + + /* Simulate the clay accumulation by increasing the plane angle as more samples are added to the + * stroke. */ + if (SCULPT_stroke_is_main_symmetry_pass(ss->cache)) { + ss->cache->clay_thumb_front_angle += 0.8f; + ss->cache->clay_thumb_front_angle = clamp_f(ss->cache->clay_thumb_front_angle, 0.0f, 60.0f); + } + + if (is_zero_v3(ss->cache->grab_delta_symmetry)) { + return; + } + + /* Displace the brush planes. */ + copy_v3_v3(area_co, ss->cache->location); + mul_v3_v3v3(temp, area_no_sp, ss->cache->scale); + mul_v3_fl(temp, displace); + add_v3_v3(area_co, temp); + + /* Initialize brush local-space matrix. */ + cross_v3_v3v3(mat[0], area_no, ss->cache->grab_delta_symmetry); + mat[0][3] = 0.0f; + cross_v3_v3v3(mat[1], area_no, mat[0]); + mat[1][3] = 0.0f; + copy_v3_v3(mat[2], area_no); + mat[2][3] = 0.0f; + copy_v3_v3(mat[3], ss->cache->location); + mat[3][3] = 1.0f; + normalize_m4(mat); + + /* Scale brush local space matrix. */ + scale_m4_fl(scale, ss->cache->radius); + mul_m4_m4m4(tmat, mat, scale); + invert_m4_m4(mat, tmat); + + float clay_strength = ss->cache->bstrength * + SCULPT_clay_thumb_get_stabilized_pressure(ss->cache); + + SculptThreadedTaskData data = { + .sd = sd, + .ob = ob, + .brush = brush, + .nodes = nodes, + .area_no_sp = area_no_sp, + .area_co = ss->cache->location, + .mat = mat, + .clay_strength = clay_strength, + }; + + TaskParallelSettings settings; + BKE_pbvh_parallel_range_settings(&settings, true, totnode); + BLI_task_parallel_range(0, totnode, &data, do_clay_thumb_brush_task_cb_ex, &settings); +} + +/** \} */ + +static void do_flatten_brush_task_cb_ex(void *__restrict userdata, + const int n, + const TaskParallelTLS *__restrict tls) +{ + SculptThreadedTaskData *data = userdata; + SculptSession *ss = data->ob->sculpt; + const Brush *brush = data->brush; + const float *area_no = data->area_no; + const float *area_co = data->area_co; + + PBVHVertexIter vd; + float(*proxy)[3]; + const float bstrength = ss->cache->bstrength; + + proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co; + + SculptBrushTest test; + SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( + ss, &test, data->brush->falloff_shape); + const int thread_id = BLI_task_parallel_thread_id(tls); + + plane_from_point_normal_v3(test.plane_tool, area_co, area_no); + + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { + if (!sculpt_brush_test_sq_fn(&test, vd.co)) { + continue; + } + float intr[3]; + float val[3]; + + closest_to_plane_normalized_v3(intr, test.plane_tool, vd.co); + + sub_v3_v3v3(val, intr, vd.co); + + if (SCULPT_plane_trim(ss->cache, brush, val)) { + const float fade = bstrength * SCULPT_brush_strength_factor(ss, + brush, + vd.co, + sqrtf(test.dist), + vd.no, + vd.fno, + vd.mask ? *vd.mask : 0.0f, + vd.index, + thread_id); + + mul_v3_v3fl(proxy[vd.i], val, fade); + + if (vd.mvert) { + vd.mvert->flag |= ME_VERT_PBVH_UPDATE; + } + } + } + BKE_pbvh_vertex_iter_end; +} + +void SCULPT_do_flatten_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) +{ + SculptSession *ss = ob->sculpt; + Brush *brush = BKE_paint_brush(&sd->paint); + + const float radius = ss->cache->radius; + + float area_no[3]; + float area_co[3]; + + float offset = SCULPT_brush_plane_offset_get(sd, ss); + float displace; + float temp[3]; + + SCULPT_calc_brush_plane(sd, ob, nodes, totnode, area_no, area_co); + + SCULPT_tilt_apply_to_normal(area_no, ss->cache, brush->tilt_strength_factor); + + displace = radius * offset; + + mul_v3_v3v3(temp, area_no, ss->cache->scale); + mul_v3_fl(temp, displace); + add_v3_v3(area_co, temp); + + SculptThreadedTaskData data = { + .sd = sd, + .ob = ob, + .brush = brush, + .nodes = nodes, + .area_no = area_no, + .area_co = area_co, + }; + + TaskParallelSettings settings; + BKE_pbvh_parallel_range_settings(&settings, true, totnode); + BLI_task_parallel_range(0, totnode, &data, do_flatten_brush_task_cb_ex, &settings); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Sculpt Clay Brush + * \{ */ + +typedef struct ClaySampleData { + float plane_dist[2]; +} ClaySampleData; + +static void calc_clay_surface_task_cb(void *__restrict userdata, + const int n, + const TaskParallelTLS *__restrict tls) +{ + SculptThreadedTaskData *data = userdata; + SculptSession *ss = data->ob->sculpt; + const Brush *brush = data->brush; + ClaySampleData *csd = tls->userdata_chunk; + const float *area_no = data->area_no; + const float *area_co = data->area_co; + float plane[4]; + + PBVHVertexIter vd; + + SculptBrushTest test; + SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( + ss, &test, brush->falloff_shape); + + /* Apply the brush normal radius to the test before sampling. */ + float test_radius = sqrtf(test.radius_squared); + test_radius *= brush->normal_radius_factor; + test.radius_squared = test_radius * test_radius; + plane_from_point_normal_v3(plane, area_co, area_no); + + if (is_zero_v4(plane)) { + return; + } + + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { + if (!sculpt_brush_test_sq_fn(&test, vd.co)) { + continue; + } + + float plane_dist = dist_signed_to_plane_v3(vd.co, plane); + float plane_dist_abs = fabsf(plane_dist); + if (plane_dist > 0.0f) { + csd->plane_dist[0] = MIN2(csd->plane_dist[0], plane_dist_abs); + } + else { + csd->plane_dist[1] = MIN2(csd->plane_dist[1], plane_dist_abs); + } + BKE_pbvh_vertex_iter_end; + } +} + +static void calc_clay_surface_reduce(const void *__restrict UNUSED(userdata), + void *__restrict chunk_join, + void *__restrict chunk) +{ + ClaySampleData *join = chunk_join; + ClaySampleData *csd = chunk; + join->plane_dist[0] = MIN2(csd->plane_dist[0], join->plane_dist[0]); + join->plane_dist[1] = MIN2(csd->plane_dist[1], join->plane_dist[1]); +} + +static void do_clay_brush_task_cb_ex(void *__restrict userdata, + const int n, + const TaskParallelTLS *__restrict tls) +{ + SculptThreadedTaskData *data = userdata; + SculptSession *ss = data->ob->sculpt; + const Brush *brush = data->brush; + const float *area_no = data->area_no; + const float *area_co = data->area_co; + + PBVHVertexIter vd; + float(*proxy)[3]; + const float bstrength = fabsf(ss->cache->bstrength); + + proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co; + + SculptBrushTest test; + SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( + ss, &test, data->brush->falloff_shape); + const int thread_id = BLI_task_parallel_thread_id(tls); + + plane_from_point_normal_v3(test.plane_tool, area_co, area_no); + + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { + if (!sculpt_brush_test_sq_fn(&test, vd.co)) { + continue; + } + + float intr[3]; + float val[3]; + closest_to_plane_normalized_v3(intr, test.plane_tool, vd.co); + + sub_v3_v3v3(val, intr, vd.co); + + const float fade = bstrength * SCULPT_brush_strength_factor(ss, + brush, + vd.co, + sqrtf(test.dist), + vd.no, + vd.fno, + vd.mask ? *vd.mask : 0.0f, + vd.index, + thread_id); + + mul_v3_v3fl(proxy[vd.i], val, fade); + + if (vd.mvert) { + vd.mvert->flag |= ME_VERT_PBVH_UPDATE; + } + } + BKE_pbvh_vertex_iter_end; +} + +void SCULPT_do_clay_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) +{ + SculptSession *ss = ob->sculpt; + Brush *brush = BKE_paint_brush(&sd->paint); + + const float radius = fabsf(ss->cache->radius); + const float initial_radius = fabsf(ss->cache->initial_radius); + bool flip = ss->cache->bstrength < 0.0f; + + float offset = SCULPT_brush_plane_offset_get(sd, ss); + float displace; + + float area_no[3]; + float area_co[3]; + float temp[3]; + + SCULPT_calc_brush_plane(sd, ob, nodes, totnode, area_no, area_co); + + SculptThreadedTaskData sample_data = { + .sd = NULL, + .ob = ob, + .brush = brush, + .nodes = nodes, + .totnode = totnode, + .area_no = area_no, + .area_co = ss->cache->location, + }; + + ClaySampleData csd = {{0}}; + + TaskParallelSettings sample_settings; + BKE_pbvh_parallel_range_settings(&sample_settings, true, totnode); + sample_settings.func_reduce = calc_clay_surface_reduce; + sample_settings.userdata_chunk = &csd; + sample_settings.userdata_chunk_size = sizeof(ClaySampleData); + + BLI_task_parallel_range(0, totnode, &sample_data, calc_clay_surface_task_cb, &sample_settings); + + float d_offset = (csd.plane_dist[0] + csd.plane_dist[1]); + d_offset = min_ff(radius, d_offset); + d_offset = d_offset / radius; + d_offset = 1.0f - d_offset; + displace = fabsf(initial_radius * (0.25f + offset + (d_offset * 0.15f))); + if (flip) { + displace = -displace; + } + + mul_v3_v3v3(temp, area_no, ss->cache->scale); + mul_v3_fl(temp, displace); + copy_v3_v3(area_co, ss->cache->location); + add_v3_v3(area_co, temp); + + SculptThreadedTaskData data = { + .sd = sd, + .ob = ob, + .brush = brush, + .nodes = nodes, + .area_no = area_no, + .area_co = area_co, + }; + + TaskParallelSettings settings; + BKE_pbvh_parallel_range_settings(&settings, true, totnode); + BLI_task_parallel_range(0, totnode, &data, do_clay_brush_task_cb_ex, &settings); +} + +static void do_clay_strips_brush_task_cb_ex(void *__restrict userdata, + const int n, + const TaskParallelTLS *__restrict tls) +{ + SculptThreadedTaskData *data = userdata; + SculptSession *ss = data->ob->sculpt; + const Brush *brush = data->brush; + float(*mat)[4] = data->mat; + const float *area_no_sp = data->area_no_sp; + const float *area_co = data->area_co; + + PBVHVertexIter vd; + SculptBrushTest test; + float(*proxy)[3]; + const bool flip = (ss->cache->bstrength < 0.0f); + const float bstrength = flip ? -ss->cache->bstrength : ss->cache->bstrength; + + proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co; + + SCULPT_brush_test_init(ss, &test); + plane_from_point_normal_v3(test.plane_tool, area_co, area_no_sp); + const int thread_id = BLI_task_parallel_thread_id(tls); + + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { + if (!SCULPT_brush_test_cube(&test, vd.co, mat, brush->tip_roundness)) { + continue; + } + + if (!plane_point_side_flip(vd.co, test.plane_tool, flip)) { + continue; + } + + float intr[3]; + float val[3]; + closest_to_plane_normalized_v3(intr, test.plane_tool, vd.co); + sub_v3_v3v3(val, intr, vd.co); + + if (!SCULPT_plane_trim(ss->cache, brush, val)) { + continue; + } + /* The normal from the vertices is ignored, it causes glitch with planes, see: T44390. */ + const float fade = bstrength * SCULPT_brush_strength_factor(ss, + brush, + vd.co, + ss->cache->radius * test.dist, + vd.no, + vd.fno, + vd.mask ? *vd.mask : 0.0f, + vd.index, + thread_id); + + mul_v3_v3fl(proxy[vd.i], val, fade); + + if (vd.mvert) { + vd.mvert->flag |= ME_VERT_PBVH_UPDATE; + } + } + BKE_pbvh_vertex_iter_end; +} + +void SCULPT_do_clay_strips_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) +{ + SculptSession *ss = ob->sculpt; + Brush *brush = BKE_paint_brush(&sd->paint); + + const bool flip = (ss->cache->bstrength < 0.0f); + const float radius = flip ? -ss->cache->radius : ss->cache->radius; + const float offset = SCULPT_brush_plane_offset_get(sd, ss); + const float displace = radius * (0.18f + offset); + + /* The sculpt-plane normal (whatever its set to). */ + float area_no_sp[3]; + + /* Geometry normal */ + float area_no[3]; + float area_co[3]; + + float temp[3]; + float mat[4][4]; + float scale[4][4]; + float tmat[4][4]; + + SCULPT_calc_brush_plane(sd, ob, nodes, totnode, area_no_sp, area_co); + SCULPT_tilt_apply_to_normal(area_no_sp, ss->cache, brush->tilt_strength_factor); + + if (brush->sculpt_plane != SCULPT_DISP_DIR_AREA || (brush->flag & BRUSH_ORIGINAL_NORMAL)) { + SCULPT_calc_area_normal(sd, ob, nodes, totnode, area_no); + } + else { + copy_v3_v3(area_no, area_no_sp); + } + + /* Delay the first daub because grab delta is not setup. */ + if (SCULPT_stroke_is_first_brush_step_of_symmetry_pass(ss->cache)) { + return; + } + + if (is_zero_v3(ss->cache->grab_delta_symmetry)) { + return; + } + + mul_v3_v3v3(temp, area_no_sp, ss->cache->scale); + mul_v3_fl(temp, displace); + add_v3_v3(area_co, temp); + + /* Clay Strips uses a cube test with falloff in the XY axis (not in Z) and a plane to deform the + * vertices. When in Add mode, vertices that are below the plane and inside the cube are move + * towards the plane. In this situation, there may be cases where a vertex is outside the cube + * but below the plane, so won't be deformed, causing artifacts. In order to prevent these + * artifacts, this displaces the test cube space in relation to the plane in order to + * deform more vertices that may be below it. */ + /* The 0.7 and 1.25 factors are arbitrary and don't have any relation between them, they were set + * by doing multiple tests using the default "Clay Strips" brush preset. */ + float area_co_displaced[3]; + madd_v3_v3v3fl(area_co_displaced, area_co, area_no, -radius * 0.7f); + + /* Initialize brush local-space matrix. */ + cross_v3_v3v3(mat[0], area_no, ss->cache->grab_delta_symmetry); + mat[0][3] = 0.0f; + cross_v3_v3v3(mat[1], area_no, mat[0]); + mat[1][3] = 0.0f; + copy_v3_v3(mat[2], area_no); + mat[2][3] = 0.0f; + copy_v3_v3(mat[3], area_co_displaced); + mat[3][3] = 1.0f; + normalize_m4(mat); + + /* Scale brush local space matrix. */ + scale_m4_fl(scale, ss->cache->radius); + mul_m4_m4m4(tmat, mat, scale); + + /* Deform the local space in Z to scale the test cube. As the test cube does not have falloff in + * Z this does not produce artifacts in the falloff cube and allows to deform extra vertices + * during big deformation while keeping the surface as uniform as possible. */ + mul_v3_fl(tmat[2], 1.25f); + + invert_m4_m4(mat, tmat); + + SculptThreadedTaskData data = { + .sd = sd, + .ob = ob, + .brush = brush, + .nodes = nodes, + .area_no_sp = area_no_sp, + .area_co = area_co, + .mat = mat, + }; + + TaskParallelSettings settings; + BKE_pbvh_parallel_range_settings(&settings, true, totnode); + BLI_task_parallel_range(0, totnode, &data, do_clay_strips_brush_task_cb_ex, &settings); +} + +static void do_snake_hook_brush_task_cb_ex(void *__restrict userdata, + const int n, + const TaskParallelTLS *__restrict tls) +{ + SculptThreadedTaskData *data = userdata; + SculptSession *ss = data->ob->sculpt; + const Brush *brush = data->brush; + SculptProjectVector *spvc = data->spvc; + const float *grab_delta = data->grab_delta; + + PBVHVertexIter vd; + float(*proxy)[3]; + const float bstrength = ss->cache->bstrength; + const bool do_rake_rotation = ss->cache->is_rake_rotation_valid; + const bool do_pinch = (brush->crease_pinch_factor != 0.5f); + const float pinch = do_pinch ? (2.0f * (0.5f - brush->crease_pinch_factor) * + (len_v3(grab_delta) / ss->cache->radius)) : + 0.0f; + + const bool do_elastic = brush->snake_hook_deform_type == BRUSH_SNAKE_HOOK_DEFORM_ELASTIC; + + proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co; + + SculptBrushTest test; + SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( + ss, &test, data->brush->falloff_shape); + const int thread_id = BLI_task_parallel_thread_id(tls); + + KelvinletParams params; + BKE_kelvinlet_init_params(¶ms, ss->cache->radius, bstrength, 1.0f, 0.4f); + + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { + if (!do_elastic && !sculpt_brush_test_sq_fn(&test, vd.co)) { + continue; + } + + float fade; + if (do_elastic) { + fade = 1.0f; + } + else { + fade = bstrength * SCULPT_brush_strength_factor(ss, + brush, + vd.co, + sqrtf(test.dist), + vd.no, + vd.fno, + vd.mask ? *vd.mask : 0.0f, + vd.index, + thread_id); + } + + mul_v3_v3fl(proxy[vd.i], grab_delta, fade); + + /* Negative pinch will inflate, helps maintain volume. */ + if (do_pinch) { + float delta_pinch_init[3], delta_pinch[3]; + + sub_v3_v3v3(delta_pinch, vd.co, test.location); + if (brush->falloff_shape == PAINT_FALLOFF_SHAPE_TUBE) { + project_plane_v3_v3v3(delta_pinch, delta_pinch, ss->cache->true_view_normal); + } + + /* Important to calculate based on the grabbed location + * (intentionally ignore fade here). */ + add_v3_v3(delta_pinch, grab_delta); + + sculpt_project_v3(spvc, delta_pinch, delta_pinch); + + copy_v3_v3(delta_pinch_init, delta_pinch); + + float pinch_fade = pinch * fade; + /* When reducing, scale reduction back by how close to the center we are, + * so we don't pinch into nothingness. */ + if (pinch > 0.0f) { + /* Square to have even less impact for close vertices. */ + pinch_fade *= pow2f(min_ff(1.0f, len_v3(delta_pinch) / ss->cache->radius)); + } + mul_v3_fl(delta_pinch, 1.0f + pinch_fade); + sub_v3_v3v3(delta_pinch, delta_pinch_init, delta_pinch); + add_v3_v3(proxy[vd.i], delta_pinch); + } + + if (do_rake_rotation) { + float delta_rotate[3]; + sculpt_rake_rotate(ss, test.location, vd.co, fade, delta_rotate); + add_v3_v3(proxy[vd.i], delta_rotate); + } + + if (do_elastic) { + float disp[3]; + BKE_kelvinlet_grab_triscale(disp, ¶ms, vd.co, ss->cache->location, proxy[vd.i]); + mul_v3_fl(disp, bstrength * 20.0f); + if (vd.mask) { + mul_v3_fl(disp, 1.0f - *vd.mask); + } + mul_v3_fl(disp, SCULPT_automasking_factor_get(ss->cache->automasking, ss, vd.index)); + copy_v3_v3(proxy[vd.i], disp); + } + + if (vd.mvert) { + vd.mvert->flag |= ME_VERT_PBVH_UPDATE; + } + } + BKE_pbvh_vertex_iter_end; +} + +void SCULPT_do_snake_hook_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) +{ + SculptSession *ss = ob->sculpt; + Brush *brush = BKE_paint_brush(&sd->paint); + const float bstrength = ss->cache->bstrength; + float grab_delta[3]; + + SculptProjectVector spvc; + + copy_v3_v3(grab_delta, ss->cache->grab_delta_symmetry); + + if (bstrength < 0.0f) { + negate_v3(grab_delta); + } + + if (ss->cache->normal_weight > 0.0f) { + sculpt_project_v3_normal_align(ss, ss->cache->normal_weight, grab_delta); + } + + /* Optionally pinch while painting. */ + if (brush->crease_pinch_factor != 0.5f) { + sculpt_project_v3_cache_init(&spvc, grab_delta); + } + + SculptThreadedTaskData data = { + .sd = sd, + .ob = ob, + .brush = brush, + .nodes = nodes, + .spvc = &spvc, + .grab_delta = grab_delta, + }; + + TaskParallelSettings settings; + BKE_pbvh_parallel_range_settings(&settings, true, totnode); + BLI_task_parallel_range(0, totnode, &data, do_snake_hook_brush_task_cb_ex, &settings); +} + +static void do_thumb_brush_task_cb_ex(void *__restrict userdata, + const int n, + const TaskParallelTLS *__restrict tls) +{ + SculptThreadedTaskData *data = userdata; + SculptSession *ss = data->ob->sculpt; + const Brush *brush = data->brush; + const float *cono = data->cono; + + PBVHVertexIter vd; + SculptOrigVertData orig_data; + float(*proxy)[3]; + const float bstrength = ss->cache->bstrength; + + SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n]); + + proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co; + + SculptBrushTest test; + SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( + ss, &test, data->brush->falloff_shape); + const int thread_id = BLI_task_parallel_thread_id(tls); + + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { + SCULPT_orig_vert_data_update(&orig_data, &vd); + + if (!sculpt_brush_test_sq_fn(&test, orig_data.co)) { + continue; + } + const float fade = bstrength * SCULPT_brush_strength_factor(ss, + brush, + orig_data.co, + sqrtf(test.dist), + orig_data.no, + NULL, + vd.mask ? *vd.mask : 0.0f, + vd.index, + thread_id); + + mul_v3_v3fl(proxy[vd.i], cono, fade); + + if (vd.mvert) { + vd.mvert->flag |= ME_VERT_PBVH_UPDATE; + } + } + BKE_pbvh_vertex_iter_end; +} + +void SCULPT_do_thumb_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) +{ + SculptSession *ss = ob->sculpt; + Brush *brush = BKE_paint_brush(&sd->paint); + float grab_delta[3]; + float tmp[3], cono[3]; + + copy_v3_v3(grab_delta, ss->cache->grab_delta_symmetry); + + cross_v3_v3v3(tmp, ss->cache->sculpt_normal_symm, grab_delta); + cross_v3_v3v3(cono, tmp, ss->cache->sculpt_normal_symm); + + SculptThreadedTaskData data = { + .sd = sd, + .ob = ob, + .brush = brush, + .nodes = nodes, + .cono = cono, + }; + + TaskParallelSettings settings; + BKE_pbvh_parallel_range_settings(&settings, true, totnode); + BLI_task_parallel_range(0, totnode, &data, do_thumb_brush_task_cb_ex, &settings); +} + +static void do_rotate_brush_task_cb_ex(void *__restrict userdata, + const int n, + const TaskParallelTLS *__restrict tls) +{ + SculptThreadedTaskData *data = userdata; + SculptSession *ss = data->ob->sculpt; + const Brush *brush = data->brush; + const float angle = data->angle; + + PBVHVertexIter vd; + SculptOrigVertData orig_data; + float(*proxy)[3]; + const float bstrength = ss->cache->bstrength; + + SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n]); + + proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co; + + SculptBrushTest test; + SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( + ss, &test, data->brush->falloff_shape); + const int thread_id = BLI_task_parallel_thread_id(tls); + + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { + SCULPT_orig_vert_data_update(&orig_data, &vd); + + if (!sculpt_brush_test_sq_fn(&test, orig_data.co)) { + continue; + } + float vec[3], rot[3][3]; + const float fade = bstrength * SCULPT_brush_strength_factor(ss, + brush, + orig_data.co, + sqrtf(test.dist), + orig_data.no, + NULL, + vd.mask ? *vd.mask : 0.0f, + vd.index, + thread_id); + + sub_v3_v3v3(vec, orig_data.co, ss->cache->location); + axis_angle_normalized_to_mat3(rot, ss->cache->sculpt_normal_symm, angle * fade); + mul_v3_m3v3(proxy[vd.i], rot, vec); + add_v3_v3(proxy[vd.i], ss->cache->location); + sub_v3_v3(proxy[vd.i], orig_data.co); + + if (vd.mvert) { + vd.mvert->flag |= ME_VERT_PBVH_UPDATE; + } + } + BKE_pbvh_vertex_iter_end; +} + +void SCULPT_do_rotate_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) +{ + SculptSession *ss = ob->sculpt; + Brush *brush = BKE_paint_brush(&sd->paint); + + static const int flip[8] = {1, -1, -1, 1, -1, 1, 1, -1}; + const float angle = ss->cache->vertex_rotation * flip[ss->cache->mirror_symmetry_pass]; + + SculptThreadedTaskData data = { + .sd = sd, + .ob = ob, + .brush = brush, + .nodes = nodes, + .angle = angle, + }; + + TaskParallelSettings settings; + BKE_pbvh_parallel_range_settings(&settings, true, totnode); + BLI_task_parallel_range(0, totnode, &data, do_rotate_brush_task_cb_ex, &settings); +} + +static void do_layer_brush_task_cb_ex(void *__restrict userdata, + const int n, + const TaskParallelTLS *__restrict tls) +{ + SculptThreadedTaskData *data = userdata; + SculptSession *ss = data->ob->sculpt; + Sculpt *sd = data->sd; + const Brush *brush = data->brush; + + const bool use_persistent_base = ss->persistent_base && brush->flag & BRUSH_PERSISTENT; + + PBVHVertexIter vd; + SculptOrigVertData orig_data; + const float bstrength = ss->cache->bstrength; + SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n]); + + SculptBrushTest test; + SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( + ss, &test, data->brush->falloff_shape); + const int thread_id = BLI_task_parallel_thread_id(tls); + + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { + SCULPT_orig_vert_data_update(&orig_data, &vd); + + if (!sculpt_brush_test_sq_fn(&test, orig_data.co)) { + continue; + } + const float fade = SCULPT_brush_strength_factor(ss, + brush, + vd.co, + sqrtf(test.dist), + vd.no, + vd.fno, + vd.mask ? *vd.mask : 0.0f, + vd.index, + thread_id); + + const int vi = vd.index; + float *disp_factor; + if (use_persistent_base) { + disp_factor = &ss->persistent_base[vi].disp; + } + else { + disp_factor = &ss->cache->layer_displacement_factor[vi]; + } + + /* When using persistent base, the layer brush (holding Control) invert mode resets the + * height of the layer to 0. This makes possible to clean edges of previously added layers + * on top of the base. */ + /* The main direction of the layers is inverted using the regular brush strength with the + * brush direction property. */ + if (use_persistent_base && ss->cache->invert) { + (*disp_factor) += fabsf(fade * bstrength * (*disp_factor)) * + ((*disp_factor) > 0.0f ? -1.0f : 1.0f); + } + else { + (*disp_factor) += fade * bstrength * (1.05f - fabsf(*disp_factor)); + } + if (vd.mask) { + const float clamp_mask = 1.0f - *vd.mask; + *disp_factor = clamp_f(*disp_factor, -clamp_mask, clamp_mask); + } + else { + *disp_factor = clamp_f(*disp_factor, -1.0f, 1.0f); + } + + float final_co[3]; + float normal[3]; + + if (use_persistent_base) { + SCULPT_vertex_persistent_normal_get(ss, vi, normal); + mul_v3_fl(normal, brush->height); + madd_v3_v3v3fl(final_co, SCULPT_vertex_persistent_co_get(ss, vi), normal, *disp_factor); + } + else { + normal_short_to_float_v3(normal, orig_data.no); + mul_v3_fl(normal, brush->height); + madd_v3_v3v3fl(final_co, orig_data.co, normal, *disp_factor); + } + + float vdisp[3]; + sub_v3_v3v3(vdisp, final_co, vd.co); + mul_v3_fl(vdisp, fabsf(fade)); + add_v3_v3v3(final_co, vd.co, vdisp); + + SCULPT_clip(sd, ss, vd.co, final_co); + + if (vd.mvert) { + vd.mvert->flag |= ME_VERT_PBVH_UPDATE; + } + } + BKE_pbvh_vertex_iter_end; +} + +void SCULPT_do_layer_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) +{ + SculptSession *ss = ob->sculpt; + Brush *brush = BKE_paint_brush(&sd->paint); + + if (ss->cache->layer_displacement_factor == NULL) { + ss->cache->layer_displacement_factor = MEM_callocN(sizeof(float) * SCULPT_vertex_count_get(ss), + "layer displacement factor"); + } + + SculptThreadedTaskData data = { + .sd = sd, + .ob = ob, + .brush = brush, + .nodes = nodes, + }; + + TaskParallelSettings settings; + BKE_pbvh_parallel_range_settings(&settings, true, totnode); + BLI_task_parallel_range(0, totnode, &data, do_layer_brush_task_cb_ex, &settings); +} + +static void do_inflate_brush_task_cb_ex(void *__restrict userdata, + const int n, + const TaskParallelTLS *__restrict tls) +{ + SculptThreadedTaskData *data = userdata; + SculptSession *ss = data->ob->sculpt; + const Brush *brush = data->brush; + + PBVHVertexIter vd; + float(*proxy)[3]; + const float bstrength = ss->cache->bstrength; + + proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co; + + SculptBrushTest test; + SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( + ss, &test, data->brush->falloff_shape); + const int thread_id = BLI_task_parallel_thread_id(tls); + + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { + if (!sculpt_brush_test_sq_fn(&test, vd.co)) { + continue; + } + const float fade = bstrength * SCULPT_brush_strength_factor(ss, + brush, + vd.co, + sqrtf(test.dist), + vd.no, + vd.fno, + vd.mask ? *vd.mask : 0.0f, + vd.index, + thread_id); + float val[3]; + + if (vd.fno) { + copy_v3_v3(val, vd.fno); + } + else { + normal_short_to_float_v3(val, vd.no); + } + + mul_v3_fl(val, fade * ss->cache->radius); + mul_v3_v3v3(proxy[vd.i], val, ss->cache->scale); + + if (vd.mvert) { + vd.mvert->flag |= ME_VERT_PBVH_UPDATE; + } + } + BKE_pbvh_vertex_iter_end; +} + +void SCULPT_do_inflate_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) +{ + Brush *brush = BKE_paint_brush(&sd->paint); + + SculptThreadedTaskData data = { + .sd = sd, + .ob = ob, + .brush = brush, + .nodes = nodes, + }; + + TaskParallelSettings settings; + BKE_pbvh_parallel_range_settings(&settings, true, totnode); + BLI_task_parallel_range(0, totnode, &data, do_inflate_brush_task_cb_ex, &settings); +} + +static void do_nudge_brush_task_cb_ex(void *__restrict userdata, + const int n, + const TaskParallelTLS *__restrict tls) +{ + SculptThreadedTaskData *data = userdata; + SculptSession *ss = data->ob->sculpt; + const Brush *brush = data->brush; + const float *cono = data->cono; + + PBVHVertexIter vd; + float(*proxy)[3]; + const float bstrength = ss->cache->bstrength; + + proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co; + + SculptBrushTest test; + SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( + ss, &test, data->brush->falloff_shape); + const int thread_id = BLI_task_parallel_thread_id(tls); + + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { + if (!sculpt_brush_test_sq_fn(&test, vd.co)) { + continue; + } + const float fade = bstrength * SCULPT_brush_strength_factor(ss, + brush, + vd.co, + sqrtf(test.dist), + vd.no, + vd.fno, + vd.mask ? *vd.mask : 0.0f, + vd.index, + thread_id); + + mul_v3_v3fl(proxy[vd.i], cono, fade); + + if (vd.mvert) { + vd.mvert->flag |= ME_VERT_PBVH_UPDATE; + } + } + BKE_pbvh_vertex_iter_end; +} + +void SCULPT_do_nudge_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) +{ + SculptSession *ss = ob->sculpt; + Brush *brush = BKE_paint_brush(&sd->paint); + float grab_delta[3]; + float tmp[3], cono[3]; + + copy_v3_v3(grab_delta, ss->cache->grab_delta_symmetry); + + cross_v3_v3v3(tmp, ss->cache->sculpt_normal_symm, grab_delta); + cross_v3_v3v3(cono, tmp, ss->cache->sculpt_normal_symm); + + SculptThreadedTaskData data = { + .sd = sd, + .ob = ob, + .brush = brush, + .nodes = nodes, + .cono = cono, + }; + + TaskParallelSettings settings; + BKE_pbvh_parallel_range_settings(&settings, true, totnode); + BLI_task_parallel_range(0, totnode, &data, do_nudge_brush_task_cb_ex, &settings); +} + +/* -------------------------------------------------------------------- */ +/** \name Sculpt Crease & Blob Brush + * \{ */ + +/** + * Used for 'SCULPT_TOOL_CREASE' and 'SCULPT_TOOL_BLOB' + */ +static void do_crease_brush_task_cb_ex(void *__restrict userdata, + const int n, + const TaskParallelTLS *__restrict tls) +{ + SculptThreadedTaskData *data = userdata; + SculptSession *ss = data->ob->sculpt; + const Brush *brush = data->brush; + SculptProjectVector *spvc = data->spvc; + const float flippedbstrength = data->flippedbstrength; + const float *offset = data->offset; + + PBVHVertexIter vd; + float(*proxy)[3]; + + proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co; + + SculptBrushTest test; + SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( + ss, &test, data->brush->falloff_shape); + const int thread_id = BLI_task_parallel_thread_id(tls); + + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { + if (!sculpt_brush_test_sq_fn(&test, vd.co)) { + continue; + } + /* Offset vertex. */ + const float fade = SCULPT_brush_strength_factor(ss, + brush, + vd.co, + sqrtf(test.dist), + vd.no, + vd.fno, + vd.mask ? *vd.mask : 0.0f, + vd.index, + thread_id); + float val1[3]; + float val2[3]; + + /* First we pinch. */ + sub_v3_v3v3(val1, test.location, vd.co); + if (brush->falloff_shape == PAINT_FALLOFF_SHAPE_TUBE) { + project_plane_v3_v3v3(val1, val1, ss->cache->view_normal); + } + + mul_v3_fl(val1, fade * flippedbstrength); + + sculpt_project_v3(spvc, val1, val1); + + /* Then we draw. */ + mul_v3_v3fl(val2, offset, fade); + + add_v3_v3v3(proxy[vd.i], val1, val2); + + if (vd.mvert) { + vd.mvert->flag |= ME_VERT_PBVH_UPDATE; + } + } + BKE_pbvh_vertex_iter_end; +} + +void SCULPT_do_crease_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) +{ + SculptSession *ss = ob->sculpt; + const Scene *scene = ss->cache->vc->scene; + Brush *brush = BKE_paint_brush(&sd->paint); + float offset[3]; + float bstrength = ss->cache->bstrength; + float flippedbstrength, crease_correction; + float brush_alpha; + + SculptProjectVector spvc; + + /* Offset with as much as possible factored in already. */ + mul_v3_v3fl(offset, ss->cache->sculpt_normal_symm, ss->cache->radius); + mul_v3_v3(offset, ss->cache->scale); + mul_v3_fl(offset, bstrength); + + /* We divide out the squared alpha and multiply by the squared crease + * to give us the pinch strength. */ + crease_correction = brush->crease_pinch_factor * brush->crease_pinch_factor; + brush_alpha = BKE_brush_alpha_get(scene, brush); + if (brush_alpha > 0.0f) { + crease_correction /= brush_alpha * brush_alpha; + } + + /* We always want crease to pinch or blob to relax even when draw is negative. */ + flippedbstrength = (bstrength < 0.0f) ? -crease_correction * bstrength : + crease_correction * bstrength; + + if (brush->sculpt_tool == SCULPT_TOOL_BLOB) { + flippedbstrength *= -1.0f; + } + + /* Use surface normal for 'spvc', so the vertices are pinched towards a line instead of a single + * point. Without this we get a 'flat' surface surrounding the pinch. */ + sculpt_project_v3_cache_init(&spvc, ss->cache->sculpt_normal_symm); + + /* Threaded loop over nodes. */ + SculptThreadedTaskData data = { + .sd = sd, + .ob = ob, + .brush = brush, + .nodes = nodes, + .spvc = &spvc, + .offset = offset, + .flippedbstrength = flippedbstrength, + }; + + TaskParallelSettings settings; + BKE_pbvh_parallel_range_settings(&settings, true, totnode); + BLI_task_parallel_range(0, totnode, &data, do_crease_brush_task_cb_ex, &settings); +} + +static void do_pinch_brush_task_cb_ex(void *__restrict userdata, + const int n, + const TaskParallelTLS *__restrict tls) +{ + SculptThreadedTaskData *data = userdata; + SculptSession *ss = data->ob->sculpt; + const Brush *brush = data->brush; + float(*stroke_xz)[3] = data->stroke_xz; + + PBVHVertexIter vd; + float(*proxy)[3]; + const float bstrength = ss->cache->bstrength; + + proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co; + + SculptBrushTest test; + SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( + ss, &test, data->brush->falloff_shape); + const int thread_id = BLI_task_parallel_thread_id(tls); + + float x_object_space[3]; + float z_object_space[3]; + copy_v3_v3(x_object_space, stroke_xz[0]); + copy_v3_v3(z_object_space, stroke_xz[1]); + + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { + if (!sculpt_brush_test_sq_fn(&test, vd.co)) { + continue; + } + const float fade = bstrength * SCULPT_brush_strength_factor(ss, + brush, + vd.co, + sqrtf(test.dist), + vd.no, + vd.fno, + vd.mask ? *vd.mask : 0.0f, + vd.index, + thread_id); + float disp_center[3]; + float x_disp[3]; + float z_disp[3]; + /* Calculate displacement from the vertex to the brush center. */ + sub_v3_v3v3(disp_center, test.location, vd.co); + + /* Project the displacement into the X vector (aligned to the stroke). */ + mul_v3_v3fl(x_disp, x_object_space, dot_v3v3(disp_center, x_object_space)); + + /* Project the displacement into the Z vector (aligned to the surface normal). */ + mul_v3_v3fl(z_disp, z_object_space, dot_v3v3(disp_center, z_object_space)); + + /* Add the two projected vectors to calculate the final displacement. + * The Y component is removed. */ + add_v3_v3v3(disp_center, x_disp, z_disp); + + if (brush->falloff_shape == PAINT_FALLOFF_SHAPE_TUBE) { + project_plane_v3_v3v3(disp_center, disp_center, ss->cache->view_normal); + } + mul_v3_v3fl(proxy[vd.i], disp_center, fade); + + if (vd.mvert) { + vd.mvert->flag |= ME_VERT_PBVH_UPDATE; + } + } + BKE_pbvh_vertex_iter_end; +} + +void SCULPT_do_pinch_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) +{ + SculptSession *ss = ob->sculpt; + Brush *brush = BKE_paint_brush(&sd->paint); + + float area_no[3]; + float area_co[3]; + + float mat[4][4]; + calc_sculpt_plane(sd, ob, nodes, totnode, area_no, area_co); + + /* delay the first daub because grab delta is not setup */ + if (SCULPT_stroke_is_first_brush_step_of_symmetry_pass(ss->cache)) { + return; + } + + if (is_zero_v3(ss->cache->grab_delta_symmetry)) { + return; + } + + /* Initialize `mat`. */ + cross_v3_v3v3(mat[0], area_no, ss->cache->grab_delta_symmetry); + mat[0][3] = 0.0f; + cross_v3_v3v3(mat[1], area_no, mat[0]); + mat[1][3] = 0.0f; + copy_v3_v3(mat[2], area_no); + mat[2][3] = 0.0f; + copy_v3_v3(mat[3], ss->cache->location); + mat[3][3] = 1.0f; + normalize_m4(mat); + + float stroke_xz[2][3]; + normalize_v3_v3(stroke_xz[0], mat[0]); + normalize_v3_v3(stroke_xz[1], mat[2]); + + SculptThreadedTaskData data = { + .sd = sd, + .ob = ob, + .brush = brush, + .nodes = nodes, + .stroke_xz = stroke_xz, + }; + + TaskParallelSettings settings; + BKE_pbvh_parallel_range_settings(&settings, true, totnode); + BLI_task_parallel_range(0, totnode, &data, do_pinch_brush_task_cb_ex, &settings); +} + +static void do_grab_brush_task_cb_ex(void *__restrict userdata, + const int n, + const TaskParallelTLS *__restrict tls) +{ + SculptThreadedTaskData *data = userdata; + SculptSession *ss = data->ob->sculpt; + const Brush *brush = data->brush; + const float *grab_delta = data->grab_delta; + + PBVHVertexIter vd; + SculptOrigVertData orig_data; + float(*proxy)[3]; + const float bstrength = ss->cache->bstrength; + + SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n]); + + proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co; + + SculptBrushTest test; + SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( + ss, &test, data->brush->falloff_shape); + const int thread_id = BLI_task_parallel_thread_id(tls); + + const bool grab_silhouette = brush->flag2 & BRUSH_GRAB_SILHOUETTE; + + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { + SCULPT_orig_vert_data_update(&orig_data, &vd); + + if (!sculpt_brush_test_sq_fn(&test, orig_data.co)) { + continue; + } + float fade = bstrength * SCULPT_brush_strength_factor(ss, + brush, + orig_data.co, + sqrtf(test.dist), + orig_data.no, + NULL, + vd.mask ? *vd.mask : 0.0f, + vd.index, + thread_id); + + if (grab_silhouette) { + float silhouette_test_dir[3]; + normalize_v3_v3(silhouette_test_dir, grab_delta); + if (dot_v3v3(ss->cache->initial_normal, ss->cache->grab_delta_symmetry) < 0.0f) { + mul_v3_fl(silhouette_test_dir, -1.0f); + } + float vno[3]; + normal_short_to_float_v3(vno, orig_data.no); + fade *= max_ff(dot_v3v3(vno, silhouette_test_dir), 0.0f); + } + + mul_v3_v3fl(proxy[vd.i], grab_delta, fade); + + if (vd.mvert) { + vd.mvert->flag |= ME_VERT_PBVH_UPDATE; + } + } + BKE_pbvh_vertex_iter_end; +} + +void SCULPT_do_grab_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) +{ + SculptSession *ss = ob->sculpt; + Brush *brush = BKE_paint_brush(&sd->paint); + float grab_delta[3]; + + copy_v3_v3(grab_delta, ss->cache->grab_delta_symmetry); + + if (ss->cache->normal_weight > 0.0f) { + sculpt_project_v3_normal_align(ss, ss->cache->normal_weight, grab_delta); + } + + SculptThreadedTaskData data = { + .sd = sd, + .ob = ob, + .brush = brush, + .nodes = nodes, + .grab_delta = grab_delta, + }; + + TaskParallelSettings settings; + BKE_pbvh_parallel_range_settings(&settings, true, totnode); + BLI_task_parallel_range(0, totnode, &data, do_grab_brush_task_cb_ex, &settings); +} + +static void do_elastic_deform_brush_task_cb_ex(void *__restrict userdata, + const int n, + const TaskParallelTLS *__restrict UNUSED(tls)) +{ + SculptThreadedTaskData *data = userdata; + SculptSession *ss = data->ob->sculpt; + const Brush *brush = data->brush; + const float *grab_delta = data->grab_delta; + const float *location = ss->cache->location; + + PBVHVertexIter vd; + SculptOrigVertData orig_data; + float(*proxy)[3]; + + const float bstrength = ss->cache->bstrength; + + SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n]); + + proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co; + + float dir; + if (ss->cache->mouse[0] > ss->cache->initial_mouse[0]) { + dir = 1.0f; + } + else { + dir = -1.0f; + } + + if (brush->elastic_deform_type == BRUSH_ELASTIC_DEFORM_TWIST) { + int symm = ss->cache->mirror_symmetry_pass; + if (ELEM(symm, 1, 2, 4, 7)) { + dir = -dir; + } + } + + KelvinletParams params; + float force = len_v3(grab_delta) * dir * bstrength; + BKE_kelvinlet_init_params( + ¶ms, ss->cache->radius, force, 1.0f, brush->elastic_deform_volume_preservation); + + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { + SCULPT_orig_vert_data_update(&orig_data, &vd); + float final_disp[3]; + switch (brush->elastic_deform_type) { + case BRUSH_ELASTIC_DEFORM_GRAB: + BKE_kelvinlet_grab(final_disp, ¶ms, orig_data.co, location, grab_delta); + mul_v3_fl(final_disp, bstrength * 20.0f); + break; + case BRUSH_ELASTIC_DEFORM_GRAB_BISCALE: { + BKE_kelvinlet_grab_biscale(final_disp, ¶ms, orig_data.co, location, grab_delta); + mul_v3_fl(final_disp, bstrength * 20.0f); + break; + } + case BRUSH_ELASTIC_DEFORM_GRAB_TRISCALE: { + BKE_kelvinlet_grab_triscale(final_disp, ¶ms, orig_data.co, location, grab_delta); + mul_v3_fl(final_disp, bstrength * 20.0f); + break; + } + case BRUSH_ELASTIC_DEFORM_SCALE: + BKE_kelvinlet_scale( + final_disp, ¶ms, orig_data.co, location, ss->cache->sculpt_normal_symm); + break; + case BRUSH_ELASTIC_DEFORM_TWIST: + BKE_kelvinlet_twist( + final_disp, ¶ms, orig_data.co, location, ss->cache->sculpt_normal_symm); + break; + } + + if (vd.mask) { + mul_v3_fl(final_disp, 1.0f - *vd.mask); + } + + mul_v3_fl(final_disp, SCULPT_automasking_factor_get(ss->cache->automasking, ss, vd.index)); + + copy_v3_v3(proxy[vd.i], final_disp); + + if (vd.mvert) { + vd.mvert->flag |= ME_VERT_PBVH_UPDATE; + } + } + BKE_pbvh_vertex_iter_end; +} + +void SCULPT_do_elastic_deform_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) +{ + SculptSession *ss = ob->sculpt; + Brush *brush = BKE_paint_brush(&sd->paint); + float grab_delta[3]; + + copy_v3_v3(grab_delta, ss->cache->grab_delta_symmetry); + + if (ss->cache->normal_weight > 0.0f) { + sculpt_project_v3_normal_align(ss, ss->cache->normal_weight, grab_delta); + } + + SculptThreadedTaskData data = { + .sd = sd, + .ob = ob, + .brush = brush, + .nodes = nodes, + .grab_delta = grab_delta, + }; + + TaskParallelSettings settings; + BKE_pbvh_parallel_range_settings(&settings, true, totnode); + BLI_task_parallel_range(0, totnode, &data, do_elastic_deform_brush_task_cb_ex, &settings); +} +/** \} */ + +static void do_draw_sharp_brush_task_cb_ex(void *__restrict userdata, + const int n, + const TaskParallelTLS *__restrict tls) +{ + SculptThreadedTaskData *data = userdata; + SculptSession *ss = data->ob->sculpt; + const Brush *brush = data->brush; + const float *offset = data->offset; + + PBVHVertexIter vd; + SculptOrigVertData orig_data; + float(*proxy)[3]; + + SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n]); + + proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co; + + SculptBrushTest test; + SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( + ss, &test, data->brush->falloff_shape); + const int thread_id = BLI_task_parallel_thread_id(tls); + + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { + SCULPT_orig_vert_data_update(&orig_data, &vd); + if (!sculpt_brush_test_sq_fn(&test, orig_data.co)) { + continue; + } + /* Offset vertex. */ + const float fade = SCULPT_brush_strength_factor(ss, + brush, + orig_data.co, + sqrtf(test.dist), + orig_data.no, + NULL, + vd.mask ? *vd.mask : 0.0f, + vd.index, + thread_id); + + mul_v3_v3fl(proxy[vd.i], offset, fade); + + if (vd.mvert) { + vd.mvert->flag |= ME_VERT_PBVH_UPDATE; + } + } + BKE_pbvh_vertex_iter_end; +} + +void SCULPT_do_draw_sharp_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) +{ + SculptSession *ss = ob->sculpt; + Brush *brush = BKE_paint_brush(&sd->paint); + float offset[3]; + const float bstrength = ss->cache->bstrength; + + /* Offset with as much as possible factored in already. */ + float effective_normal[3]; + SCULPT_tilt_effective_normal_get(ss, brush, effective_normal); + mul_v3_v3fl(offset, effective_normal, ss->cache->radius); + mul_v3_v3(offset, ss->cache->scale); + mul_v3_fl(offset, bstrength); + + /* XXX: this shouldn't be necessary, but sculpting crashes in blender2.8 otherwise + * initialize before threads so they can do curve mapping. */ + BKE_curvemapping_init(brush->curve); + + /* Threaded loop over nodes. */ + SculptThreadedTaskData data = { + .sd = sd, + .ob = ob, + .brush = brush, + .nodes = nodes, + .offset = offset, + }; + + TaskParallelSettings settings; + BKE_pbvh_parallel_range_settings(&settings, true, totnode); + BLI_task_parallel_range(0, totnode, &data, do_draw_sharp_brush_task_cb_ex, &settings); +} + +/* -------------------------------------------------------------------- */ +/** \name Sculpt Topology Brush + * \{ */ + +static void do_topology_slide_task_cb_ex(void *__restrict userdata, + const int n, + const TaskParallelTLS *__restrict tls) +{ + SculptThreadedTaskData *data = userdata; + SculptSession *ss = data->ob->sculpt; + const Brush *brush = data->brush; + + PBVHVertexIter vd; + SculptOrigVertData orig_data; + float(*proxy)[3]; + + SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n]); + + proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co; + + SculptBrushTest test; + SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( + ss, &test, data->brush->falloff_shape); + const int thread_id = BLI_task_parallel_thread_id(tls); + + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { + SCULPT_orig_vert_data_update(&orig_data, &vd); + if (!sculpt_brush_test_sq_fn(&test, orig_data.co)) { + continue; + } + const float fade = SCULPT_brush_strength_factor(ss, + brush, + orig_data.co, + sqrtf(test.dist), + orig_data.no, + NULL, + vd.mask ? *vd.mask : 0.0f, + vd.index, + thread_id); + float current_disp[3]; + float current_disp_norm[3]; + float final_disp[3] = {0.0f, 0.0f, 0.0f}; + + switch (brush->slide_deform_type) { + case BRUSH_SLIDE_DEFORM_DRAG: + sub_v3_v3v3(current_disp, ss->cache->location, ss->cache->last_location); + break; + case BRUSH_SLIDE_DEFORM_PINCH: + sub_v3_v3v3(current_disp, ss->cache->location, vd.co); + break; + case BRUSH_SLIDE_DEFORM_EXPAND: + sub_v3_v3v3(current_disp, vd.co, ss->cache->location); + break; + } + + normalize_v3_v3(current_disp_norm, current_disp); + mul_v3_v3fl(current_disp, current_disp_norm, ss->cache->bstrength); + + SculptVertexNeighborIter ni; + SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.index, ni) { + float vertex_disp[3]; + float vertex_disp_norm[3]; + sub_v3_v3v3(vertex_disp, SCULPT_vertex_co_get(ss, ni.index), vd.co); + normalize_v3_v3(vertex_disp_norm, vertex_disp); + if (dot_v3v3(current_disp_norm, vertex_disp_norm) > 0.0f) { + madd_v3_v3fl(final_disp, vertex_disp_norm, dot_v3v3(current_disp, vertex_disp)); + } + } + SCULPT_VERTEX_NEIGHBORS_ITER_END(ni); + + mul_v3_v3fl(proxy[vd.i], final_disp, fade); + + if (vd.mvert) { + vd.mvert->flag |= ME_VERT_PBVH_UPDATE; + } + } + BKE_pbvh_vertex_iter_end; +} + +void SCULPT_relax_vertex(SculptSession *ss, + PBVHVertexIter *vd, + float factor, + bool filter_boundary_face_sets, + float *r_final_pos) +{ + float smooth_pos[3]; + float final_disp[3]; + float boundary_normal[3]; + int avg_count = 0; + int neighbor_count = 0; + zero_v3(smooth_pos); + zero_v3(boundary_normal); + const bool is_boundary = SCULPT_vertex_is_boundary(ss, vd->index); + + SculptVertexNeighborIter ni; + SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd->index, ni) { + neighbor_count++; + if (!filter_boundary_face_sets || + (filter_boundary_face_sets && !SCULPT_vertex_has_unique_face_set(ss, ni.index))) { + + /* When the vertex to relax is boundary, use only connected boundary vertices for the average + * position. */ + if (is_boundary) { + if (!SCULPT_vertex_is_boundary(ss, ni.index)) { + continue; + } + add_v3_v3(smooth_pos, SCULPT_vertex_co_get(ss, ni.index)); + avg_count++; + + /* Calculate a normal for the constraint plane using the edges of the boundary. */ + float to_neighbor[3]; + sub_v3_v3v3(to_neighbor, SCULPT_vertex_co_get(ss, ni.index), vd->co); + normalize_v3(to_neighbor); + add_v3_v3(boundary_normal, to_neighbor); + } + else { + add_v3_v3(smooth_pos, SCULPT_vertex_co_get(ss, ni.index)); + avg_count++; + } + } + } + SCULPT_VERTEX_NEIGHBORS_ITER_END(ni); + + /* Don't modify corner vertices. */ + if (neighbor_count <= 2) { + copy_v3_v3(r_final_pos, vd->co); + return; + } + + if (avg_count > 0) { + mul_v3_fl(smooth_pos, 1.0f / avg_count); + } + else { + copy_v3_v3(r_final_pos, vd->co); + return; + } + + float plane[4]; + float smooth_closest_plane[3]; + float vno[3]; + + if (is_boundary && avg_count == 2) { + normalize_v3_v3(vno, boundary_normal); + } + else { + SCULPT_vertex_normal_get(ss, vd->index, vno); + } + + if (is_zero_v3(vno)) { + copy_v3_v3(r_final_pos, vd->co); + return; + } + + plane_from_point_normal_v3(plane, vd->co, vno); + closest_to_plane_v3(smooth_closest_plane, plane, smooth_pos); + sub_v3_v3v3(final_disp, smooth_closest_plane, vd->co); + + mul_v3_fl(final_disp, factor); + add_v3_v3v3(r_final_pos, vd->co, final_disp); +} + +static void do_topology_relax_task_cb_ex(void *__restrict userdata, + const int n, + const TaskParallelTLS *__restrict tls) +{ + SculptThreadedTaskData *data = userdata; + SculptSession *ss = data->ob->sculpt; + const Brush *brush = data->brush; + const float bstrength = ss->cache->bstrength; + + PBVHVertexIter vd; + SculptOrigVertData orig_data; + + SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n]); + + BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n]); + + SculptBrushTest test; + SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( + ss, &test, data->brush->falloff_shape); + const int thread_id = BLI_task_parallel_thread_id(tls); + + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { + SCULPT_orig_vert_data_update(&orig_data, &vd); + if (!sculpt_brush_test_sq_fn(&test, orig_data.co)) { + continue; + } + const float fade = SCULPT_brush_strength_factor(ss, + brush, + orig_data.co, + sqrtf(test.dist), + orig_data.no, + NULL, + vd.mask ? *vd.mask : 0.0f, + vd.index, + thread_id); + + SCULPT_relax_vertex(ss, &vd, fade * bstrength, false, vd.co); + if (vd.mvert) { + vd.mvert->flag |= ME_VERT_PBVH_UPDATE; + } + } + BKE_pbvh_vertex_iter_end; +} + +void SCULPT_do_slide_relax_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) +{ + SculptSession *ss = ob->sculpt; + Brush *brush = BKE_paint_brush(&sd->paint); + + if (SCULPT_stroke_is_first_brush_step_of_symmetry_pass(ss->cache)) { + return; + } + + BKE_curvemapping_init(brush->curve); + + SculptThreadedTaskData data = { + .sd = sd, + .ob = ob, + .brush = brush, + .nodes = nodes, + }; + + TaskParallelSettings settings; + BKE_pbvh_parallel_range_settings(&settings, true, totnode); + if (ss->cache->alt_smooth) { + SCULPT_boundary_info_ensure(ob); + for (int i = 0; i < 4; i++) { + BLI_task_parallel_range(0, totnode, &data, do_topology_relax_task_cb_ex, &settings); + } + } + else { + BLI_task_parallel_range(0, totnode, &data, do_topology_slide_task_cb_ex, &settings); + } +} +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Sculpt Multires Displacement Eraser Brush + * \{ */ + +static void do_displacement_eraser_brush_task_cb_ex(void *__restrict userdata, + const int n, + const TaskParallelTLS *__restrict tls) +{ + SculptThreadedTaskData *data = userdata; + SculptSession *ss = data->ob->sculpt; + const Brush *brush = data->brush; + const float bstrength = clamp_f(ss->cache->bstrength, 0.0f, 1.0f); + + float(*proxy)[3] = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co; + + SculptBrushTest test; + SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( + ss, &test, data->brush->falloff_shape); + const int thread_id = BLI_task_parallel_thread_id(tls); + + PBVHVertexIter vd; + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { + if (!sculpt_brush_test_sq_fn(&test, vd.co)) { + continue; + } + const float fade = bstrength * SCULPT_brush_strength_factor(ss, + brush, + vd.co, + sqrtf(test.dist), + vd.no, + vd.fno, + vd.mask ? *vd.mask : 0.0f, + vd.index, + thread_id); + + float limit_co[3]; + float disp[3]; + SCULPT_vertex_limit_surface_get(ss, vd.index, limit_co); + sub_v3_v3v3(disp, limit_co, vd.co); + mul_v3_v3fl(proxy[vd.i], disp, fade); + + if (vd.mvert) { + vd.mvert->flag |= ME_VERT_PBVH_UPDATE; + } + } + BKE_pbvh_vertex_iter_end; +} + +void SCULPT_do_displacement_eraser_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) +{ + Brush *brush = BKE_paint_brush(&sd->paint); + BKE_curvemapping_init(brush->curve); + + /* Threaded loop over nodes. */ + SculptThreadedTaskData data = { + .sd = sd, + .ob = ob, + .brush = brush, + .nodes = nodes, + }; + + TaskParallelSettings settings; + BKE_pbvh_parallel_range_settings(&settings, true, totnode); + BLI_task_parallel_range(0, totnode, &data, do_displacement_eraser_brush_task_cb_ex, &settings); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Sculpt Multires Displacement Smear Brush + * \{ */ + +static void do_displacement_smear_brush_task_cb_ex(void *__restrict userdata, + const int n, + const TaskParallelTLS *__restrict tls) +{ + SculptThreadedTaskData *data = userdata; + SculptSession *ss = data->ob->sculpt; + const Brush *brush = data->brush; + const float bstrength = clamp_f(ss->cache->bstrength, 0.0f, 1.0f); + + SculptBrushTest test; + SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( + ss, &test, data->brush->falloff_shape); + const int thread_id = BLI_task_parallel_thread_id(tls); + + PBVHVertexIter vd; + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { + if (!sculpt_brush_test_sq_fn(&test, vd.co)) { + continue; + } + const float fade = bstrength * SCULPT_brush_strength_factor(ss, + brush, + vd.co, + sqrtf(test.dist), + vd.no, + vd.fno, + vd.mask ? *vd.mask : 0.0f, + vd.index, + thread_id); + + float current_disp[3]; + float current_disp_norm[3]; + float interp_limit_surface_disp[3]; + + copy_v3_v3(interp_limit_surface_disp, ss->cache->prev_displacement[vd.index]); + + switch (brush->smear_deform_type) { + case BRUSH_SMEAR_DEFORM_DRAG: + sub_v3_v3v3(current_disp, ss->cache->location, ss->cache->last_location); + break; + case BRUSH_SMEAR_DEFORM_PINCH: + sub_v3_v3v3(current_disp, ss->cache->location, vd.co); + break; + case BRUSH_SMEAR_DEFORM_EXPAND: + sub_v3_v3v3(current_disp, vd.co, ss->cache->location); + break; + } + + normalize_v3_v3(current_disp_norm, current_disp); + mul_v3_v3fl(current_disp, current_disp_norm, ss->cache->bstrength); + + float weights_accum = 1.0f; + + SculptVertexNeighborIter ni; + SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.index, ni) { + float vertex_disp[3]; + float vertex_disp_norm[3]; + float neighbor_limit_co[3]; + SCULPT_vertex_limit_surface_get(ss, ni.index, neighbor_limit_co); + sub_v3_v3v3(vertex_disp, + ss->cache->limit_surface_co[ni.index], + ss->cache->limit_surface_co[vd.index]); + const float *neighbor_limit_surface_disp = ss->cache->prev_displacement[ni.index]; + normalize_v3_v3(vertex_disp_norm, vertex_disp); + + if (dot_v3v3(current_disp_norm, vertex_disp_norm) >= 0.0f) { + continue; + } + + const float disp_interp = clamp_f( + -dot_v3v3(current_disp_norm, vertex_disp_norm), 0.0f, 1.0f); + madd_v3_v3fl(interp_limit_surface_disp, neighbor_limit_surface_disp, disp_interp); + weights_accum += disp_interp; + } + SCULPT_VERTEX_NEIGHBORS_ITER_END(ni); + + mul_v3_fl(interp_limit_surface_disp, 1.0f / weights_accum); + + float new_co[3]; + add_v3_v3v3(new_co, ss->cache->limit_surface_co[vd.index], interp_limit_surface_disp); + interp_v3_v3v3(vd.co, vd.co, new_co, fade); + + if (vd.mvert) { + vd.mvert->flag |= ME_VERT_PBVH_UPDATE; + } + } + BKE_pbvh_vertex_iter_end; +} + +static void do_displacement_smear_store_prev_disp_task_cb_ex( + void *__restrict userdata, const int n, const TaskParallelTLS *__restrict UNUSED(tls)) +{ + SculptThreadedTaskData *data = userdata; + SculptSession *ss = data->ob->sculpt; + + PBVHVertexIter vd; + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { + sub_v3_v3v3(ss->cache->prev_displacement[vd.index], + SCULPT_vertex_co_get(ss, vd.index), + ss->cache->limit_surface_co[vd.index]); + } + BKE_pbvh_vertex_iter_end; +} + +void SCULPT_do_displacement_smear_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) +{ + Brush *brush = BKE_paint_brush(&sd->paint); + SculptSession *ss = ob->sculpt; + + BKE_curvemapping_init(brush->curve); + + const int totvert = SCULPT_vertex_count_get(ss); + if (!ss->cache->prev_displacement) { + ss->cache->prev_displacement = MEM_malloc_arrayN( + totvert, sizeof(float[3]), "prev displacement"); + ss->cache->limit_surface_co = MEM_malloc_arrayN(totvert, sizeof(float[3]), "limit surface co"); + for (int i = 0; i < totvert; i++) { + SCULPT_vertex_limit_surface_get(ss, i, ss->cache->limit_surface_co[i]); + sub_v3_v3v3(ss->cache->prev_displacement[i], + SCULPT_vertex_co_get(ss, i), + ss->cache->limit_surface_co[i]); + } + } + /* Threaded loop over nodes. */ + SculptThreadedTaskData data = { + .sd = sd, + .ob = ob, + .brush = brush, + .nodes = nodes, + }; + + TaskParallelSettings settings; + BKE_pbvh_parallel_range_settings(&settings, true, totnode); + BLI_task_parallel_range( + 0, totnode, &data, do_displacement_smear_store_prev_disp_task_cb_ex, &settings); + BLI_task_parallel_range(0, totnode, &data, do_displacement_smear_brush_task_cb_ex, &settings); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Sculpt Topology Rake (Shared Utility) + * \{ */ + +static void do_topology_rake_bmesh_task_cb_ex(void *__restrict userdata, + const int n, + const TaskParallelTLS *__restrict tls) +{ + SculptThreadedTaskData *data = userdata; + SculptSession *ss = data->ob->sculpt; + Sculpt *sd = data->sd; + const Brush *brush = data->brush; + + float direction[3]; + copy_v3_v3(direction, ss->cache->grab_delta_symmetry); + + float tmp[3]; + mul_v3_v3fl( + tmp, ss->cache->sculpt_normal_symm, dot_v3v3(ss->cache->sculpt_normal_symm, direction)); + sub_v3_v3(direction, tmp); + normalize_v3(direction); + + /* Cancel if there's no grab data. */ + if (is_zero_v3(direction)) { + return; + } + + const float bstrength = clamp_f(data->strength, 0.0f, 1.0f); + + SculptBrushTest test; + SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( + ss, &test, data->brush->falloff_shape); + const int thread_id = BLI_task_parallel_thread_id(tls); + + PBVHVertexIter vd; + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { + if (!sculpt_brush_test_sq_fn(&test, vd.co)) { + continue; + } + const float fade = + bstrength * + SCULPT_brush_strength_factor( + ss, brush, vd.co, sqrtf(test.dist), vd.no, vd.fno, *vd.mask, vd.index, thread_id) * + ss->cache->pressure; + + float avg[3], val[3]; + + SCULPT_bmesh_four_neighbor_average(avg, direction, vd.bm_vert); + + sub_v3_v3v3(val, avg, vd.co); + + madd_v3_v3v3fl(val, vd.co, val, fade); + + SCULPT_clip(sd, ss, vd.co, val); + + if (vd.mvert) { + vd.mvert->flag |= ME_VERT_PBVH_UPDATE; + } + } + BKE_pbvh_vertex_iter_end; +} + +void SCULPT_bmesh_topology_rake( + Sculpt *sd, Object *ob, PBVHNode **nodes, const int totnode, float bstrength) +{ + Brush *brush = BKE_paint_brush(&sd->paint); + const float strength = clamp_f(bstrength, 0.0f, 1.0f); + + /* Interactions increase both strength and quality. */ + const int iterations = 3; + + int iteration; + const int count = iterations * strength + 1; + const float factor = iterations * strength / count; + + for (iteration = 0; iteration <= count; iteration++) { + + SculptThreadedTaskData data = { + .sd = sd, + .ob = ob, + .brush = brush, + .nodes = nodes, + .strength = factor, + }; + TaskParallelSettings settings; + BKE_pbvh_parallel_range_settings(&settings, true, totnode); + + BLI_task_parallel_range(0, totnode, &data, do_topology_rake_bmesh_task_cb_ex, &settings); + } +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Sculpt Mask Brush + * \{ */ + +static void do_mask_brush_draw_task_cb_ex(void *__restrict userdata, + const int n, + const TaskParallelTLS *__restrict tls) +{ + SculptThreadedTaskData *data = userdata; + SculptSession *ss = data->ob->sculpt; + const Brush *brush = data->brush; + const float bstrength = ss->cache->bstrength; + + PBVHVertexIter vd; + + SculptBrushTest test; + SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( + ss, &test, data->brush->falloff_shape); + const int thread_id = BLI_task_parallel_thread_id(tls); + + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { + if (!sculpt_brush_test_sq_fn(&test, vd.co)) { + continue; + } + + const float fade = SCULPT_brush_strength_factor( + ss, brush, vd.co, sqrtf(test.dist), vd.no, vd.fno, 0.0f, vd.index, thread_id); + + if (bstrength > 0.0f) { + (*vd.mask) += fade * bstrength * (1.0f - *vd.mask); + } + else { + (*vd.mask) += fade * bstrength * (*vd.mask); + } + *vd.mask = clamp_f(*vd.mask, 0.0f, 1.0f); + + if (vd.mvert) { + vd.mvert->flag |= ME_VERT_PBVH_UPDATE; + } + BKE_pbvh_vertex_iter_end; + } +} + +void SCULPT_do_mask_brush_draw(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) +{ + Brush *brush = BKE_paint_brush(&sd->paint); + + /* Threaded loop over nodes. */ + SculptThreadedTaskData data = { + .sd = sd, + .ob = ob, + .brush = brush, + .nodes = nodes, + }; + + TaskParallelSettings settings; + BKE_pbvh_parallel_range_settings(&settings, true, totnode); + BLI_task_parallel_range(0, totnode, &data, do_mask_brush_draw_task_cb_ex, &settings); +} + +void SCULPT_do_mask_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) +{ + SculptSession *ss = ob->sculpt; + Brush *brush = BKE_paint_brush(&sd->paint); + + switch ((BrushMaskTool)brush->mask_tool) { + case BRUSH_MASK_DRAW: + SCULPT_do_mask_brush_draw(sd, ob, nodes, totnode); + break; + case BRUSH_MASK_SMOOTH: + SCULPT_smooth(sd, ob, nodes, totnode, ss->cache->bstrength, true); + break; + } +} + +/** \} */ diff --git a/source/blender/editors/sculpt_paint/sculpt_intern.h b/source/blender/editors/sculpt_paint/sculpt_intern.h index 4dd2a786922..b85b00fb636 100644 --- a/source/blender/editors/sculpt_paint/sculpt_intern.h +++ b/source/blender/editors/sculpt_paint/sculpt_intern.h @@ -29,13 +29,13 @@ #include "DNA_meshdata_types.h" #include "DNA_vec_types.h" +#include "BKE_paint.h" +#include "BKE_pbvh.h" #include "BLI_bitmap.h" +#include "BLI_compiler_compat.h" #include "BLI_gsqueue.h" #include "BLI_threads.h" -#include "BKE_paint.h" -#include "BKE_pbvh.h" - struct AutomaskingCache; struct KeyBlock; struct Object; @@ -300,6 +300,10 @@ void SCULPT_calc_brush_plane(struct Sculpt *sd, void SCULPT_calc_area_normal( Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode, float r_area_no[3]); +void SCULPT_calc_area_normal_and_center( + Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode, float r_area_no[3], float r_area_co[3]); +void SCULPT_calc_area_center( + Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode, float r_area_co[3]); int SCULPT_nearest_vertex_get(struct Sculpt *sd, struct Object *ob, @@ -1506,3 +1510,115 @@ void SCULPT_OT_dyntopo_detail_size_edit(struct wmOperatorType *ot); /* Dyntopo. */ void SCULPT_OT_dynamic_topology_toggle(struct wmOperatorType *ot); + +/* sculpt_brushes.c */ + +float SCULPT_clay_thumb_get_stabilized_pressure(struct StrokeCache *cache); + +void SCULPT_do_draw_brush(struct Sculpt *sd, + struct Object *ob, + struct PBVHNode **nodes, + int totnode); + +void SCULPT_do_fill_brush(struct Sculpt *sd, + struct Object *ob, + struct PBVHNode **nodes, + int totnode); +void SCULPT_do_scrape_brush(struct Sculpt *sd, + struct Object *ob, + struct PBVHNode **nodes, + int totnode); +void SCULPT_do_clay_thumb_brush(struct Sculpt *sd, + struct Object *ob, + struct PBVHNode **nodes, + int totnode); +void SCULPT_do_flatten_brush(struct Sculpt *sd, + struct Object *ob, + struct PBVHNode **nodes, + int totnode); +void SCULPT_do_clay_brush(struct Sculpt *sd, + struct Object *ob, + struct PBVHNode **nodes, + int totnode); +void SCULPT_do_clay_strips_brush(struct Sculpt *sd, + struct Object *ob, + struct PBVHNode **nodes, + int totnode); +void SCULPT_do_snake_hook_brush(struct Sculpt *sd, + struct Object *ob, + struct PBVHNode **nodes, + int totnode); +void SCULPT_do_thumb_brush(struct Sculpt *sd, + struct Object *ob, + struct PBVHNode **nodes, + int totnode); +void SCULPT_do_rotate_brush(struct Sculpt *sd, + struct Object *ob, + struct PBVHNode **nodes, + int totnode); +void SCULPT_do_layer_brush(struct Sculpt *sd, + struct Object *ob, + struct PBVHNode **nodes, + int totnode); +void SCULPT_do_inflate_brush(struct Sculpt *sd, + struct Object *ob, + struct PBVHNode **nodes, + int totnode); +void SCULPT_do_nudge_brush(struct Sculpt *sd, + struct Object *ob, + struct PBVHNode **nodes, + int totnode); +void SCULPT_do_crease_brush(struct Sculpt *sd, + struct Object *ob, + struct PBVHNode **nodes, + int totnode); +void SCULPT_do_pinch_brush(struct Sculpt *sd, + struct Object *ob, + struct PBVHNode **nodes, + int totnode); +void SCULPT_do_grab_brush(struct Sculpt *sd, + struct Object *ob, + struct PBVHNode **nodes, + int totnode); +void SCULPT_do_elastic_deform_brush(struct Sculpt *sd, + struct Object *ob, + struct PBVHNode **nodes, + int totnode); +void SCULPT_do_draw_sharp_brush(struct Sculpt *sd, + struct Object *ob, + struct PBVHNode **nodes, + int totnode); +void SCULPT_do_slide_relax_brush(struct Sculpt *sd, + struct Object *ob, + struct PBVHNode **nodes, + int totnode); + +void SCULPT_do_displacement_smear_brush(struct Sculpt *sd, + struct Object *ob, + struct PBVHNode **nodes, + int totnode); +void SCULPT_do_displacement_eraser_brush(struct Sculpt *sd, + struct Object *ob, + struct PBVHNode **nodes, + int totnode); +void SCULPT_do_mask_brush_draw(struct Sculpt *sd, + struct Object *ob, + struct PBVHNode **nodes, + int totnode); +void SCULPT_do_mask_brush(struct Sculpt *sd, + struct Object *ob, + struct PBVHNode **nodes, + int totnode); + +void SCULPT_bmesh_topology_rake(struct Sculpt *sd, + struct Object *ob, + struct PBVHNode **nodes, + const int totnode, + float bstrength); + +/* end sculpt_brushes.c */ + +/* sculpt_ops.c */ +void SCULPT_OT_brush_stroke(struct wmOperatorType *ot); + +/* end sculpt_ops.c */ diff --git a/source/blender/editors/sculpt_paint/sculpt_ops.c b/source/blender/editors/sculpt_paint/sculpt_ops.c new file mode 100644 index 00000000000..119d246a770 --- /dev/null +++ b/source/blender/editors/sculpt_paint/sculpt_ops.c @@ -0,0 +1,1141 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2006 by Nicholas Bishop + * All rights reserved. + * Implements the Sculpt Mode tools + */ + +/** \file + * \ingroup edsculpt + */ + +#include "MEM_guardedalloc.h" + +#include "BLI_array.h" +#include "BLI_blenlib.h" +#include "BLI_dial_2d.h" +#include "BLI_ghash.h" +#include "BLI_gsqueue.h" +#include "BLI_hash.h" +#include "BLI_link_utils.h" +#include "BLI_linklist.h" +#include "BLI_linklist_stack.h" +#include "BLI_listbase.h" +#include "BLI_math.h" +#include "BLI_math_color_blend.h" +#include "BLI_memarena.h" +#include "BLI_rand.h" +#include "BLI_task.h" +#include "BLI_utildefines.h" +#include "atomic_ops.h" + +#include "BLT_translation.h" + +#include "PIL_time.h" + +#include "DNA_brush_types.h" +#include "DNA_customdata_types.h" +#include "DNA_listBase.h" +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" +#include "DNA_node_types.h" +#include "DNA_object_types.h" +#include "DNA_scene_types.h" + +#include "BKE_attribute.h" +#include "BKE_brush.h" +#include "BKE_ccg.h" +#include "BKE_colortools.h" +#include "BKE_context.h" +#include "BKE_image.h" +#include "BKE_kelvinlet.h" +#include "BKE_key.h" +#include "BKE_lib_id.h" +#include "BKE_main.h" +#include "BKE_mesh.h" +#include "BKE_mesh_fair.h" +#include "BKE_mesh_mapping.h" +#include "BKE_mesh_mirror.h" +#include "BKE_modifier.h" +#include "BKE_multires.h" +#include "BKE_node.h" +#include "BKE_object.h" +#include "BKE_paint.h" +#include "BKE_particle.h" +#include "BKE_pbvh.h" +#include "BKE_pointcache.h" +#include "BKE_report.h" +#include "BKE_scene.h" +#include "BKE_screen.h" +#include "BKE_subdiv_ccg.h" +#include "BKE_subsurf.h" + +#include "DEG_depsgraph.h" +#include "DEG_depsgraph_query.h" + +#include "IMB_colormanagement.h" + +#include "GPU_batch.h" +#include "GPU_batch_presets.h" +#include "GPU_immediate.h" +#include "GPU_immediate_util.h" +#include "GPU_matrix.h" +#include "GPU_state.h" + +#include "WM_api.h" +#include "WM_message.h" +#include "WM_toolsystem.h" +#include "WM_types.h" + +#include "ED_object.h" +#include "ED_screen.h" +#include "ED_sculpt.h" +#include "ED_space_api.h" +#include "ED_transform_snap_object_context.h" +#include "ED_view3d.h" + +#include "paint_intern.h" +#include "sculpt_intern.h" + +#include "RNA_access.h" +#include "RNA_define.h" + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "bmesh.h" +#include "bmesh_tools.h" + +#include <math.h> +#include <stdlib.h> +#include <string.h> + +/* Reset the copy of the mesh that is being sculpted on (currently just for the layer brush). */ + +static int sculpt_set_persistent_base_exec(bContext *C, wmOperator *UNUSED(op)) +{ + Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C); + Object *ob = CTX_data_active_object(C); + SculptSession *ss = ob->sculpt; + + if (!ss) { + return OPERATOR_FINISHED; + } + SCULPT_vertex_random_access_ensure(ss); + BKE_sculpt_update_object_for_edit(depsgraph, ob, false, false, false); + + MEM_SAFE_FREE(ss->persistent_base); + + const int totvert = SCULPT_vertex_count_get(ss); + ss->persistent_base = MEM_mallocN(sizeof(SculptPersistentBase) * totvert, + "layer persistent base"); + + for (int i = 0; i < totvert; i++) { + copy_v3_v3(ss->persistent_base[i].co, SCULPT_vertex_co_get(ss, i)); + SCULPT_vertex_normal_get(ss, i, ss->persistent_base[i].no); + ss->persistent_base[i].disp = 0.0f; + } + + return OPERATOR_FINISHED; +} + +static void SCULPT_OT_set_persistent_base(wmOperatorType *ot) +{ + /* Identifiers. */ + ot->name = "Set Persistent Base"; + ot->idname = "SCULPT_OT_set_persistent_base"; + ot->description = "Reset the copy of the mesh that is being sculpted on"; + + /* API callbacks. */ + ot->exec = sculpt_set_persistent_base_exec; + ot->poll = SCULPT_mode_poll; + + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +/************************* SCULPT_OT_optimize *************************/ + +static int sculpt_optimize_exec(bContext *C, wmOperator *UNUSED(op)) +{ + Object *ob = CTX_data_active_object(C); + + SCULPT_pbvh_clear(ob); + WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob); + + return OPERATOR_FINISHED; +} + +/* The BVH gets less optimal more quickly with dynamic topology than + * regular sculpting. There is no doubt more clever stuff we can do to + * optimize it on the fly, but for now this gives the user a nicer way + * to recalculate it than toggling modes. */ +static void SCULPT_OT_optimize(wmOperatorType *ot) +{ + /* Identifiers. */ + ot->name = "Rebuild BVH"; + ot->idname = "SCULPT_OT_optimize"; + ot->description = "Recalculate the sculpt BVH to improve performance"; + + /* API callbacks. */ + ot->exec = sculpt_optimize_exec; + ot->poll = SCULPT_mode_poll; + + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +/********************* Dynamic topology symmetrize ********************/ + +static bool sculpt_no_multires_poll(bContext *C) +{ + Object *ob = CTX_data_active_object(C); + if (SCULPT_mode_poll(C) && ob->sculpt && ob->sculpt->pbvh) { + return BKE_pbvh_type(ob->sculpt->pbvh) != PBVH_GRIDS; + } + return false; +} + +static int sculpt_symmetrize_exec(bContext *C, wmOperator *op) +{ + Main *bmain = CTX_data_main(C); + Object *ob = CTX_data_active_object(C); + const Sculpt *sd = CTX_data_tool_settings(C)->sculpt; + SculptSession *ss = ob->sculpt; + PBVH *pbvh = ss->pbvh; + const float dist = RNA_float_get(op->ptr, "merge_tolerance"); + + if (!pbvh) { + return OPERATOR_CANCELLED; + } + + switch (BKE_pbvh_type(pbvh)) { + case PBVH_BMESH: + /* Dyntopo Symmetrize. */ + + /* To simplify undo for symmetrize, all BMesh elements are logged + * as deleted, then after symmetrize operation all BMesh elements + * are logged as added (as opposed to attempting to store just the + * parts that symmetrize modifies). */ + SCULPT_undo_push_begin(ob, "Dynamic topology symmetrize"); + SCULPT_undo_push_node(ob, NULL, SCULPT_UNDO_DYNTOPO_SYMMETRIZE); + BM_log_before_all_removed(ss->bm, ss->bm_log); + + BM_mesh_toolflags_set(ss->bm, true); + + /* Symmetrize and re-triangulate. */ + BMO_op_callf(ss->bm, + (BMO_FLAG_DEFAULTS & ~BMO_FLAG_RESPECT_HIDE), + "symmetrize input=%avef direction=%i dist=%f use_shapekey=%b", + sd->symmetrize_direction, + dist, + true); + SCULPT_dynamic_topology_triangulate(ss->bm); + + /* Bisect operator flags edges (keep tags clean for edge queue). */ + BM_mesh_elem_hflag_disable_all(ss->bm, BM_EDGE, BM_ELEM_TAG, false); + + BM_mesh_toolflags_set(ss->bm, false); + + /* Finish undo. */ + BM_log_all_added(ss->bm, ss->bm_log); + SCULPT_undo_push_end(); + + break; + case PBVH_FACES: + /* Mesh Symmetrize. */ + ED_sculpt_undo_geometry_begin(ob, "mesh symmetrize"); + Mesh *mesh = ob->data; + + BKE_mesh_mirror_apply_mirror_on_axis(bmain, mesh, sd->symmetrize_direction, dist); + + ED_sculpt_undo_geometry_end(ob); + BKE_mesh_calc_normals(ob->data); + BKE_mesh_batch_cache_dirty_tag(ob->data, BKE_MESH_BATCH_DIRTY_ALL); + + break; + case PBVH_GRIDS: + return OPERATOR_CANCELLED; + } + + /* Redraw. */ + SCULPT_pbvh_clear(ob); + WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob); + + return OPERATOR_FINISHED; +} + +static void SCULPT_OT_symmetrize(wmOperatorType *ot) +{ + /* Identifiers. */ + ot->name = "Symmetrize"; + ot->idname = "SCULPT_OT_symmetrize"; + ot->description = "Symmetrize the topology modifications"; + + /* API callbacks. */ + ot->exec = sculpt_symmetrize_exec; + ot->poll = sculpt_no_multires_poll; + + RNA_def_float(ot->srna, + "merge_tolerance", + 0.001f, + 0.0f, + FLT_MAX, + "Merge Distance", + "Distance within which symmetrical vertices are merged", + 0.0f, + 1.0f); +} + +/**** Toggle operator for turning sculpt mode on or off ****/ + +static void sculpt_init_session(Main *bmain, Depsgraph *depsgraph, Scene *scene, Object *ob) +{ + /* Create persistent sculpt mode data. */ + BKE_sculpt_toolsettings_data_ensure(scene); + + /* Create sculpt mode session data. */ + if (ob->sculpt != NULL) { + BKE_sculptsession_free(ob); + } + ob->sculpt = MEM_callocN(sizeof(SculptSession), "sculpt session"); + ob->sculpt->mode_type = OB_MODE_SCULPT; + + BKE_sculpt_ensure_orig_mesh_data(scene, ob); + + BKE_scene_graph_evaluated_ensure(depsgraph, bmain); + + /* This function expects a fully evaluated depsgraph. */ + BKE_sculpt_update_object_for_edit(depsgraph, ob, false, false, false); + + /* Here we can detect geometry that was just added to Sculpt Mode as it has the + * SCULPT_FACE_SET_NONE assigned, so we can create a new Face Set for it. */ + /* In sculpt mode all geometry that is assigned to SCULPT_FACE_SET_NONE is considered as not + * initialized, which is used is some operators that modify the mesh topology to perform certain + * actions in the new polys. After these operations are finished, all polys should have a valid + * face set ID assigned (different from SCULPT_FACE_SET_NONE) to manage their visibility + * correctly. */ + /* TODO(pablodp606): Based on this we can improve the UX in future tools for creating new + * objects, like moving the transform pivot position to the new area or masking existing + * geometry. */ + SculptSession *ss = ob->sculpt; + const int new_face_set = SCULPT_face_set_next_available_get(ss); + for (int i = 0; i < ss->totfaces; i++) { + if (ss->face_sets[i] == SCULPT_FACE_SET_NONE) { + ss->face_sets[i] = new_face_set; + } + } +} + +void ED_object_sculptmode_enter_ex(Main *bmain, + Depsgraph *depsgraph, + Scene *scene, + Object *ob, + const bool force_dyntopo, + ReportList *reports) +{ + const int mode_flag = OB_MODE_SCULPT; + Mesh *me = BKE_mesh_from_object(ob); + + /* Enter sculpt mode. */ + ob->mode |= mode_flag; + + sculpt_init_session(bmain, depsgraph, scene, ob); + + if (!(fabsf(ob->scale[0] - ob->scale[1]) < 1e-4f && + fabsf(ob->scale[1] - ob->scale[2]) < 1e-4f)) { + BKE_report( + reports, RPT_WARNING, "Object has non-uniform scale, sculpting may be unpredictable"); + } + else if (is_negative_m4(ob->obmat)) { + BKE_report(reports, RPT_WARNING, "Object has negative scale, sculpting may be unpredictable"); + } + + Paint *paint = BKE_paint_get_active_from_paintmode(scene, PAINT_MODE_SCULPT); + BKE_paint_init(bmain, scene, PAINT_MODE_SCULPT, PAINT_CURSOR_SCULPT); + + paint_cursor_start(paint, SCULPT_mode_poll_view3d); + + /* Check dynamic-topology flag; re-enter dynamic-topology mode when changing modes, + * As long as no data was added that is not supported. */ + if (me->flag & ME_SCULPT_DYNAMIC_TOPOLOGY) { + MultiresModifierData *mmd = BKE_sculpt_multires_active(scene, ob); + + const char *message_unsupported = NULL; + if (me->totloop != me->totpoly * 3) { + message_unsupported = TIP_("non-triangle face"); + } + else if (mmd != NULL) { + message_unsupported = TIP_("multi-res modifier"); + } + else { + enum eDynTopoWarnFlag flag = SCULPT_dynamic_topology_check(scene, ob); + if (flag == 0) { + /* pass */ + } + else if (flag & DYNTOPO_WARN_VDATA) { + message_unsupported = TIP_("vertex data"); + } + else if (flag & DYNTOPO_WARN_EDATA) { + message_unsupported = TIP_("edge data"); + } + else if (flag & DYNTOPO_WARN_LDATA) { + message_unsupported = TIP_("face data"); + } + else if (flag & DYNTOPO_WARN_MODIFIER) { + message_unsupported = TIP_("constructive modifier"); + } + else { + BLI_assert(0); + } + } + + if ((message_unsupported == NULL) || force_dyntopo) { + /* Needed because we may be entering this mode before the undo system loads. */ + wmWindowManager *wm = bmain->wm.first; + bool has_undo = wm->undo_stack != NULL; + /* Undo push is needed to prevent memory leak. */ + if (has_undo) { + SCULPT_undo_push_begin(ob, "Dynamic topology enable"); + } + SCULPT_dynamic_topology_enable_ex(bmain, depsgraph, scene, ob); + if (has_undo) { + SCULPT_undo_push_node(ob, NULL, SCULPT_UNDO_DYNTOPO_BEGIN); + SCULPT_undo_push_end(); + } + } + else { + BKE_reportf( + reports, RPT_WARNING, "Dynamic Topology found: %s, disabled", message_unsupported); + me->flag &= ~ME_SCULPT_DYNAMIC_TOPOLOGY; + } + } + + /* Flush object mode. */ + DEG_id_tag_update(&ob->id, ID_RECALC_COPY_ON_WRITE); +} + +void ED_object_sculptmode_enter(struct bContext *C, Depsgraph *depsgraph, ReportList *reports) +{ + Main *bmain = CTX_data_main(C); + Scene *scene = CTX_data_scene(C); + ViewLayer *view_layer = CTX_data_view_layer(C); + Object *ob = OBACT(view_layer); + ED_object_sculptmode_enter_ex(bmain, depsgraph, scene, ob, false, reports); +} + +void ED_object_sculptmode_exit_ex(Main *bmain, Depsgraph *depsgraph, Scene *scene, Object *ob) +{ + const int mode_flag = OB_MODE_SCULPT; + Mesh *me = BKE_mesh_from_object(ob); + + multires_flush_sculpt_updates(ob); + + /* Not needed for now. */ +#if 0 + MultiresModifierData *mmd = BKE_sculpt_multires_active(scene, ob); + const int flush_recalc = ed_object_sculptmode_flush_recalc_flag(scene, ob, mmd); +#endif + + /* Always for now, so leaving sculpt mode always ensures scene is in + * a consistent state. */ + if (true || /* flush_recalc || */ (ob->sculpt && ob->sculpt->bm)) { + DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); + } + + if (me->flag & ME_SCULPT_DYNAMIC_TOPOLOGY) { + /* Dynamic topology must be disabled before exiting sculpt + * mode to ensure the undo stack stays in a consistent + * state. */ + sculpt_dynamic_topology_disable_with_undo(bmain, depsgraph, scene, ob); + + /* Store so we know to re-enable when entering sculpt mode. */ + me->flag |= ME_SCULPT_DYNAMIC_TOPOLOGY; + } + + /* Leave sculpt mode. */ + ob->mode &= ~mode_flag; + + BKE_sculptsession_free(ob); + + paint_cursor_delete_textures(); + + /* Never leave derived meshes behind. */ + BKE_object_free_derived_caches(ob); + + /* Flush object mode. */ + DEG_id_tag_update(&ob->id, ID_RECALC_COPY_ON_WRITE); +} + +void ED_object_sculptmode_exit(bContext *C, Depsgraph *depsgraph) +{ + Main *bmain = CTX_data_main(C); + Scene *scene = CTX_data_scene(C); + ViewLayer *view_layer = CTX_data_view_layer(C); + Object *ob = OBACT(view_layer); + ED_object_sculptmode_exit_ex(bmain, depsgraph, scene, ob); +} + +static int sculpt_mode_toggle_exec(bContext *C, wmOperator *op) +{ + struct wmMsgBus *mbus = CTX_wm_message_bus(C); + Main *bmain = CTX_data_main(C); + Depsgraph *depsgraph = CTX_data_depsgraph_on_load(C); + Scene *scene = CTX_data_scene(C); + ToolSettings *ts = scene->toolsettings; + ViewLayer *view_layer = CTX_data_view_layer(C); + Object *ob = OBACT(view_layer); + const int mode_flag = OB_MODE_SCULPT; + const bool is_mode_set = (ob->mode & mode_flag) != 0; + + if (!is_mode_set) { + if (!ED_object_mode_compat_set(C, ob, mode_flag, op->reports)) { + return OPERATOR_CANCELLED; + } + } + + if (is_mode_set) { + ED_object_sculptmode_exit_ex(bmain, depsgraph, scene, ob); + } + else { + if (depsgraph) { + depsgraph = CTX_data_ensure_evaluated_depsgraph(C); + } + ED_object_sculptmode_enter_ex(bmain, depsgraph, scene, ob, false, op->reports); + BKE_paint_toolslots_brush_validate(bmain, &ts->sculpt->paint); + + if (ob->mode & mode_flag) { + Mesh *me = ob->data; + /* Dyntopo adds its own undo step. */ + if ((me->flag & ME_SCULPT_DYNAMIC_TOPOLOGY) == 0) { + /* Without this the memfile undo step is used, + * while it works it causes lag when undoing the first undo step, see T71564. */ + wmWindowManager *wm = CTX_wm_manager(C); + if (wm->op_undo_depth <= 1) { + SCULPT_undo_push_begin(ob, op->type->name); + } + } + } + } + + WM_event_add_notifier(C, NC_SCENE | ND_MODE, scene); + + WM_msg_publish_rna_prop(mbus, &ob->id, ob, Object, mode); + + WM_toolsystem_update_from_context_view3d(C); + + return OPERATOR_FINISHED; +} + +static void SCULPT_OT_sculptmode_toggle(wmOperatorType *ot) +{ + /* Identifiers. */ + ot->name = "Sculpt Mode"; + ot->idname = "SCULPT_OT_sculptmode_toggle"; + ot->description = "Toggle sculpt mode in 3D view"; + + /* API callbacks. */ + ot->exec = sculpt_mode_toggle_exec; + ot->poll = ED_operator_object_active_editable_mesh; + + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +void SCULPT_geometry_preview_lines_update(bContext *C, SculptSession *ss, float radius) +{ + Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C); + Object *ob = CTX_data_active_object(C); + + ss->preview_vert_index_count = 0; + int totpoints = 0; + + /* This function is called from the cursor drawing code, so the PBVH may not be build yet. */ + if (!ss->pbvh) { + return; + } + + if (!ss->deform_modifiers_active) { + return; + } + + if (BKE_pbvh_type(ss->pbvh) == PBVH_GRIDS) { + return; + } + + BKE_sculpt_update_object_for_edit(depsgraph, ob, true, true, false); + + if (!ss->pmap) { + return; + } + + float brush_co[3]; + copy_v3_v3(brush_co, SCULPT_active_vertex_co_get(ss)); + + BLI_bitmap *visited_vertices = BLI_BITMAP_NEW(SCULPT_vertex_count_get(ss), "visited_vertices"); + + /* Assuming an average of 6 edges per vertex in a triangulated mesh. */ + const int max_preview_vertices = SCULPT_vertex_count_get(ss) * 3 * 2; + + if (ss->preview_vert_index_list == NULL) { + ss->preview_vert_index_list = MEM_callocN(max_preview_vertices * sizeof(int), "preview lines"); + } + + GSQueue *not_visited_vertices = BLI_gsqueue_new(sizeof(int)); + int active_v = SCULPT_active_vertex_get(ss); + BLI_gsqueue_push(not_visited_vertices, &active_v); + + while (!BLI_gsqueue_is_empty(not_visited_vertices)) { + int from_v; + BLI_gsqueue_pop(not_visited_vertices, &from_v); + SculptVertexNeighborIter ni; + SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, from_v, ni) { + if (totpoints + (ni.size * 2) < max_preview_vertices) { + int to_v = ni.index; + ss->preview_vert_index_list[totpoints] = from_v; + totpoints++; + ss->preview_vert_index_list[totpoints] = to_v; + totpoints++; + if (BLI_BITMAP_TEST(visited_vertices, to_v)) { + continue; + } + BLI_BITMAP_ENABLE(visited_vertices, to_v); + const float *co = SCULPT_vertex_co_for_grab_active_get(ss, to_v); + if (len_squared_v3v3(brush_co, co) < radius * radius) { + BLI_gsqueue_push(not_visited_vertices, &to_v); + } + } + } + SCULPT_VERTEX_NEIGHBORS_ITER_END(ni); + } + + BLI_gsqueue_free(not_visited_vertices); + + MEM_freeN(visited_vertices); + + ss->preview_vert_index_count = totpoints; +} + +static int vertex_to_loop_colors_exec(bContext *C, wmOperator *UNUSED(op)) +{ + Object *ob = CTX_data_active_object(C); + + ID *data; + data = ob->data; + if (data && ID_IS_LINKED(data)) { + return OPERATOR_CANCELLED; + } + + if (ob->type != OB_MESH) { + return OPERATOR_CANCELLED; + } + + Mesh *mesh = ob->data; + + const int mloopcol_layer_n = CustomData_get_active_layer(&mesh->ldata, CD_MLOOPCOL); + if (mloopcol_layer_n == -1) { + return OPERATOR_CANCELLED; + } + MLoopCol *loopcols = CustomData_get_layer_n(&mesh->ldata, CD_MLOOPCOL, mloopcol_layer_n); + + const int MPropCol_layer_n = CustomData_get_active_layer(&mesh->vdata, CD_PROP_COLOR); + if (MPropCol_layer_n == -1) { + return OPERATOR_CANCELLED; + } + MPropCol *vertcols = CustomData_get_layer_n(&mesh->vdata, CD_PROP_COLOR, MPropCol_layer_n); + + MLoop *loops = CustomData_get_layer(&mesh->ldata, CD_MLOOP); + MPoly *polys = CustomData_get_layer(&mesh->pdata, CD_MPOLY); + + for (int i = 0; i < mesh->totpoly; i++) { + MPoly *c_poly = &polys[i]; + for (int j = 0; j < c_poly->totloop; j++) { + int loop_index = c_poly->loopstart + j; + MLoop *c_loop = &loops[c_poly->loopstart + j]; + float srgb_color[4]; + linearrgb_to_srgb_v4(srgb_color, vertcols[c_loop->v].color); + loopcols[loop_index].r = (char)(srgb_color[0] * 255); + loopcols[loop_index].g = (char)(srgb_color[1] * 255); + loopcols[loop_index].b = (char)(srgb_color[2] * 255); + loopcols[loop_index].a = (char)(srgb_color[3] * 255); + } + } + + DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); + WM_event_add_notifier(C, NC_GEOM | ND_DATA, ob->data); + + return OPERATOR_FINISHED; +} + +static void SCULPT_OT_vertex_to_loop_colors(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Sculpt Vertex Color to Vertex Color"; + ot->description = "Copy the Sculpt Vertex Color to a regular color layer"; + ot->idname = "SCULPT_OT_vertex_to_loop_colors"; + + /* api callbacks */ + ot->poll = SCULPT_vertex_colors_poll; + ot->exec = vertex_to_loop_colors_exec; + + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +static int loop_to_vertex_colors_exec(bContext *C, wmOperator *UNUSED(op)) +{ + Object *ob = CTX_data_active_object(C); + + ID *data; + data = ob->data; + if (data && ID_IS_LINKED(data)) { + return OPERATOR_CANCELLED; + } + + if (ob->type != OB_MESH) { + return OPERATOR_CANCELLED; + } + + Mesh *mesh = ob->data; + + const int mloopcol_layer_n = CustomData_get_active_layer(&mesh->ldata, CD_MLOOPCOL); + if (mloopcol_layer_n == -1) { + return OPERATOR_CANCELLED; + } + MLoopCol *loopcols = CustomData_get_layer_n(&mesh->ldata, CD_MLOOPCOL, mloopcol_layer_n); + + const int MPropCol_layer_n = CustomData_get_active_layer(&mesh->vdata, CD_PROP_COLOR); + if (MPropCol_layer_n == -1) { + return OPERATOR_CANCELLED; + } + MPropCol *vertcols = CustomData_get_layer_n(&mesh->vdata, CD_PROP_COLOR, MPropCol_layer_n); + + MLoop *loops = CustomData_get_layer(&mesh->ldata, CD_MLOOP); + MPoly *polys = CustomData_get_layer(&mesh->pdata, CD_MPOLY); + + for (int i = 0; i < mesh->totpoly; i++) { + MPoly *c_poly = &polys[i]; + for (int j = 0; j < c_poly->totloop; j++) { + int loop_index = c_poly->loopstart + j; + MLoop *c_loop = &loops[c_poly->loopstart + j]; + vertcols[c_loop->v].color[0] = (loopcols[loop_index].r / 255.0f); + vertcols[c_loop->v].color[1] = (loopcols[loop_index].g / 255.0f); + vertcols[c_loop->v].color[2] = (loopcols[loop_index].b / 255.0f); + vertcols[c_loop->v].color[3] = (loopcols[loop_index].a / 255.0f); + srgb_to_linearrgb_v4(vertcols[c_loop->v].color, vertcols[c_loop->v].color); + } + } + + DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); + WM_event_add_notifier(C, NC_GEOM | ND_DATA, ob->data); + + return OPERATOR_FINISHED; +} + +static void SCULPT_OT_loop_to_vertex_colors(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Vertex Color to Sculpt Vertex Color"; + ot->description = "Copy the active loop color layer to the vertex color"; + ot->idname = "SCULPT_OT_loop_to_vertex_colors"; + + /* api callbacks */ + ot->poll = SCULPT_vertex_colors_poll; + ot->exec = loop_to_vertex_colors_exec; + + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +static int sculpt_sample_color_invoke(bContext *C, + wmOperator *UNUSED(op), + const wmEvent *UNUSED(e)) +{ + Sculpt *sd = CTX_data_tool_settings(C)->sculpt; + Scene *scene = CTX_data_scene(C); + Object *ob = CTX_data_active_object(C); + Brush *brush = BKE_paint_brush(&sd->paint); + SculptSession *ss = ob->sculpt; + int active_vertex = SCULPT_active_vertex_get(ss); + const float *active_vertex_color = SCULPT_vertex_color_get(ss, active_vertex); + if (!active_vertex_color) { + return OPERATOR_CANCELLED; + } + + float color_srgb[3]; + copy_v3_v3(color_srgb, active_vertex_color); + IMB_colormanagement_scene_linear_to_srgb_v3(color_srgb); + BKE_brush_color_set(scene, brush, color_srgb); + + WM_event_add_notifier(C, NC_BRUSH | NA_EDITED, brush); + + return OPERATOR_FINISHED; +} + +static void SCULPT_OT_sample_color(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Sample Color"; + ot->idname = "SCULPT_OT_sample_color"; + ot->description = "Sample the vertex color of the active vertex"; + + /* api callbacks */ + ot->invoke = sculpt_sample_color_invoke; + ot->poll = SCULPT_vertex_colors_poll; + + ot->flag = OPTYPE_REGISTER; +} + +/** + * #sculpt_mask_by_color_delta_get returns values in the (0,1) range that are used to generate the + * mask based on the difference between two colors (the active color and the color of any other + * vertex). Ideally, a threshold of 0 should mask only the colors that are equal to the active + * color and threshold of 1 should mask all colors. In order to avoid artifacts and produce softer + * falloffs in the mask, the MASK_BY_COLOR_SLOPE defines the size of the transition values between + * masked and unmasked vertices. The smaller this value is, the sharper the generated mask is going + * to be. + */ +#define MASK_BY_COLOR_SLOPE 0.25f + +static float sculpt_mask_by_color_delta_get(const float *color_a, + const float *color_b, + const float threshold, + const bool invert) +{ + float len = len_v3v3(color_a, color_b); + /* Normalize len to the (0, 1) range. */ + len = len / M_SQRT3; + + if (len < threshold - MASK_BY_COLOR_SLOPE) { + len = 1.0f; + } + else if (len >= threshold) { + len = 0.0f; + } + else { + len = (-len + threshold) / MASK_BY_COLOR_SLOPE; + } + + if (invert) { + return 1.0f - len; + } + return len; +} + +static float sculpt_mask_by_color_final_mask_get(const float current_mask, + const float new_mask, + const bool invert, + const bool preserve_mask) +{ + if (preserve_mask) { + if (invert) { + return min_ff(current_mask, new_mask); + } + return max_ff(current_mask, new_mask); + } + return new_mask; +} + +typedef struct MaskByColorContiguousFloodFillData { + float threshold; + bool invert; + float *new_mask; + float initial_color[3]; +} MaskByColorContiguousFloodFillData; + +static void do_mask_by_color_contiguous_update_nodes_cb( + void *__restrict userdata, const int n, const TaskParallelTLS *__restrict UNUSED(tls)) +{ + SculptThreadedTaskData *data = userdata; + SculptSession *ss = data->ob->sculpt; + + SCULPT_undo_push_node(data->ob, data->nodes[n], SCULPT_UNDO_MASK); + bool update_node = false; + + const bool invert = data->mask_by_color_invert; + const bool preserve_mask = data->mask_by_color_preserve_mask; + + PBVHVertexIter vd; + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { + const float current_mask = *vd.mask; + const float new_mask = data->mask_by_color_floodfill[vd.index]; + *vd.mask = sculpt_mask_by_color_final_mask_get(current_mask, new_mask, invert, preserve_mask); + if (current_mask == *vd.mask) { + continue; + } + update_node = true; + if (vd.mvert) { + vd.mvert->flag |= ME_VERT_PBVH_UPDATE; + } + } + BKE_pbvh_vertex_iter_end; + if (update_node) { + BKE_pbvh_node_mark_redraw(data->nodes[n]); + } +} + +static bool sculpt_mask_by_color_contiguous_floodfill_cb( + SculptSession *ss, int from_v, int to_v, bool is_duplicate, void *userdata) +{ + MaskByColorContiguousFloodFillData *data = userdata; + const float *current_color = SCULPT_vertex_color_get(ss, to_v); + float new_vertex_mask = sculpt_mask_by_color_delta_get( + current_color, data->initial_color, data->threshold, data->invert); + data->new_mask[to_v] = new_vertex_mask; + + if (is_duplicate) { + data->new_mask[to_v] = data->new_mask[from_v]; + } + + float len = len_v3v3(current_color, data->initial_color); + len = len / M_SQRT3; + return len <= data->threshold; +} + +static void sculpt_mask_by_color_contiguous(Object *object, + const int vertex, + const float threshold, + const bool invert, + const bool preserve_mask) +{ + SculptSession *ss = object->sculpt; + const int totvert = SCULPT_vertex_count_get(ss); + + float *new_mask = MEM_calloc_arrayN(totvert, sizeof(float), "new mask"); + + if (invert) { + for (int i = 0; i < totvert; i++) { + new_mask[i] = 1.0f; + } + } + + SculptFloodFill flood; + SCULPT_floodfill_init(ss, &flood); + SCULPT_floodfill_add_initial(&flood, vertex); + + MaskByColorContiguousFloodFillData ffd; + ffd.threshold = threshold; + ffd.invert = invert; + ffd.new_mask = new_mask; + copy_v3_v3(ffd.initial_color, SCULPT_vertex_color_get(ss, vertex)); + + SCULPT_floodfill_execute(ss, &flood, sculpt_mask_by_color_contiguous_floodfill_cb, &ffd); + SCULPT_floodfill_free(&flood); + + int totnode; + PBVHNode **nodes; + BKE_pbvh_search_gather(ss->pbvh, NULL, NULL, &nodes, &totnode); + + SculptThreadedTaskData data = { + .ob = object, + .nodes = nodes, + .mask_by_color_floodfill = new_mask, + .mask_by_color_vertex = vertex, + .mask_by_color_threshold = threshold, + .mask_by_color_invert = invert, + .mask_by_color_preserve_mask = preserve_mask, + }; + + TaskParallelSettings settings; + BKE_pbvh_parallel_range_settings(&settings, true, totnode); + BLI_task_parallel_range( + 0, totnode, &data, do_mask_by_color_contiguous_update_nodes_cb, &settings); + + MEM_SAFE_FREE(nodes); + + MEM_freeN(new_mask); +} + +static void do_mask_by_color_task_cb(void *__restrict userdata, + const int n, + const TaskParallelTLS *__restrict UNUSED(tls)) +{ + SculptThreadedTaskData *data = userdata; + SculptSession *ss = data->ob->sculpt; + + SCULPT_undo_push_node(data->ob, data->nodes[n], SCULPT_UNDO_MASK); + bool update_node = false; + + const float threshold = data->mask_by_color_threshold; + const bool invert = data->mask_by_color_invert; + const bool preserve_mask = data->mask_by_color_preserve_mask; + const float *active_color = SCULPT_vertex_color_get(ss, data->mask_by_color_vertex); + + PBVHVertexIter vd; + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { + const float current_mask = *vd.mask; + const float new_mask = sculpt_mask_by_color_delta_get(active_color, vd.col, threshold, invert); + *vd.mask = sculpt_mask_by_color_final_mask_get(current_mask, new_mask, invert, preserve_mask); + + if (current_mask == *vd.mask) { + continue; + } + update_node = true; + if (vd.mvert) { + vd.mvert->flag |= ME_VERT_PBVH_UPDATE; + } + } + BKE_pbvh_vertex_iter_end; + if (update_node) { + BKE_pbvh_node_mark_redraw(data->nodes[n]); + } +} + +static void sculpt_mask_by_color_full_mesh(Object *object, + const int vertex, + const float threshold, + const bool invert, + const bool preserve_mask) +{ + SculptSession *ss = object->sculpt; + + int totnode; + PBVHNode **nodes; + BKE_pbvh_search_gather(ss->pbvh, NULL, NULL, &nodes, &totnode); + + SculptThreadedTaskData data = { + .ob = object, + .nodes = nodes, + .mask_by_color_vertex = vertex, + .mask_by_color_threshold = threshold, + .mask_by_color_invert = invert, + .mask_by_color_preserve_mask = preserve_mask, + }; + + TaskParallelSettings settings; + BKE_pbvh_parallel_range_settings(&settings, true, totnode); + BLI_task_parallel_range(0, totnode, &data, do_mask_by_color_task_cb, &settings); + + MEM_SAFE_FREE(nodes); +} + +static int sculpt_mask_by_color_invoke(bContext *C, wmOperator *op, const wmEvent *event) +{ + Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C); + Object *ob = CTX_data_active_object(C); + SculptSession *ss = ob->sculpt; + + BKE_sculpt_update_object_for_edit(depsgraph, ob, true, true, false); + + /* Color data is not available in Multires. */ + if (BKE_pbvh_type(ss->pbvh) != PBVH_FACES) { + return OPERATOR_CANCELLED; + } + + if (!ss->vcol) { + return OPERATOR_CANCELLED; + } + + SCULPT_vertex_random_access_ensure(ss); + + /* Tools that are not brushes do not have the brush gizmo to update the vertex as the mouse move, + * so it needs to be updated here. */ + SculptCursorGeometryInfo sgi; + float mouse[2]; + mouse[0] = event->mval[0]; + mouse[1] = event->mval[1]; + SCULPT_cursor_geometry_info_update(C, &sgi, mouse, false); + + SCULPT_undo_push_begin(ob, "Mask by color"); + + const int active_vertex = SCULPT_active_vertex_get(ss); + const float threshold = RNA_float_get(op->ptr, "threshold"); + const bool invert = RNA_boolean_get(op->ptr, "invert"); + const bool preserve_mask = RNA_boolean_get(op->ptr, "preserve_previous_mask"); + + if (RNA_boolean_get(op->ptr, "contiguous")) { + sculpt_mask_by_color_contiguous(ob, active_vertex, threshold, invert, preserve_mask); + } + else { + sculpt_mask_by_color_full_mesh(ob, active_vertex, threshold, invert, preserve_mask); + } + + BKE_pbvh_update_vertex_data(ss->pbvh, PBVH_UpdateMask); + SCULPT_undo_push_end(); + + SCULPT_flush_update_done(C, ob, SCULPT_UPDATE_MASK); + + return OPERATOR_FINISHED; +} + +static void SCULPT_OT_mask_by_color(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Mask by Color"; + ot->idname = "SCULPT_OT_mask_by_color"; + ot->description = "Creates a mask based on the sculpt vertex colors"; + + /* api callbacks */ + ot->invoke = sculpt_mask_by_color_invoke; + ot->poll = SCULPT_vertex_colors_poll; + + ot->flag = OPTYPE_REGISTER; + + ot->prop = RNA_def_boolean( + ot->srna, "contiguous", false, "Contiguous", "Mask only contiguous color areas"); + + ot->prop = RNA_def_boolean(ot->srna, "invert", false, "Invert", "Invert the generated mask"); + ot->prop = RNA_def_boolean( + ot->srna, + "preserve_previous_mask", + false, + "Preserve Previous Mask", + "Preserve the previous mask and add or subtract the new one generated by the colors"); + + RNA_def_float(ot->srna, + "threshold", + 0.35f, + 0.0f, + 1.0f, + "Threshold", + "How much changes in color affect the mask generation", + 0.0f, + 1.0f); +} + +void ED_operatortypes_sculpt(void) +{ + WM_operatortype_append(SCULPT_OT_brush_stroke); + WM_operatortype_append(SCULPT_OT_sculptmode_toggle); + WM_operatortype_append(SCULPT_OT_set_persistent_base); + WM_operatortype_append(SCULPT_OT_dynamic_topology_toggle); + WM_operatortype_append(SCULPT_OT_optimize); + WM_operatortype_append(SCULPT_OT_symmetrize); + WM_operatortype_append(SCULPT_OT_detail_flood_fill); + WM_operatortype_append(SCULPT_OT_sample_detail_size); + WM_operatortype_append(SCULPT_OT_set_detail_size); + WM_operatortype_append(SCULPT_OT_mesh_filter); + WM_operatortype_append(SCULPT_OT_mask_filter); + WM_operatortype_append(SCULPT_OT_dirty_mask); + WM_operatortype_append(SCULPT_OT_mask_expand); + WM_operatortype_append(SCULPT_OT_set_pivot_position); + WM_operatortype_append(SCULPT_OT_face_sets_create); + WM_operatortype_append(SCULPT_OT_face_sets_change_visibility); + WM_operatortype_append(SCULPT_OT_face_sets_randomize_colors); + WM_operatortype_append(SCULPT_OT_face_sets_init); + WM_operatortype_append(SCULPT_OT_cloth_filter); + WM_operatortype_append(SCULPT_OT_face_sets_edit); + WM_operatortype_append(SCULPT_OT_face_set_lasso_gesture); + WM_operatortype_append(SCULPT_OT_face_set_box_gesture); + WM_operatortype_append(SCULPT_OT_trim_box_gesture); + WM_operatortype_append(SCULPT_OT_trim_lasso_gesture); + WM_operatortype_append(SCULPT_OT_project_line_gesture); + + WM_operatortype_append(SCULPT_OT_sample_color); + WM_operatortype_append(SCULPT_OT_loop_to_vertex_colors); + WM_operatortype_append(SCULPT_OT_vertex_to_loop_colors); + WM_operatortype_append(SCULPT_OT_color_filter); + WM_operatortype_append(SCULPT_OT_mask_by_color); + WM_operatortype_append(SCULPT_OT_dyntopo_detail_size_edit); + WM_operatortype_append(SCULPT_OT_mask_init); + + WM_operatortype_append(SCULPT_OT_expand); +} diff --git a/source/blender/editors/space_file/asset_catalog_tree_view.cc b/source/blender/editors/space_file/asset_catalog_tree_view.cc index 86c4b78dea4..f6c449a0584 100644 --- a/source/blender/editors/space_file/asset_catalog_tree_view.cc +++ b/source/blender/editors/space_file/asset_catalog_tree_view.cc @@ -679,7 +679,7 @@ using namespace blender::ed::asset_browser; FileAssetCatalogFilterSettingsHandle *file_create_asset_catalog_filter_settings() { - AssetCatalogFilterSettings *filter_settings = OBJECT_GUARDED_NEW(AssetCatalogFilterSettings); + AssetCatalogFilterSettings *filter_settings = MEM_new<AssetCatalogFilterSettings>(__func__); return reinterpret_cast<FileAssetCatalogFilterSettingsHandle *>(filter_settings); } @@ -688,7 +688,8 @@ void file_delete_asset_catalog_filter_settings( { AssetCatalogFilterSettings **filter_settings = reinterpret_cast<AssetCatalogFilterSettings **>( filter_settings_handle); - OBJECT_GUARDED_SAFE_DELETE(*filter_settings, AssetCatalogFilterSettings); + MEM_delete(*filter_settings); + *filter_settings = nullptr; } bool file_set_asset_catalog_filter_settings( diff --git a/source/blender/editors/space_info/info_stats.cc b/source/blender/editors/space_info/info_stats.cc index bcf26743030..005ae0214cd 100644 --- a/source/blender/editors/space_info/info_stats.cc +++ b/source/blender/editors/space_info/info_stats.cc @@ -43,6 +43,7 @@ #include "BLT_translation.h" +#include "BKE_action.h" #include "BKE_armature.h" #include "BKE_blender_version.h" #include "BKE_context.h" @@ -351,7 +352,7 @@ static void stats_object_pose(const Object *ob, SceneStats *stats) LISTBASE_FOREACH (bPoseChannel *, pchan, &ob->pose->chanbase) { stats->totbone++; if (pchan->bone && (pchan->bone->flag & BONE_SELECTED)) { - if (pchan->bone->layer & arm->layer) { + if (BKE_pose_is_layer_visible(arm, pchan)) { stats->totbonesel++; } } diff --git a/source/blender/editors/space_node/drawnode.cc b/source/blender/editors/space_node/drawnode.cc index bbfd886ce56..90fd6e7d657 100644 --- a/source/blender/editors/space_node/drawnode.cc +++ b/source/blender/editors/space_node/drawnode.cc @@ -36,6 +36,7 @@ #include "BKE_image.h" #include "BKE_main.h" #include "BKE_node.h" +#include "BKE_node_tree_update.h" #include "BKE_scene.h" #include "BKE_tracking.h" @@ -1558,7 +1559,8 @@ static void node_property_update_default(Main *bmain, Scene *UNUSED(scene), Poin { bNodeTree *ntree = (bNodeTree *)ptr->owner_id; bNode *node = (bNode *)ptr->data; - ED_node_tag_update_nodetree(bmain, ntree, node); + BKE_ntree_update_tag_node_property(ntree, node); + ED_node_tree_propagate_change(nullptr, bmain, ntree); } static void node_socket_template_properties_update(bNodeType *ntype, bNodeSocketTemplate *stemp) diff --git a/source/blender/editors/space_node/link_drag_search.cc b/source/blender/editors/space_node/link_drag_search.cc index e1ba36e81c0..a2dd32b7cc6 100644 --- a/source/blender/editors/space_node/link_drag_search.cc +++ b/source/blender/editors/space_node/link_drag_search.cc @@ -29,6 +29,8 @@ #include "WM_api.h" +#include "ED_node.h" + #include "node_intern.hh" using blender::nodes::SocketLinkOperation; @@ -77,7 +79,7 @@ static void add_group_input_node_fn(nodes::LinkSearchOpParams ¶ms) bNode &group_input = params.add_node("NodeGroupInput"); /* This is necessary to create the new sockets in the other input nodes. */ - ntreeUpdateTree(CTX_data_main(¶ms.C), ¶ms.node_tree); + ED_node_tree_propagate_change(¶ms.C, CTX_data_main(¶ms.C), ¶ms.node_tree); /* Hide the new input in all other group input nodes, to avoid making them taller. */ LISTBASE_FOREACH (bNode *, node, ¶ms.node_tree.nodes) { @@ -203,9 +205,7 @@ static void link_drag_search_exec_fn(bContext *C, void *arg1, void *arg2) /* Ideally it would be possible to tag the node tree in some way so it updates only after the * translate operation is finished, but normally moving nodes around doesn't cause updates. */ - ntreeUpdateTree(&bmain, snode.edittree); - snode_notify(*C, snode); - snode_dag_update(*C, snode); + ED_node_tree_propagate_change(C, &bmain, snode.edittree); /* Start translation operator with the new node. */ wmOperatorType *ot = WM_operatortype_find("TRANSFORM_OT_translate", true); @@ -288,4 +288,4 @@ void invoke_node_link_drag_add_menu(bContext &C, UI_popup_block_invoke_ex(&C, create_search_popup_block, storage, nullptr, false); } -} // namespace blender::ed::space_node
\ No newline at end of file +} // namespace blender::ed::space_node diff --git a/source/blender/editors/space_node/node_add.cc b/source/blender/editors/space_node/node_add.cc index c6a5e8e68c0..0bb090f9a5f 100644 --- a/source/blender/editors/space_node/node_add.cc +++ b/source/blender/editors/space_node/node_add.cc @@ -36,6 +36,7 @@ #include "BKE_lib_id.h" #include "BKE_main.h" #include "BKE_node.h" +#include "BKE_node_tree_update.h" #include "BKE_report.h" #include "BKE_scene.h" #include "BKE_texture.h" @@ -83,15 +84,8 @@ bNode *node_add_node(const bContext &C, const char *idname, int type, float locx nodeSetSelected(node, true); - ntreeUpdateTree(&bmain, snode.edittree); ED_node_set_active(&bmain, &snode, snode.edittree, node, nullptr); - - snode_update(snode, node); - - if (snode.nodetree->type == NTREE_TEXTURE) { - ntreeTexCheckCyclics(snode.edittree); - } - + BKE_ntree_update_main_tree(&bmain, snode.edittree, nullptr); return node; } @@ -281,10 +275,7 @@ static int add_reroute_exec(bContext *C, wmOperator *op) BLI_freelistN(&input_links); /* always last */ - ntreeUpdateTree(CTX_data_main(C), &ntree); - snode_notify(*C, snode); - snode_dag_update(*C, snode); - + ED_node_tree_propagate_change(C, CTX_data_main(C), &ntree); return OPERATOR_FINISHED; } @@ -385,12 +376,7 @@ static int node_add_group_exec(bContext *C, wmOperator *op) id_us_plus(group_node->id); nodeSetActive(ntree, group_node); - ntreeUpdateTree(bmain, node_group); - ntreeUpdateTree(bmain, ntree); - - snode_notify(*C, *snode); - snode_dag_update(*C, *snode); - + ED_node_tree_propagate_change(C, bmain, nullptr); return OPERATOR_FINISHED; } @@ -479,12 +465,7 @@ static int node_add_object_exec(bContext *C, wmOperator *op) id_us_plus(&object->id); nodeSetActive(ntree, object_node); - ntreeUpdateTree(bmain, ntree); - - snode_notify(*C, *snode); - snode_dag_update(*C, *snode); - - ED_node_tag_update_nodetree(bmain, ntree, object_node); + ED_node_tree_propagate_change(C, bmain, ntree); DEG_relations_tag_update(bmain); return OPERATOR_FINISHED; @@ -587,14 +568,9 @@ static int node_add_texture_exec(bContext *C, wmOperator *op) id_us_plus(&texture->id); nodeSetActive(ntree, texture_node); - ntreeUpdateTree(bmain, ntree); - - snode_notify(*C, *snode); - snode_dag_update(*C, *snode); + ED_node_tree_propagate_change(C, bmain, ntree); DEG_relations_tag_update(bmain); - ED_node_tag_update_nodetree(bmain, ntree, texture_node); - return OPERATOR_FINISHED; } @@ -701,14 +677,9 @@ static int node_add_collection_exec(bContext *C, wmOperator *op) id_us_plus(&collection->id); nodeSetActive(ntree, collection_node); - ntreeUpdateTree(bmain, ntree); - - snode_notify(*C, snode); - snode_dag_update(*C, snode); + ED_node_tree_propagate_change(C, bmain, ntree); DEG_relations_tag_update(bmain); - ED_node_tag_update_nodetree(bmain, ntree, collection_node); - return OPERATOR_FINISHED; } @@ -834,8 +805,7 @@ static int node_add_file_exec(bContext *C, wmOperator *op) WM_event_add_notifier(C, NC_IMAGE | NA_EDITED, ima); } - snode_notify(*C, snode); - snode_dag_update(*C, snode); + ED_node_tree_propagate_change(C, bmain, snode.edittree); DEG_relations_tag_update(bmain); return OPERATOR_FINISHED; @@ -937,8 +907,7 @@ static int node_add_mask_exec(bContext *C, wmOperator *op) node->id = mask; id_us_plus(mask); - snode_notify(*C, snode); - snode_dag_update(*C, snode); + ED_node_tree_propagate_change(C, bmain, snode.edittree); DEG_relations_tag_update(bmain); return OPERATOR_FINISHED; diff --git a/source/blender/editors/space_node/node_draw.cc b/source/blender/editors/space_node/node_draw.cc index d68f16f6197..635ef41d859 100644 --- a/source/blender/editors/space_node/node_draw.cc +++ b/source/blender/editors/space_node/node_draw.cc @@ -176,35 +176,6 @@ void ED_node_tag_update_id(ID *id) } } -void ED_node_tag_update_nodetree(Main *bmain, bNodeTree *ntree, bNode *node) -{ - if (!ntree) { - return; - } - - bool do_tag_update = true; - if (node != nullptr) { - if (!node_connected_to_output(*bmain, *ntree, *node)) { - do_tag_update = false; - } - } - - /* Look through all datablocks to support groups. */ - if (do_tag_update) { - FOREACH_NODETREE_BEGIN (bmain, tntree, id) { - /* Check if nodetree uses the group. */ - if (ntreeHasTree(tntree, ntree)) { - ED_node_tag_update_id(id); - } - } - FOREACH_NODETREE_END; - } - - if (ntree->type == NTREE_TEXTURE) { - ntreeTexCheckCyclics(ntree); - } -} - static bool compare_nodes(const bNode *a, const bNode *b) { /* These tell if either the node or any of the parent nodes is selected. @@ -365,6 +336,10 @@ static void node_update_basis(const bContext &C, bNodeTree &ntree, bNode &node, PointerRNA nodeptr; RNA_pointer_create(&ntree.id, &RNA_Node, &node, &nodeptr); + const bool node_options = node.typeinfo->draw_buttons && (node.flag & NODE_OPTIONS); + const bool inputs_first = node.inputs.first && + !(node.outputs.first || (node.flag & NODE_PREVIEW) || node_options); + /* Get "global" coordinates. */ float2 loc = node_to_view(node, float2(0)); /* Round the node origin because text contents are always pixel-aligned. */ @@ -377,7 +352,7 @@ static void node_update_basis(const bContext &C, bNodeTree &ntree, bNode &node, dy -= NODE_DY; /* Add a little bit of padding above the top socket. */ - if (node.outputs.first || node.inputs.first) { + if (node.outputs.first || inputs_first) { dy -= NODE_DYS / 2; } @@ -478,7 +453,7 @@ static void node_update_basis(const bContext &C, bNodeTree &ntree, bNode &node, } /* Buttons rect? */ - if (node.typeinfo->draw_buttons && (node.flag & NODE_OPTIONS)) { + if (node_options) { dy -= NODE_DYS / 2; uiLayout *layout = UI_block_layout(&block, @@ -2414,9 +2389,11 @@ static void node_update_nodetree(const bContext &C, bNode &node = *nodes[i]; uiBlock &block = *blocks[i]; if (node.type == NODE_FRAME) { - frame_node_prepare_for_draw(node, nodes); + /* Frame sizes are calculated after all other nodes have calculating their #totr. */ + continue; } - else if (node.type == NODE_REROUTE) { + + if (node.type == NODE_REROUTE) { reroute_node_prepare_for_draw(node); } else { @@ -2428,6 +2405,13 @@ static void node_update_nodetree(const bContext &C, } } } + + /* Now calculate the size of frame nodes, which can depend on the size of other nodes. */ + for (const int i : nodes.index_range()) { + if (nodes[i]->type == NODE_FRAME) { + frame_node_prepare_for_draw(*nodes[i], nodes); + } + } } static void frame_node_draw_label(const bNodeTree &ntree, diff --git a/source/blender/editors/space_node/node_edit.cc b/source/blender/editors/space_node/node_edit.cc index fb90e2bfe50..ba2327c742b 100644 --- a/source/blender/editors/space_node/node_edit.cc +++ b/source/blender/editors/space_node/node_edit.cc @@ -38,6 +38,7 @@ #include "BKE_main.h" #include "BKE_material.h" #include "BKE_node.h" +#include "BKE_node_tree_update.h" #include "BKE_report.h" #include "BKE_scene.h" #include "BKE_workspace.h" @@ -383,31 +384,11 @@ bool composite_node_editable(bContext *C) return false; } -void snode_dag_update(bContext &C, SpaceNode &snode) +static void send_notifiers_after_tree_change(ID *id, bNodeTree *ntree) { - Main *bmain = CTX_data_main(&C); + WM_main_add_notifier(NC_NODE | NA_EDITED, nullptr); - /* for groups, update all ID's using this */ - if ((snode.edittree->id.flag & LIB_EMBEDDED_DATA) == 0) { - FOREACH_NODETREE_BEGIN (bmain, tntree, id) { - if (ntreeHasTree(tntree, snode.edittree)) { - DEG_id_tag_update(id, 0); - } - } - FOREACH_NODETREE_END; - } - - DEG_id_tag_update(snode.id, 0); - DEG_id_tag_update(&snode.nodetree->id, 0); -} - -void snode_notify(bContext &C, SpaceNode &snode) -{ - ID *id = snode.id; - - WM_event_add_notifier(&C, NC_NODE | NA_EDITED, nullptr); - - if (ED_node_is_shader(&snode)) { + if (ntree->type == NTREE_SHADER) { if (GS(id->name) == ID_MA) { WM_main_add_notifier(NC_MATERIAL | ND_SHADING, id); } @@ -418,17 +399,37 @@ void snode_notify(bContext &C, SpaceNode &snode) WM_main_add_notifier(NC_WORLD | ND_WORLD, id); } } - else if (ED_node_is_compositor(&snode)) { - WM_event_add_notifier(&C, NC_SCENE | ND_NODES, id); + else if (ntree->type == NTREE_COMPOSIT) { + WM_main_add_notifier(NC_SCENE | ND_NODES, id); } - else if (ED_node_is_texture(&snode)) { - WM_event_add_notifier(&C, NC_TEXTURE | ND_NODES, id); + else if (ntree->type == NTREE_TEXTURE) { + WM_main_add_notifier(NC_TEXTURE | ND_NODES, id); } - else if (ED_node_is_geometry(&snode)) { + else if (ntree->type == NTREE_GEOMETRY) { WM_main_add_notifier(NC_OBJECT | ND_MODIFIER, id); } } +void ED_node_tree_propagate_change(const bContext *C, Main *bmain, bNodeTree *root_ntree) +{ + if (C != nullptr) { + SpaceNode *snode = CTX_wm_space_node(C); + if (snode != nullptr && root_ntree != nullptr) { + send_notifiers_after_tree_change(snode->id, root_ntree); + } + } + + NodeTreeUpdateExtraParams params = {nullptr}; + params.tree_changed_fn = [](ID *id, bNodeTree *ntree, void *UNUSED(user_data)) { + send_notifiers_after_tree_change(id, ntree); + }; + params.tree_output_changed_fn = [](ID *UNUSED(id), bNodeTree *ntree, void *UNUSED(user_data)) { + DEG_id_tag_update(&ntree->id, ID_RECALC_NTREE_OUTPUT); + }; + + BKE_ntree_update_main_tree(bmain, root_ntree, ¶ms); +} + void ED_node_set_tree_type(SpaceNode *snode, bNodeTreeType *typeinfo) { if (typeinfo) { @@ -477,7 +478,7 @@ void ED_node_shader_default(const bContext *C, ID *id) } ma->nodetree = ntreeCopyTree(bmain, ma_default->nodetree); - ntreeUpdateTree(bmain, ma->nodetree); + BKE_ntree_update_main_tree(bmain, ma->nodetree, nullptr); } else if (ELEM(GS(id->name), ID_WO, ID_LA)) { /* Emission */ @@ -517,7 +518,7 @@ void ED_node_shader_default(const bContext *C, ID *id) output->locx = 300.0f; output->locy = 300.0f; nodeSetActive(ntree, output); - ntreeUpdateTree(bmain, ntree); + BKE_ntree_update_main_tree(bmain, ntree, nullptr); } else { printf("ED_node_shader_default called on wrong ID type.\n"); @@ -555,7 +556,7 @@ void ED_node_composit_default(const bContext *C, struct Scene *sce) bNodeSocket *tosock = (bNodeSocket *)out->inputs.first; nodeAddLink(sce->nodetree, in, fromsock, out, tosock); - ntreeUpdateTree(CTX_data_main(C), sce->nodetree); + BKE_ntree_update_main_tree(CTX_data_main(C), sce->nodetree, nullptr); } void ED_node_texture_default(const bContext *C, Tex *tex) @@ -583,7 +584,7 @@ void ED_node_texture_default(const bContext *C, Tex *tex) bNodeSocket *tosock = (bNodeSocket *)out->inputs.first; nodeAddLink(tex->nodetree, in, fromsock, out, tosock); - ntreeUpdateTree(CTX_data_main(C), tex->nodetree); + BKE_ntree_update_main_tree(CTX_data_main(C), tex->nodetree, nullptr); } /** @@ -628,28 +629,6 @@ void snode_set_context(const bContext &C) } } -void snode_update(SpaceNode &snode, bNode *node) -{ - /* XXX this only updates nodes in the current node space tree path. - * The function supposedly should update any potential group node linking to changed tree, - * this really requires a working depsgraph ... - */ - - /* update all edited group nodes */ - bNodeTreePath *path = (bNodeTreePath *)snode.treepath.last; - if (path) { - bNodeTree *ngroup = path->nodetree; - for (path = path->prev; path; path = path->prev) { - nodeUpdateID(path->nodetree, (ID *)ngroup); - ngroup = path->nodetree; - } - } - - if (node) { - nodeUpdate(snode.edittree, node); - } -} - void ED_node_set_active( Main *bmain, SpaceNode *snode, bNodeTree *ntree, bNode *node, bool *r_active_texture_changed) { @@ -697,14 +676,10 @@ void ED_node_set_active( } node->flag |= NODE_DO_OUTPUT; - if (was_output == 0) { - ED_node_tag_update_nodetree(bmain, ntree, node); - } - } - else if (do_update) { - ED_node_tag_update_nodetree(bmain, ntree, node); } + ED_node_tree_propagate_change(nullptr, bmain, ntree); + if ((node->flag & NODE_ACTIVE_TEXTURE) && !was_active_texture) { /* If active texture changed, free glsl materials. */ LISTBASE_FOREACH (Material *, ma, &bmain->materials) { @@ -750,7 +725,7 @@ void ED_node_set_active( if (r_active_texture_changed) { *r_active_texture_changed = true; } - ED_node_tag_update_nodetree(bmain, ntree, node); + ED_node_tree_propagate_change(nullptr, bmain, ntree); WM_main_add_notifier(NC_IMAGE, nullptr); } @@ -767,7 +742,7 @@ void ED_node_set_active( node->flag |= NODE_DO_OUTPUT; if (was_output == 0) { - ED_node_tag_update_nodetree(bmain, ntree, node); + ED_node_tree_propagate_change(nullptr, bmain, ntree); } /* Adding a node doesn't link this yet. */ @@ -782,11 +757,11 @@ void ED_node_set_active( } node->flag |= NODE_DO_OUTPUT; - ED_node_tag_update_nodetree(bmain, ntree, node); + ED_node_tree_propagate_change(nullptr, bmain, ntree); } } else if (do_update) { - ED_node_tag_update_nodetree(bmain, ntree, node); + ED_node_tree_propagate_change(nullptr, bmain, ntree); } } else if (ntree->type == NTREE_TEXTURE) { @@ -1302,7 +1277,6 @@ static int node_duplicate_exec(bContext *C, wmOperator *op) SpaceNode *snode = CTX_wm_space_node(C); bNodeTree *ntree = snode->edittree; const bool keep_inputs = RNA_boolean_get(op->ptr, "keep_inputs"); - bool do_tag_update = false; ED_preview_kill_jobs(CTX_wm_manager(C), bmain); @@ -1310,9 +1284,6 @@ static int node_duplicate_exec(bContext *C, wmOperator *op) LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { if (node->flag & SELECT) { BKE_node_copy_store_new_pointers(ntree, node, LIB_ID_COPY_DEFAULT); - - /* To ensure redraws or re-renders happen. */ - ED_node_tag_update_id(snode->id); } /* make sure we don't copy new nodes again! */ @@ -1378,8 +1349,6 @@ static int node_duplicate_exec(bContext *C, wmOperator *op) nodeSetSelected(node, false); node->flag &= ~(NODE_ACTIVE | NODE_ACTIVE_TEXTURE); nodeSetSelected(newnode, true); - - do_tag_update |= (do_tag_update || node_connected_to_output(*bmain, *ntree, *newnode)); } /* make sure we don't copy new nodes again! */ @@ -1388,13 +1357,7 @@ static int node_duplicate_exec(bContext *C, wmOperator *op) } } - ntreeUpdateTree(CTX_data_main(C), snode->edittree); - - snode_notify(*C, *snode); - if (do_tag_update) { - snode_dag_update(*C, *snode); - } - + ED_node_tree_propagate_change(C, bmain, snode->edittree); return OPERATOR_FINISHED; } @@ -1485,8 +1448,7 @@ static int node_read_viewlayers_exec(bContext *C, wmOperator *UNUSED(op)) } } - snode_notify(*C, *snode); - snode_dag_update(*C, *snode); + ED_node_tree_propagate_change(C, bmain, snode->edittree); return OPERATOR_FINISHED; } @@ -1653,7 +1615,7 @@ static int node_preview_toggle_exec(bContext *C, wmOperator *UNUSED(op)) node_flag_toggle_exec(snode, NODE_PREVIEW); - snode_notify(*C, *snode); + ED_node_tree_propagate_change(C, CTX_data_main(C), snode->edittree); return OPERATOR_FINISHED; } @@ -1732,7 +1694,7 @@ static int node_socket_toggle_exec(bContext *C, wmOperator *UNUSED(op)) } } - ntreeUpdateTree(CTX_data_main(C), snode->edittree); + ED_node_tree_propagate_change(C, CTX_data_main(C), snode->edittree); WM_event_add_notifier(C, NC_NODE | ND_DISPLAY, nullptr); @@ -1760,23 +1722,16 @@ static int node_mute_exec(bContext *C, wmOperator *UNUSED(op)) { Main *bmain = CTX_data_main(C); SpaceNode *snode = CTX_wm_space_node(C); - bool do_tag_update = false; ED_preview_kill_jobs(CTX_wm_manager(C), bmain); LISTBASE_FOREACH (bNode *, node, &snode->edittree->nodes) { if ((node->flag & SELECT) && !node->typeinfo->no_muting) { node->flag ^= NODE_MUTED; - snode_update(*snode, node); - do_tag_update |= (do_tag_update || - node_connected_to_output(*bmain, *snode->edittree, *node)); } } - snode_notify(*C, *snode); - if (do_tag_update) { - snode_dag_update(*C, *snode); - } + ED_node_tree_propagate_change(C, bmain, snode->edittree); return OPERATOR_FINISHED; } @@ -1802,24 +1757,16 @@ static int node_delete_exec(bContext *C, wmOperator *UNUSED(op)) { Main *bmain = CTX_data_main(C); SpaceNode *snode = CTX_wm_space_node(C); - bool do_tag_update = false; ED_preview_kill_jobs(CTX_wm_manager(C), bmain); LISTBASE_FOREACH_MUTABLE (bNode *, node, &snode->edittree->nodes) { if (node->flag & SELECT) { - do_tag_update |= (do_tag_update || - node_connected_to_output(*bmain, *snode->edittree, *node)); nodeRemoveNode(bmain, snode->edittree, node, true); } } - ntreeUpdateTree(CTX_data_main(C), snode->edittree); - - snode_notify(*C, *snode); - if (do_tag_update) { - snode_dag_update(*C, *snode); - } + ED_node_tree_propagate_change(C, bmain, snode->edittree); return OPERATOR_FINISHED; } @@ -1863,10 +1810,7 @@ static int node_switch_view_exec(bContext *C, wmOperator *UNUSED(op)) } } - ntreeUpdateTree(CTX_data_main(C), snode->edittree); - - snode_notify(*C, *snode); - snode_dag_update(*C, *snode); + ED_node_tree_propagate_change(C, CTX_data_main(C), snode->edittree); return OPERATOR_FINISHED; } @@ -1901,10 +1845,7 @@ static int node_delete_reconnect_exec(bContext *C, wmOperator *UNUSED(op)) } } - ntreeUpdateTree(CTX_data_main(C), snode->edittree); - - snode_notify(*C, *snode); - snode_dag_update(*C, *snode); + ED_node_tree_propagate_change(C, bmain, snode->edittree); return OPERATOR_FINISHED; } @@ -1951,7 +1892,7 @@ static int node_output_file_add_socket_exec(bContext *C, wmOperator *op) RNA_string_get(op->ptr, "file_path", file_path); ntreeCompositOutputFileAddSocket(ntree, node, file_path, &scene->r.im_format); - snode_notify(*C, *snode); + ED_node_tree_propagate_change(C, CTX_data_main(C), snode->edittree); return OPERATOR_FINISHED; } @@ -2000,7 +1941,7 @@ static int node_output_file_remove_active_socket_exec(bContext *C, wmOperator *U return OPERATOR_CANCELLED; } - snode_notify(*C, *snode); + ED_node_tree_propagate_change(C, CTX_data_main(C), ntree); return OPERATOR_FINISHED; } @@ -2067,7 +2008,7 @@ static int node_output_file_move_active_socket_exec(bContext *C, wmOperator *op) nimf->active_input++; } - snode_notify(*C, *snode); + ED_node_tree_propagate_change(C, CTX_data_main(C), snode->edittree); return OPERATOR_FINISHED; } @@ -2313,10 +2254,7 @@ static int node_clipboard_paste_exec(bContext *C, wmOperator *op) } Main *bmain = CTX_data_main(C); - ntreeUpdateTree(bmain, snode->edittree); - - snode_notify(*C, *snode); - snode_dag_update(*C, *snode); + ED_node_tree_propagate_change(C, bmain, snode->edittree); /* Pasting nodes can create arbitrary new relations, because nodes can reference IDs. */ DEG_relations_tag_update(bmain); @@ -2384,10 +2322,7 @@ static int ntree_socket_add_exec(bContext *C, wmOperator *op) /* make the new socket active */ sock->flag |= SELECT; - ntreeUpdateTree(CTX_data_main(C), ntree); - - snode_notify(*C, *snode); - snode_dag_update(*C, *snode); + ED_node_tree_propagate_change(C, CTX_data_main(C), snode->edittree); WM_event_add_notifier(C, NC_NODE | ND_DISPLAY, nullptr); @@ -2434,10 +2369,7 @@ static int ntree_socket_remove_exec(bContext *C, wmOperator *op) active_sock->flag |= SELECT; } - ntreeUpdateTree(CTX_data_main(C), ntree); - - snode_notify(*C, *snode); - snode_dag_update(*C, *snode); + ED_node_tree_propagate_change(C, CTX_data_main(C), ntree); WM_event_add_notifier(C, NC_NODE | ND_DISPLAY, nullptr); @@ -2488,7 +2420,7 @@ static int ntree_socket_change_type_exec(bContext *C, wmOperator *op) /* Need the extra update here because the loop above does not check for valid links in the node * group we're currently editing. */ - ntree->update |= NTREE_UPDATE_GROUP | NTREE_UPDATE_LINKS; + BKE_ntree_update_tag_interface(ntree); /* Deactivate sockets. */ LISTBASE_FOREACH (bNodeSocket *, socket_iter, sockets) { @@ -2497,10 +2429,7 @@ static int ntree_socket_change_type_exec(bContext *C, wmOperator *op) /* Make the new socket active. */ iosock->flag |= SELECT; - ntreeUpdateTree(main, ntree); - - snode_notify(*C, *snode); - snode_dag_update(*C, *snode); + ED_node_tree_propagate_change(C, main, ntree); WM_event_add_notifier(C, NC_NODE | ND_DISPLAY, nullptr); @@ -2611,11 +2540,8 @@ static int ntree_socket_move_exec(bContext *C, wmOperator *op) } } - ntree->update |= NTREE_UPDATE_GROUP; - ntreeUpdateTree(CTX_data_main(C), ntree); - - snode_notify(*C, *snode); - snode_dag_update(*C, *snode); + BKE_ntree_update_tag_interface(ntree); + ED_node_tree_propagate_change(C, CTX_data_main(C), ntree); WM_event_add_notifier(C, NC_NODE | ND_DISPLAY, nullptr); @@ -2846,7 +2772,7 @@ static int viewer_border_exec(bContext *C, wmOperator *op) btree->flag |= NTREE_VIEWER_BORDER; } - snode_notify(*C, *snode); + ED_node_tree_propagate_change(C, bmain, btree); WM_event_add_notifier(C, NC_NODE | ND_DISPLAY, nullptr); } else { @@ -2886,7 +2812,7 @@ static int clear_viewer_border_exec(bContext *C, wmOperator *UNUSED(op)) bNodeTree *btree = snode->nodetree; btree->flag &= ~NTREE_VIEWER_BORDER; - snode_notify(*C, *snode); + ED_node_tree_propagate_change(C, CTX_data_main(C), btree); WM_event_add_notifier(C, NC_NODE | ND_DISPLAY, nullptr); return OPERATOR_FINISHED; @@ -2931,7 +2857,7 @@ static int node_cryptomatte_add_socket_exec(bContext *C, wmOperator *UNUSED(op)) ntreeCompositCryptomatteAddSocket(ntree, node); - snode_notify(*C, *snode); + ED_node_tree_propagate_change(C, CTX_data_main(C), ntree); return OPERATOR_FINISHED; } @@ -2977,7 +2903,7 @@ static int node_cryptomatte_remove_socket_exec(bContext *C, wmOperator *UNUSED(o return OPERATOR_CANCELLED; } - snode_notify(*C, *snode); + ED_node_tree_propagate_change(C, CTX_data_main(C), ntree); return OPERATOR_FINISHED; } diff --git a/source/blender/editors/space_node/node_geometry_attribute_search.cc b/source/blender/editors/space_node/node_geometry_attribute_search.cc index 4890c3e39cf..6f96e08d749 100644 --- a/source/blender/editors/space_node/node_geometry_attribute_search.cc +++ b/source/blender/editors/space_node/node_geometry_attribute_search.cc @@ -125,8 +125,8 @@ void node_geometry_add_attribute_search_button(const bContext &UNUSED(C), 0.0f, ""); - AttributeSearchData *data = OBJECT_GUARDED_NEW( - AttributeSearchData, {&node_tree, &node, (bNodeSocket *)socket_ptr.data}); + AttributeSearchData *data = MEM_new<AttributeSearchData>( + __func__, AttributeSearchData{&node_tree, &node, (bNodeSocket *)socket_ptr.data}); UI_but_func_search_set_results_are_suggestions(but, true); UI_but_func_search_set_sep_string(but, UI_MENU_ARROW_SEP); diff --git a/source/blender/editors/space_node/node_group.cc b/source/blender/editors/space_node/node_group.cc index 704ffe1e478..290ed172a48 100644 --- a/source/blender/editors/space_node/node_group.cc +++ b/source/blender/editors/space_node/node_group.cc @@ -40,6 +40,7 @@ #include "BKE_context.h" #include "BKE_lib_id.h" #include "BKE_main.h" +#include "BKE_node_tree_update.h" #include "BKE_report.h" #include "DEG_depsgraph_build.h" @@ -258,6 +259,7 @@ static int node_group_ungroup(Main *bmain, bNodeTree *ntree, bNode *gnode) /* migrate node */ BLI_remlink(&wgroup->nodes, node); BLI_addtail(&ntree->nodes, node); + BKE_ntree_update_tag_node_new(ntree, node); /* ensure unique node name in the node tree */ nodeUniqueName(ntree, node); @@ -284,6 +286,7 @@ static int node_group_ungroup(Main *bmain, bNodeTree *ntree, bNode *gnode) LISTBASE_FOREACH_MUTABLE (bNodeLink *, link, &wgroup->links) { BLI_remlink(&wgroup->links, link); BLI_addtail(&ntree->links, link); + BKE_ntree_update_tag_link_added(ntree, link); } bNodeLink *glinks_last = (bNodeLink *)ntree->links.last; @@ -393,8 +396,6 @@ static int node_group_ungroup(Main *bmain, bNodeTree *ntree, bNode *gnode) /* delete the group instance and dereference group tree */ nodeRemoveNode(bmain, ntree, gnode, true); - ntree->update |= NTREE_UPDATE_NODES | NTREE_UPDATE_LINKS; - return 1; } @@ -412,16 +413,13 @@ static int node_group_ungroup_exec(bContext *C, wmOperator *op) } if (gnode->id && node_group_ungroup(bmain, snode->edittree, gnode)) { - ntreeUpdateTree(bmain, snode->nodetree); + ED_node_tree_propagate_change(C, CTX_data_main(C), nullptr); } else { BKE_report(op->reports, RPT_WARNING, "Cannot ungroup"); return OPERATOR_CANCELLED; } - snode_notify(*C, *snode); - snode_dag_update(*C, *snode); - return OPERATOR_FINISHED; } @@ -558,9 +556,9 @@ static bool node_group_separate_selected( } } - ntree.update |= NTREE_UPDATE_NODES | NTREE_UPDATE_LINKS; + BKE_ntree_update_tag_all(&ntree); if (!make_copy) { - ngroup.update |= NTREE_UPDATE_NODES | NTREE_UPDATE_LINKS; + BKE_ntree_update_tag_all(&ngroup); } return true; @@ -614,10 +612,7 @@ static int node_group_separate_exec(bContext *C, wmOperator *op) /* switch to parent tree */ ED_node_tree_pop(snode); - ntreeUpdateTree(CTX_data_main(C), snode->nodetree); - - snode_notify(*C, *snode); - snode_dag_update(*C, *snode); + ED_node_tree_propagate_change(C, CTX_data_main(C), nullptr); return OPERATOR_FINISHED; } @@ -812,6 +807,8 @@ static void node_group_make_insert_selected(const bContext &C, bNodeTree &ntree, /* change node-collection membership */ BLI_remlink(&ntree.nodes, node); BLI_addtail(&ngroup->nodes, node); + BKE_ntree_update_tag_node_removed(&ntree); + BKE_ntree_update_tag_node_new(ngroup, node); /* ensure unique node name in the ngroup */ nodeUniqueName(ngroup, node); @@ -983,11 +980,6 @@ static void node_group_make_insert_selected(const bContext &C, bNodeTree &ntree, } } } - - /* update of the group tree */ - ngroup->update |= NTREE_UPDATE | NTREE_UPDATE_LINKS; - /* update of the tree containing the group instance node */ - ntree.update |= NTREE_UPDATE_NODES | NTREE_UPDATE_LINKS; } static bNode *node_group_make_from_selected(const bContext &C, @@ -1016,9 +1008,6 @@ static bNode *node_group_make_from_selected(const bContext &C, node_group_make_insert_selected(C, ntree, gnode); - /* update of the tree containing the group instance node */ - ntree.update |= NTREE_UPDATE_NODES; - return gnode; } @@ -1047,14 +1036,10 @@ static int node_group_make_exec(bContext *C, wmOperator *op) LISTBASE_FOREACH (bNode *, node, &ngroup->nodes) { sort_multi_input_socket_links(snode, *node, nullptr, nullptr); } - ntreeUpdateTree(bmain, ngroup); } } - ntreeUpdateTree(bmain, &ntree); - - snode_notify(*C, snode); - snode_dag_update(*C, snode); + ED_node_tree_propagate_change(C, bmain, nullptr); /* We broke relations in node tree, need to rebuild them in the graphs. */ DEG_relations_tag_update(bmain); @@ -1107,12 +1092,7 @@ static int node_group_insert_exec(bContext *C, wmOperator *op) nodeSetActive(ntree, gnode); ED_node_tree_push(snode, ngroup, gnode); - ntreeUpdateTree(bmain, ngroup); - - ntreeUpdateTree(bmain, ntree); - - snode_notify(*C, *snode); - snode_dag_update(*C, *snode); + ED_node_tree_propagate_change(C, bmain, nullptr); return OPERATOR_FINISHED; } diff --git a/source/blender/editors/space_node/node_intern.hh b/source/blender/editors/space_node/node_intern.hh index 2e55bb0cb28..52dde965114 100644 --- a/source/blender/editors/space_node/node_intern.hh +++ b/source/blender/editors/space_node/node_intern.hh @@ -235,7 +235,6 @@ void sort_multi_input_socket_links(SpaceNode &snode, bNode &node, bNodeLink *drag_link, const blender::float2 *cursor); -bool node_connected_to_output(Main &bmain, bNodeTree &ntree, bNode &node); void NODE_OT_link(wmOperatorType *ot); void NODE_OT_link_make(wmOperatorType *ot); @@ -252,12 +251,8 @@ void NODE_OT_link_viewer(wmOperatorType *ot); void NODE_OT_insert_offset(wmOperatorType *ot); -void snode_notify(bContext &C, SpaceNode &snode); -void snode_dag_update(bContext &C, SpaceNode &snode); void snode_set_context(const bContext &C); -void snode_update(SpaceNode &snode, bNode *node); -/** Operator poll callback. */ bool composite_node_active(bContext *C); /** Operator poll callback. */ bool composite_node_editable(bContext *C); diff --git a/source/blender/editors/space_node/node_relationships.cc b/source/blender/editors/space_node/node_relationships.cc index 90b53258d5e..c441cf14683 100644 --- a/source/blender/editors/space_node/node_relationships.cc +++ b/source/blender/editors/space_node/node_relationships.cc @@ -34,6 +34,7 @@ #include "BKE_lib_id.h" #include "BKE_main.h" #include "BKE_node.h" +#include "BKE_node_tree_update.h" #include "BKE_screen.h" #include "ED_node.h" /* own include */ @@ -73,121 +74,6 @@ using blender::StringRefNull; using blender::Vector; /* -------------------------------------------------------------------- */ -/** \name Relations Helpers - * \{ */ - -static bool ntree_has_drivers(bNodeTree &ntree) -{ - const AnimData *adt = BKE_animdata_from_id(&ntree.id); - if (adt == nullptr) { - return false; - } - return !BLI_listbase_is_empty(&adt->drivers); -} - -static bool ntree_check_nodes_connected_dfs(bNodeTree &ntree, bNode &from, bNode &to) -{ - if (from.flag & NODE_TEST) { - return false; - } - from.flag |= NODE_TEST; - LISTBASE_FOREACH (bNodeLink *, link, &ntree.links) { - if (link->fromnode == &from) { - if (link->tonode == &to) { - return true; - } - - if (ntree_check_nodes_connected_dfs(ntree, *link->tonode, to)) { - return true; - } - } - } - return false; -} - -static bool ntree_check_nodes_connected(bNodeTree &ntree, bNode &from, bNode &to) -{ - if (&from == &to) { - return true; - } - ntreeNodeFlagSet(&ntree, NODE_TEST, false); - return ntree_check_nodes_connected_dfs(ntree, from, to); -} - -static bool node_group_has_output_dfs(bNode &node) -{ - bNodeTree *ntree = (bNodeTree *)node.id; - if (ntree->id.tag & LIB_TAG_DOIT) { - return false; - } - ntree->id.tag |= LIB_TAG_DOIT; - LISTBASE_FOREACH (bNode *, current_node, &ntree->nodes) { - if (current_node->type == NODE_GROUP) { - if (current_node->id && node_group_has_output_dfs(*current_node)) { - return true; - } - } - if (current_node->flag & NODE_DO_OUTPUT && current_node->type != NODE_GROUP_OUTPUT) { - return true; - } - } - return false; -} - -static bool node_group_has_output(Main &bmain, bNode &node) -{ - BLI_assert(ELEM(node.type, NODE_GROUP, NODE_CUSTOM_GROUP)); - bNodeTree *ntree = (bNodeTree *)node.id; - if (ntree == nullptr) { - return false; - } - BKE_main_id_tag_listbase(&bmain.nodetrees, LIB_TAG_DOIT, false); - return node_group_has_output_dfs(node); -} - -bool node_connected_to_output(Main &bmain, bNodeTree &ntree, bNode &node) -{ - /* Special case for drivers: if node tree has any drivers we assume it is - * always to be tagged for update when node changes. Otherwise we will be - * doomed to do some deep and nasty deep search of indirect dependencies, - * which will be too complicated without real benefit. - */ - if (ntree_has_drivers(ntree)) { - return true; - } - LISTBASE_FOREACH (bNode *, current_node, &ntree.nodes) { - /* Special case for group nodes -- if modified node connected to a group - * with active output inside we consider refresh is needed. - * - * We could make check more grained here by taking which socket the node - * is connected to and so eventually. - */ - if (ELEM(current_node->type, NODE_GROUP, NODE_CUSTOM_GROUP)) { - if (current_node->id != nullptr && ntree_has_drivers((bNodeTree &)current_node->id)) { - return true; - } - if (ntree_check_nodes_connected(ntree, node, *current_node) && - node_group_has_output(bmain, *current_node)) { - return true; - } - } - if (current_node->flag & NODE_DO_OUTPUT) { - if (ntree_check_nodes_connected(ntree, node, *current_node)) { - return true; - } - } - if (current_node->type == GEO_NODE_VIEWER) { - if (ntree_check_nodes_connected(ntree, node, *current_node)) { - return true; - } - } - } - return false; -} - -/** \} */ - -/* -------------------------------------------------------------------- */ /** \name Add Node * \{ */ @@ -214,7 +100,7 @@ static void clear_picking_highlight(ListBase *links) } } -static bNodeLink *create_drag_link(Main &bmain, SpaceNode &snode, bNode &node, bNodeSocket &sock) +static bNodeLink *create_drag_link(bNode &node, bNodeSocket &sock) { bNodeLink *oplink = (bNodeLink *)MEM_callocN(sizeof(bNodeLink), __func__); if (sock.in_out == SOCK_OUT) { @@ -226,27 +112,17 @@ static bNodeLink *create_drag_link(Main &bmain, SpaceNode &snode, bNode &node, b oplink->tosock = &sock; } oplink->flag |= NODE_LINK_VALID; - oplink->flag &= ~NODE_LINK_TEST; - if (node_connected_to_output(bmain, *snode.edittree, node)) { - oplink->flag |= NODE_LINK_TEST; - } oplink->flag |= NODE_LINK_DRAGGED; return oplink; } -static void pick_link(const bContext &C, - wmOperator &op, - bNodeLinkDrag &nldrag, - SpaceNode &snode, - bNode *node, - bNodeLink &link_to_pick) +static void pick_link( + wmOperator &op, bNodeLinkDrag &nldrag, SpaceNode &snode, bNode *node, bNodeLink &link_to_pick) { clear_picking_highlight(&snode.edittree->links); RNA_boolean_set(op.ptr, "has_link_picked", true); - Main *bmain = CTX_data_main(&C); - bNodeLink *link = create_drag_link( - *bmain, snode, *link_to_pick.fromnode, *link_to_pick.fromsock); + bNodeLink *link = create_drag_link(*link_to_pick.fromnode, *link_to_pick.fromsock); nldrag.links.append(link); nodeRemLink(snode.edittree, &link_to_pick); @@ -258,7 +134,7 @@ static void pick_link(const bContext &C, /* Send changed event to original link->tonode. */ if (node) { - snode_update(snode, node); + BKE_ntree_update_tag_node_property(snode.edittree, node); } } @@ -324,7 +200,7 @@ static void pick_input_link_by_link_intersect(const bContext &C, ED_area_tag_redraw(CTX_wm_area(&C)); if (!node_find_indicated_socket(*snode, &node, &socket, cursor, SOCK_IN)) { - pick_link(C, op, nldrag, *snode, node, *link_to_pick); + pick_link(op, nldrag, *snode, node, *link_to_pick); } } } @@ -566,7 +442,7 @@ static void snode_autoconnect(Main &bmain, } if (numlinks > 0) { - ntreeUpdateTree(&bmain, ntree); + BKE_ntree_update_main_tree(&bmain, ntree, nullptr); } } @@ -640,7 +516,7 @@ static bNodeSocket *node_link_viewer_get_socket(bNodeTree &ntree, (eNodeSocketDatatype)src_socket.type); BLI_assert(data_type != CD_AUTO_FROM_NAME); storage->data_type = data_type; - nodeUpdate(&ntree, &viewer_node); + viewer_node.typeinfo->updatefunc(&ntree, &viewer_node); return viewer_socket; } } @@ -810,7 +686,7 @@ static int link_socket_to_viewer(const bContext &C, else { link_to_change->fromnode = &bnode_to_view; link_to_change->fromsock = &bsocket_to_view; - btree.update |= NTREE_UPDATE_LINKS; + BKE_ntree_update_tag_link_changed(&btree); } remove_links_to_unavailable_viewer_sockets(btree, *viewer_bnode); @@ -819,10 +695,7 @@ static int link_socket_to_viewer(const bContext &C, ED_spreadsheet_context_paths_set_geometry_node(CTX_data_main(&C), &snode, viewer_bnode); } - ntreeUpdateTree(CTX_data_main(&C), &btree); - snode_update(snode, viewer_bnode); - DEG_id_tag_update(&btree.id, 0); - + BKE_ntree_update_main_tree(CTX_data_main(&C), &btree, nullptr); return OPERATOR_FINISHED; } @@ -863,7 +736,7 @@ static int node_active_link_viewer_exec(bContext *C, wmOperator *UNUSED(op)) return OPERATOR_CANCELLED; } - snode_notify(*C, snode); + ED_node_tree_propagate_change(C, CTX_data_main(C), snode.edittree); return OPERATOR_FINISHED; } @@ -1041,17 +914,10 @@ static void node_link_exit(bContext &C, wmOperator &op, const bool apply_links) SpaceNode &snode = *CTX_wm_space_node(&C); bNodeTree &ntree = *snode.edittree; bNodeLinkDrag *nldrag = (bNodeLinkDrag *)op.customdata; - bool do_tag_update = false; /* avoid updates while applying links */ ntree.is_updating = true; for (bNodeLink *link : nldrag->links) { - /* See note below, but basically TEST flag means that the link - * was connected to output (or to a node which affects the - * output). - */ - do_tag_update |= (link->flag & NODE_LINK_TEST) != 0; - link->flag &= ~NODE_LINK_DRAGGED; if (apply_links && link->tosock && link->fromsock) { @@ -1067,18 +933,10 @@ static void node_link_exit(bContext &C, wmOperator &op, const bool apply_links) /* add link to the node tree */ BLI_addtail(&ntree.links, link); - - ntree.update |= NTREE_UPDATE_LINKS; - - /* tag tonode for update */ - link->tonode->update |= NODE_UPDATE; + BKE_ntree_update_tag_link_added(&ntree, link); /* we might need to remove a link */ node_remove_extra_links(snode, *link); - - if (link->tonode) { - do_tag_update |= (do_tag_update || node_connected_to_output(*bmain, ntree, *link->tonode)); - } } else { nodeRemLink(&ntree, link); @@ -1086,11 +944,7 @@ static void node_link_exit(bContext &C, wmOperator &op, const bool apply_links) } ntree.is_updating = false; - ntreeUpdateTree(bmain, &ntree); - snode_notify(C, snode); - if (do_tag_update) { - snode_dag_update(C, snode); - } + ED_node_tree_propagate_change(&C, bmain, &ntree); /* Ensure draglink tooltip is disabled. */ draw_draglink_tooltip_deactivate(*CTX_wm_region(&C), *nldrag); @@ -1250,8 +1104,7 @@ static int node_link_modal(bContext *C, wmOperator *op, const wmEvent *event) return OPERATOR_RUNNING_MODAL; } -static std::unique_ptr<bNodeLinkDrag> node_link_init(Main &bmain, - SpaceNode &snode, +static std::unique_ptr<bNodeLinkDrag> node_link_init(SpaceNode &snode, float2 cursor, const bool detach) { @@ -1276,17 +1129,6 @@ static std::unique_ptr<bNodeLinkDrag> node_link_init(Main &bmain, oplink->flag |= NODE_LINK_VALID; oplink->flag |= NODE_LINK_DRAGGED; - /* The link could be disconnected and in that case we - * wouldn't be able to check whether tag update is - * needed or not when releasing mouse button. So we - * cache whether the link affects output or not here - * using TEST flag. - */ - oplink->flag &= ~NODE_LINK_TEST; - if (node_connected_to_output(bmain, *snode.edittree, *link->tonode)) { - oplink->flag |= NODE_LINK_TEST; - } - nldrag->links.append(oplink); nodeRemLink(snode.edittree, link); } @@ -1296,7 +1138,7 @@ static std::unique_ptr<bNodeLinkDrag> node_link_init(Main &bmain, /* dragged links are fixed on output side */ nldrag->in_out = SOCK_OUT; /* create a new link */ - nldrag->links.append(create_drag_link(bmain, snode, *node, *sock)); + nldrag->links.append(create_drag_link(*node, *sock)); } return nldrag; } @@ -1329,17 +1171,13 @@ static std::unique_ptr<bNodeLinkDrag> node_link_init(Main &bmain, oplink->next = oplink->prev = nullptr; oplink->flag |= NODE_LINK_VALID; oplink->flag |= NODE_LINK_DRAGGED; - oplink->flag &= ~NODE_LINK_TEST; - if (node_connected_to_output(bmain, *snode.edittree, *link_to_pick->tonode)) { - oplink->flag |= NODE_LINK_TEST; - } nldrag->links.append(oplink); nodeRemLink(snode.edittree, link_to_pick); /* send changed event to original link->tonode */ if (node) { - snode_update(snode, node); + BKE_ntree_update_tag_node_property(snode.edittree, node); } } } @@ -1347,7 +1185,7 @@ static std::unique_ptr<bNodeLinkDrag> node_link_init(Main &bmain, /* dragged links are fixed on input side */ nldrag->in_out = SOCK_IN; /* create a new link */ - nldrag->links.append(create_drag_link(bmain, snode, *node, *sock)); + nldrag->links.append(create_drag_link(*node, *sock)); } return nldrag; } @@ -1370,7 +1208,7 @@ static int node_link_invoke(bContext *C, wmOperator *op, const wmEvent *event) ED_preview_kill_jobs(CTX_wm_manager(C), &bmain); - std::unique_ptr<bNodeLinkDrag> nldrag = node_link_init(bmain, snode, cursor, detach); + std::unique_ptr<bNodeLinkDrag> nldrag = node_link_init(snode, cursor, detach); if (nldrag) { UI_view2d_edge_pan_operator_init(C, &nldrag->pan_data, op); @@ -1473,9 +1311,7 @@ static int node_make_link_exec(bContext *C, wmOperator *op) node_deselect_all_input_sockets(snode, false); node_deselect_all_output_sockets(snode, false); - ntreeUpdateTree(CTX_data_main(C), snode.edittree); - snode_notify(*C, snode); - snode_dag_update(*C, snode); + ED_node_tree_propagate_change(C, CTX_data_main(C), snode.edittree); return OPERATOR_FINISHED; } @@ -1532,7 +1368,6 @@ static int cut_links_exec(bContext *C, wmOperator *op) Main &bmain = *CTX_data_main(C); SpaceNode &snode = *CTX_wm_space_node(C); ARegion ®ion = *CTX_wm_region(C); - bool do_tag_update = false; int i = 0; float mcoords[256][2]; @@ -1567,23 +1402,14 @@ static int cut_links_exec(bContext *C, wmOperator *op) found = true; } - do_tag_update |= (do_tag_update || - node_connected_to_output(bmain, *snode.edittree, *link->tonode)); - - snode_update(snode, link->tonode); bNode *to_node = link->tonode; nodeRemLink(snode.edittree, link); sort_multi_input_socket_links(snode, *to_node, nullptr, nullptr); } } + ED_node_tree_propagate_change(C, CTX_data_main(C), snode.edittree); if (found) { - ntreeUpdateTree(CTX_data_main(C), snode.edittree); - snode_notify(*C, snode); - if (do_tag_update) { - snode_dag_update(*C, snode); - } - return OPERATOR_FINISHED; } @@ -1629,7 +1455,6 @@ static int mute_links_exec(bContext *C, wmOperator *op) Main &bmain = *CTX_data_main(C); SpaceNode &snode = *CTX_wm_space_node(C); ARegion ®ion = *CTX_wm_region(C); - bool do_tag_update = false; int i = 0; float mcoords[256][2]; @@ -1671,10 +1496,6 @@ static int mute_links_exec(bContext *C, wmOperator *op) } if (node_links_intersect(*link, mcoords, i)) { - do_tag_update |= (do_tag_update || - node_connected_to_output(bmain, *snode.edittree, *link->tonode)); - - snode_update(snode, link->tonode); nodeMuteLinkToggle(snode.edittree, link); } } @@ -1687,12 +1508,7 @@ static int mute_links_exec(bContext *C, wmOperator *op) link->flag &= ~NODE_LINK_TEST; } - ntreeUpdateTree(CTX_data_main(C), snode.edittree); - snode_notify(*C, snode); - if (do_tag_update) { - snode_dag_update(*C, snode); - } - + ED_node_tree_propagate_change(C, CTX_data_main(C), snode.edittree); return OPERATOR_FINISHED; } @@ -1743,11 +1559,7 @@ static int detach_links_exec(bContext *C, wmOperator *UNUSED(op)) } } - ntreeUpdateTree(CTX_data_main(C), &ntree); - - snode_notify(*C, snode); - snode_dag_update(*C, snode); - + ED_node_tree_propagate_change(C, CTX_data_main(C), &ntree); return OPERATOR_FINISHED; } @@ -2663,10 +2475,7 @@ void ED_node_link_insert(Main *bmain, ScrArea *area) snode->runtime->iofsd = iofsd; } - ntreeUpdateTree(bmain, snode->edittree); /* needed for pointers */ - snode_update(*snode, select); - ED_node_tag_update_id((ID *)snode->edittree); - ED_node_tag_update_id(snode->id); + ED_node_tree_propagate_change(nullptr, bmain, snode->edittree); } } } diff --git a/source/blender/editors/space_node/node_templates.cc b/source/blender/editors/space_node/node_templates.cc index 386178596af..defb1e82c3e 100644 --- a/source/blender/editors/space_node/node_templates.cc +++ b/source/blender/editors/space_node/node_templates.cc @@ -37,6 +37,7 @@ #include "BKE_context.h" #include "BKE_lib_id.h" #include "BKE_main.h" +#include "BKE_node_tree_update.h" #include "RNA_access.h" @@ -84,7 +85,7 @@ static void node_link_item_apply(Main *bmain, bNode *node, NodeLinkItem *item) { if (ELEM(node->type, NODE_GROUP, NODE_CUSTOM_GROUP)) { node->id = (ID *)item->ngroup; - ntreeUpdateTree(bmain, item->ngroup); + BKE_ntree_update_main_tree(bmain, item->ngroup, nullptr); } else { /* nothing to do for now */ @@ -179,10 +180,8 @@ static void node_socket_disconnect(Main *bmain, nodeRemLink(ntree, sock_to->link); sock_to->flag |= SOCK_COLLAPSED; - nodeUpdate(ntree, node_to); - ntreeUpdateTree(bmain, ntree); - - ED_node_tag_update_nodetree(bmain, ntree, node_to); + BKE_ntree_update_tag_node_property(ntree, node_to); + ED_node_tree_propagate_change(nullptr, bmain, ntree); } /* remove all nodes connected to this socket, if they aren't connected to other nodes */ @@ -195,10 +194,8 @@ static void node_socket_remove(Main *bmain, bNodeTree *ntree, bNode *node_to, bN node_remove_linked(bmain, ntree, sock_to->link->fromnode); sock_to->flag |= SOCK_COLLAPSED; - nodeUpdate(ntree, node_to); - ntreeUpdateTree(bmain, ntree); - - ED_node_tag_update_nodetree(bmain, ntree, node_to); + BKE_ntree_update_tag_node_property(ntree, node_to); + ED_node_tree_propagate_change(nullptr, bmain, ntree); } /* add new node connected to this socket, or replace an existing one */ @@ -299,11 +296,9 @@ static void node_socket_add_replace(const bContext *C, node_remove_linked(bmain, ntree, node_prev); } - nodeUpdate(ntree, node_from); - nodeUpdate(ntree, node_to); - ntreeUpdateTree(CTX_data_main(C), ntree); - - ED_node_tag_update_nodetree(CTX_data_main(C), ntree, node_to); + BKE_ntree_update_tag_node_property(ntree, node_from); + BKE_ntree_update_tag_node_property(ntree, node_to); + ED_node_tree_propagate_change(nullptr, bmain, ntree); } /****************************** Node Link Menu *******************************/ diff --git a/source/blender/editors/space_node/space_node.cc b/source/blender/editors/space_node/space_node.cc index 4cc0bed1928..96c6dd89447 100644 --- a/source/blender/editors/space_node/space_node.cc +++ b/source/blender/editors/space_node/space_node.cc @@ -313,6 +313,19 @@ static void node_init(struct wmWindowManager *UNUSED(wm), ScrArea *area) } } +static bool any_node_uses_id(const bNodeTree *ntree, const ID *id) +{ + if (ELEM(nullptr, ntree, id)) { + return false; + } + LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { + if (node->id == id) { + return true; + } + } + return false; +} + static void node_area_listener(const wmSpaceTypeListenerParams *params) { ScrArea *area = params->area; @@ -436,10 +449,9 @@ static void node_area_listener(const wmSpaceTypeListenerParams *params) case NC_IMAGE: if (wmn->action == NA_EDITED) { if (ED_node_is_compositor(snode)) { - /* note that nodeUpdateID is already called by BKE_image_signal() on all - * scenes so really this is just to know if the images is used in the compo else - * painting on images could become very slow when the compositor is open. */ - if (nodeUpdateID(snode->nodetree, (ID *)wmn->reference)) { + /* Without this check drawing on an image could become very slow when the compositor is + * open. */ + if (any_node_uses_id(snode->nodetree, (ID *)wmn->reference)) { ED_area_tag_refresh(area); } } @@ -449,7 +461,7 @@ static void node_area_listener(const wmSpaceTypeListenerParams *params) case NC_MOVIECLIP: if (wmn->action == NA_EDITED) { if (ED_node_is_compositor(snode)) { - if (nodeUpdateID(snode->nodetree, (ID *)wmn->reference)) { + if (any_node_uses_id(snode->nodetree, (ID *)wmn->reference)) { ED_area_tag_refresh(area); } } diff --git a/source/blender/editors/space_outliner/outliner_dragdrop.c b/source/blender/editors/space_outliner/outliner_dragdrop.c index a4d5f2635d4..85fee590447 100644 --- a/source/blender/editors/space_outliner/outliner_dragdrop.c +++ b/source/blender/editors/space_outliner/outliner_dragdrop.c @@ -123,7 +123,7 @@ static ID *outliner_ID_drop_find(bContext *C, const wmEvent *event, short idcode /* Find tree element to drop into, with additional before and after reorder support. */ static TreeElement *outliner_drop_insert_find(bContext *C, - const wmEvent *event, + const int xy[2], TreeElementInsertType *r_insert_type) { SpaceOutliner *space_outliner = CTX_wm_space_outliner(C); @@ -136,8 +136,11 @@ static TreeElement *outliner_drop_insert_find(bContext *C, return NULL; } - UI_view2d_region_to_view( - ®ion->v2d, event->mval[0], event->mval[1], &view_mval[0], &view_mval[1]); + int mval[2]; + mval[0] = xy[0] - region->winrct.xmin; + mval[1] = xy[1] - region->winrct.ymin; + + UI_view2d_region_to_view(®ion->v2d, mval[0], mval[1], &view_mval[0], &view_mval[1]); te_hovered = outliner_find_item_at_y(space_outliner, &space_outliner->tree, view_mval[1]); if (te_hovered) { @@ -216,10 +219,10 @@ static bool is_pchan_element(TreeElement *te) } static TreeElement *outliner_drop_insert_collection_find(bContext *C, - const wmEvent *event, + const int xy[2], TreeElementInsertType *r_insert_type) { - TreeElement *te = outliner_drop_insert_find(C, event, r_insert_type); + TreeElement *te = outliner_drop_insert_find(C, xy, r_insert_type); if (!te) { return NULL; } @@ -707,7 +710,7 @@ static bool datastack_drop_init(bContext *C, const wmEvent *event, StackDropData return false; } - TreeElement *te_target = outliner_drop_insert_find(C, event, &drop_data->insert_type); + TreeElement *te_target = outliner_drop_insert_find(C, event->xy, &drop_data->insert_type); if (!te_target) { return false; } @@ -1088,14 +1091,12 @@ static Collection *collection_parent_from_ID(ID *id) return NULL; } -static bool collection_drop_init(bContext *C, - wmDrag *drag, - const wmEvent *event, - CollectionDrop *data) +static bool collection_drop_init( + bContext *C, wmDrag *drag, const int xy[2], const bool is_link, CollectionDrop *data) { /* Get collection to drop into. */ TreeElementInsertType insert_type; - TreeElement *te = outliner_drop_insert_collection_find(C, event, &insert_type); + TreeElement *te = outliner_drop_insert_collection_find(C, xy, &insert_type); if (!te) { return false; } @@ -1123,7 +1124,7 @@ static bool collection_drop_init(bContext *C, /* Get collection to drag out of. */ ID *parent = drag_id->from_parent; Collection *from_collection = collection_parent_from_ID(parent); - if (event->ctrl) { + if (is_link) { from_collection = NULL; } @@ -1164,7 +1165,7 @@ static bool collection_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event &space_outliner->tree, TSE_HIGHLIGHTED_ANY | TSE_DRAG_ANY, false); CollectionDrop data; - if (!event->shift && collection_drop_init(C, drag, event, &data)) { + if (!event->shift && collection_drop_init(C, drag, event->xy, event->ctrl, &data)) { TreeElement *te = data.te; TreeStoreElem *tselem = TREESTORE(te); if (!data.from || event->ctrl) { @@ -1201,13 +1202,14 @@ static bool collection_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event static char *collection_drop_tooltip(bContext *C, wmDrag *drag, - const int UNUSED(xy[2]), + const int xy[2], wmDropBox *UNUSED(drop)) { - wmWindowManager *wm = CTX_wm_manager(C); - const wmEvent *event = wm->winactive ? wm->winactive->eventstate : NULL; + wmWindow *win = CTX_wm_window(C); + const wmEvent *event = win ? win->eventstate : NULL; + CollectionDrop data; - if (event && !event->shift && collection_drop_init(C, drag, event, &data)) { + if (event && !event->shift && collection_drop_init(C, drag, xy, event->ctrl, &data)) { TreeElement *te = data.te; if (!data.from || event->ctrl) { return BLI_strdup(TIP_("Link inside Collection")); @@ -1260,7 +1262,7 @@ static int collection_drop_invoke(bContext *C, wmOperator *UNUSED(op), const wmE wmDrag *drag = lb->first; CollectionDrop data; - if (!collection_drop_init(C, drag, event, &data)) { + if (!collection_drop_init(C, drag, event->xy, event->ctrl, &data)) { return OPERATOR_CANCELLED; } diff --git a/source/blender/editors/space_outliner/outliner_edit.c b/source/blender/editors/space_outliner/outliner_edit.c index 9d4b14a1f57..ba4759826cd 100644 --- a/source/blender/editors/space_outliner/outliner_edit.c +++ b/source/blender/editors/space_outliner/outliner_edit.c @@ -38,6 +38,7 @@ #include "BLT_translation.h" +#include "BKE_action.h" #include "BKE_animsys.h" #include "BKE_appdir.h" #include "BKE_armature.h" @@ -48,6 +49,7 @@ #include "BKE_lib_query.h" #include "BKE_lib_remap.h" #include "BKE_main.h" +#include "BKE_object.h" #include "BKE_report.h" #include "BKE_workspace.h" @@ -654,7 +656,7 @@ static int outliner_id_remap_invoke(bContext *C, wmOperator *op, const wmEvent * outliner_id_remap_find_tree_element(C, op, &space_outliner->tree, fmval[1]); } - return WM_operator_props_dialog_popup(C, op, 200); + return WM_operator_props_dialog_popup(C, op, 400); } static const EnumPropertyItem *outliner_id_itemf(bContext *C, @@ -703,6 +705,9 @@ void OUTLINER_OT_id_remap(wmOperatorType *ot) prop = RNA_def_enum(ot->srna, "id_type", rna_enum_id_type_items, ID_OB, "ID Type", ""); RNA_def_property_translation_context(prop, BLT_I18NCONTEXT_ID_ID); + /* Changing ID type wont make sense, would return early with "Invalid old/new ID pair" anyways. + */ + RNA_def_property_flag(prop, PROP_HIDDEN); prop = RNA_def_enum(ot->srna, "old_id", DummyRNA_NULL_items, 0, "Old ID", "Old ID to replace"); RNA_def_property_enum_funcs_runtime(prop, NULL, NULL, outliner_id_itemf); @@ -1262,7 +1267,8 @@ static TreeElement *outliner_show_active_get_element(bContext *C, TreeElement *te_obact = te; if (obact->mode & OB_MODE_POSE) { - bPoseChannel *pchan = CTX_data_active_pose_bone(C); + Object *obpose = BKE_object_pose_armature_get(obact); + bPoseChannel *pchan = BKE_pose_channel_active(obpose, false); if (pchan) { te = outliner_find_posechannel(&te_obact->subtree, pchan); } diff --git a/source/blender/editors/space_sequencer/sequencer_add.c b/source/blender/editors/space_sequencer/sequencer_add.c index 616953e720a..9f31e55439d 100644 --- a/source/blender/editors/space_sequencer/sequencer_add.c +++ b/source/blender/editors/space_sequencer/sequencer_add.c @@ -627,6 +627,19 @@ static void seq_build_proxy(bContext *C, Sequence *seq) ED_area_tag_redraw(CTX_wm_area(C)); } +static void sequencer_add_movie_clamp_sound_strip_length(Scene *scene, + ListBase *seqbase, + Sequence *seq_movie, + Sequence *seq_sound) +{ + if (ELEM(NULL, seq_movie, seq_sound) || seq_sound->len <= seq_movie->len) { + return; + } + + SEQ_transform_set_right_handle_frame(seq_sound, SEQ_transform_get_right_handle_frame(seq_movie)); + SEQ_time_update_sequence(scene, seqbase, seq_sound); +} + static void sequencer_add_movie_multiple_strips(bContext *C, wmOperator *op, SeqLoadData *load_data) @@ -689,6 +702,7 @@ static void sequencer_add_movie_multiple_strips(bContext *C, else { load_data->start_frame += seq_movie->enddisp - seq_movie->startdisp; } + sequencer_add_movie_clamp_sound_strip_length(scene, ed->seqbasep, seq_movie, seq_sound); seq_load_apply_generic_options(C, op, seq_sound); seq_load_apply_generic_options(C, op, seq_movie); seq_build_proxy(C, seq_movie); @@ -740,6 +754,7 @@ static bool sequencer_add_movie_single_strip(bContext *C, wmOperator *op, SeqLoa load_data->start_frame += audio_frame_offset; seq_sound = SEQ_add_sound_strip(bmain, scene, ed->seqbasep, load_data, audio_skip); } + sequencer_add_movie_clamp_sound_strip_length(scene, ed->seqbasep, seq_movie, seq_sound); seq_load_apply_generic_options(C, op, seq_sound); seq_load_apply_generic_options(C, op, seq_movie); seq_build_proxy(C, seq_movie); diff --git a/source/blender/editors/space_spreadsheet/space_spreadsheet.cc b/source/blender/editors/space_spreadsheet/space_spreadsheet.cc index 61cc70830af..7526a61bf4a 100644 --- a/source/blender/editors/space_spreadsheet/space_spreadsheet.cc +++ b/source/blender/editors/space_spreadsheet/space_spreadsheet.cc @@ -114,7 +114,7 @@ static void spreadsheet_free(SpaceLink *sl) { SpaceSpreadsheet *sspreadsheet = (SpaceSpreadsheet *)sl; - delete sspreadsheet->runtime; + MEM_delete(sspreadsheet->runtime); LISTBASE_FOREACH_MUTABLE (SpreadsheetRowFilter *, row_filter, &sspreadsheet->row_filters) { spreadsheet_row_filter_free(row_filter); @@ -131,7 +131,7 @@ static void spreadsheet_init(wmWindowManager *UNUSED(wm), ScrArea *area) { SpaceSpreadsheet *sspreadsheet = (SpaceSpreadsheet *)area->spacedata.first; if (sspreadsheet->runtime == nullptr) { - sspreadsheet->runtime = new SpaceSpreadsheet_Runtime(); + sspreadsheet->runtime = MEM_new<SpaceSpreadsheet_Runtime>(__func__); } } @@ -140,10 +140,11 @@ static SpaceLink *spreadsheet_duplicate(SpaceLink *sl) const SpaceSpreadsheet *sspreadsheet_old = (SpaceSpreadsheet *)sl; SpaceSpreadsheet *sspreadsheet_new = (SpaceSpreadsheet *)MEM_dupallocN(sspreadsheet_old); if (sspreadsheet_old->runtime) { - sspreadsheet_new->runtime = new SpaceSpreadsheet_Runtime(*sspreadsheet_old->runtime); + sspreadsheet_new->runtime = MEM_new<SpaceSpreadsheet_Runtime>(__func__, + *sspreadsheet_old->runtime); } else { - sspreadsheet_new->runtime = new SpaceSpreadsheet_Runtime(); + sspreadsheet_new->runtime = MEM_new<SpaceSpreadsheet_Runtime>(__func__); } BLI_listbase_clear(&sspreadsheet_new->row_filters); diff --git a/source/blender/editors/space_topbar/space_topbar.c b/source/blender/editors/space_topbar/space_topbar.c index 41eddd32854..7f0f30624cb 100644 --- a/source/blender/editors/space_topbar/space_topbar.c +++ b/source/blender/editors/space_topbar/space_topbar.c @@ -35,6 +35,7 @@ #include "BKE_context.h" #include "BKE_global.h" #include "BKE_screen.h" +#include "BKE_undo_system.h" #include "ED_screen.h" #include "ED_space_api.h" @@ -236,6 +237,59 @@ static void recent_files_menu_register(void) WM_menutype_add(mt); } +static void undo_history_draw_menu(const bContext *C, Menu *menu) +{ + wmWindowManager *wm = CTX_wm_manager(C); + if (wm->undo_stack == NULL) { + return; + } + int undo_step_count = 0; + for (UndoStep *us = wm->undo_stack->steps.first; us; us = us->next) { + if (us->skip) { + continue; + } + undo_step_count += 1; + } + + uiLayout *split = uiLayoutSplit(menu->layout, 0.0f, false); + uiLayout *column = NULL; + + const int col_size = 20 + (undo_step_count / 12); + int i = 0; + + undo_step_count = 0; + for (UndoStep *us = wm->undo_stack->steps.first; us; us = us->next, i++) { + if (us->skip) { + continue; + } + if (!(undo_step_count % col_size)) { + column = uiLayoutColumn(split, false); + } + const bool is_active = (us == wm->undo_stack->step_active); + uiLayout *row = uiLayoutRow(column, false); + uiLayoutSetEnabled(row, !is_active); + uiItemIntO(row, + IFACE_(us->name), + is_active ? ICON_LAYER_ACTIVE : ICON_NONE, + "ED_OT_undo_history", + "item", + i); + undo_step_count += 1; + } +} + +static void undo_history_menu_register(void) +{ + MenuType *mt; + + mt = MEM_callocN(sizeof(MenuType), __func__); + strcpy(mt->idname, "TOPBAR_MT_undo_history"); + strcpy(mt->label, N_("Undo History")); + strcpy(mt->translation_context, BLT_I18NCONTEXT_DEFAULT_BPYRNA); + mt->draw = undo_history_draw_menu; + WM_menutype_add(mt); +} + void ED_spacetype_topbar(void) { SpaceType *st = MEM_callocN(sizeof(SpaceType), "spacetype topbar"); @@ -278,6 +332,7 @@ void ED_spacetype_topbar(void) BLI_addhead(&st->regiontypes, art); recent_files_menu_register(); + undo_history_menu_register(); BKE_spacetype_register(st); } diff --git a/source/blender/editors/space_view3d/view3d_buttons.c b/source/blender/editors/space_view3d/view3d_buttons.c index b79303551a1..5451aa5a2e0 100644 --- a/source/blender/editors/space_view3d/view3d_buttons.c +++ b/source/blender/editors/space_view3d/view3d_buttons.c @@ -1552,7 +1552,7 @@ static void v3d_posearmature_buts(uiLayout *layout, Object *ob) PointerRNA pchanptr; uiLayout *col; - pchan = BKE_pose_channel_active(ob); + pchan = BKE_pose_channel_active_if_layer_visible(ob); if (!pchan) { uiItemL(layout, IFACE_("No Bone Active"), ICON_NONE); diff --git a/source/blender/editors/space_view3d/view3d_edit.c b/source/blender/editors/space_view3d/view3d_edit.c index 830f7cbeff1..80089815284 100644 --- a/source/blender/editors/space_view3d/view3d_edit.c +++ b/source/blender/editors/space_view3d/view3d_edit.c @@ -3216,7 +3216,7 @@ static int view_lock_to_active_exec(bContext *C, wmOperator *UNUSED(op)) if (obact->mode & OB_MODE_POSE) { Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); Object *obact_eval = DEG_get_evaluated_object(depsgraph, obact); - bPoseChannel *pcham_act = BKE_pose_channel_active(obact_eval); + bPoseChannel *pcham_act = BKE_pose_channel_active_if_layer_visible(obact_eval); if (pcham_act) { BLI_strncpy(v3d->ob_center_bone, pcham_act->name, sizeof(v3d->ob_center_bone)); } diff --git a/source/blender/editors/space_view3d/view3d_gizmo_armature.c b/source/blender/editors/space_view3d/view3d_gizmo_armature.c index 83d3286c8b3..31ae8a92f81 100644 --- a/source/blender/editors/space_view3d/view3d_gizmo_armature.c +++ b/source/blender/editors/space_view3d/view3d_gizmo_armature.c @@ -134,7 +134,7 @@ static bool WIDGETGROUP_armature_spline_poll(const bContext *C, wmGizmoGroupType if (ob) { const bArmature *arm = ob->data; if (arm->drawtype == ARM_B_BONE) { - bPoseChannel *pchan = BKE_pose_channel_active(ob); + bPoseChannel *pchan = BKE_pose_channel_active_if_layer_visible(ob); if (pchan && pchan->bone->segments > 1) { return true; } @@ -148,7 +148,7 @@ static void WIDGETGROUP_armature_spline_setup(const bContext *C, wmGizmoGroup *g { ViewLayer *view_layer = CTX_data_view_layer(C); Object *ob = BKE_object_pose_armature_get(OBACT(view_layer)); - bPoseChannel *pchan = BKE_pose_channel_active(ob); + bPoseChannel *pchan = BKE_pose_channel_active_if_layer_visible(ob); const wmGizmoType *gzt_move = WM_gizmotype_find("GIZMO_GT_move_3d", true); @@ -187,7 +187,7 @@ static void WIDGETGROUP_armature_spline_refresh(const bContext *C, wmGizmoGroup } struct BoneSplineWidgetGroup *bspline_group = gzgroup->customdata; - bPoseChannel *pchan = BKE_pose_channel_active(ob); + bPoseChannel *pchan = BKE_pose_channel_active_if_layer_visible(ob); /* Handles */ for (int i = 0; i < ARRAY_SIZE(bspline_group->handles); i++) { diff --git a/source/blender/editors/transform/transform_convert.c b/source/blender/editors/transform/transform_convert.c index 8f3d13176a3..4107cc3a71c 100644 --- a/source/blender/editors/transform/transform_convert.c +++ b/source/blender/editors/transform/transform_convert.c @@ -914,6 +914,9 @@ void special_aftertrans_update(bContext *C, TransInfo *t) case TC_SEQ_DATA: special_aftertrans_update__sequencer(C, t); break; + case TC_SEQ_IMAGE_DATA: + special_aftertrans_update__sequencer_image(C, t); + break; case TC_TRACKING_DATA: special_aftertrans_update__movieclip(C, t); break; @@ -930,7 +933,6 @@ void special_aftertrans_update(bContext *C, TransInfo *t) case TC_OBJECT_TEXSPACE: case TC_PAINT_CURVE_VERTS: case TC_PARTICLE_VERTS: - case TC_SEQ_IMAGE_DATA: case TC_NONE: default: break; diff --git a/source/blender/editors/transform/transform_convert.h b/source/blender/editors/transform/transform_convert.h index 5ed8182857d..12f3b39927e 100644 --- a/source/blender/editors/transform/transform_convert.h +++ b/source/blender/editors/transform/transform_convert.h @@ -304,6 +304,7 @@ void special_aftertrans_update__sequencer(bContext *C, TransInfo *t); /* transform_convert_sequencer_image.c */ void createTransSeqImageData(TransInfo *t); void recalcData_sequencer_image(TransInfo *t); +void special_aftertrans_update__sequencer_image(bContext *C, TransInfo *t); /* transform_convert_tracking.c */ void createTransTrackingData(bContext *C, TransInfo *t); diff --git a/source/blender/editors/transform/transform_convert_armature.c b/source/blender/editors/transform/transform_convert_armature.c index 63aada0f797..5d0a3bd9dd1 100644 --- a/source/blender/editors/transform/transform_convert_armature.c +++ b/source/blender/editors/transform/transform_convert_armature.c @@ -427,7 +427,7 @@ static short pose_grab_with_ik(Main *bmain, Object *ob) /* Rule: allow multiple Bones * (but they must be selected, and only one ik-solver per chain should get added) */ for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) { - if (pchan->bone->layer & arm->layer) { + if (BKE_pose_is_layer_visible(arm, pchan)) { if (pchan->bone->flag & (BONE_SELECTED | BONE_TRANSFORM_MIRROR)) { /* Rule: no IK for solitary (unconnected) bones. */ for (bonec = pchan->bone->childbase.first; bonec; bonec = bonec->next) { diff --git a/source/blender/editors/transform/transform_convert_mask.c b/source/blender/editors/transform/transform_convert_mask.c index 1a25cfd1efb..e26172cd764 100644 --- a/source/blender/editors/transform/transform_convert_mask.c +++ b/source/blender/editors/transform/transform_convert_mask.c @@ -472,12 +472,7 @@ void special_aftertrans_update__mask(bContext *C, TransInfo *t) } if (t->scene->nodetree) { - /* tracks can be used for stabilization nodes, - * flush update for such nodes */ - // if (nodeUpdateID(t->scene->nodetree, &mask->id)) - { - WM_event_add_notifier(C, NC_MASK | ND_DATA, &mask->id); - } + WM_event_add_notifier(C, NC_MASK | ND_DATA, &mask->id); } /* TODO: don't key all masks. */ diff --git a/source/blender/editors/transform/transform_convert_node.c b/source/blender/editors/transform/transform_convert_node.c index da11666d445..e8cdfaf1f40 100644 --- a/source/blender/editors/transform/transform_convert_node.c +++ b/source/blender/editors/transform/transform_convert_node.c @@ -31,6 +31,7 @@ #include "BKE_context.h" #include "BKE_node.h" +#include "BKE_node_tree_update.h" #include "BKE_report.h" #include "ED_node.h" @@ -246,7 +247,7 @@ void special_aftertrans_update__node(bContext *C, TransInfo *t) nodeRemoveNode(bmain, ntree, node, true); } } - ntreeUpdateTree(bmain, ntree); + ED_node_tree_propagate_change(C, bmain, ntree); } } diff --git a/source/blender/editors/transform/transform_convert_sequencer_image.c b/source/blender/editors/transform/transform_convert_sequencer_image.c index d5a59885014..3a5770c2863 100644 --- a/source/blender/editors/transform/transform_convert_sequencer_image.c +++ b/source/blender/editors/transform/transform_convert_sequencer_image.c @@ -38,8 +38,12 @@ #include "SEQ_transform.h" #include "SEQ_utils.h" +#include "ED_keyframing.h" + #include "UI_view2d.h" +#include "RNA_access.h" + #include "transform.h" #include "transform_convert.h" @@ -215,3 +219,44 @@ void recalcData_sequencer_image(TransInfo *t) SEQ_relations_invalidate_cache_preprocessed(t->scene, seq); } } + +void special_aftertrans_update__sequencer_image(bContext *UNUSED(C), TransInfo *t) +{ + if (t->state == TRANS_CANCEL) { + return; + } + + TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t); + TransData *td = NULL; + TransData2D *td2d = NULL; + int i; + + PointerRNA ptr; + PropertyRNA *prop; + + for (i = 0, td = tc->data, td2d = tc->data_2d; i < tc->data_len; i++, td++, td2d++) { + TransDataSeq *tdseq = td->extra; + Sequence *seq = tdseq->seq; + StripTransform *transform = seq->strip->transform; + Scene *scene = t->scene; + + RNA_pointer_create(&scene->id, &RNA_SequenceTransform, transform, &ptr); + + if (t->mode == TFM_ROTATION) { + prop = RNA_struct_find_property(&ptr, "rotation"); + ED_autokeyframe_property(t->context, scene, &ptr, prop, -1, CFRA); + } + if (t->mode == TFM_TRANSLATION) { + prop = RNA_struct_find_property(&ptr, "offset_x"); + ED_autokeyframe_property(t->context, scene, &ptr, prop, -1, CFRA); + prop = RNA_struct_find_property(&ptr, "offset_y"); + ED_autokeyframe_property(t->context, scene, &ptr, prop, -1, CFRA); + } + if (t->mode == TFM_RESIZE) { + prop = RNA_struct_find_property(&ptr, "scale_x"); + ED_autokeyframe_property(t->context, scene, &ptr, prop, -1, CFRA); + prop = RNA_struct_find_property(&ptr, "scale_y"); + ED_autokeyframe_property(t->context, scene, &ptr, prop, -1, CFRA); + } + } +} diff --git a/source/blender/editors/transform/transform_convert_tracking.c b/source/blender/editors/transform/transform_convert_tracking.c index dc37f2796bf..f2d0fb7ac2f 100644 --- a/source/blender/editors/transform/transform_convert_tracking.c +++ b/source/blender/editors/transform/transform_convert_tracking.c @@ -29,8 +29,10 @@ #include "BLI_math.h" #include "BKE_context.h" +#include "BKE_main.h" #include "BKE_movieclip.h" #include "BKE_node.h" +#include "BKE_node_tree_update.h" #include "BKE_tracking.h" #include "ED_clip.h" @@ -793,8 +795,12 @@ void special_aftertrans_update__movieclip(bContext *C, TransInfo *t) /* Tracks can be used for stabilization nodes, * flush update for such nodes. */ - nodeUpdateID(t->scene->nodetree, &clip->id); - WM_event_add_notifier(C, NC_SCENE | ND_NODES, NULL); + if (t->context != NULL) { + Main *bmain = CTX_data_main(C); + BKE_ntree_update_tag_id_changed(bmain, &clip->id); + BKE_ntree_update_main(bmain, NULL); + WM_event_add_notifier(C, NC_SCENE | ND_NODES, NULL); + } } } diff --git a/source/blender/editors/transform/transform_mode_timetranslate.c b/source/blender/editors/transform/transform_mode_timetranslate.c index 5f2a2e472c5..84ca5d3ae54 100644 --- a/source/blender/editors/transform/transform_mode_timetranslate.c +++ b/source/blender/editors/transform/transform_mode_timetranslate.c @@ -62,27 +62,28 @@ static void headerTimeTranslate(TransInfo *t, char str[UI_MAX_DRAW_STR]) float ival = TRANS_DATA_CONTAINER_FIRST_OK(t)->data->ival; float val = ival + t->values_final[0]; - float snap_val = val; - snapFrameTransform(t, autosnap, ival, val, &snap_val); + snapFrameTransform(t, autosnap, ival, val, &val); + float delta_x = val - ival; if (ELEM(autosnap, SACTSNAP_SECOND, SACTSNAP_TSTEP)) { /* Convert to seconds. */ const Scene *scene = t->scene; const double secf = FPS; - snap_val /= secf; + delta_x /= secf; + val /= secf; } if (autosnap == SACTSNAP_FRAME) { - BLI_snprintf(&tvec[0], NUM_STR_REP_LEN, "%.2f (%.4f)", snap_val, val); + BLI_snprintf(&tvec[0], NUM_STR_REP_LEN, "%.2f (%.4f)", delta_x, val); } else if (autosnap == SACTSNAP_SECOND) { - BLI_snprintf(&tvec[0], NUM_STR_REP_LEN, "%.2f sec (%.4f)", snap_val, val); + BLI_snprintf(&tvec[0], NUM_STR_REP_LEN, "%.2f sec (%.4f)", delta_x, val); } else if (autosnap == SACTSNAP_TSTEP) { - BLI_snprintf(&tvec[0], NUM_STR_REP_LEN, "%.4f sec", snap_val); + BLI_snprintf(&tvec[0], NUM_STR_REP_LEN, "%.4f sec", delta_x); } else { - BLI_snprintf(&tvec[0], NUM_STR_REP_LEN, "%.4f", snap_val); + BLI_snprintf(&tvec[0], NUM_STR_REP_LEN, "%.4f", delta_x); } } diff --git a/source/blender/editors/transform/transform_mode_translate.c b/source/blender/editors/transform/transform_mode_translate.c index 19d0c6d39a3..b8b043c650f 100644 --- a/source/blender/editors/transform/transform_mode_translate.c +++ b/source/blender/editors/transform/transform_mode_translate.c @@ -225,11 +225,12 @@ static void headerTranslation(TransInfo *t, const float vec[3], char str[UI_MAX_ if (t->spacetype == SPACE_GRAPH) { /* WORKAROUND: * Special case where snapping is done in #recalData. - * Update the header based on the first element. */ + * Update the header based on the #center_local. */ const short autosnap = getAnimEdit_SnapMode(t); - float ival = TRANS_DATA_CONTAINER_FIRST_OK(t)->data->ival; + float ival = TRANS_DATA_CONTAINER_FIRST_OK(t)->center_local[0]; float val = ival + dvec[0]; - snapFrameTransform(t, autosnap, ival, val, &dvec[0]); + snapFrameTransform(t, autosnap, ival, val, &val); + dvec[0] = val - ival; } if (t->con.mode & CON_APPLY) { diff --git a/source/blender/editors/transform/transform_orientations.c b/source/blender/editors/transform/transform_orientations.c index fa2485c33c2..5ac5bccd69c 100644 --- a/source/blender/editors/transform/transform_orientations.c +++ b/source/blender/editors/transform/transform_orientations.c @@ -513,7 +513,7 @@ short ED_transform_calc_orientation_from_type_ex(const Scene *scene, if (ob) { if (ob->mode & OB_MODE_POSE) { - const bPoseChannel *pchan = BKE_pose_channel_active(ob); + const bPoseChannel *pchan = BKE_pose_channel_active_if_layer_visible(ob); if (pchan && gimbal_axis_pose(ob, pchan, r_mat)) { break; } @@ -1224,7 +1224,7 @@ int getTransformOrientation_ex(ViewLayer *view_layer, float imat[3][3], mat[3][3]; bool ok = false; - if (activeOnly && (pchan = BKE_pose_channel_active(ob))) { + if (activeOnly && (pchan = BKE_pose_channel_active_if_layer_visible(ob))) { add_v3_v3(normal, pchan->pose_mat[2]); add_v3_v3(plane, pchan->pose_mat[1]); ok = true; diff --git a/source/blender/editors/undo/ed_undo.c b/source/blender/editors/undo/ed_undo.c index aca99f81e7b..c6fa5acfff5 100644 --- a/source/blender/editors/undo/ed_undo.c +++ b/source/blender/editors/undo/ed_undo.c @@ -64,6 +64,7 @@ #include "RNA_access.h" #include "RNA_define.h" +#include "RNA_enum_types.h" #include "UI_interface.h" #include "UI_resources.h" @@ -759,81 +760,16 @@ void ED_undo_operator_repeat_cb_evt(bContext *C, void *arg_op, int UNUSED(arg_un /* -------------------------------------------------------------------- */ /** \name Undo History Operator + * + * See `TOPBAR_MT_undo_history` which is used to access this operator. * \{ */ -/* create enum based on undo items */ -static const EnumPropertyItem *rna_undo_itemf(bContext *C, int *totitem) -{ - EnumPropertyItem item_tmp = {0}, *item = NULL; - int i = 0; - - wmWindowManager *wm = CTX_wm_manager(C); - if (wm->undo_stack == NULL) { - return NULL; - } - - for (UndoStep *us = wm->undo_stack->steps.first; us; us = us->next, i++) { - if (us->skip == false) { - item_tmp.identifier = us->name; - item_tmp.name = IFACE_(us->name); - if (us == wm->undo_stack->step_active) { - item_tmp.icon = ICON_LAYER_ACTIVE; - } - else { - item_tmp.icon = ICON_NONE; - } - item_tmp.value = i; - RNA_enum_item_add(&item, totitem, &item_tmp); - } - } - RNA_enum_item_end(&item, totitem); - - return item; -} - -static int undo_history_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) -{ - int totitem = 0; - - { - const EnumPropertyItem *item = rna_undo_itemf(C, &totitem); - - if (totitem > 0) { - uiPopupMenu *pup = UI_popup_menu_begin( - C, WM_operatortype_name(op->type, op->ptr), ICON_NONE); - uiLayout *layout = UI_popup_menu_layout(pup); - uiLayout *split = uiLayoutSplit(layout, 0.0f, false); - uiLayout *column = NULL; - const int col_size = 20 + totitem / 12; - int i, c; - bool add_col = true; - - for (c = 0, i = totitem; i--;) { - if (add_col && !(c % col_size)) { - column = uiLayoutColumn(split, false); - add_col = false; - } - if (item[i].identifier) { - uiItemIntO(column, item[i].name, item[i].icon, op->type->idname, "item", item[i].value); - c++; - add_col = true; - } - } - - MEM_freeN((void *)item); - - UI_popup_menu_end(C, pup); - } - } - return OPERATOR_CANCELLED; -} - /* NOTE: also check #ed_undo_step() in top if you change notifiers. */ static int undo_history_exec(bContext *C, wmOperator *op) { PropertyRNA *prop = RNA_struct_find_property(op->ptr, "item"); if (RNA_property_is_set(op->ptr, prop)) { - int item = RNA_property_int_get(op->ptr, prop); + const int item = RNA_property_int_get(op->ptr, prop); const int ret = ed_undo_step_by_index(C, item, op->reports); if (ret & OPERATOR_FINISHED) { ed_undo_refresh_for_op(C); @@ -845,14 +781,15 @@ static int undo_history_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } -static bool undo_history_poll(bContext *C) +static int undo_history_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) { - if (!ed_undo_is_init_and_screenactive_poll(C)) { - return false; + PropertyRNA *prop = RNA_struct_find_property(op->ptr, "item"); + if (RNA_property_is_set(op->ptr, prop)) { + return undo_history_exec(C, op); } - UndoStack *undo_stack = CTX_wm_manager(C)->undo_stack; - /* More than just original state entry. */ - return BLI_listbase_count_at_most(&undo_stack->steps, 2) > 1; + + WM_menu_name_call(C, "TOPBAR_MT_undo_history", WM_OP_INVOKE_DEFAULT); + return OPERATOR_FINISHED; } void ED_OT_undo_history(wmOperatorType *ot) @@ -865,7 +802,7 @@ void ED_OT_undo_history(wmOperatorType *ot) /* api callbacks */ ot->invoke = undo_history_invoke; ot->exec = undo_history_exec; - ot->poll = undo_history_poll; + ot->poll = ed_undo_is_init_and_screenactive_poll; RNA_def_int(ot->srna, "item", 0, 0, INT_MAX, "Item", "", 0, INT_MAX); } diff --git a/source/blender/editors/uvedit/uvedit_ops.c b/source/blender/editors/uvedit/uvedit_ops.c index 342afa847b4..64e1fa2f768 100644 --- a/source/blender/editors/uvedit/uvedit_ops.c +++ b/source/blender/editors/uvedit/uvedit_ops.c @@ -180,7 +180,7 @@ void ED_object_assign_active_image(Main *bmain, Object *ob, int mat_nr, Image *i if (node && is_image_texture_node(node)) { node->id = &ima->id; - ED_node_tag_update_nodetree(bmain, ma->nodetree, node); + ED_node_tree_propagate_change(NULL, bmain, ma->nodetree); } } diff --git a/source/blender/freestyle/intern/blender_interface/BlenderStrokeRenderer.cpp b/source/blender/freestyle/intern/blender_interface/BlenderStrokeRenderer.cpp index 0a82c237256..6b33e17070a 100644 --- a/source/blender/freestyle/intern/blender_interface/BlenderStrokeRenderer.cpp +++ b/source/blender/freestyle/intern/blender_interface/BlenderStrokeRenderer.cpp @@ -48,6 +48,7 @@ #include "BKE_material.h" #include "BKE_mesh.h" #include "BKE_node.h" +#include "BKE_node_tree_update.h" #include "BKE_object.h" #include "BKE_scene.h" @@ -415,7 +416,7 @@ Material *BlenderStrokeRenderer::GetStrokeShader(Main *bmain, } nodeSetActive(ntree, output_material); - ntreeUpdateTree(bmain, ntree); + BKE_ntree_update_main_tree(bmain, ntree, NULL); return ma; } diff --git a/source/blender/geometry/intern/realize_instances.cc b/source/blender/geometry/intern/realize_instances.cc index ae513bf88e9..4022794d53f 100644 --- a/source/blender/geometry/intern/realize_instances.cc +++ b/source/blender/geometry/intern/realize_instances.cc @@ -1167,7 +1167,7 @@ static void execute_realize_curve_task(const RealizeInstancesOptions &options, const AttributeIDRef &attribute_id = ordered_attributes.ids[attribute_index]; const void *attribute_fallback = task.attribute_fallbacks.array[attribute_index]; const std::optional<GSpan> src_span_opt = src_point_attributes.get_for_read(attribute_id); - void *dst_buffer = MEM_malloc_arrayN(spline_size, cpp_type.size(), __func__); + void *dst_buffer = MEM_malloc_arrayN(spline_size, cpp_type.size(), "Curve Attribute"); if (src_span_opt.has_value()) { const GSpan src_span = *src_span_opt; cpp_type.copy_construct_n(src_span.data(), dst_buffer, spline_size); diff --git a/source/blender/gpu/GPU_state.h b/source/blender/gpu/GPU_state.h index fa70a8934db..7b69012dcbc 100644 --- a/source/blender/gpu/GPU_state.h +++ b/source/blender/gpu/GPU_state.h @@ -40,9 +40,11 @@ typedef enum eGPUBarrier { GPU_BARRIER_SHADER_IMAGE_ACCESS = (1 << 0), GPU_BARRIER_TEXTURE_FETCH = (1 << 1), GPU_BARRIER_SHADER_STORAGE = (1 << 2), + GPU_BARRIER_VERTEX_ATTRIB_ARRAY = (1 << 3), + GPU_BARRIER_ELEMENT_ARRAY = (1 << 4), } eGPUBarrier; -ENUM_OPERATORS(eGPUBarrier, GPU_BARRIER_SHADER_STORAGE) +ENUM_OPERATORS(eGPUBarrier, GPU_BARRIER_ELEMENT_ARRAY) /** * Defines the fixed pipeline blending equation. diff --git a/source/blender/gpu/intern/gpu_shader_interface.cc b/source/blender/gpu/intern/gpu_shader_interface.cc index 1648446d21b..937f49ccaec 100644 --- a/source/blender/gpu/intern/gpu_shader_interface.cc +++ b/source/blender/gpu/intern/gpu_shader_interface.cc @@ -74,6 +74,8 @@ void ShaderInterface::sort_inputs() sort_input_list(MutableSpan<ShaderInput>(inputs_, attr_len_)); sort_input_list(MutableSpan<ShaderInput>(inputs_ + attr_len_, ubo_len_)); sort_input_list(MutableSpan<ShaderInput>(inputs_ + attr_len_ + ubo_len_, uniform_len_)); + sort_input_list( + MutableSpan<ShaderInput>(inputs_ + attr_len_ + ubo_len_ + uniform_len_, ssbo_len_)); } void ShaderInterface::debug_print() diff --git a/source/blender/gpu/opengl/gl_state.hh b/source/blender/gpu/opengl/gl_state.hh index 979644b41c9..f5c38a33628 100644 --- a/source/blender/gpu/opengl/gl_state.hh +++ b/source/blender/gpu/opengl/gl_state.hh @@ -130,6 +130,12 @@ static inline GLbitfield to_gl(eGPUBarrier barrier_bits) if (barrier_bits & GPU_BARRIER_SHADER_STORAGE) { barrier |= GL_SHADER_STORAGE_BARRIER_BIT; } + if (barrier_bits & GPU_BARRIER_VERTEX_ATTRIB_ARRAY) { + barrier |= GL_VERTEX_ATTRIB_ARRAY_BARRIER_BIT; + } + if (barrier_bits & GPU_BARRIER_ELEMENT_ARRAY) { + barrier |= GL_ELEMENT_ARRAY_BARRIER_BIT; + } return barrier; } diff --git a/source/blender/imbuf/intern/transform.cc b/source/blender/imbuf/intern/transform.cc index 3e071a9e4d7..e1c451a8412 100644 --- a/source/blender/imbuf/intern/transform.cc +++ b/source/blender/imbuf/intern/transform.cc @@ -305,19 +305,22 @@ class Sampler { void sample(const ImBuf *source, const float u, const float v, SampleType &r_sample) { - const float wrapped_u = uv_wrapper.modify_u(source, u); - const float wrapped_v = uv_wrapper.modify_v(source, v); - if constexpr (Filter == IMB_FILTER_BILINEAR && std::is_same_v<StorageType, float> && NumChannels == 4) { + const float wrapped_u = uv_wrapper.modify_u(source, u); + const float wrapped_v = uv_wrapper.modify_v(source, v); bilinear_interpolation_color_fl(source, nullptr, &r_sample[0], wrapped_u, wrapped_v); } else if constexpr (Filter == IMB_FILTER_NEAREST && std::is_same_v<StorageType, unsigned char> && NumChannels == 4) { + const float wrapped_u = uv_wrapper.modify_u(source, u); + const float wrapped_v = uv_wrapper.modify_v(source, v); nearest_interpolation_color_char(source, &r_sample[0], nullptr, wrapped_u, wrapped_v); } else if constexpr (Filter == IMB_FILTER_BILINEAR && std::is_same_v<StorageType, unsigned char> && NumChannels == 4) { + const float wrapped_u = uv_wrapper.modify_u(source, u); + const float wrapped_v = uv_wrapper.modify_v(source, v); bilinear_interpolation_color_char(source, &r_sample[0], nullptr, wrapped_u, wrapped_v); } else if constexpr (Filter == IMB_FILTER_BILINEAR && std::is_same_v<StorageType, float>) { @@ -326,6 +329,8 @@ class Sampler { source->rect_float, &r_sample[0], source->x, source->y, NumChannels, u, v, true, true); } else { + const float wrapped_u = uv_wrapper.modify_u(source, u); + const float wrapped_v = uv_wrapper.modify_v(source, v); BLI_bilinear_interpolation_fl(source->rect_float, &r_sample[0], source->x, @@ -336,6 +341,8 @@ class Sampler { } } else if constexpr (Filter == IMB_FILTER_NEAREST && std::is_same_v<StorageType, float>) { + const float wrapped_u = uv_wrapper.modify_u(source, u); + const float wrapped_v = uv_wrapper.modify_v(source, v); sample_nearest_float(source, wrapped_u, wrapped_v, r_sample); } else { diff --git a/source/blender/io/collada/Materials.cpp b/source/blender/io/collada/Materials.cpp index 1f358accc4e..6e17172f642 100644 --- a/source/blender/io/collada/Materials.cpp +++ b/source/blender/io/collada/Materials.cpp @@ -16,6 +16,8 @@ #include "Materials.h" +#include "BKE_node_tree_update.h" + MaterialNode::MaterialNode(bContext *C, Material *ma, KeyImageMap &key_image_map) : mContext(C), material(ma), effect(nullptr), key_image_map(&key_image_map) { @@ -106,7 +108,7 @@ bNodeTree *MaterialNode::prepare_material_nodetree() void MaterialNode::update_material_nodetree() { - ntreeUpdateTree(CTX_data_main(mContext), ntree); + BKE_ntree_update_main_tree(CTX_data_main(mContext), ntree, nullptr); } bNode *MaterialNode::add_node(int node_type, int locx, int locy, std::string label) diff --git a/source/blender/io/usd/intern/usd_reader_material.cc b/source/blender/io/usd/intern/usd_reader_material.cc index db0c5785a68..645dea42971 100644 --- a/source/blender/io/usd/intern/usd_reader_material.cc +++ b/source/blender/io/usd/intern/usd_reader_material.cc @@ -23,6 +23,7 @@ #include "BKE_main.h" #include "BKE_material.h" #include "BKE_node.h" +#include "BKE_node_tree_update.h" #include "BLI_math_vector.h" #include "BLI_string.h" @@ -339,7 +340,7 @@ void USDMaterialReader::import_usd_preview(Material *mtl, nodeSetActive(ntree, output); - ntreeUpdateTree(bmain_, ntree); + BKE_ntree_update_main_tree(bmain_, ntree, nullptr); /* Optionally, set the material blend mode. */ diff --git a/source/blender/makesdna/DNA_ID.h b/source/blender/makesdna/DNA_ID.h index 6a478f9abb5..17b3fb302e6 100644 --- a/source/blender/makesdna/DNA_ID.h +++ b/source/blender/makesdna/DNA_ID.h @@ -802,6 +802,9 @@ typedef enum IDRecalcFlag { */ ID_RECALC_TAG_FOR_UNDO = (1 << 24), + /* The node tree has changed in a way that affects its output nodes. */ + ID_RECALC_NTREE_OUTPUT = (1 << 25), + /*************************************************************************** * Pseudonyms, to have more semantic meaning in the actual code without * using too much low-level and implementation specific tags. */ diff --git a/source/blender/makesdna/DNA_mesh_types.h b/source/blender/makesdna/DNA_mesh_types.h index 77cb553c7c7..c053baf9f7e 100644 --- a/source/blender/makesdna/DNA_mesh_types.h +++ b/source/blender/makesdna/DNA_mesh_types.h @@ -74,7 +74,7 @@ struct MLoopTri_Store { int len_alloc; }; -/* Runtime data, not saved in files. */ +/** Runtime data, not saved in files. */ typedef struct Mesh_Runtime { /* Evaluated mesh for objects which do not have effective modifiers. * This mesh is used as a result of modifier stack evaluation. @@ -359,13 +359,13 @@ typedef enum eMeshWrapperType { /* ME_WRAPPER_TYPE_SUBD = 2, */ /* TODO */ } eMeshWrapperType; -/* texflag */ +/** #Mesh.texflag */ enum { ME_AUTOSPACE = 1, ME_AUTOSPACE_EVALUATED = 2, }; -/* me->editflag */ +/** #Mesh.editflag */ enum { ME_EDIT_MIRROR_VERTEX_GROUPS = 1 << 0, ME_EDIT_MIRROR_Y = 1 << 1, /* unused so far */ @@ -387,7 +387,7 @@ enum { ((_me)->editflag & ME_EDIT_PAINT_VERT_SEL) ? SCE_SELECT_VERTEX : \ 0) -/* me->flag */ +/** #Mesh.flag */ enum { ME_FLAG_UNUSED_0 = 1 << 0, /* cleared */ ME_FLAG_UNUSED_1 = 1 << 1, /* cleared */ @@ -407,26 +407,26 @@ enum { ME_REMESH_REPROJECT_SCULPT_FACE_SETS = 1 << 15, }; -/* me->cd_flag */ +/** #Mesh.cd_flag */ enum { ME_CDFLAG_VERT_BWEIGHT = 1 << 0, ME_CDFLAG_EDGE_BWEIGHT = 1 << 1, ME_CDFLAG_EDGE_CREASE = 1 << 2, }; -/* me->remesh_mode */ +/** #Mesh.remesh_mode */ enum { REMESH_VOXEL = 0, REMESH_QUAD = 1, }; -/* Subsurf Type */ +/** #SubsurfModifierData.subdivType */ enum { ME_CC_SUBSURF = 0, ME_SIMPLE_SUBSURF = 1, }; -/* me->symmetry */ +/** #Mesh.symmetry */ typedef enum eMeshSymmetryType { ME_SYMMETRY_X = 1 << 0, ME_SYMMETRY_Y = 1 << 1, diff --git a/source/blender/makesdna/DNA_modifier_types.h b/source/blender/makesdna/DNA_modifier_types.h index f7a468264c3..85cc1361adf 100644 --- a/source/blender/makesdna/DNA_modifier_types.h +++ b/source/blender/makesdna/DNA_modifier_types.h @@ -121,28 +121,28 @@ typedef struct ModifierData { int type, mode; char _pad0[4]; short flag; - /* An "expand" bit for each of the modifier's (sub)panels (uiPanelDataExpansion). */ + /** An "expand" bit for each of the modifier's (sub)panels (#uiPanelDataExpansion). */ short ui_expand_flag; /** MAX_NAME. */ char name[64]; char *error; - /* Pointer to a ModifierData in the original domain. */ + /** Pointer to a #ModifierData in the original domain. */ struct ModifierData *orig_modifier_data; - /* Runtime field which contains unique identifier of the modifier. */ + /** Runtime field which contains unique identifier of the modifier. */ SessionUUID session_uuid; - /* Runtime field which contains runtime data which is specific to a modifier type. */ + /** Runtime field which contains runtime data which is specific to a modifier type. */ void *runtime; void *_pad1; } ModifierData; typedef enum { - /* This modifier has been inserted in local override, and hence can be fully edited. */ + /** This modifier has been inserted in local override, and hence can be fully edited. */ eModifierFlag_OverrideLibrary_Local = (1 << 0), - /* This modifier does not own its caches, but instead shares them with another modifier. */ + /** This modifier does not own its caches, but instead shares them with another modifier. */ eModifierFlag_SharedCaches = (1 << 1), /** * This modifier is the object's active modifier. Used for context in the node editor. @@ -151,7 +151,9 @@ typedef enum { eModifierFlag_Active = (1 << 2), } ModifierFlag; -/* not a real modifier */ +/** + * \note Not a real modifier. + */ typedef struct MappingInfoModifierData { ModifierData modifier; @@ -219,7 +221,7 @@ typedef struct LatticeModifierData { void *_pad1; } LatticeModifierData; -/* Lattice modifier flags. */ +/** #LatticeModifierData.flag */ enum { MOD_LATTICE_INVERT_VGROUP = (1 << 0), }; @@ -237,12 +239,12 @@ typedef struct CurveModifierData { void *_pad1; } CurveModifierData; -/* Curve modifier flags */ +/** #CurveModifierData.flag */ enum { MOD_CURVE_INVERT_VGROUP = (1 << 0), }; -/* CurveModifierData->defaxis */ +/** #CurveModifierData.defaxis */ enum { MOD_CURVE_POSX = 1, MOD_CURVE_POSY = 2, @@ -264,7 +266,7 @@ typedef struct BuildModifierData { int seed; } BuildModifierData; -/* Build Modifier -> flag */ +/** #BuildModifierData.flag */ enum { /** order of vertices is randomized */ MOD_BUILD_FLAG_RANDOMIZE = (1 << 0), @@ -272,7 +274,7 @@ enum { MOD_BUILD_FLAG_REVERSE = (1 << 1), }; -/* Mask Modifier */ +/** Mask Modifier. */ typedef struct MaskModifierData { ModifierData modifier; @@ -289,13 +291,13 @@ typedef struct MaskModifierData { void *_pad1; } MaskModifierData; -/* Mask Modifier -> mode */ +/** #MaskModifierData.mode */ enum { MOD_MASK_MODE_VGROUP = 0, MOD_MASK_MODE_ARM = 1, }; -/* Mask Modifier -> flag */ +/** #MaskModifierData.flag */ enum { MOD_MASK_INV = (1 << 0), MOD_MASK_SMOOTH = (1 << 1), @@ -304,63 +306,68 @@ enum { typedef struct ArrayModifierData { ModifierData modifier; - /* the object with which to cap the start of the array. */ + /** The object with which to cap the start of the array. */ struct Object *start_cap; - /* the object with which to cap the end of the array. */ + /** The object with which to cap the end of the array. */ struct Object *end_cap; - /* the curve object to use for MOD_ARR_FITCURVE. */ + /** The curve object to use for #MOD_ARR_FITCURVE. */ struct Object *curve_ob; - /* the object to use for object offset. */ + /** The object to use for object offset. */ struct Object *offset_ob; - /* a constant duplicate offset; - * 1 means the duplicates are 1 unit apart + /** + * A constant duplicate offset; + * 1 means the duplicates are 1 unit apart. */ float offset[3]; - /* a scaled factor for duplicate offsets; - * 1 means the duplicates are 1 object-width apart + /** + * A scaled factor for duplicate offsets; + * 1 means the duplicates are 1 object-width apart. */ float scale[3]; - /* the length over which to distribute the duplicates */ + /** The length over which to distribute the duplicates. */ float length; - /* the limit below which to merge vertices in adjacent duplicates */ + /** The limit below which to merge vertices in adjacent duplicates. */ float merge_dist; - /* determines how duplicate count is calculated; one of: - * - MOD_ARR_FIXEDCOUNT -> fixed - * - MOD_ARR_FITLENGTH -> calculated to fit a set length - * - MOD_ARR_FITCURVE -> calculated to fit the length of a Curve object + /** + * Determines how duplicate count is calculated; one of: + * - #MOD_ARR_FIXEDCOUNT -> fixed. + * - #MOD_ARR_FITLENGTH -> calculated to fit a set length. + * - #MOD_ARR_FITCURVE -> calculated to fit the length of a Curve object. */ int fit_type; - /* flags specifying how total offset is calculated; binary OR of: - * - MOD_ARR_OFF_CONST -> total offset += offset - * - MOD_ARR_OFF_RELATIVE -> total offset += relative * object width - * - MOD_ARR_OFF_OBJ -> total offset += offset_ob's matrix - * total offset is the sum of the individual enabled offsets + /** + * Flags specifying how total offset is calculated; binary OR of: + * - #MOD_ARR_OFF_CONST -> total offset += offset. + * - #MOD_ARR_OFF_RELATIVE -> total offset += relative * object width. + * - #MOD_ARR_OFF_OBJ -> total offset += offset_ob's matrix. + * Total offset is the sum of the individual enabled offsets. */ int offset_type; - /* general flags: - * MOD_ARR_MERGE -> merge vertices in adjacent duplicates + /** + * General flags: + * #MOD_ARR_MERGE -> merge vertices in adjacent duplicates. */ int flags; - /* the number of duplicates to generate for MOD_ARR_FIXEDCOUNT */ + /** The number of duplicates to generate for #MOD_ARR_FIXEDCOUNT. */ int count; float uv_offset[2]; } ArrayModifierData; -/* ArrayModifierData->fit_type */ +/** #ArrayModifierData.fit_type */ enum { MOD_ARR_FIXEDCOUNT = 0, MOD_ARR_FITLENGTH = 1, MOD_ARR_FITCURVE = 2, }; -/* ArrayModifierData->offset_type */ +/** #ArrayModifierData.offset_type */ enum { MOD_ARR_OFF_CONST = (1 << 0), MOD_ARR_OFF_RELATIVE = (1 << 1), MOD_ARR_OFF_OBJ = (1 << 2), }; -/* ArrayModifierData->flags */ +/** #ArrayModifierData.flags */ enum { MOD_ARR_MERGE = (1 << 0), MOD_ARR_MERGEFINAL = (1 << 1), @@ -389,7 +396,7 @@ typedef struct MirrorModifierData { void *_pad1; } MirrorModifierData; -/* MirrorModifierData->flag */ +/** #MirrorModifierData.flag */ enum { MOD_MIR_CLIPPING = (1 << 0), MOD_MIR_MIRROR_U = (1 << 1), @@ -416,7 +423,7 @@ typedef struct EdgeSplitModifierData { int flags; } EdgeSplitModifierData; -/* EdgeSplitModifierData->flags */ +/** #EdgeSplitModifierData.flags */ enum { MOD_EDGESPLIT_FROMANGLE = (1 << 1), MOD_EDGESPLIT_FROMFLAG = (1 << 2), @@ -468,7 +475,7 @@ typedef struct BevelModifierData { void *_pad2; } BevelModifierData; -/* BevelModifierData->flags and BevelModifierData->lim_flags */ +/** #BevelModifierData.flags and BevelModifierData.lim_flags */ enum { #ifdef DNA_DEPRECATED_ALLOW MOD_BEVEL_VERT_DEPRECATED = (1 << 1), @@ -491,7 +498,7 @@ enum { MOD_BEVEL_HARDEN_NORMALS = (1 << 15), }; -/* BevelModifierData->val_flags (not used as flags any more) */ +/** #BevelModifierData.val_flags (not used as flags any more) */ enum { MOD_BEVEL_AMT_OFFSET = 0, MOD_BEVEL_AMT_WIDTH = 1, @@ -500,19 +507,19 @@ enum { MOD_BEVEL_AMT_ABSOLUTE = 4, }; -/* BevelModifierData->profile_type */ +/** #BevelModifierData.profile_type */ enum { MOD_BEVEL_PROFILE_SUPERELLIPSE = 0, MOD_BEVEL_PROFILE_CUSTOM = 1, }; -/* BevelModifierData->edge_flags */ +/** #BevelModifierData.edge_flags */ enum { MOD_BEVEL_MARK_SEAM = (1 << 0), MOD_BEVEL_MARK_SHARP = (1 << 1), }; -/* BevelModifierData->face_str_mode */ +/** #BevelModifierData.face_str_mode */ enum { MOD_BEVEL_FACE_STRENGTH_NONE = 0, MOD_BEVEL_FACE_STRENGTH_NEW = 1, @@ -520,20 +527,20 @@ enum { MOD_BEVEL_FACE_STRENGTH_ALL = 3, }; -/* BevelModifier->miter_inner and ->miter_outer */ +/** #BevelModifier.miter_inner & #BevelModifier.miter_outer */ enum { MOD_BEVEL_MITER_SHARP = 0, MOD_BEVEL_MITER_PATCH = 1, MOD_BEVEL_MITER_ARC = 2, }; -/* BevelModifier->vmesh_method */ +/** #BevelModifier.vmesh_method */ enum { MOD_BEVEL_VMESH_ADJ = 0, MOD_BEVEL_VMESH_CUTOFF = 1, }; -/* BevelModifier->affect_type */ +/** #BevelModifier.affect_type */ enum { MOD_BEVEL_AFFECT_VERTICES = 0, MOD_BEVEL_AFFECT_EDGES = 1, @@ -553,7 +560,7 @@ typedef struct FluidModifierData { void *_pad1; } FluidModifierData; -/* Fluid modifier flags */ +/** #FluidModifierData.type */ enum { MOD_FLUID_TYPE_DOMAIN = (1 << 0), MOD_FLUID_TYPE_FLOW = (1 << 1), @@ -563,7 +570,8 @@ enum { typedef struct DisplaceModifierData { ModifierData modifier; - /* keep in sync with MappingInfoModifierData */ + /* Keep in sync with #MappingInfoModifierData. */ + struct Tex *texture; struct Object *map_object; char map_bone[64]; @@ -583,12 +591,12 @@ typedef struct DisplaceModifierData { char _pad[6]; } DisplaceModifierData; -/* DisplaceModifierData->flag */ +/** #DisplaceModifierData.flag */ enum { MOD_DISP_INVERT_VGROUP = (1 << 0), }; -/* DisplaceModifierData->direction */ +/** #DisplaceModifierData.direction */ enum { MOD_DISP_DIR_X = 0, MOD_DISP_DIR_Y = 1, @@ -598,7 +606,7 @@ enum { MOD_DISP_DIR_CLNOR = 5, }; -/* DisplaceModifierData->texmapping */ +/** #DisplaceModifierData.texmapping */ enum { MOD_DISP_MAP_LOCAL = 0, MOD_DISP_MAP_GLOBAL = 1, @@ -606,7 +614,7 @@ enum { MOD_DISP_MAP_UV = 3, }; -/* DisplaceModifierData->space */ +/** #DisplaceModifierData.space */ enum { MOD_DISP_SPACE_LOCAL = 0, MOD_DISP_SPACE_GLOBAL = 1, @@ -614,9 +622,10 @@ enum { typedef struct UVProjectModifierData { ModifierData modifier; - - /* the objects which do the projecting */ - /** MOD_UVPROJECT_MAXPROJECTORS. */ + /** + * The objects which do the projecting. + * \note 10=MOD_UVPROJECT_MAXPROJECTORS. + */ struct Object *projectors[10]; char _pad2[4]; int num_projectors; @@ -649,7 +658,7 @@ typedef struct DecimateModifierData { float defgrp_factor; short flag, mode; - /* runtime only */ + /** runtime only. */ int face_count; } DecimateModifierData; @@ -678,7 +687,7 @@ typedef struct SmoothModifierData { } SmoothModifierData; -/* Smooth modifier flags */ +/** #SmoothModifierData.flag */ enum { MOD_SMOOTH_INVERT_VGROUP = (1 << 0), MOD_SMOOTH_X = (1 << 1), @@ -695,11 +704,13 @@ typedef struct CastModifierData { float size; /** MAX_VGROUP_NAME. */ char defgrp_name[64]; - short flag, type; + short flag; + /** Cast modifier projection type. */ + short type; void *_pad1; } CastModifierData; -/* Cast modifier flags */ +/** #CastModifierData.flag */ enum { /* And what bout (1 << 0) flag? ;) */ MOD_CAST_INVERT_VGROUP = (1 << 0), @@ -710,7 +721,7 @@ enum { MOD_CAST_SIZE_FROM_RADIUS = (1 << 5), }; -/* Cast modifier projection types */ +/** #CastModifierData.type */ enum { MOD_CAST_TYPE_SPHERE = 0, MOD_CAST_TYPE_CYLINDER = 1, @@ -720,7 +731,8 @@ enum { typedef struct WaveModifierData { ModifierData modifier; - /* keep in sync with MappingInfoModifierData */ + /* Keep in sync with #MappingInfoModifierData. */ + struct Tex *texture; struct Object *map_object; char map_bone[64]; @@ -728,7 +740,7 @@ typedef struct WaveModifierData { char uvlayer_name[64]; int uvlayer_tmp; int texmapping; - /* end MappingInfoModifierData */ + /* End MappingInfoModifierData. */ struct Object *objectcenter; /** MAX_VGROUP_NAME. */ @@ -745,7 +757,7 @@ typedef struct WaveModifierData { void *_pad2; } WaveModifierData; -/* WaveModifierData.flag */ +/** #WaveModifierData.flag */ enum { MOD_WAVE_INVERT_VGROUP = (1 << 0), MOD_WAVE_X = (1 << 1), @@ -775,7 +787,7 @@ enum { MOD_HOOK_INVERT_VGROUP = (1 << 1), }; -/* same as WarpModifierFalloff */ +/** \note same as #WarpModifierFalloff */ typedef enum { eHook_Falloff_None = 0, eHook_Falloff_Curve = 1, @@ -832,15 +844,17 @@ typedef struct ClothModifierData { /** Definition is in DNA_cloth_types.h. */ struct ClothCollSettings *coll_parms; - /* PointCache can be shared with other instances of ClothModifierData. - * Inspect (modifier.flag & eModifierFlag_SharedCaches) to find out. */ + /** + * PointCache can be shared with other instances of #ClothModifierData. + * Inspect `modifier.flag & eModifierFlag_SharedCaches` to find out. + */ /** Definition is in DNA_object_force_types.h. */ struct PointCache *point_cache; struct ListBase ptcaches; - /* XXX nasty hack, remove once hair can be separated from cloth modifier data */ + /** XXX: nasty hack, remove once hair can be separated from cloth modifier data. */ struct ClothHairData *hairdata; - /* grid geometry values of hair continuum */ + /** Grid geometry values of hair continuum. */ float hair_grid_min[3]; float hair_grid_max[3]; int hair_grid_res[3]; @@ -907,20 +921,20 @@ typedef struct BooleanModifierData { char bm_flag; } BooleanModifierData; -/* BooleanModifierData->operation */ +/** #BooleanModifierData.operation */ typedef enum { eBooleanModifierOp_Intersect = 0, eBooleanModifierOp_Union = 1, eBooleanModifierOp_Difference = 2, } BooleanModifierOp; -/* BooleanModifierData->solver */ +/** #BooleanModifierData.solver */ typedef enum { eBooleanModifierSolver_Fast = 0, eBooleanModifierSolver_Exact = 1, } BooleanModifierSolver; -/* BooleanModifierData->flag */ +/** #BooleanModifierData.flag */ enum { eBooleanModifierFlag_Self = (1 << 0), eBooleanModifierFlag_Object = (1 << 1), @@ -928,7 +942,7 @@ enum { eBooleanModifierFlag_HoleTolerant = (1 << 3), }; -/* bm_flag only used when G_DEBUG. */ +/** #BooleanModifierData.bm_flag (only used when #G_DEBUG is set). */ enum { eBooleanModifierBMeshFlag_BMesh_Separate = (1 << 0), eBooleanModifierBMeshFlag_BMesh_NoDissolve = (1 << 1), @@ -1102,7 +1116,7 @@ typedef enum { eMultiresModifierFlag_UseSculptBaseMesh = (1 << 4), } MultiresModifierFlag; -/* DEPRECATED, only used for versioning. */ +/** DEPRECATED: only used for versioning. */ typedef struct FluidsimModifierData { ModifierData modifier; @@ -1111,7 +1125,7 @@ typedef struct FluidsimModifierData { void *_pad1; } FluidsimModifierData; -/* DEPRECATED, only used for versioning. */ +/** DEPRECATED: only used for versioning. */ typedef struct SmokeModifierData { ModifierData modifier; @@ -1150,7 +1164,7 @@ typedef struct ShrinkwrapModifierData { char _pad[2]; } ShrinkwrapModifierData; -/* Shrinkwrap->shrinkType */ +/** #ShrinkwrapModifierData.shrinkType */ enum { MOD_SHRINKWRAP_NEAREST_SURFACE = 0, MOD_SHRINKWRAP_PROJECT = 1, @@ -1158,7 +1172,7 @@ enum { MOD_SHRINKWRAP_TARGET_PROJECT = 3, }; -/* Shrinkwrap->shrinkMode */ +/** #ShrinkwrapModifierData.shrinkMode */ enum { /** Move vertex to the surface of the target object (keepDist towards original position) */ MOD_SHRINKWRAP_ON_SURFACE = 0, @@ -1172,7 +1186,7 @@ enum { MOD_SHRINKWRAP_ABOVE_SURFACE = 4, }; -/* Shrinkwrap->shrinkOpts */ +/** #ShrinkwrapModifierData.shrinkOpts */ enum { /** allow shrinkwrap to move the vertex in the positive direction of axis */ MOD_SHRINKWRAP_PROJECT_ALLOW_POS_DIR = (1 << 0), @@ -1196,7 +1210,7 @@ enum { #define MOD_SHRINKWRAP_CULL_TARGET_MASK \ (MOD_SHRINKWRAP_CULL_TARGET_FRONTFACE | MOD_SHRINKWRAP_CULL_TARGET_BACKFACE) -/* Shrinkwrap->projAxis */ +/** #ShrinkwrapModifierData.projAxis */ enum { /** projection over normal is used if no axis is selected */ MOD_SHRINKWRAP_PROJECT_OVER_NORMAL = 0, @@ -1228,7 +1242,7 @@ typedef struct SimpleDeformModifierData { void *_pad1; } SimpleDeformModifierData; -/* SimpleDeform->flag */ +/** #SimpleDeformModifierData.flag */ enum { MOD_SIMPLEDEFORM_FLAG_INVERT_VGROUP = (1 << 0), }; @@ -1434,7 +1448,9 @@ enum { typedef struct WarpModifierData { ModifierData modifier; - /* keep in sync with MappingInfoModifierData */ + + /* Keep in sync with #MappingInfoModifierData. */ + struct Tex *texture; struct Object *map_object; char map_bone[64]; @@ -1442,7 +1458,7 @@ typedef struct WarpModifierData { char uvlayer_name[64]; int uvlayer_tmp; int texmapping; - /* end MappingInfoModifierData */ + /* End #MappingInfoModifierData. */ struct Object *object_from; struct Object *object_to; @@ -1462,12 +1478,13 @@ typedef struct WarpModifierData { void *_pad1; } WarpModifierData; -/* WarpModifierData->flag */ +/** #WarpModifierData.flag */ enum { MOD_WARP_VOLUME_PRESERVE = (1 << 0), MOD_WARP_INVERT_VGROUP = (1 << 1), }; +/** \note same as #HookModifierFalloff. */ typedef enum { eWarp_Falloff_None = 0, eWarp_Falloff_Curve = 1, @@ -1508,7 +1525,7 @@ typedef struct WeightVGEditModifierData { char mask_defgrp_name[64]; /* Texture masking. */ - /** Which channel to use as weightf. */ + /** Which channel to use as weight/mask. */ int mask_tex_use_channel; /** The texture. */ struct Tex *mask_texture; @@ -1526,7 +1543,7 @@ typedef struct WeightVGEditModifierData { void *_pad1; } WeightVGEditModifierData; -/* WeightVGEdit flags. */ +/** #WeightVGEdit.edit_flags */ enum { MOD_WVG_EDIT_WEIGHTS_NORMALIZE = (1 << 0), MOD_WVG_INVERT_FALLOFF = (1 << 1), @@ -1581,7 +1598,7 @@ typedef struct WeightVGMixModifierData { char _pad1[3]; } WeightVGMixModifierData; -/* How second vgroup's weights affect first ones. */ +/** #WeightVGMixModifierData.mix_mode (how second vgroup's weights affect first ones). */ enum { /** Second weights replace weights. */ MOD_WVG_MIX_SET = 1, @@ -1599,7 +1616,7 @@ enum { MOD_WVG_MIX_AVG = 7, }; -/* What vertices to affect. */ +/** #WeightVGMixModifierData.mix_set (what vertices to affect). */ enum { /** Affect all vertices. */ MOD_WVG_SET_ALL = 1, @@ -1613,7 +1630,7 @@ enum { MOD_WVG_SET_AND = 5, }; -/* WeightVGMix->flag */ +/** #WeightVGMixModifierData.flag */ enum { MOD_WVG_MIX_INVERT_VGROUP_MASK = (1 << 0), MOD_WVG_MIX_WEIGHTS_NORMALIZE = (1 << 1), @@ -1631,8 +1648,9 @@ typedef struct WeightVGProximityModifierData { /** The custom mapping curve. */ struct CurveMapping *cmap_curve; - /* Proximity modes. */ + /** Modes of proximity weighting. */ int proximity_mode; + /** Options for proximity weighting. */ int proximity_flags; /* Target object from which to calculate vertices distances. */ @@ -1662,20 +1680,21 @@ typedef struct WeightVGProximityModifierData { float min_dist, max_dist; /* Put here to avoid breaking existing struct... */ - /** Using MOD_WVG_MAPPING_* enums. */ + /** + * Mapping modes (using MOD_WVG_MAPPING_* enums). */ short falloff_type; /* Padding... */ char _pad0[2]; } WeightVGProximityModifierData; -/* Modes of proximity weighting. */ +/** #WeightVGProximityModifierData.proximity_mode */ enum { MOD_WVG_PROXIMITY_OBJECT = 1, /* source vertex to other location */ MOD_WVG_PROXIMITY_GEOMETRY = 2, /* source vertex to other geometry */ }; -/* Flags options for proximity weighting. */ +/** #WeightVGProximityModifierData.proximity_flags */ enum { /* Use nearest vertices of target obj, in MOD_WVG_PROXIMITY_GEOMETRY mode. */ MOD_WVG_PROXIMITY_GEOM_VERTS = (1 << 0), @@ -1689,7 +1708,8 @@ enum { }; /* Defines common to all WeightVG modifiers. */ -/* Mapping modes. */ + +/** #WeightVGProximityModifierData.falloff_type */ enum { MOD_WVG_MAPPING_NONE = 0, MOD_WVG_MAPPING_CURVE = 1, @@ -1703,7 +1723,7 @@ enum { MOD_WVG_MAPPING_STEP = 9, /* Median Step. */ }; -/* Tex channel to be used as mask. */ +/** #WeightVGProximityModifierData.mask_tex_use_channel */ enum { MOD_WVG_MASK_TEX_USE_INT = 1, MOD_WVG_MASK_TEX_USE_RED = 2, @@ -1725,13 +1745,13 @@ typedef struct DynamicPaintModifierData { char _pad[4]; } DynamicPaintModifierData; -/* Dynamic paint modifier flags */ +/** #DynamicPaintModifierData.type */ enum { MOD_DYNAMICPAINT_TYPE_CANVAS = (1 << 0), MOD_DYNAMICPAINT_TYPE_BRUSH = (1 << 1), }; -/* Remesh modifier */ +/** Remesh modifier. */ typedef enum eRemeshModifierFlags { MOD_REMESH_FLOOD_FILL = (1 << 0), MOD_REMESH_SMOOTH_SHADING = (1 << 1), @@ -1770,7 +1790,7 @@ typedef struct RemeshModifierData { float adaptivity; } RemeshModifierData; -/* Skin modifier */ +/** Skin modifier. */ typedef struct SkinModifierData { ModifierData modifier; @@ -1783,19 +1803,19 @@ typedef struct SkinModifierData { char _pad[2]; } SkinModifierData; -/* SkinModifierData.symmetry_axes */ +/** #SkinModifierData.symmetry_axes */ enum { MOD_SKIN_SYMM_X = (1 << 0), MOD_SKIN_SYMM_Y = (1 << 1), MOD_SKIN_SYMM_Z = (1 << 2), }; -/* SkinModifierData.flag */ +/** #SkinModifierData.flag */ enum { MOD_SKIN_SMOOTH_SHADING = 1, }; -/* Triangulate modifier */ +/** Triangulate modifier. */ typedef struct TriangulateModifierData { ModifierData modifier; @@ -1805,7 +1825,7 @@ typedef struct TriangulateModifierData { int min_vertices; } TriangulateModifierData; -/* TriangulateModifierData.flag */ +/** #TriangulateModifierData.flag */ enum { #ifdef DNA_DEPRECATED_ALLOW MOD_TRIANGULATE_BEAUTY = (1 << 0), /* deprecated */ @@ -1813,13 +1833,13 @@ enum { MOD_TRIANGULATE_KEEP_CUSTOMLOOP_NORMALS = 1 << 1, }; -/* Triangulate methods - NGons */ +/** #TriangulateModifierData.ngon_method triangulate method (N-gons). */ enum { MOD_TRIANGULATE_NGON_BEAUTY = 0, MOD_TRIANGULATE_NGON_EARCLIP = 1, }; -/* Triangulate methods - Quads */ +/** #TriangulateModifierData.quad_method triangulate method (quads). */ enum { MOD_TRIANGULATE_QUAD_BEAUTY = 0, MOD_TRIANGULATE_QUAD_FIXED = 1, @@ -1837,7 +1857,7 @@ typedef struct LaplacianSmoothModifierData { short flag, repeat; } LaplacianSmoothModifierData; -/* Smooth modifier flags */ +/** #LaplacianSmoothModifierData.flag */ enum { MOD_LAPLACIANSMOOTH_X = (1 << 1), MOD_LAPLACIANSMOOTH_Y = (1 << 2), @@ -1892,7 +1912,7 @@ enum { MOD_CORRECTIVESMOOTH_RESTSOURCE_BIND = 1, }; -/* Corrective Smooth modifier flags */ +/** #CorrectiveSmoothModifierData.flag */ enum { MOD_CORRECTIVESMOOTH_INVERT_VGROUP = (1 << 0), MOD_CORRECTIVESMOOTH_ONLY_SMOOTH = (1 << 1), @@ -1926,12 +1946,12 @@ typedef struct UVWarpModifierData { char uvlayer_name[64]; } UVWarpModifierData; -/* UVWarp modifier flags */ +/** #UVWarpModifierData.flag */ enum { MOD_UVWARP_INVERT_VGROUP = 1 << 0, }; -/* cache modifier */ +/** Mesh cache modifier. */ typedef struct MeshCacheModifierData { ModifierData modifier; @@ -1967,7 +1987,7 @@ typedef struct MeshCacheModifierData { char filepath[1024]; } MeshCacheModifierData; -/* MeshCache modifier flags. */ +/** #MeshCacheModifierData.flag */ enum { MOD_MESHCACHE_INVERT_VERTEX_GROUP = 1 << 0, }; @@ -2012,13 +2032,15 @@ typedef struct LaplacianDeformModifierData { } LaplacianDeformModifierData; -/* Laplacian Deform modifier flags */ +/** #LaplacianDeformModifierData.flag */ enum { MOD_LAPLACIANDEFORM_BIND = 1 << 0, MOD_LAPLACIANDEFORM_INVERT_VGROUP = 1 << 1, }; -/* many of these options match 'solidify' */ +/** + * \note many of these options match 'solidify'. + */ typedef struct WireframeModifierData { ModifierData modifier; /** MAX_VGROUP_NAME. */ @@ -2053,13 +2075,13 @@ typedef struct WeldModifierData { char _pad[2]; } WeldModifierData; -/* WeldModifierData->flag */ +/** #WeldModifierData.flag */ enum { MOD_WELD_INVERT_VGROUP = (1 << 0), MOD_WELD_LOOSE_EDGES = (1 << 1), }; -/* #WeldModifierData.mode */ +/** #WeldModifierData.mode */ enum { MOD_WELD_MODE_ALL = 0, MOD_WELD_MODE_CONNECTED = 1, @@ -2100,7 +2122,7 @@ typedef struct DataTransferModifierData { void *_pad2; } DataTransferModifierData; -/* DataTransferModifierData.flags */ +/** #DataTransferModifierData.flags */ enum { MOD_DATATRANSFER_OBSRC_TRANSFORM = 1 << 0, MOD_DATATRANSFER_MAP_MAXDIST = 1 << 1, @@ -2113,7 +2135,7 @@ enum { MOD_DATATRANSFER_USE_POLY = 1u << 31, }; -/* Set Split Normals modifier */ +/** Set Split Normals modifier. */ typedef struct NormalEditModifierData { ModifierData modifier; /** MAX_VGROUP_NAME. */ @@ -2131,20 +2153,20 @@ typedef struct NormalEditModifierData { void *_pad1; } NormalEditModifierData; -/* NormalEditModifierData.mode */ +/** #NormalEditModifierData.mode */ enum { MOD_NORMALEDIT_MODE_RADIAL = 0, MOD_NORMALEDIT_MODE_DIRECTIONAL = 1, }; -/* NormalEditModifierData.flags */ +/** #NormalEditModifierData.flags */ enum { MOD_NORMALEDIT_INVERT_VGROUP = (1 << 0), MOD_NORMALEDIT_USE_DIRECTION_PARALLEL = (1 << 1), MOD_NORMALEDIT_NO_POLYNORS_FIX = (1 << 2), }; -/* NormalEditModifierData.mix_mode */ +/** #NormalEditModifierData.mix_mode */ enum { MOD_NORMALEDIT_MIX_COPY = 0, MOD_NORMALEDIT_MIX_ADD = 1, @@ -2169,7 +2191,7 @@ typedef struct MeshSeqCacheModifierData { char reader_object_path[1024]; } MeshSeqCacheModifierData; -/* MeshSeqCacheModifierData.read_flag */ +/** #MeshSeqCacheModifierData.read_flag */ enum { MOD_MESHSEQ_READ_VERT = (1 << 0), MOD_MESHSEQ_READ_POLY = (1 << 1), @@ -2214,7 +2236,7 @@ typedef struct SurfaceDeformModifierData { void *_pad1; } SurfaceDeformModifierData; -/* Surface Deform modifier flags */ +/** Surface Deform modifier flags. */ enum { /* This indicates "do bind on next modifier evaluation" as well as "is bound". */ MOD_SDEF_BIND = (1 << 0), @@ -2223,7 +2245,7 @@ enum { MOD_SDEF_SPARSE_BIND = (1 << 2), }; -/* Surface Deform vertex bind modes */ +/** Surface Deform vertex bind modes. */ enum { MOD_SDEF_MODE_LOOPTRI = 0, MOD_SDEF_MODE_NGON = 1, @@ -2243,14 +2265,14 @@ typedef struct WeightedNormalModifierData { /* Name/id of the generic PROP_INT cdlayer storing face weights. */ #define MOD_WEIGHTEDNORMALS_FACEWEIGHT_CDLAYER_ID "__mod_weightednormals_faceweight" -/* WeightedNormalModifierData.mode */ +/** #WeightedNormalModifierData.mode */ enum { MOD_WEIGHTEDNORMAL_MODE_FACE = 0, MOD_WEIGHTEDNORMAL_MODE_ANGLE = 1, MOD_WEIGHTEDNORMAL_MODE_FACE_ANGLE = 2, }; -/* WeightedNormalModifierData.flag */ +/** #WeightedNormalModifierData.flag */ enum { MOD_WEIGHTEDNORMAL_KEEP_SHARP = (1 << 0), MOD_WEIGHTEDNORMAL_INVERT_VGROUP = (1 << 1), @@ -2270,8 +2292,10 @@ typedef struct NodesModifierData { struct bNodeTree *node_group; struct NodesModifierSettings settings; - /* Contains logged information from the last evaluation. This can be used to help the user to - * debug a node tree. */ + /** + * Contains logged information from the last evaluation. + * This can be used to help the user to debug a node tree. + */ void *runtime_eval_log; void *_pad1; } NodesModifierData; @@ -2304,7 +2328,7 @@ typedef struct MeshToVolumeModifierData { void *_pad3; } MeshToVolumeModifierData; -/* MeshToVolumeModifierData->resolution_mode */ +/** #MeshToVolumeModifierData.resolution_mode */ typedef enum MeshToVolumeModifierResolutionMode { MESH_TO_VOLUME_RESOLUTION_MODE_VOXEL_AMOUNT = 0, MESH_TO_VOLUME_RESOLUTION_MODE_VOXEL_SIZE = 1, @@ -2322,7 +2346,7 @@ typedef struct VolumeDisplaceModifierData { float texture_sample_radius; } VolumeDisplaceModifierData; -/* VolumeDisplaceModifierData->texture_map_mode */ +/** #VolumeDisplaceModifierData.texture_map_mode */ enum { MOD_VOLUME_DISPLACE_MAP_LOCAL = 0, MOD_VOLUME_DISPLACE_MAP_GLOBAL = 1, diff --git a/source/blender/makesdna/DNA_movieclip_types.h b/source/blender/makesdna/DNA_movieclip_types.h index 8e01a9e1f1f..08064e6242b 100644 --- a/source/blender/makesdna/DNA_movieclip_types.h +++ b/source/blender/makesdna/DNA_movieclip_types.h @@ -160,7 +160,7 @@ typedef struct MovieClipScopes { float slide_scale[2]; } MovieClipScopes; -/* MovieClipProxy->build_size_flag */ +/** #MovieClipProxy.build_size_flag */ enum { MCLIP_PROXY_SIZE_25 = (1 << 0), MCLIP_PROXY_SIZE_50 = (1 << 1), @@ -172,13 +172,13 @@ enum { MCLIP_PROXY_UNDISTORTED_SIZE_100 = (1 << 7), }; -/* MovieClip->source */ +/** #MovieClip.source */ enum { MCLIP_SRC_SEQUENCE = 1, MCLIP_SRC_MOVIE = 2, }; -/* MovieClip->flag */ +/** #MovieClip.flag */ enum { MCLIP_USE_PROXY = (1 << 0), MCLIP_USE_PROXY_CUSTOM_DIR = (1 << 1), @@ -188,7 +188,7 @@ enum { MCLIP_TIMECODE_FLAGS = (MCLIP_USE_PROXY | MCLIP_USE_PROXY_CUSTOM_DIR), }; -/* MovieClip->render_size */ +/** #MovieClip.render_size */ enum { MCLIP_PROXY_RENDER_SIZE_FULL = 0, MCLIP_PROXY_RENDER_SIZE_25 = 1, @@ -197,7 +197,7 @@ enum { MCLIP_PROXY_RENDER_SIZE_100 = 4, }; -/* MovieClip->render_flag */ +/** #MovieClip.render_flag */ enum { MCLIP_PROXY_RENDER_UNDISTORT = 1, /** Use original, if proxy is not found. */ diff --git a/source/blender/makesdna/DNA_nla_types.h b/source/blender/makesdna/DNA_nla_types.h index 7808dd68384..82edc0e1816 100644 --- a/source/blender/makesdna/DNA_nla_types.h +++ b/source/blender/makesdna/DNA_nla_types.h @@ -33,7 +33,7 @@ struct Ipo; struct Object; struct bAction; -/* simple uniform modifier structure, assumed it can hold all type info */ +/** Simple uniform modifier structure, assumed it can hold all type info. */ typedef struct bActionModifier { struct bActionModifier *next, *prev; short type, flag; @@ -95,7 +95,7 @@ typedef struct bActionStrip { #define ACTSTRIPMODE_BLEND 0 #define ACTSTRIPMODE_ADD 1 -/* strip->flag */ +/** #bActionStrip.flag */ typedef enum eActStrip_Flag { ACTSTRIP_SELECT = (1 << 0), ACTSTRIP_USESTRIDE = (1 << 1), diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h index dc5acb9d5b2..5d51d8eb606 100644 --- a/source/blender/makesdna/DNA_node_types.h +++ b/source/blender/makesdna/DNA_node_types.h @@ -159,7 +159,7 @@ typedef struct bNodeSocket { * restores pointer from matching own_index. */ struct bNodeSocket *groupsock DNA_DEPRECATED; - /** A link pointer, set in ntreeUpdateTree. */ + /** A link pointer, set in #BKE_ntree_update_main. */ struct bNodeLink *link; /* XXX deprecated, socket input values are stored in default_value now. @@ -172,9 +172,13 @@ typedef struct bNodeSocket { * data. It has to be updated when the node declaration changes. */ const SocketDeclarationHandle *declaration; + + /** #eNodeTreeChangedFlag. */ + uint32_t changed_flag; + char _pad[4]; } bNodeSocket; -/* sock->type */ +/** #bNodeSocket.type & #bNodeSocketType.type */ typedef enum eNodeSocketDatatype { SOCK_CUSTOM = -1, /* socket has no integer type */ SOCK_FLOAT = 0, @@ -193,7 +197,7 @@ typedef enum eNodeSocketDatatype { SOCK_MATERIAL = 13, } eNodeSocketDatatype; -/* Socket shape. */ +/** Socket shape. */ typedef enum eNodeSocketDisplayShape { SOCK_DISPLAY_SHAPE_CIRCLE = 0, SOCK_DISPLAY_SHAPE_SQUARE = 1, @@ -203,13 +207,13 @@ typedef enum eNodeSocketDisplayShape { SOCK_DISPLAY_SHAPE_DIAMOND_DOT = 5, } eNodeSocketDisplayShape; -/* Socket side (input/output). */ +/** Socket side (input/output). */ typedef enum eNodeSocketInOut { SOCK_IN = 1 << 0, SOCK_OUT = 1 << 1, } eNodeSocketInOut; -/* #bNodeSocket.flag, first bit is selection. */ +/** #bNodeSocket.flag, first bit is selection. */ typedef enum eNodeSocketFlag { /** Hidden is user defined, to hide unused sockets. */ SOCK_HIDDEN = (1 << 1), @@ -239,7 +243,7 @@ typedef enum eNodeSocketFlag { SOCK_HIDE_LABEL = (1 << 12), } eNodeSocketFlag; -/* TODO: Limit data in bNode to what we want to see saved. */ +/** TODO: Limit data in #bNode to what we want to see saved. */ typedef struct bNode { struct bNode *next, *prev, *new_node; @@ -260,8 +264,9 @@ typedef struct bNode { /** Used as a boolean for execution. */ uint8_t need_exec; - - char _pad[1]; + char _pad2[5]; + /** #eNodeTreeChangedFlag. */ + uint32_t changed_flag; /** Custom user-defined color. */ float color[3]; @@ -400,10 +405,6 @@ typedef struct bNode { #define __NODE_ACTIVE_PREVIEW (1 << 18) /* deprecated */ /* node->update */ -/* XXX NODE_UPDATE is a generic update flag. More fine-grained updates - * might be used in the future, but currently all work the same way. - */ -#define NODE_UPDATE 0xFFFF /* generic update flag (includes all others) */ #define NODE_UPDATE_ID 1 /* associated id data block has changed */ #define NODE_UPDATE_OPERATOR 2 /* node update triggered from update operator */ @@ -511,8 +512,12 @@ typedef struct bNodeTree { */ int cur_index; int flag; - /** Update flags. */ - int update; + /** + * Keeps track of what changed in the node tree until the next update. + * Should not be changed directly, instead use the functions in `BKE_node_tree_update.h`. + * #eNodeTreeChangedFlag. + */ + uint32_t changed_flag; /** Flag to prevent re-entrant update calls. */ short is_updating; /** Generic temporary flag for recursion check (DFS/BFS). */ @@ -546,7 +551,11 @@ typedef struct bNodeTree { * in case multiple different editors are used and make context ambiguous. */ bNodeInstanceKey active_viewer_key; - char _pad[4]; + /** + * A hash of the topology of the node tree leading up to the outputs. This is used to determine + * of the node tree changed in a way that requires updating geometry nodes or shaders. + */ + uint32_t output_topology_hash; /** Execution data. * @@ -571,7 +580,7 @@ typedef struct bNodeTree { struct PreviewImage *preview; } bNodeTree; -/* ntree->type, index */ +/** #NodeTree.type, index */ #define NTREE_UNDEFINED -2 /* Represents #NodeTreeTypeUndefined type. */ #define NTREE_CUSTOM -1 /* for dynamically registered custom types */ @@ -580,10 +589,10 @@ typedef struct bNodeTree { #define NTREE_TEXTURE 2 #define NTREE_GEOMETRY 3 -/* ntree->init, flag */ +/** #NodeTree.init, flag */ #define NTREE_TYPE_INIT 1 -/* ntree->flag */ +/** #NodeTree.flag */ #define NTREE_DS_EXPAND (1 << 0) /* for animation editors */ #define NTREE_COM_OPENCL (1 << 1) /* use opencl */ #define NTREE_TWO_PASS (1 << 2) /* two pass */ @@ -594,20 +603,6 @@ typedef struct bNodeTree { /* tree is localized copy, free when deleting node groups */ /* #define NTREE_IS_LOCALIZED (1 << 5) */ -/* ntree->update */ -typedef enum eNodeTreeUpdate { - NTREE_UPDATE = 0xFFFF, /* generic update flag (includes all others) */ - NTREE_UPDATE_LINKS = (1 << 0), /* links have been added or removed */ - NTREE_UPDATE_NODES = (1 << 1), /* nodes or sockets have been added or removed */ - NTREE_UPDATE_GROUP_IN = (1 << 4), /* group inputs have changed */ - NTREE_UPDATE_GROUP_OUT = (1 << 5), /* group outputs have changed */ - /* The field interface has changed. So e.g. an output that was always a field before is not - * anymore. This implies that the field type inferencing has to be done again. */ - NTREE_UPDATE_FIELD_INFERENCING = (1 << 6), - /* group has changed (generic flag including all other group flags) */ - NTREE_UPDATE_GROUP = (NTREE_UPDATE_GROUP_IN | NTREE_UPDATE_GROUP_OUT), -} eNodeTreeUpdate; - /* tree->execution_mode */ typedef enum eNodeTreeExecutionMode { NTREE_EXECUTION_MODE_TILED = 0, @@ -674,7 +669,8 @@ typedef struct bNodeSocketValueMaterial { struct Material *value; } bNodeSocketValueMaterial; -/* Data structs, for node->storage. */ +/* Data structs, for `node->storage`. */ + enum { CMP_NODE_MASKTYPE_ADD = 0, CMP_NODE_MASKTYPE_SUBTRACT = 1, @@ -713,7 +709,7 @@ typedef struct NodeFrame { short label_size; } NodeFrame; -/* This one has been replaced with ImageUser, keep it for do_versions(). */ +/** \note This one has been replaced with #ImageUser, keep it for do_versions(). */ typedef struct NodeImageAnim { int frames DNA_DEPRECATED; int sfra DNA_DEPRECATED; @@ -767,7 +763,7 @@ typedef struct NodeEllipseMask { char _pad[4]; } NodeEllipseMask; -/* Layer info for image node outputs. */ +/** Layer info for image node outputs. */ typedef struct NodeImageLayer { /* index in the Image->layers->passes lists */ int pass_index DNA_DEPRECATED; @@ -805,7 +801,7 @@ typedef struct NodeAntiAliasingData { float corner_rounding; } NodeAntiAliasingData; -/* NOTE: Only for do-version code. */ +/** \note Only for do-version code. */ typedef struct NodeHueSat { float hue, sat, val; } NodeHueSat; @@ -817,7 +813,9 @@ typedef struct NodeImageFile { int sfra, efra; } NodeImageFile; -/* XXX first struct fields should match NodeImageFile to ensure forward compatibility */ +/** + * XXX: first struct fields should match #NodeImageFile to ensure forward compatibility. + */ typedef struct NodeImageMultiFile { /** 1024 = FILE_MAX. */ char base_path[1024]; @@ -1066,7 +1064,7 @@ typedef struct NodeShaderPrincipled { char _pad[3]; } NodeShaderPrincipled; -/* TEX_output */ +/** TEX_output. */ typedef struct TexNodeOutput { char name[64]; } TexNodeOutput; @@ -1632,14 +1630,17 @@ typedef struct NodeFunctionCompare { #define NODE_IES_INTERNAL 0 #define NODE_IES_EXTERNAL 1 -/* frame node flags */ +/* Frame node flags. */ + #define NODE_FRAME_SHRINK 1 /* keep the bounding box minimal */ #define NODE_FRAME_RESIZEABLE 2 /* test flag, if frame can be resized by user */ -/* proxy node flags */ +/* Proxy node flags. */ + #define NODE_PROXY_AUTOTYPE 1 /* automatically change output type based on link */ -/* comp channel matte */ +/* Comp channel matte. */ + #define CMP_NODE_CHANNEL_MATTE_CS_RGB 1 #define CMP_NODE_CHANNEL_MATTE_CS_HSV 2 #define CMP_NODE_CHANNEL_MATTE_CS_YUV 3 @@ -1661,7 +1662,7 @@ typedef struct NodeFunctionCompare { #define SHD_VECT_TRANSFORM_SPACE_OBJECT 1 #define SHD_VECT_TRANSFORM_SPACE_CAMERA 2 -/* attribute */ +/** #NodeShaderAttribute.type */ enum { SHD_ATTRIBUTE_GEOMETRY = 0, SHD_ATTRIBUTE_OBJECT = 1, @@ -1795,7 +1796,7 @@ enum { #define SHD_AO_INSIDE 1 #define SHD_AO_LOCAL 2 -/* Mapping node vector types. */ +/** Mapping node vector types. */ enum { NODE_MAPPING_TYPE_POINT = 0, NODE_MAPPING_TYPE_TEXTURE = 1, @@ -1803,7 +1804,7 @@ enum { NODE_MAPPING_TYPE_NORMAL = 3, }; -/* Rotation node vector types. */ +/** Rotation node vector types. */ enum { NODE_VECTOR_ROTATE_TYPE_AXIS = 0, NODE_VECTOR_ROTATE_TYPE_AXIS_X = 1, @@ -1815,7 +1816,7 @@ enum { /* math node clamp */ #define SHD_MATH_CLAMP 1 -/* Math node operations. */ +/** Math node operations. */ typedef enum NodeMathOperation { NODE_MATH_ADD = 0, NODE_MATH_SUBTRACT = 1, @@ -1859,7 +1860,7 @@ typedef enum NodeMathOperation { NODE_MATH_SMOOTH_MAX = 39, } NodeMathOperation; -/* Vector Math node operations. */ +/** Vector Math node operations. */ typedef enum NodeVectorMathOperation { NODE_VECTOR_MATH_ADD = 0, NODE_VECTOR_MATH_SUBTRACT = 1, @@ -1893,14 +1894,14 @@ typedef enum NodeVectorMathOperation { NODE_VECTOR_MATH_MULTIPLY_ADD = 26, } NodeVectorMathOperation; -/* Boolean math node operations. */ +/** Boolean math node operations. */ enum { NODE_BOOLEAN_MATH_AND = 0, NODE_BOOLEAN_MATH_OR = 1, NODE_BOOLEAN_MATH_NOT = 2, }; -/* Float compare node operations. */ +/** Float compare node operations. */ typedef enum NodeCompareMode { NODE_COMPARE_MODE_ELEMENT = 0, NODE_COMPARE_MODE_LENGTH = 1, @@ -1921,7 +1922,7 @@ typedef enum NodeCompareOperation { } NodeCompareOperation; -/* Float to Int node operations. */ +/** Float to Int node operations. */ typedef enum FloatToIntRoundingMode { FN_NODE_FLOAT_TO_INT_ROUND = 0, FN_NODE_FLOAT_TO_INT_FLOOR = 1, @@ -1929,13 +1930,13 @@ typedef enum FloatToIntRoundingMode { FN_NODE_FLOAT_TO_INT_TRUNCATE = 3, } FloatToIntRoundingMode; -/* Clamp node types. */ +/** Clamp node types. */ enum { NODE_CLAMP_MINMAX = 0, NODE_CLAMP_RANGE = 1, }; -/* Map range node types. */ +/** Map range node types. */ enum { NODE_MAP_RANGE_LINEAR = 0, NODE_MAP_RANGE_STEPPED = 1, @@ -1947,7 +1948,8 @@ enum { #define SHD_MIXRGB_USE_ALPHA 1 #define SHD_MIXRGB_CLAMP 2 -/* subsurface */ +/* Subsurface. */ + enum { #ifdef DNA_DEPRECATED_ALLOW SHD_SUBSURFACE_COMPATIBLE = 0, /* Deprecated */ @@ -1978,25 +1980,29 @@ enum { /* viewer and composite output. */ #define CMP_NODE_OUTPUT_IGNORE_ALPHA 1 -/* Plane track deform node */ +/* Plane track deform node. */ + enum { CMP_NODEFLAG_PLANETRACKDEFORM_MOTION_BLUR = 1, }; -/* Stabilization node */ +/* Stabilization node. */ + enum { CMP_NODEFLAG_STABILIZE_INVERSE = 1, }; /* Set Alpha Node. */ -/* `NodeSetAlpha.mode` */ + +/** #NodeSetAlpha.mode */ typedef enum CMPNodeSetAlphaMode { CMP_NODE_SETALPHA_MODE_APPLY = 0, CMP_NODE_SETALPHA_MODE_REPLACE_ALPHA = 1, } CMPNodeSetAlphaMode; /* Denoise Node. */ -/* `NodeDenoise.prefilter` */ + +/** #NodeDenoise.prefilter */ typedef enum CMPNodeDenoisePrefilter { CMP_NODE_DENOISE_PREFILTER_FAST = 0, CMP_NODE_DENOISE_PREFILTER_NONE = 1, diff --git a/source/blender/makesdna/DNA_object_force_types.h b/source/blender/makesdna/DNA_object_force_types.h index 0aa4a171511..7d0d4c0d460 100644 --- a/source/blender/makesdna/DNA_object_force_types.h +++ b/source/blender/makesdna/DNA_object_force_types.h @@ -32,7 +32,7 @@ extern "C" { struct BodySpring; -/* pd->forcefield: Effector Fields types */ +/** #PartDeflect.forcefield: Effector Fields types. */ typedef enum ePFieldType { /** (this is used for general effector weight). */ PFIELD_NULL = 0, diff --git a/source/blender/makesdna/DNA_object_types.h b/source/blender/makesdna/DNA_object_types.h index 57c8ef200ae..c9345b6a950 100644 --- a/source/blender/makesdna/DNA_object_types.h +++ b/source/blender/makesdna/DNA_object_types.h @@ -57,7 +57,7 @@ struct SculptSession; struct SoftBody; struct bGPdata; -/* Vertex Groups - Name Info */ +/** Vertex Groups - Name Info */ typedef struct bDeformGroup { struct bDeformGroup *next, *prev; /** MAX_VGROUP_NAME. */ @@ -66,7 +66,7 @@ typedef struct bDeformGroup { char flag, _pad0[7]; } bDeformGroup; -/* Face Maps. */ +/** Face Maps. */ typedef struct bFaceMap { struct bFaceMap *next, *prev; /** MAX_VGROUP_NAME. */ @@ -107,7 +107,7 @@ typedef struct BoundBox { char _pad0[4]; } BoundBox; -/* boundbox flag */ +/** #BoundBox.flag */ enum { BOUNDBOX_DISABLED = (1 << 0), BOUNDBOX_DIRTY = (1 << 1), @@ -115,7 +115,7 @@ enum { struct CustomData_MeshMasks; -/* Not saved in file! */ +/** Not saved in file! */ typedef struct Object_Runtime { /** * The custom data layer mask that was last used @@ -246,7 +246,7 @@ typedef struct Object { /** String describing subobject info, MAX_ID_NAME-2. */ char parsubstr[64]; struct Object *parent, *track; - /* if ob->proxy (or proxy_group), this object is proxy for object ob->proxy */ + /* If `ob->proxy` (or proxy_group), this object is proxy for object `ob->proxy`. */ /* proxy_from is set in target back to the proxy. */ struct Object *proxy, *proxy_group, *proxy_from; /** Old animation system, deprecated for 2.5. */ @@ -322,8 +322,7 @@ typedef struct Object { * Inverse matrix of 'obmat' for any other use than rendering! * * \note this isn't assured to be valid as with 'obmat', - * before using this value you should do... - * invert_m4_m4(ob->imat, ob->obmat); + * before using this value you should do: `invert_m4_m4(ob->imat, ob->obmat)` */ float imat[4][4]; @@ -436,7 +435,7 @@ typedef struct Object { Object_Runtime runtime; } Object; -/* Warning, this is not used anymore because hooks are now modifiers */ +/** DEPRECATED: this is not used anymore because hooks are now modifiers. */ typedef struct ObHook { struct ObHook *next, *prev; @@ -466,7 +465,7 @@ typedef struct ObHook { /* used many places, should be specialized. */ #define SELECT 1 -/* type */ +/** #Object.type */ enum { OB_EMPTY = 0, OB_MESH = 1, @@ -544,7 +543,7 @@ enum { case ID_PT: \ case ID_VO -/* partype: first 4 bits: type */ +/** #Object.partype: first 4 bits: type. */ enum { PARTYPE = (1 << 4) - 1, PAROBJECT = 0, @@ -555,7 +554,7 @@ enum { }; -/* (short) transflag */ +/** #Object.transflag (short) */ enum { OB_TRANSFORM_ADJUST_ROOT_PARENT_FOR_VIEW_LOCK = 1 << 0, OB_TRANSFLAG_UNUSED_1 = 1 << 1, /* cleared */ @@ -577,7 +576,7 @@ enum { OB_DUPLI = OB_DUPLIVERTS | OB_DUPLICOLLECTION | OB_DUPLIFACES | OB_DUPLIPARTS, }; -/* (short) trackflag / upflag */ +/** #Object.trackflag / #Object.upflag (short) */ enum { OB_POSX = 0, OB_POSY = 1, @@ -587,7 +586,7 @@ enum { OB_NEGZ = 5, }; -/* dtx: flags (short) */ +/** #Object.dtx draw type extra flags (short) */ enum { OB_DRAWBOUNDOX = 1 << 0, OB_AXIS = 1 << 1, @@ -606,7 +605,7 @@ enum { OB_USE_GPENCIL_LIGHTS = 1 << 10, }; -/* empty_drawtype: no flags */ +/** #Object.empty_drawtype: no flags */ enum { OB_ARROWS = 1, OB_PLAINAXES = 2, @@ -618,7 +617,10 @@ enum { OB_EMPTY_IMAGE = 8, }; -/* gpencil add types */ +/** + * Grease-pencil add types. + * TODO: doesn't need to be DNA, local to `OBJECT_OT_gpencil_add`. + */ enum { GP_EMPTY = 0, GP_STROKE = 1, @@ -628,7 +630,7 @@ enum { GP_LRT_COLLECTION = 5, }; -/* boundtype */ +/** #Object.boundtype */ enum { OB_BOUND_BOX = 0, OB_BOUND_SPHERE = 1, @@ -642,7 +644,7 @@ enum { /* **************** BASE ********************* */ -/* base->flag_legacy */ +/** #Base.flag_legacy */ enum { BA_WAS_SEL = (1 << 1), /* NOTE: BA_HAS_RECALC_DATA can be re-used later if freed in readfile.c. */ @@ -671,7 +673,7 @@ enum { # define OB_FLAG_UNUSED_12 (1 << 12) /* cleared */ #endif -/* ob->visibility_flag */ +/** #Object.visibility_flag */ enum { OB_HIDE_VIEWPORT = 1 << 0, OB_HIDE_SELECT = 1 << 1, @@ -686,7 +688,7 @@ enum { OB_SHADOW_CATCHER = 1 << 10 }; -/* ob->shapeflag */ +/** #Object.shapeflag */ enum { OB_SHAPE_LOCK = 1 << 0, #ifdef DNA_DEPRECATED_ALLOW @@ -695,7 +697,7 @@ enum { OB_SHAPE_EDIT_MODE = 1 << 2, }; -/* ob->nlaflag */ +/** #Object.nlaflag */ enum { OB_ADS_UNUSED_1 = 1 << 0, /* cleared */ OB_ADS_UNUSED_2 = 1 << 1, /* cleared */ @@ -711,7 +713,7 @@ enum { /* OB_ADS_SHOWPARTS = 1 << 14, */ /* UNUSED */ }; -/* ob->protectflag */ +/** #Object.protectflag */ enum { OB_LOCK_LOCX = 1 << 0, OB_LOCK_LOCY = 1 << 1, @@ -729,13 +731,13 @@ enum { OB_LOCK_ROT4D = 1 << 10, }; -/* ob->duplicator_visibility_flag */ +/** #Object.duplicator_visibility_flag */ enum { OB_DUPLI_FLAG_VIEWPORT = 1 << 0, OB_DUPLI_FLAG_RENDER = 1 << 1, }; -/* ob->empty_image_depth */ +/** #Object.empty_image_depth */ #define OB_EMPTY_IMAGE_DEPTH_DEFAULT 0 #define OB_EMPTY_IMAGE_DEPTH_FRONT 1 #define OB_EMPTY_IMAGE_DEPTH_BACK 2 diff --git a/source/blender/makesdna/DNA_particle_types.h b/source/blender/makesdna/DNA_particle_types.h index a51c532dfb3..5add664f624 100644 --- a/source/blender/makesdna/DNA_particle_types.h +++ b/source/blender/makesdna/DNA_particle_types.h @@ -72,7 +72,7 @@ typedef struct ParticleSpring { unsigned int particle_index[2], delete_flag; } ParticleSpring; -/* Child particles are created around or between parent particles */ +/** Child particles are created around or between parent particles. */ typedef struct ChildParticle { /** Face index on the final derived mesh. */ int num; @@ -364,8 +364,7 @@ typedef struct ParticleSystem { int flag, totpart, totunexist, totchild, totcached, totchildcache; /* NOTE: Recalc is one of ID_RECALC_PSYS_ALL flags. * - * TODO(sergey): Use part->id.recalc instead of this duplicated flag - * somehow. */ + * TODO(sergey): Use #ParticleSettings.id.recalc instead of this duplicated flag somehow. */ int recalc; short target_psys, totkeyed, bakespace; char _pad1[6]; @@ -438,9 +437,11 @@ typedef enum eParticleDrawFlag { PART_DRAW_HAIR_GRID = (1 << 18), } eParticleDrawFlag; -/* part->type +/** + * #ParticleSettings.type * Hair is always baked static in object/geometry space. - * Other types (normal particles) are in global space and not static baked. */ + * Other types (normal particles) are in global space and not static baked. + */ enum { PART_EMITTER = 0, /* REACTOR type currently unused */ @@ -458,7 +459,7 @@ enum { PART_FLUID_SPRAYFOAMBUBBLE = 12, }; -/* Mirroring Mantaflow particle types from particle.h (Mantaflow header). */ +/** Mirroring Mantaflow particle types from particle.h (Mantaflow header). */ enum { /* PARTICLE_TYPE_NONE = (0 << 0), */ /* UNUSED */ /* PARTICLE_TYPE_NEW = (1 << 0), */ /* UNUSED */ @@ -470,7 +471,7 @@ enum { /* PARTICLE_TYPE_INVALID = (1 << 30), */ /* UNUSED */ }; -/* part->flag */ +/** #ParticleSettings.flag */ #define PART_REACT_STA_END 1 #define PART_REACT_MULTIPLE 2 @@ -514,26 +515,26 @@ enum { #define PART_SELF_EFFECT (1 << 22) -/* part->from */ +/** #ParticleSettings.from */ #define PART_FROM_VERT 0 #define PART_FROM_FACE 1 #define PART_FROM_VOLUME 2 /* #define PART_FROM_PARTICLE 3 deprecated! */ #define PART_FROM_CHILD 4 -/* part->distr */ +/** #ParticleSettings.distr */ #define PART_DISTR_JIT 0 #define PART_DISTR_RAND 1 #define PART_DISTR_GRID 2 -/* part->phystype */ +/** #ParticleSettings.phystype */ #define PART_PHYS_NO 0 #define PART_PHYS_NEWTON 1 #define PART_PHYS_KEYED 2 #define PART_PHYS_BOIDS 3 #define PART_PHYS_FLUID 4 -/* part->kink */ +/** #ParticleSettings.kink */ typedef enum eParticleKink { PART_KINK_NO = 0, PART_KINK_CURL = 1, @@ -543,7 +544,7 @@ typedef enum eParticleKink { PART_KINK_SPIRAL = 5, } eParticleKink; -/* part->child_flag */ +/** #ParticleSettings.child_flag */ typedef enum eParticleChildFlag { PART_CHILD_USE_CLUMP_NOISE = (1 << 0), PART_CHILD_USE_CLUMP_CURVE = (1 << 1), @@ -551,22 +552,22 @@ typedef enum eParticleChildFlag { PART_CHILD_USE_TWIST_CURVE = (1 << 3), } eParticleChildFlag; -/* part->shape_flag */ +/** #ParticleSettings.shape_flag */ typedef enum eParticleShapeFlag { PART_SHAPE_CLOSE_TIP = (1 << 0), } eParticleShapeFlag; -/* part->draw_col */ +/* #ParticleSettings.draw_col */ #define PART_DRAW_COL_NONE 0 #define PART_DRAW_COL_MAT 1 #define PART_DRAW_COL_VEL 2 #define PART_DRAW_COL_ACC 3 -/* part->time_flag */ +/* #ParticleSettings.time_flag */ #define PART_TIME_AUTOSF 1 /* Automatic subframes */ -/* part->draw_as */ -/* part->ren_as */ +/* #ParticleSettings.draw_as */ +/* #ParticleSettings.ren_as */ #define PART_DRAW_NOT 0 #define PART_DRAW_DOT 1 #define PART_DRAW_HALO 1 @@ -580,13 +581,13 @@ typedef enum eParticleShapeFlag { #define PART_DRAW_BB 9 /* deprecated */ #define PART_DRAW_REND 10 -/* part->integrator */ +/* #ParticleSettings.integrator */ #define PART_INT_EULER 0 #define PART_INT_MIDPOINT 1 #define PART_INT_RK4 2 #define PART_INT_VERLET 3 -/* part->rotmode */ +/* #ParticleSettings.rotmode */ #define PART_ROT_NOR 1 #define PART_ROT_VEL 2 #define PART_ROT_GLOB_X 3 @@ -597,7 +598,7 @@ typedef enum eParticleShapeFlag { #define PART_ROT_OB_Z 8 #define PART_ROT_NOR_TAN 9 -/* part->avemode */ +/* #ParticleSettings.avemode */ #define PART_AVE_VELOCITY 1 #define PART_AVE_RAND 2 #define PART_AVE_HORIZONTAL 3 @@ -606,12 +607,12 @@ typedef enum eParticleShapeFlag { #define PART_AVE_GLOBAL_Y 6 #define PART_AVE_GLOBAL_Z 7 -/* part->reactevent */ +/* #ParticleSettings.reactevent */ #define PART_EVENT_DEATH 0 #define PART_EVENT_COLLIDE 1 #define PART_EVENT_NEAR 2 -/* part->childtype */ +/* #ParticleSettings.childtype */ #define PART_CHILD_PARTICLES 1 #define PART_CHILD_FACES 2 @@ -675,7 +676,7 @@ typedef enum eParticleShapeFlag { #define PTARGET_MODE_FRIEND 1 #define PTARGET_MODE_ENEMY 2 -/* mapto */ +/** #MTex.mapto */ typedef enum eParticleTextureInfluence { /* init */ PAMAP_TIME = (1 << 0), /* emission time */ diff --git a/source/blender/makesdna/DNA_pointcloud_types.h b/source/blender/makesdna/DNA_pointcloud_types.h index 26f1db3324b..53fda29a33a 100644 --- a/source/blender/makesdna/DNA_pointcloud_types.h +++ b/source/blender/makesdna/DNA_pointcloud_types.h @@ -54,7 +54,7 @@ typedef struct PointCloud { void *batch_cache; } PointCloud; -/* PointCloud.flag */ +/** #PointCloud.flag */ enum { PT_DS_EXPAND = (1 << 0), }; diff --git a/source/blender/makesdna/DNA_rigidbody_types.h b/source/blender/makesdna/DNA_rigidbody_types.h index aa11e74e89d..f653905e169 100644 --- a/source/blender/makesdna/DNA_rigidbody_types.h +++ b/source/blender/makesdna/DNA_rigidbody_types.h @@ -38,7 +38,7 @@ struct EffectorWeights; /* ******************************** */ /* RigidBody World */ -/* Container for data shared by original and evaluated copies of RigidBodyWorld */ +/** Container for data shared by original and evaluated copies of #RigidBodyWorld. */ typedef struct RigidBodyWorld_Shared { /* cache */ struct PointCache *pointcache; @@ -90,7 +90,7 @@ typedef struct RigidBodyWorld { float time_scale; } RigidBodyWorld; -/* Flags for RigidBodyWorld */ +/** RigidBodyWorld.flag */ typedef enum eRigidBodyWorld_Flag { /* should sim world be skipped when evaluating (user setting) */ RBW_FLAG_MUTED = (1 << 0), @@ -170,7 +170,7 @@ typedef struct RigidBodyOb { struct RigidBodyOb_Shared *shared; } RigidBodyOb; -/* Participation types for RigidBodyOb */ +/** #RigidBodyOb.type */ typedef enum eRigidBodyOb_Type { /* active geometry participant in simulation. is directly controlled by sim */ RBO_TYPE_ACTIVE = 0, @@ -178,7 +178,7 @@ typedef enum eRigidBodyOb_Type { RBO_TYPE_PASSIVE = 1, } eRigidBodyOb_Type; -/* Flags for RigidBodyOb */ +/** #RigidBodyOb.flag */ typedef enum eRigidBodyOb_Flag { /* rigidbody is kinematic (controlled by the animation system) */ RBO_FLAG_KINEMATIC = (1 << 0), @@ -198,7 +198,7 @@ typedef enum eRigidBodyOb_Flag { RBO_FLAG_USE_DEFORM = (1 << 7), } eRigidBodyOb_Flag; -/* RigidBody Collision Shape */ +/** Rigid Body Collision Shape. */ typedef enum eRigidBody_Shape { /** Simple box (i.e. bounding box). */ RB_SHAPE_BOX = 0, @@ -304,7 +304,7 @@ typedef struct RigidBodyCon { void *physics_constraint; } RigidBodyCon; -/* Participation types for RigidBodyOb */ +/** Participation types for #RigidBodyOb.type */ typedef enum eRigidBodyCon_Type { /** lets bodies rotate around a specified point */ RBC_TYPE_POINT = 0, @@ -333,13 +333,13 @@ typedef enum eRigidBodyCon_Type { RBC_TYPE_MOTOR = 11, } eRigidBodyCon_Type; -/* Spring implementation type for RigidBodyOb */ +/** Spring implementation type for RigidBodyOb. */ typedef enum eRigidBodyCon_SpringType { RBC_SPRING_TYPE1 = 0, /* btGeneric6DofSpringConstraint */ RBC_SPRING_TYPE2 = 1, /* btGeneric6DofSpring2Constraint */ } eRigidBodyCon_SpringType; -/* Flags for RigidBodyCon */ +/** #RigidBodyCon.flag */ typedef enum eRigidBodyCon_Flag { /* constraint influences rigid body motion */ RBC_FLAG_ENABLED = (1 << 0), diff --git a/source/blender/makesdna/DNA_scene_types.h b/source/blender/makesdna/DNA_scene_types.h index c66ac3a6211..4607a47a9a8 100644 --- a/source/blender/makesdna/DNA_scene_types.h +++ b/source/blender/makesdna/DNA_scene_types.h @@ -195,7 +195,7 @@ typedef struct AudioData { /* *************************************************************** */ /* Render Layers */ -/* Render Layer */ +/** Render Layer. */ typedef struct SceneRenderLayer { struct SceneRenderLayer *next, *prev; @@ -323,7 +323,7 @@ typedef enum eScenePassType { #define RE_PASSNAME_BLOOM "BloomCol" #define RE_PASSNAME_VOLUME_LIGHT "VolumeDir" -/* View - MultiView */ +/** View - MultiView. */ typedef struct SceneRenderView { struct SceneRenderView *next, *prev; @@ -785,12 +785,12 @@ typedef struct RenderData { struct CurveMapping mblur_shutter_curve; } RenderData; -/* RenderData.quality_flag */ +/** #RenderData.quality_flag */ typedef enum eQualityOption { SCE_PERF_HQ_NORMALS = (1 << 0), } eQualityOption; -/* RenderData.hair_type */ +/** #RenderData.hair_type */ typedef enum eHairType { SCE_HAIR_SHAPE_STRAND = 0, SCE_HAIR_SHAPE_STRIP = 1, @@ -799,7 +799,7 @@ typedef enum eHairType { /* *************************************************************** */ /* Render Conversion/Simplification Settings */ -/* control render convert and shading engine */ +/** Control render convert and shading engine. */ typedef struct RenderProfile { struct RenderProfile *next, *prev; char name[32]; @@ -829,7 +829,7 @@ typedef struct RenderProfile { #define STEREO_RIGHT_SUFFIX "_R" #define STEREO_LEFT_SUFFIX "_L" -/* View3D.stereo3d_camera / View3D.multiview_eye / ImageUser.multiview_eye */ +/** #View3D.stereo3d_camera / #View3D.multiview_eye / #ImageUser.multiview_eye */ typedef enum eStereoViews { STEREO_LEFT_ID = 0, STEREO_RIGHT_ID = 1, @@ -861,12 +861,12 @@ typedef struct Paint_Runtime { char _pad[2]; } Paint_Runtime; -/* We might want to store other things here. */ +/** We might want to store other things here. */ typedef struct PaintToolSlot { struct Brush *brush; } PaintToolSlot; -/* Paint Tool Base */ +/** Paint Tool Base. */ typedef struct Paint { struct Brush *brush; @@ -903,7 +903,7 @@ typedef struct Paint { /* ------------------------------------------- */ /* Image Paint */ -/* Texture/Image Editor */ +/** Texture/Image Editor. */ typedef struct ImagePaintSettings { Paint paint; @@ -934,7 +934,7 @@ typedef struct ImagePaintSettings { /* ------------------------------------------- */ /* Particle Edit */ -/* Settings for a Particle Editing Brush */ +/** Settings for a Particle Editing Brush. */ typedef struct ParticleBrushData { /** Common setting. */ short size; @@ -944,7 +944,7 @@ typedef struct ParticleBrushData { float strength; } ParticleBrushData; -/* Particle Edit Mode Settings */ +/** Particle Edit Mode Settings. */ typedef struct ParticleEditSettings { short flag; short totrekey; @@ -971,7 +971,7 @@ typedef struct ParticleEditSettings { /* ------------------------------------------- */ /* Sculpt */ -/* Sculpt */ +/** Sculpt. */ typedef struct Sculpt { Paint paint; @@ -1006,7 +1006,7 @@ typedef struct UvSculpt { Paint paint; } UvSculpt; -/* grease pencil drawing brushes */ +/** Grease pencil drawing brushes. */ typedef struct GpPaint { Paint paint; int flag; @@ -1020,21 +1020,21 @@ enum { GPPAINT_FLAG_USE_VERTEXCOLOR = 1, }; -/* Grease pencil vertex paint. */ +/** Grease pencil vertex paint. */ typedef struct GpVertexPaint { Paint paint; int flag; char _pad[4]; } GpVertexPaint; -/* Grease pencil sculpt paint. */ +/** Grease pencil sculpt paint. */ typedef struct GpSculptPaint { Paint paint; int flag; char _pad[4]; } GpSculptPaint; -/* Grease pencil weight paint. */ +/** Grease pencil weight paint. */ typedef struct GpWeightPaint { Paint paint; int flag; @@ -1044,7 +1044,7 @@ typedef struct GpWeightPaint { /* ------------------------------------------- */ /* Vertex Paint */ -/* Vertex Paint */ +/** Vertex Paint. */ typedef struct VPaint { Paint paint; char flag; @@ -1062,7 +1062,7 @@ enum { /* ------------------------------------------- */ /* GPencil Stroke Sculpting */ -/* GP_Sculpt_Settings.lock_axis */ +/** #GP_Sculpt_Settings.lock_axis */ typedef enum eGP_Lockaxis_Types { GP_LOCKAXIS_VIEW = 0, GP_LOCKAXIS_X = 1, @@ -1071,7 +1071,7 @@ typedef enum eGP_Lockaxis_Types { GP_LOCKAXIS_CURSOR = 4, } eGP_Lockaxis_Types; -/* Settings for a GPencil Speed Guide */ +/** Settings for a GPencil Speed Guide. */ typedef struct GP_Sculpt_Guide { char use_guide; char use_snapping; @@ -1085,7 +1085,7 @@ typedef struct GP_Sculpt_Guide { struct Object *reference_object; } GP_Sculpt_Guide; -/* GPencil Stroke Sculpting Settings */ +/** GPencil Stroke Sculpting Settings. */ typedef struct GP_Sculpt_Settings { /** Runtime. */ void *paintcursor; @@ -1134,7 +1134,7 @@ typedef enum eGP_vertex_SelectMaskFlag { GP_VERTEX_MASK_SELECTMODE_SEGMENT = (1 << 2), } eGP_Vertex_SelectMaskFlag; -/* Settings for GP Interpolation Operators */ +/** Settings for GP Interpolation Operators. */ typedef struct GP_Interpolate_Settings { /** Custom interpolation curve (for use with GP_IPO_CURVEMAP). */ struct CurveMapping *custom_ipo; @@ -1255,7 +1255,7 @@ typedef struct UnifiedPaintSettings { struct ColorSpace *colorspace; } UnifiedPaintSettings; -/* UnifiedPaintSettings.flag */ +/** #UnifiedPaintSettings.flag */ typedef enum { UNIFIED_PAINT_SIZE = (1 << 0), UNIFIED_PAINT_ALPHA = (1 << 1), @@ -1313,7 +1313,7 @@ enum { /* *************************************************************** */ /* Stats */ -/* Stats for Meshes */ +/** Stats for Meshes. */ typedef struct MeshStatVis { char type; char _pad1[2]; @@ -1570,8 +1570,8 @@ typedef struct PhysicsSettings { char _pad0[4]; } PhysicsSettings; -/* ------------------------------------------- */ -/* Safe Area options used in Camera View & Sequencer +/** + * Safe Area options used in Camera View & Sequencer. */ typedef struct DisplaySafeAreas { /* each value represents the (x,y) margins as a multiplier. @@ -1587,8 +1587,9 @@ typedef struct DisplaySafeAreas { float action_center[2]; } DisplaySafeAreas; -/* ------------------------------------------- */ -/* Scene Display - used for store scene specific display settings for the 3d view */ +/** + * Scene Display - used for store scene specific display settings for the 3d view. + */ typedef struct SceneDisplay { /** Light direction for shadows/highlight. */ float light_direction[3]; diff --git a/source/blender/makesdna/DNA_screen_types.h b/source/blender/makesdna/DNA_screen_types.h index a4c254d6e5a..1a1d7cba7af 100644 --- a/source/blender/makesdna/DNA_screen_types.h +++ b/source/blender/makesdna/DNA_screen_types.h @@ -244,7 +244,7 @@ typedef struct PanelCategoryDyn { rcti rect; } PanelCategoryDyn; -/* region stack of active tabs */ +/** Region stack of active tabs. */ typedef struct PanelCategoryStack { struct PanelCategoryStack *next, *prev; char idname[64]; @@ -654,8 +654,10 @@ enum { #define UILST_FLT_SORT_MASK (((unsigned int)(UILST_FLT_SORT_REVERSE | UILST_FLT_SORT_LOCK)) - 1) -/* regiontype, first two are the default set */ -/* Do NOT change order, append on end. Types are hardcoded needed */ +/** + * regiontype, first two are the default set. + * \warning Do NOT change order, append on end. Types are hard-coded needed. + */ typedef enum eRegion_Type { RGN_TYPE_WINDOW = 0, RGN_TYPE_HEADER = 1, diff --git a/source/blender/makesdna/DNA_sequence_types.h b/source/blender/makesdna/DNA_sequence_types.h index e1bba60396a..5fe67a34dae 100644 --- a/source/blender/makesdna/DNA_sequence_types.h +++ b/source/blender/makesdna/DNA_sequence_types.h @@ -47,6 +47,10 @@ struct SequenceLookup; struct VFont; struct bSound; +/* -------------------------------------------------------------------- */ +/** \name Sequence & Editing Structs + * \{ */ + /* strlens; 256= FILE_MAXFILE, 768= FILE_MAXDIR */ typedef struct StripAnim { @@ -311,7 +315,12 @@ typedef struct Editing { void *_pad1; } Editing; -/* ************* Effect Variable Structs ********* */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Effect Variable Structs + * \{ */ + typedef struct WipeVars { float edgeWidth, angle; short forward, wipetype; @@ -360,7 +369,7 @@ typedef struct SpeedControlVars { float speed_fader_frame_number; } SpeedControlVars; -/* SpeedControlVars.speed_control_type */ +/** #SpeedControlVars.speed_control_type */ enum { SEQ_SPEED_STRETCH = 0, SEQ_SPEED_MULTIPLY = 1, @@ -387,7 +396,7 @@ typedef struct TextVars { char _pad[5]; } TextVars; -/* TextVars.flag */ +/** #TextVars.flag */ enum { SEQ_TEXT_SHADOW = (1 << 0), SEQ_TEXT_BOX = (1 << 1), @@ -395,14 +404,14 @@ enum { SEQ_TEXT_ITALIC = (1 << 3), }; -/* TextVars.align */ +/** #TextVars.align */ enum { SEQ_TEXT_ALIGN_X_LEFT = 0, SEQ_TEXT_ALIGN_X_CENTER = 1, SEQ_TEXT_ALIGN_X_RIGHT = 2, }; -/* TextVars.align_y */ +/** #TextVars.align_y */ enum { SEQ_TEXT_ALIGN_Y_TOP = 0, SEQ_TEXT_ALIGN_Y_CENTER = 1, @@ -418,7 +427,11 @@ typedef struct ColorMixVars { float factor; } ColorMixVars; -/* ***************** Sequence modifiers ****************** */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Sequence Modifiers + * \{ */ typedef struct SequenceModifierData { struct SequenceModifierData *next, *prev; @@ -489,7 +502,11 @@ enum { SEQ_TONEMAP_RD_PHOTORECEPTOR = 1, }; -/* ***************** Scopes ****************** */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Scopes + * \{ */ typedef struct SequencerScopes { struct ImBuf *reference_ibuf; @@ -522,10 +539,15 @@ typedef struct SequencerScopes { #define SEQ_SPEED_UNUSED_3 (1 << 2) /* cleared */ #define SEQ_SPEED_USE_INTERPOLATION (1 << 3) -/* ***************** SEQUENCE ****************** */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Flags & Types + * \{ */ + #define SEQ_NAME_MAXSTR 64 -/* seq->flag */ +/** #Sequence.flag */ enum { /* SELECT */ SEQ_LEFTSEL = (1 << 1), @@ -568,7 +590,7 @@ enum { SEQ_INVALID_EFFECT = (1u << 31), }; -/* StripProxy->storage */ +/** #StripProxy.storage */ enum { SEQ_STORAGE_PROXY_CUSTOM_FILE = (1 << 1), /* store proxy in custom directory */ SEQ_STORAGE_PROXY_CUSTOM_DIR = (1 << 2), /* store proxy in custom file */ @@ -601,18 +623,22 @@ enum { #define SEQ_PROXY_TC_RECORD_RUN_NO_GAPS 8 #define SEQ_PROXY_TC_ALL 15 -/* SeqProxy->build_flags */ +/** SeqProxy.build_flags */ enum { SEQ_PROXY_SKIP_EXISTING = 1, }; -/* seq->alpha_mode */ +/** #Sequence.alpha_mode */ enum { SEQ_ALPHA_STRAIGHT = 0, SEQ_ALPHA_PREMUL = 1, }; -/* seq->type WATCH IT: SEQ_TYPE_EFFECT BIT is used to determine if this is an effect strip!!! */ +/** + * #Sequence.type + * + * \warning #SEQ_TYPE_EFFECT BIT is used to determine if this is an effect strip! + */ enum { SEQ_TYPE_IMAGE = 0, SEQ_TYPE_META = 1, @@ -681,7 +707,7 @@ enum { /* modifiers */ -/* SequenceModifierData->type */ +/** #SequenceModifierData.type */ enum { seqModifierType_ColorBalance = 1, seqModifierType_Curves = 2, @@ -694,7 +720,7 @@ enum { NUM_SEQUENCE_MODIFIER_TYPES, }; -/* SequenceModifierData->flag */ +/** #SequenceModifierData.flag */ enum { SEQUENCE_MODIFIER_MUTE = (1 << 0), SEQUENCE_MODIFIER_EXPANDED = (1 << 1), @@ -712,13 +738,14 @@ enum { SEQUENCE_MASK_TIME_ABSOLUTE = 1, }; -/* Sequence->cache_flag - * SEQ_CACHE_STORE_RAW - * SEQ_CACHE_STORE_PREPROCESSED - * SEQ_CACHE_STORE_COMPOSITE - * FINAL_OUT is ignored +/** + * #Sequence.cache_flag + * - #SEQ_CACHE_STORE_RAW + * - #SEQ_CACHE_STORE_PREPROCESSED + * - #SEQ_CACHE_STORE_COMPOSITE + * - #FINAL_OUT is ignored * - * Editing->cache_flag + * #Editing.cache_flag * all entries */ enum { @@ -745,7 +772,7 @@ enum { SEQ_CACHE_STORE_THUMBNAIL = (1 << 12), }; -/* Sequence->color_tag. */ +/** #Sequence.color_tag. */ typedef enum SequenceColorTag { SEQUENCE_COLOR_NONE = -1, SEQUENCE_COLOR_01, diff --git a/source/blender/makesdna/DNA_shader_fx_types.h b/source/blender/makesdna/DNA_shader_fx_types.h index 01e3b3a5230..be787c1760f 100644 --- a/source/blender/makesdna/DNA_shader_fx_types.h +++ b/source/blender/makesdna/DNA_shader_fx_types.h @@ -77,7 +77,7 @@ typedef struct ShaderFxData { char *error; } ShaderFxData; -/* Runtime temp data */ +/** Runtime temp data. */ typedef struct ShaderFxData_Runtime { float loc[3]; char _pad[4]; diff --git a/source/blender/makesdna/DNA_simulation_types.h b/source/blender/makesdna/DNA_simulation_types.h index a700c9fe2f8..b14301ed32d 100644 --- a/source/blender/makesdna/DNA_simulation_types.h +++ b/source/blender/makesdna/DNA_simulation_types.h @@ -38,7 +38,7 @@ typedef struct Simulation { char _pad[4]; } Simulation; -/* Simulation.flag */ +/** #Simulation.flag */ enum { SIM_DS_EXPAND = (1 << 0), }; diff --git a/source/blender/makesdna/DNA_space_types.h b/source/blender/makesdna/DNA_space_types.h index a7a19be5b3e..7837409cece 100644 --- a/source/blender/makesdna/DNA_space_types.h +++ b/source/blender/makesdna/DNA_space_types.h @@ -61,16 +61,16 @@ struct bNodeTree; struct wmOperator; struct wmTimer; -/* Defined in `buttons_intern.h`. */ +/** Defined in `buttons_intern.h`. */ typedef struct SpaceProperties_Runtime SpaceProperties_Runtime; -/* Defined in `node_intern.hh`. */ +/** Defined in `node_intern.hh`. */ typedef struct SpaceNode_Runtime SpaceNode_Runtime; -/* Defined in `file_intern.h`. */ +/** Defined in `file_intern.h`. */ typedef struct SpaceFile_Runtime SpaceFile_Runtime; -/* Defined in `spreadsheet_intern.hh`. */ +/** Defined in `spreadsheet_intern.hh`. */ typedef struct SpaceSpreadsheet_Runtime SpaceSpreadsheet_Runtime; /* -------------------------------------------------------------------- */ @@ -91,7 +91,7 @@ typedef struct SpaceLink { char _pad0[6]; } SpaceLink; -/* SpaceLink.link_flag */ +/** #SpaceLink.link_flag */ enum { /** * The space is not a regular one opened through the editor menu (for example) but spawned by an @@ -113,7 +113,7 @@ enum { /** \name Space Info * \{ */ -/* Info Header */ +/** Info Header. */ typedef struct SpaceInfo { SpaceLink *next, *prev; /** Storage of regions for inactive spaces. */ @@ -127,7 +127,7 @@ typedef struct SpaceInfo { char _pad[7]; } SpaceInfo; -/* SpaceInfo.rpt_mask */ +/** #SpaceInfo.rpt_mask */ typedef enum eSpaceInfo_RptMask { INFO_RPT_DEBUG = (1 << 0), INFO_RPT_INFO = (1 << 1), @@ -142,7 +142,7 @@ typedef enum eSpaceInfo_RptMask { /** \name Properties Editor * \{ */ -/* Properties Editor */ +/** Properties Editor. */ typedef struct SpaceProperties { SpaceLink *next, *prev; /** Storage of regions for inactive spaces. */ @@ -210,7 +210,7 @@ typedef struct SpaceProperties { // #define BUTS_EFFECTS 14 #endif /* DNA_DEPRECATED_ALLOW */ -/* SpaceProperties.mainb new */ +/** #SpaceProperties.mainb new */ typedef enum eSpaceButtons_Context { BCONTEXT_RENDER = 0, BCONTEXT_SCENE = 1, @@ -235,7 +235,7 @@ typedef enum eSpaceButtons_Context { BCONTEXT_TOT, } eSpaceButtons_Context; -/* SpaceProperties.flag */ +/** #SpaceProperties.flag */ typedef enum eSpaceButtons_Flag { /* SB_PRV_OSA = (1 << 0), */ /* UNUSED */ SB_PIN_CONTEXT = (1 << 1), @@ -246,7 +246,7 @@ typedef enum eSpaceButtons_Flag { SB_SHADING_CONTEXT = (1 << 4), } eSpaceButtons_Flag; -/* SpaceProperties.outliner_sync */ +/** #SpaceProperties.outliner_sync */ typedef enum eSpaceButtons_OutlinerSync { PROPERTIES_SYNC_AUTO = 0, PROPERTIES_SYNC_NEVER = 1, @@ -259,10 +259,10 @@ typedef enum eSpaceButtons_OutlinerSync { /** \name Outliner * \{ */ -/* Defined in `outliner_intern.h`. */ +/** Defined in `outliner_intern.h`. */ typedef struct SpaceOutliner_Runtime SpaceOutliner_Runtime; -/* Outliner */ +/** Outliner */ typedef struct SpaceOutliner { SpaceLink *next, *prev; /** Storage of regions for inactive spaces. */ @@ -303,7 +303,7 @@ typedef struct SpaceOutliner { SpaceOutliner_Runtime *runtime; } SpaceOutliner; -/* SpaceOutliner.flag */ +/** #SpaceOutliner.flag */ typedef enum eSpaceOutliner_Flag { /* SO_TESTBLOCKS = (1 << 0), */ /* UNUSED */ /* SO_NEWSELECTED = (1 << 1), */ /* UNUSED */ @@ -314,7 +314,7 @@ typedef enum eSpaceOutliner_Flag { SO_MODE_COLUMN = (1 << 6), } eSpaceOutliner_Flag; -/* SpaceOutliner.filter */ +/** #SpaceOutliner.filter */ typedef enum eSpaceOutliner_Filter { SO_FILTER_SEARCH = (1 << 0), /* Run-time flag. */ SO_FILTER_CLEARED_1 = (1 << 1), @@ -356,7 +356,7 @@ typedef enum eSpaceOutliner_Filter { (SO_FILTER_NO_OB_CONTENT | SO_FILTER_NO_CHILDREN | SO_FILTER_OB_TYPE | SO_FILTER_OB_STATE | \ SO_FILTER_NO_COLLECTION | SO_FILTER_NO_VIEW_LAYERS | SO_FILTER_NO_LIB_OVERRIDE) -/* SpaceOutliner.filter_state */ +/** #SpaceOutliner.filter_state */ typedef enum eSpaceOutliner_StateFilter { SO_FILTER_OB_ALL = 0, SO_FILTER_OB_VISIBLE = 1, @@ -366,7 +366,7 @@ typedef enum eSpaceOutliner_StateFilter { SO_FILTER_OB_SELECTABLE = 5, } eSpaceOutliner_StateFilter; -/* SpaceOutliner.show_restrict_flags */ +/** #SpaceOutliner.show_restrict_flags */ typedef enum eSpaceOutliner_ShowRestrictFlag { SO_RESTRICT_ENABLE = (1 << 0), SO_RESTRICT_SELECT = (1 << 1), @@ -377,7 +377,7 @@ typedef enum eSpaceOutliner_ShowRestrictFlag { SO_RESTRICT_INDIRECT_ONLY = (1 << 6), } eSpaceOutliner_Restrict; -/* SpaceOutliner.outlinevis */ +/** #SpaceOutliner.outlinevis */ typedef enum eSpaceOutliner_Mode { SO_SCENES = 0, /* SO_CUR_SCENE = 1, */ /* deprecated! */ @@ -398,7 +398,7 @@ typedef enum eSpaceOutliner_Mode { SO_OVERRIDES_LIBRARY = 16, } eSpaceOutliner_Mode; -/* SpaceOutliner.storeflag */ +/** #SpaceOutliner.storeflag */ typedef enum eSpaceOutliner_StoreFlag { /* cleanup tree */ SO_TREESTORE_CLEANUP = (1 << 0), @@ -408,7 +408,7 @@ typedef enum eSpaceOutliner_StoreFlag { SO_TREESTORE_REBUILD = (1 << 2), } eSpaceOutliner_StoreFlag; -/* outliner search flags (SpaceOutliner.search_flags) */ +/** Outliner search flags (#SpaceOutliner.search_flags) */ typedef enum eSpaceOutliner_Search_Flags { SO_FIND_CASE_SENSITIVE = (1 << 0), SO_FIND_COMPLETE = (1 << 1), @@ -429,7 +429,7 @@ typedef struct SpaceGraph_Runtime { ListBase ghost_curves; } SpaceGraph_Runtime; -/* 'Graph' Editor (formerly known as the IPO Editor) */ +/** 'Graph' Editor (formerly known as the IPO Editor). */ typedef struct SpaceGraph { SpaceLink *next, *prev; /** Storage of regions for inactive spaces. */ @@ -467,7 +467,7 @@ typedef struct SpaceGraph { SpaceGraph_Runtime runtime; } SpaceGraph; -/* SpaceGraph.flag (Graph Editor Settings) */ +/** #SpaceGraph.flag (Graph Editor Settings) */ typedef enum eGraphEdit_Flag { /* OLD DEPRECATED SETTING */ /* SIPO_LOCK_VIEW = (1 << 0), */ @@ -504,7 +504,7 @@ typedef enum eGraphEdit_Flag { SIPO_NO_DRAW_EXTRAPOLATION = (1 << 17), } eGraphEdit_Flag; -/* SpaceGraph.mode (Graph Editor Mode) */ +/** #SpaceGraph.mode (Graph Editor Mode) */ typedef enum eGraphEdit_Mode { /* all animation curves (from all over Blender) */ SIPO_MODE_ANIMATION = 0, @@ -532,7 +532,7 @@ typedef enum eGraphEdit_Runtime_Flag { /** \name NLA Editor * \{ */ -/* NLA Editor */ +/** NLA Editor */ typedef struct SpaceNla { struct SpaceLink *next, *prev; /** Storage of regions for inactive spaces. */ @@ -552,7 +552,7 @@ typedef struct SpaceNla { View2D v2d DNA_DEPRECATED; } SpaceNla; -/* SpaceNla.flag */ +/** #SpaceNla.flag */ typedef enum eSpaceNla_Flag { SNLA_FLAG_UNUSED_0 = (1 << 0), SNLA_FLAG_UNUSED_1 = (1 << 1), @@ -581,7 +581,7 @@ typedef struct SequencerPreviewOverlay { char _pad0[4]; } SequencerPreviewOverlay; -/* SequencerPreviewOverlay.flag */ +/** #SequencerPreviewOverlay.flag */ typedef enum eSpaceSeq_SequencerPreviewOverlay_Flag { SEQ_PREVIEW_SHOW_2D_CURSOR = (1 << 1), SEQ_PREVIEW_SHOW_OUTLINE_SELECTED = (1 << 2), @@ -596,7 +596,7 @@ typedef struct SequencerTimelineOverlay { char _pad0[4]; } SequencerTimelineOverlay; -/* SequencerTimelineOverlay.flag */ +/** #SequencerTimelineOverlay.flag */ typedef enum eSpaceSeq_SequencerTimelineOverlay_Flag { SEQ_TIMELINE_SHOW_STRIP_OFFSETS = (1 << 1), SEQ_TIMELINE_SHOW_THUMBNAILS = (1 << 2), @@ -617,7 +617,7 @@ typedef struct SpaceSeqRuntime { struct GHash *last_displayed_thumbnails; } SpaceSeqRuntime; -/* Sequencer */ +/** Sequencer. */ typedef struct SpaceSeq { SpaceLink *next, *prev; /** Storage of regions for inactive spaces. */ @@ -667,7 +667,7 @@ typedef struct SpaceSeq { SpaceSeqRuntime runtime; } SpaceSeq; -/* SpaceSeq.mainb */ +/** #SpaceSeq.mainb */ typedef enum eSpaceSeq_RegionType { SEQ_DRAW_IMG_IMBUF = 1, SEQ_DRAW_IMG_WAVEFORM = 2, @@ -675,14 +675,14 @@ typedef enum eSpaceSeq_RegionType { SEQ_DRAW_IMG_HISTOGRAM = 4, } eSpaceSeq_RegionType; -/* SpaceSeq.draw_flag */ +/** #SpaceSeq.draw_flag */ typedef enum eSpaceSeq_DrawFlag { SEQ_DRAW_BACKDROP = (1 << 0), SEQ_DRAW_UNUSED_1 = (1 << 1), SEQ_DRAW_TRANSFORM_PREVIEW = (1 << 2), } eSpaceSeq_DrawFlag; -/* SpaceSeq.flag */ +/** #SpaceSeq.flag */ typedef enum eSpaceSeq_Flag { SEQ_DRAWFRAMES = (1 << 0), SEQ_MARKER_TRANS = (1 << 1), @@ -703,14 +703,14 @@ typedef enum eSpaceSeq_Flag { SEQ_SHOW_GRID = (1 << 18), } eSpaceSeq_Flag; -/* SpaceSeq.view */ +/** #SpaceSeq.view */ typedef enum eSpaceSeq_Displays { SEQ_VIEW_SEQUENCE = 1, SEQ_VIEW_PREVIEW = 2, SEQ_VIEW_SEQUENCE_PREVIEW = 3, } eSpaceSeq_Dispays; -/* SpaceSeq.render_size */ +/** #SpaceSeq.render_size */ typedef enum eSpaceSeq_Proxy_RenderSize { SEQ_RENDER_SIZE_NONE = -1, SEQ_RENDER_SIZE_SCENE = 0, @@ -740,7 +740,7 @@ enum { SEQ_GIZMO_HIDE_TOOL = (1 << 3), }; -/* SpaceSeq.mainb */ +/** #SpaceSeq.mainb */ typedef enum eSpaceSeq_OverlayFrameType { SEQ_OVERLAY_FRAME_TYPE_RECT = 0, SEQ_OVERLAY_FRAME_TYPE_REFERENCE = 1, @@ -753,7 +753,7 @@ typedef enum eSpaceSeq_OverlayFrameType { /** \name File Selector * \{ */ -/* Config and Input for File Selector */ +/** Config and Input for File Selector. */ typedef struct FileSelectParams { /** Title, also used for the text of the execute button. */ char title[96]; @@ -857,7 +857,7 @@ typedef struct FileFolderHistory { ListBase folders_next; } FileFolderHistory; -/* File Browser */ +/** File Browser. */ typedef struct SpaceFile { SpaceLink *next, *prev; /** Storage of regions for inactive spaces. */ @@ -921,7 +921,7 @@ typedef struct SpaceFile { SpaceFile_Runtime *runtime; } SpaceFile; -/* SpaceFile.browse_mode (File Space Browsing Mode) */ +/** #SpaceFile.browse_mode (File Space Browsing Mode). */ typedef enum eFileBrowse_Mode { /* Regular Blender File Browser */ FILE_BROWSE_MODE_FILES = 0, @@ -929,7 +929,7 @@ typedef enum eFileBrowse_Mode { FILE_BROWSE_MODE_ASSETS = 1, } eFileBrowse_Mode; -/* FileSelectParams.display */ +/** #FileSelectParams.display */ enum eFileDisplayType { /** Internal (not exposed to users): Keep whatever display type was used during the last File * Browser use, or the default if no such record is found. Use this unless there's a good reason @@ -943,7 +943,7 @@ enum eFileDisplayType { FILE_IMGDISPLAY = 3, }; -/* FileSelectParams.sort */ +/** #FileSelectParams.sort */ enum eFileSortType { /** Internal (not exposed to users): Sort by whatever was sorted by during the last File Browser * use, or the default if no such record is found. Use this unless there's a good reason to set a @@ -958,14 +958,14 @@ enum eFileSortType { FILE_SORT_SIZE = 4, }; -/* SpaceFile.tags */ +/** #SpaceFile.tags */ enum eFileTags { /** Tag the space as having to update files representing or containing main data. Must be set * after file read and undo/redo. */ FILE_TAG_REBUILD_MAIN_FILES = (1 << 0), }; -/* FileSelectParams.details_flags */ +/** #FileSelectParams.details_flags */ enum eFileDetails { FILE_DETAILS_SIZE = (1 << 0), FILE_DETAILS_DATETIME = (1 << 1), @@ -986,7 +986,7 @@ enum eFileDetails { */ #define FILE_SELECT_MAX_RECURSIONS (FILE_MAX_LIBEXTRA / 2) -/* filesel types */ +/** Filesel types. */ typedef enum eFileSelectType { FILE_LOADLIB = 1, FILE_MAIN = 2, @@ -1000,14 +1000,14 @@ typedef enum eFileSelectType { FILE_SPECIAL = 9, } eFileSelectType; -/* filesel op property -> action */ +/** filesel op property -> action. */ typedef enum eFileSel_Action { FILE_OPENFILE = 0, FILE_SAVE = 1, } eFileSel_Action; -/* sfile->params->flag */ /** + * #FileSelectParams.flag / `sfile->params->flag`. * \note short flag, also used as 16 lower bits of flags in link/append code * (WM and BLO code area, see #eBLOLibLinkFlags in BLO_readfile.h). */ @@ -1037,8 +1037,10 @@ typedef enum eFileSel_Params_AssetCatalogVisibility { FILE_SHOW_ASSETS_WITHOUT_CATALOG, } eFileSel_Params_AssetCatalogVisibility; -/* sfile->params->rename_flag */ -/* NOTE: short flag. Defined as bitflags, but currently only used as exclusive status markers... */ +/** + * #FileSelectParams.rename_flag / `sfile->params->rename_flag`. + * \note short flag. Defined as bit-flags, but currently only used as exclusive status markers. + */ typedef enum eFileSel_Params_RenameFlag { /** Used when we only have the name of the entry we want to rename, * but not yet access to its matching file entry. */ @@ -1084,7 +1086,7 @@ typedef enum eFileSel_File_Types { FILE_TYPE_BLENDERLIB = (1u << 31), } eFileSel_File_Types; -/* Selection Flags in filesel: struct direntry, unsigned char selflag */ +/** Selection Flags in filesel: struct direntry, unsigned char selflag. */ typedef enum eDirEntry_SelectFlag { /* FILE_SEL_ACTIVE = (1 << 1), */ /* UNUSED */ FILE_SEL_HIGHLIGHTED = (1 << 2), @@ -1156,7 +1158,7 @@ typedef struct FileDirEntryArr { char root[1024]; } FileDirEntryArr; -/* FileDirEntry.flags */ +/** #FileDirEntry.flags */ enum { /* The preview for this entry could not be generated. */ FILE_ENTRY_INVALID_PREVIEW = 1 << 0, @@ -1243,7 +1245,7 @@ typedef struct SpaceImage { SpaceImageOverlay overlay; } SpaceImage; -/* SpaceImage.dt_uv */ +/** #SpaceImage.dt_uv */ typedef enum eSpaceImage_UVDT { SI_UVDT_OUTLINE = 0, SI_UVDT_DASH = 1, @@ -1251,20 +1253,20 @@ typedef enum eSpaceImage_UVDT { SI_UVDT_WHITE = 3, } eSpaceImage_UVDT; -/* SpaceImage.dt_uvstretch */ +/** #SpaceImage.dt_uvstretch */ typedef enum eSpaceImage_UVDT_Stretch { SI_UVDT_STRETCH_ANGLE = 0, SI_UVDT_STRETCH_AREA = 1, } eSpaceImage_UVDT_Stretch; -/* SpaceImage.pixel_snap_mode */ +/** #SpaceImage.pixel_snap_mode */ typedef enum eSpaceImage_PixelSnapMode { SI_PIXEL_SNAP_DISABLED = 0, SI_PIXEL_SNAP_CENTER = 1, SI_PIXEL_SNAP_CORNER = 2, } eSpaceImage_Snap_Mode; -/* SpaceImage.mode */ +/** #SpaceImage.mode */ typedef enum eSpaceImage_Mode { SI_MODE_VIEW = 0, SI_MODE_PAINT = 1, @@ -1281,7 +1283,7 @@ typedef enum eSpaceImage_Sticky { SI_STICKY_VERTEX = 2, } eSpaceImage_Sticky; -/* SpaceImage.flag */ +/** #SpaceImage.flag */ typedef enum eSpaceImage_Flag { SI_FLAG_UNUSED_0 = (1 << 0), /* cleared */ SI_FLAG_UNUSED_1 = (1 << 1), /* cleared */ @@ -1375,7 +1377,7 @@ typedef struct SpaceText_Runtime { } SpaceText_Runtime; -/* Text Editor */ +/** Text Editor. */ typedef struct SpaceText { SpaceLink *next, *prev; /** Storage of regions for inactive spaces. */ @@ -1425,7 +1427,7 @@ typedef struct SpaceText { SpaceText_Runtime runtime; } SpaceText; -/* SpaceText flags (moved from DNA_text_types.h) */ +/** SpaceText flags (moved from DNA_text_types.h). */ typedef enum eSpaceText_Flags { /* scrollable */ ST_SCROLL_SELECT = (1 << 0), @@ -1449,7 +1451,7 @@ typedef enum eSpaceText_Flags { /** \name Script View (Obsolete) * \{ */ -/* Script Runtime Data - Obsolete (pre 2.5) */ +/** Script Runtime Data - Obsolete (pre 2.5). */ typedef struct Script { ID id; @@ -1474,7 +1476,7 @@ typedef struct Script { _script->py_globaldict = NULL; \ _script->flags = 0 -/* Script View - Obsolete (pre 2.5) */ +/** Script View - Obsolete (pre 2.5). */ typedef struct SpaceScript { SpaceLink *next, *prev; /** Storage of regions for inactive spaces. */ @@ -1584,7 +1586,7 @@ typedef struct SpaceNode { SpaceNode_Runtime *runtime; } SpaceNode; -/* SpaceNode.flag */ +/** #SpaceNode.flag */ typedef enum eSpaceNode_Flag { SNODE_BACKDRAW = (1 << 1), SNODE_SHOW_GPENCIL = (1 << 2), @@ -1602,7 +1604,7 @@ typedef enum eSpaceNode_Flag { SNODE_SKIP_INSOFFSET = (1 << 13), } eSpaceNode_Flag; -/* SpaceNode.texfrom */ +/** #SpaceNode.texfrom */ typedef enum eSpaceNode_TexFrom { /* SNODE_TEX_OBJECT = 0, */ SNODE_TEX_WORLD = 1, @@ -1610,14 +1612,14 @@ typedef enum eSpaceNode_TexFrom { SNODE_TEX_LINESTYLE = 3, } eSpaceNode_TexFrom; -/* SpaceNode.shaderfrom */ +/** #SpaceNode.shaderfrom */ typedef enum eSpaceNode_ShaderFrom { SNODE_SHADER_OBJECT = 0, SNODE_SHADER_WORLD = 1, SNODE_SHADER_LINESTYLE = 2, } eSpaceNode_ShaderFrom; -/* SpaceNode.insert_ofs_dir */ +/** #SpaceNode.insert_ofs_dir */ enum { SNODE_INSERTOFS_DIR_RIGHT = 0, SNODE_INSERTOFS_DIR_LEFT = 1, @@ -1629,7 +1631,7 @@ enum { /** \name Console * \{ */ -/* Console content */ +/** Console content. */ typedef struct ConsoleLine { struct ConsoleLine *next, *prev; @@ -1645,7 +1647,7 @@ typedef struct ConsoleLine { int type; } ConsoleLine; -/* ConsoleLine.type */ +/** #ConsoleLine.type */ typedef enum eConsoleLine_Type { CONSOLE_LINE_OUTPUT = 0, CONSOLE_LINE_INPUT = 1, @@ -1653,7 +1655,7 @@ typedef enum eConsoleLine_Type { CONSOLE_LINE_ERROR = 3, } eConsoleLine_Type; -/* Console View */ +/** Console View. */ typedef struct SpaceConsole { SpaceLink *next, *prev; /** Storage of regions for inactive spaces. */ @@ -1707,7 +1709,7 @@ typedef struct SpaceUserPref { /** \name Motion Tracking * \{ */ -/* Clip Editor */ +/** Clip Editor. */ typedef struct SpaceClip { SpaceLink *next, *prev; /** Storage of regions for inactive spaces. */ @@ -1770,7 +1772,7 @@ typedef struct SpaceClip { MaskSpaceInfo mask_info; } SpaceClip; -/* SpaceClip.flag */ +/** #SpaceClip.flag */ typedef enum eSpaceClip_Flag { SC_SHOW_MARKER_PATTERN = (1 << 0), SC_SHOW_MARKER_SEARCH = (1 << 1), @@ -1797,7 +1799,7 @@ typedef enum eSpaceClip_Flag { SC_SHOW_METADATA = (1 << 22), } eSpaceClip_Flag; -/* SpaceClip.mode */ +/** #SpaceClip.mode */ typedef enum eSpaceClip_Mode { SC_MODE_TRACKING = 0, // SC_MODE_RECONSTRUCTION = 1, /* DEPRECATED */ @@ -1805,14 +1807,14 @@ typedef enum eSpaceClip_Mode { SC_MODE_MASKEDIT = 3, } eSpaceClip_Mode; -/* SpaceClip.view */ +/** #SpaceClip.view */ typedef enum eSpaceClip_View { SC_VIEW_CLIP = 0, SC_VIEW_GRAPH = 1, SC_VIEW_DOPESHEET = 2, } eSpaceClip_View; -/* SpaceClip.gpencil_src */ +/** #SpaceClip.gpencil_src */ typedef enum eSpaceClip_GPencil_Source { SC_GPENCIL_SRC_CLIP = 0, SC_GPENCIL_SRC_TRACK = 1, @@ -2030,8 +2032,10 @@ typedef enum eSpreadsheetColumnValueType { /** \name Space Defines (eSpace_Type) * \{ */ -/* space types, moved from DNA_screen_types.h */ -/* Do NOT change order, append on end. types are hardcoded needed */ +/** + * Space types: #SpaceLink.spacetype & #ScrArea.spacetype. + * \note Do NOT change order, append on end. types are hardcoded needed. + */ typedef enum eSpace_Type { SPACE_EMPTY = 0, SPACE_VIEW3D = 1, diff --git a/source/blender/makesdna/DNA_texture_types.h b/source/blender/makesdna/DNA_texture_types.h index 2c3cd8eab77..cd19825d29f 100644 --- a/source/blender/makesdna/DNA_texture_types.h +++ b/source/blender/makesdna/DNA_texture_types.h @@ -98,8 +98,10 @@ typedef struct CBData { int cur; } CBData; -/* 32 = MAXCOLORBAND */ -/* note that this has to remain a single struct, for UserDef */ +/** + * 32 = #MAXCOLORBAND + * \note that this has to remain a single struct, for UserDef. + */ typedef struct ColorBand { short tot, cur; char ipotype, ipotype_hue; @@ -454,14 +456,14 @@ typedef struct ColorMapping { /* **************** ColorBand ********************* */ -/* colormode */ +/** color-mode. */ enum { COLBAND_BLEND_RGB = 0, COLBAND_BLEND_HSV = 1, COLBAND_BLEND_HSL = 2, }; -/* interpolation */ +/** Interpolation. */ enum { COLBAND_INTERP_LINEAR = 0, COLBAND_INTERP_EASE = 1, @@ -470,7 +472,7 @@ enum { COLBAND_INTERP_CONSTANT = 4, }; -/* color interpolation */ +/** Color interpolation. */ enum { COLBAND_HUE_NEAR = 0, COLBAND_HUE_FAR = 1, @@ -509,7 +511,7 @@ enum { /* #define TEX_PD_NOISE_AGE 2 */ /* Deprecated */ /* #define TEX_PD_NOISE_TIME 3 */ /* Deprecated */ -/* color_source */ +/** color_source. */ enum { TEX_PD_COLOR_CONSTANT = 0, /* color_source: particles */ diff --git a/source/blender/makesdna/DNA_tracking_types.h b/source/blender/makesdna/DNA_tracking_types.h index 0e313183300..815fab59876 100644 --- a/source/blender/makesdna/DNA_tracking_types.h +++ b/source/blender/makesdna/DNA_tracking_types.h @@ -462,7 +462,7 @@ typedef struct MovieTracking { MovieTrackingDopesheet dopesheet; } MovieTracking; -/* MovieTrackingCamera->distortion_model */ +/** #MovieTrackingCamera.distortion_model */ enum { TRACKING_DISTORTION_MODEL_POLYNOMIAL = 0, TRACKING_DISTORTION_MODEL_DIVISION = 1, @@ -470,13 +470,13 @@ enum { TRACKING_DISTORTION_MODEL_BROWN = 3, }; -/* MovieTrackingCamera->units */ +/** #MovieTrackingCamera.units */ enum { CAMERA_UNITS_PX = 0, CAMERA_UNITS_MM = 1, }; -/* MovieTrackingMarker->flag */ +/** #MovieTrackingMarker.flag */ enum { MARKER_DISABLED = (1 << 0), MARKER_TRACKED = (1 << 1), @@ -485,7 +485,7 @@ enum { MARKER_GRAPH_SEL = (MARKER_GRAPH_SEL_X | MARKER_GRAPH_SEL_Y), }; -/* MovieTrackingTrack->flag */ +/** #MovieTrackingTrack.flag */ enum { TRACK_HAS_BUNDLE = (1 << 1), TRACK_DISABLE_RED = (1 << 2), @@ -501,7 +501,7 @@ enum { TRACK_USE_2D_STAB_ROT = (1 << 12), }; -/* MovieTrackingTrack->motion_model */ +/** #MovieTrackingTrack.motion_model */ enum { TRACK_MOTION_MODEL_TRANSLATION = 0, TRACK_MOTION_MODEL_TRANSLATION_ROTATION = 1, @@ -511,27 +511,27 @@ enum { TRACK_MOTION_MODEL_HOMOGRAPHY = 5, }; -/* MovieTrackingTrack->algorithm_flag */ +/** #MovieTrackingTrack.algorithm_flag */ enum { TRACK_ALGORITHM_FLAG_USE_BRUTE = (1 << 0), TRACK_ALGORITHM_FLAG_USE_NORMALIZATION = (1 << 2), TRACK_ALGORITHM_FLAG_USE_MASK = (1 << 3), }; -/* MovieTrackingTrack->pattern_match */ +/** #MovieTrackingTrack.pattern_match */ typedef enum eTrackFrameMatch { TRACK_MATCH_KEYFRAME = 0, TRACK_MATCH_PREVIOS_FRAME = 1, } eTrackFrameMatch; -/* MovieTrackingSettings->motion_flag */ +/** #MovieTrackingSettings.motion_flag */ enum { TRACKING_MOTION_TRIPOD = (1 << 0), TRACKING_MOTION_MODAL = (TRACKING_MOTION_TRIPOD), }; -/* MovieTrackingSettings->speed */ +/** #MovieTrackingSettings.speed */ enum { TRACKING_SPEED_FASTEST = 0, TRACKING_SPEED_REALTIME = 1, @@ -540,13 +540,13 @@ enum { TRACKING_SPEED_DOUBLE = 5, }; -/* MovieTrackingSettings->reconstruction_flag */ +/** #MovieTrackingSettings.reconstruction_flag */ enum { /* TRACKING_USE_FALLBACK_RECONSTRUCTION = (1 << 0), */ /* DEPRECATED */ TRACKING_USE_KEYFRAME_SELECTION = (1 << 1), }; -/* MovieTrackingSettings->refine_camera_intrinsics */ +/** #MovieTrackingSettings.refine_camera_intrinsics */ enum { REFINE_NO_INTRINSICS = (0), @@ -556,7 +556,7 @@ enum { REFINE_TANGENTIAL_DISTORTION = (1 << 3), }; -/* MovieTrackingStabilization->flag */ +/** #MovieTrackingStabilization.flag */ enum { TRACKING_2D_STABILIZATION = (1 << 0), TRACKING_AUTOSCALE = (1 << 1), @@ -565,19 +565,19 @@ enum { TRACKING_SHOW_STAB_TRACKS = (1 << 5), }; -/* MovieTrackingStabilization->filter */ +/** #MovieTrackingStabilization.filter */ enum { TRACKING_FILTER_NEAREST = 0, TRACKING_FILTER_BILINEAR = 1, TRACKING_FILTER_BICUBIC = 2, }; -/* MovieTrackingReconstruction->flag */ +/** #MovieTrackingReconstruction.flag */ enum { TRACKING_RECONSTRUCTED = (1 << 0), }; -/* MovieTrackingObject->flag */ +/** #MovieTrackingObject.flag */ enum { TRACKING_OBJECT_CAMERA = (1 << 0), }; @@ -588,7 +588,7 @@ enum { TRACKING_CLEAN_DELETE_SEGMENT = 2, }; -/* MovieTrackingDopesheet->sort_method */ +/** #MovieTrackingDopesheet.sort_method */ enum { TRACKING_DOPE_SORT_NAME = 0, TRACKING_DOPE_SORT_LONGEST = 1, @@ -598,27 +598,27 @@ enum { TRACKING_DOPE_SORT_END = 5, }; -/* MovieTrackingDopesheet->flag */ +/** #MovieTrackingDopesheet.flag */ enum { TRACKING_DOPE_SORT_INVERSE = (1 << 0), TRACKING_DOPE_SELECTED_ONLY = (1 << 1), TRACKING_DOPE_SHOW_HIDDEN = (1 << 2), }; -/* MovieTrackingDopesheetCoverageSegment->trackness */ +/** #MovieTrackingDopesheetCoverageSegment.trackness */ enum { TRACKING_COVERAGE_BAD = 0, TRACKING_COVERAGE_ACCEPTABLE = 1, TRACKING_COVERAGE_OK = 2, }; -/* MovieTrackingPlaneMarker->flag */ +/** #MovieTrackingPlaneMarker.flag */ enum { PLANE_MARKER_DISABLED = (1 << 0), PLANE_MARKER_TRACKED = (1 << 1), }; -/* MovieTrackingPlaneTrack->flag */ +/** #MovieTrackingPlaneTrack.flag */ enum { PLANE_TRACK_HIDDEN = (1 << 1), PLANE_TRACK_LOCKED = (1 << 2), diff --git a/source/blender/makesdna/DNA_userdef_types.h b/source/blender/makesdna/DNA_userdef_types.h index c8fdac19b61..c99651f0717 100644 --- a/source/blender/makesdna/DNA_userdef_types.h +++ b/source/blender/makesdna/DNA_userdef_types.h @@ -30,7 +30,8 @@ extern "C" { #endif -/* themes; defines in BIF_resource.h */ +/* Themes; defines in `BIF_resource.h`. */ + struct ColorBand; /* ************************ style definitions ******************** */ @@ -50,8 +51,10 @@ typedef enum eUIFont_ID { /* UIFONT_CUSTOM2 = 3, */ /* UNUSED */ } eUIFont_ID; -/* default fonts to load/initialize */ -/* first font is the default (index 0), others optional */ +/** + * Default fonts to load/initialize. + * First font is the default (index 0), others optional. + */ typedef struct uiFont { struct uiFont *next, *prev; /** 1024 = FILE_MAX. */ @@ -445,7 +448,7 @@ typedef enum eBackgroundGradientTypes { TH_BACKGROUND_GRADIENT_RADIAL = 2, } eBackgroundGradientTypes; -/* set of colors for use as a custom color set for Objects/Bones wire drawing */ +/** Set of colors for use as a custom color set for Objects/Bones wire drawing. */ typedef struct ThemeWireColor { unsigned char solid[4]; unsigned char select[4]; @@ -959,7 +962,7 @@ typedef struct UserDef { UserDef_Runtime runtime; } UserDef; -/* from blenkernel blender.c */ +/** From blenkernel `blender.c`. */ extern UserDef U; /* ***************** USERDEF ****************** */ diff --git a/source/blender/makesdna/DNA_view2d_types.h b/source/blender/makesdna/DNA_view2d_types.h index f8166305fd9..6c03422963d 100644 --- a/source/blender/makesdna/DNA_view2d_types.h +++ b/source/blender/makesdna/DNA_view2d_types.h @@ -31,7 +31,7 @@ extern "C" { /* ---------------------------------- */ -/* View 2D data - stored per region */ +/** View 2D data - stored per region. */ typedef struct View2D { /** Tot - area that data can be drawn in; cur - region of tot that is visible in viewport. */ rctf tot, cur; @@ -83,7 +83,7 @@ typedef struct View2D { /* ---------------------------------- */ -/* view zooming restrictions, per axis (v2d->keepzoom) */ +/** View zooming restrictions, per axis (#View2D.keepzoom) */ enum { /* zoom is clamped to lie within limits set by minzoom and maxzoom */ V2D_LIMITZOOM = (1 << 0), @@ -97,7 +97,7 @@ enum { V2D_LOCKZOOM_Y = (1 << 9), }; -/* view panning restrictions, per axis (v2d->keepofs) */ +/** View panning restrictions, per axis (#View2D.keepofs). */ enum { /* panning on x-axis is not allowed */ V2D_LOCKOFS_X = (1 << 1), @@ -109,7 +109,7 @@ enum { V2D_KEEPOFS_Y = (1 << 4), }; -/* view extent restrictions (v2d->keeptot) */ +/** View extent restrictions (#View2D.keeptot). */ enum { /** 'cur' view can be out of extents of 'tot' */ V2D_KEEPTOT_FREE = 0, @@ -120,7 +120,7 @@ enum { V2D_KEEPTOT_STRICT = 2, }; -/* general refresh settings (v2d->flag) */ +/** General refresh settings (#View2D.flag). */ enum { /* global view2d horizontal locking (for showing same time interval) */ /* TODO: this flag may be set in old files but is not accessible currently, @@ -138,7 +138,7 @@ enum { V2D_IS_INIT = (1 << 10), }; -/* scroller flags for View2D (v2d->scroll) */ +/** Scroller flags for View2D (#View2D.scroll). */ enum { /* left scrollbar */ V2D_SCROLL_LEFT = (1 << 0), @@ -162,13 +162,15 @@ enum { V2D_SCROLL_HORIZONTAL_FULLR = (1 << 10), }; -/* scroll_ui, activate flag for drawing */ +/** scroll_ui, activate flag for drawing. */ enum { V2D_SCROLL_H_ACTIVE = (1 << 0), V2D_SCROLL_V_ACTIVE = (1 << 1), }; -/* alignment flags for totrect, flags use 'shading-out' convention (v2d->align) */ +/** + * Alignment flags for `totrect`, flags use 'shading-out' convention (#View2D.align). + */ enum { /* all quadrants free */ V2D_ALIGN_FREE = 0, diff --git a/source/blender/makesdna/DNA_view3d_types.h b/source/blender/makesdna/DNA_view3d_types.h index 3fd2f1208dd..5f4353b6f3a 100644 --- a/source/blender/makesdna/DNA_view3d_types.h +++ b/source/blender/makesdna/DNA_view3d_types.h @@ -245,7 +245,7 @@ typedef struct View3DOverlay { char _pad[4]; } View3DOverlay; -/* View3DOverlay->handle_display */ +/** #View3DOverlay.handle_display */ typedef enum eHandleDisplay { /* Display only selected points. */ CURVE_HANDLE_SELECTED = 0, diff --git a/source/blender/makesdna/DNA_volume_types.h b/source/blender/makesdna/DNA_volume_types.h index 1344f295ea9..df5a122faaf 100644 --- a/source/blender/makesdna/DNA_volume_types.h +++ b/source/blender/makesdna/DNA_volume_types.h @@ -30,13 +30,13 @@ struct PackedFile; struct VolumeGridVector; typedef struct Volume_Runtime { - /* OpenVDB Grids */ + /** OpenVDB Grids. */ struct VolumeGridVector *grids; - /* Current frame in sequence for evaluated volume */ + /** Current frame in sequence for evaluated volume. */ int frame; - /* Default simplify level for volume grids loaded from files. */ + /** Default simplify level for volume grids loaded from files. */ int default_simplify_level; } Volume_Runtime; @@ -96,12 +96,12 @@ typedef struct Volume { Volume_Runtime runtime; } Volume; -/* Volume.flag */ +/** #Volume.flag */ enum { VO_DS_EXPAND = (1 << 0), }; -/* Volume.sequence_mode */ +/** #Volume.sequence_mode */ typedef enum VolumeSequenceMode { VOLUME_SEQUENCE_CLIP = 0, VOLUME_SEQUENCE_EXTEND = 1, @@ -109,7 +109,7 @@ typedef enum VolumeSequenceMode { VOLUME_SEQUENCE_PING_PONG = 3, } VolumeSequenceMode; -/* VolumeDisplay.wireframe_type */ +/** #VolumeDisplay.wireframe_type */ typedef enum VolumeWireframeType { VOLUME_WIREFRAME_NONE = 0, VOLUME_WIREFRAME_BOUNDS = 1, @@ -117,32 +117,32 @@ typedef enum VolumeWireframeType { VOLUME_WIREFRAME_POINTS = 3, } VolumeWireframeType; -/* VolumeDisplay.wireframe_detail */ +/** #VolumeDisplay.wireframe_detail */ typedef enum VolumeWireframeDetail { VOLUME_WIREFRAME_COARSE = 0, VOLUME_WIREFRAME_FINE = 1, } VolumeWireframeDetail; -/* VolumeRender.space */ +/** #VolumeRender.space */ typedef enum VolumeRenderSpace { VOLUME_SPACE_OBJECT = 0, VOLUME_SPACE_WORLD = 1, } VolumeRenderSpace; -/* VolumeDisplay.interpolation_method */ +/** #VolumeDisplay.interpolation_method */ typedef enum VolumeDisplayInterpMethod { VOLUME_DISPLAY_INTERP_LINEAR = 0, VOLUME_DISPLAY_INTERP_CUBIC = 1, VOLUME_DISPLAY_INTERP_CLOSEST = 2, } VolumeDisplayInterpMethod; -/* VolumeDisplay.axis_slice_method */ +/** #VolumeDisplay.axis_slice_method */ typedef enum AxisAlignedSlicingMethod { VOLUME_AXIS_SLICE_FULL = 0, VOLUME_AXIS_SLICE_SINGLE = 1, } AxisAlignedSlicingMethod; -/* VolumeDisplay.slice_axis */ +/** #VolumeDisplay.slice_axis */ typedef enum SliceAxis { VOLUME_SLICE_AXIS_AUTO = 0, VOLUME_SLICE_AXIS_X = 1, diff --git a/source/blender/makesdna/DNA_windowmanager_types.h b/source/blender/makesdna/DNA_windowmanager_types.h index 841edaf8724..d0e4184d2a5 100644 --- a/source/blender/makesdna/DNA_windowmanager_types.h +++ b/source/blender/makesdna/DNA_windowmanager_types.h @@ -33,7 +33,8 @@ extern "C" { #endif -/* defined here: */ +/* Defined here: */ + struct wmWindow; struct wmWindowManager; @@ -45,7 +46,8 @@ struct wmMsgBus; struct wmOperator; struct wmOperatorType; -/* forwards */ +/* Forward declarations: */ + struct PointerRNA; struct Report; struct ReportList; @@ -58,7 +60,7 @@ struct wmTimer; #define OP_MAX_TYPENAME 64 #define KMAP_MAX_NAME 64 -/* keep in sync with 'rna_enum_wm_report_items' in wm_rna.c */ +/** Keep in sync with 'rna_enum_wm_report_items' in `wm_rna.c`. */ typedef enum eReportType { RPT_DEBUG = (1 << 0), RPT_INFO = (1 << 1), @@ -100,7 +102,9 @@ typedef struct Report { const char *message; } Report; -/* saved in the wm, don't remove */ +/** + * \note Saved in the wm, don't remove. + */ typedef struct ReportList { ListBase list; /** eReportType. */ @@ -133,7 +137,7 @@ typedef struct wmXrData { /* reports need to be before wmWindowManager */ -/* windowmanager is saved, tag WMAN */ +/** Window-manager is saved, tag WMAN. */ typedef struct wmWindowManager { ID id; @@ -204,13 +208,13 @@ typedef struct wmWindowManager { //#endif } wmWindowManager; -/* wmWindowManager.initialized */ +/** #wmWindowManager.initialized */ enum { WM_WINDOW_IS_INIT = (1 << 0), WM_KEYCONFIG_IS_INIT = (1 << 1), }; -/* wmWindowManager.outliner_sync_select_dirty */ +/** #wmWindowManager.outliner_sync_select_dirty */ enum { WM_OUTLINER_SYNC_SELECT_FROM_OBJECT = (1 << 0), WM_OUTLINER_SYNC_SELECT_FROM_EDIT_BONE = (1 << 1), @@ -231,7 +235,9 @@ enum { # endif #endif -/* the saveable part, rest of data is local in ghostwinlay */ +/** + * The saveable part, the rest of the data is local in GHOST. + */ typedef struct wmWindow { struct wmWindow *next, *prev; @@ -352,7 +358,9 @@ typedef struct wmOperatorTypeMacro { struct PointerRNA *ptr; } wmOperatorTypeMacro; -/* Partial copy of the event, for matching by event handler. */ +/** + * Partial copy of the event, for matching by event handler. + */ typedef struct wmKeyMapItem { struct wmKeyMapItem *next, *prev; @@ -436,7 +444,9 @@ enum { KMI_TYPE_NDOF = 5, }; -/* stored in WM, the actively used keymaps */ +/** + * Stored in WM, the actively used key-maps. + */ typedef struct wmKeyMap { struct wmKeyMap *next, *prev; diff --git a/source/blender/makesdna/DNA_workspace_types.h b/source/blender/makesdna/DNA_workspace_types.h index a0856588a58..95530c7b0f7 100644 --- a/source/blender/makesdna/DNA_workspace_types.h +++ b/source/blender/makesdna/DNA_workspace_types.h @@ -60,7 +60,9 @@ typedef struct bToolRef_Runtime { int flag; } bToolRef_Runtime; -/* Stored per mode. */ +/** + * \note Stored per mode. + */ typedef struct bToolRef { struct bToolRef *next, *prev; char idname[64]; diff --git a/source/blender/makesdna/intern/dna_utils.h b/source/blender/makesdna/intern/dna_utils.h index b89c45a7a43..7ba71fb970b 100644 --- a/source/blender/makesdna/intern/dna_utils.h +++ b/source/blender/makesdna/intern/dna_utils.h @@ -60,7 +60,9 @@ char *DNA_elem_id_rename(struct MemArena *mem_arena, const int elem_src_full_len, const uint elem_src_full_offset_len); -/* When requesting version info, support both directions. */ +/** + * When requesting version info, support both directions. + */ enum eDNA_RenameDir { DNA_RENAME_STATIC_FROM_ALIAS = -1, DNA_RENAME_ALIAS_FROM_STATIC = 1, diff --git a/source/blender/makesrna/intern/rna_color.c b/source/blender/makesrna/intern/rna_color.c index 5c12fc3a227..091b457cc83 100644 --- a/source/blender/makesrna/intern/rna_color.c +++ b/source/blender/makesrna/intern/rna_color.c @@ -321,7 +321,7 @@ static void rna_ColorRamp_update(Main *bmain, Scene *UNUSED(scene), PointerRNA * CMP_NODE_VALTORGB, TEX_NODE_VALTORGB, GEO_NODE_LEGACY_ATTRIBUTE_COLOR_RAMP)) { - ED_node_tag_update_nodetree(bmain, ntree, node); + ED_node_tree_propagate_change(NULL, bmain, ntree); } } break; diff --git a/source/blender/makesrna/intern/rna_fcurve.c b/source/blender/makesrna/intern/rna_fcurve.c index a38bbd3d6d2..d3175c445a9 100644 --- a/source/blender/makesrna/intern/rna_fcurve.c +++ b/source/blender/makesrna/intern/rna_fcurve.c @@ -388,6 +388,7 @@ void rna_DriverVariable_name_set(PointerRNA *ptr, const char *value) BLI_strncpy_utf8(data->name, value, 64); driver_variable_name_validate(data); + driver_variable_unique_name(data); } /* ----------- */ diff --git a/source/blender/makesrna/intern/rna_image.c b/source/blender/makesrna/intern/rna_image.c index 2f42e521b52..0d86572357f 100644 --- a/source/blender/makesrna/intern/rna_image.c +++ b/source/blender/makesrna/intern/rna_image.c @@ -168,7 +168,7 @@ static void rna_ImageUser_update(Main *bmain, Scene *scene, PointerRNA *ptr) if (id) { if (GS(id->name) == ID_NT) { /* Special update for nodetrees to find parent datablock. */ - ED_node_tag_update_nodetree(bmain, (bNodeTree *)id, NULL); + ED_node_tree_propagate_change(NULL, bmain, NULL); } else { /* Update material or texture for render preview. */ diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c index df0271d81d5..e8f9a0b423b 100644 --- a/source/blender/makesrna/intern/rna_nodetree.c +++ b/source/blender/makesrna/intern/rna_nodetree.c @@ -42,6 +42,7 @@ #include "BKE_geometry_set.h" #include "BKE_image.h" #include "BKE_node.h" +#include "BKE_node_tree_update.h" #include "BKE_texture.h" #include "RNA_access.h" @@ -1219,7 +1220,7 @@ static void rna_NodeTree_update(Main *bmain, Scene *UNUSED(scene), PointerRNA *p WM_main_add_notifier(NC_NODE | NA_EDITED, NULL); WM_main_add_notifier(NC_SCENE | ND_NODES, &ntree->id); - ED_node_tag_update_nodetree(bmain, ntree, NULL); + ED_node_tree_propagate_change(NULL, bmain, ntree); } static bNode *rna_NodeTree_node_new(bNodeTree *ntree, @@ -1269,8 +1270,7 @@ static bNode *rna_NodeTree_node_new(bNodeTree *ntree, } Main *bmain = CTX_data_main(C); - ntreeUpdateTree(bmain, ntree); - nodeUpdate(ntree, node); + ED_node_tree_propagate_change(C, bmain, ntree); WM_main_add_notifier(NC_NODE | NA_EDITED, ntree); if (node->type == GEO_NODE_INPUT_SCENE_TIME) { @@ -1300,7 +1300,7 @@ static void rna_NodeTree_node_remove(bNodeTree *ntree, RNA_POINTER_INVALIDATE(node_ptr); - ntreeUpdateTree(bmain, ntree); /* update group node socket links */ + ED_node_tree_propagate_change(NULL, bmain, ntree); WM_main_add_notifier(NC_NODE | NA_EDITED, ntree); } @@ -1320,8 +1320,7 @@ static void rna_NodeTree_node_clear(bNodeTree *ntree, Main *bmain, ReportList *r node = next_node; } - ntreeUpdateTree(bmain, ntree); - + ED_node_tree_propagate_change(NULL, bmain, ntree); WM_main_add_notifier(NC_NODE | NA_EDITED, ntree); } @@ -1400,13 +1399,7 @@ static bNodeLink *rna_NodeTree_link_new(bNodeTree *ntree, fromsock->flag &= ~SOCK_HIDDEN; tosock->flag &= ~SOCK_HIDDEN; - if (tonode) { - nodeUpdate(ntree, tonode); - } - - ntreeUpdateTree(bmain, ntree); - - ED_node_tag_update_nodetree(bmain, ntree, ret->tonode); + ED_node_tree_propagate_change(NULL, bmain, ntree); WM_main_add_notifier(NC_NODE | NA_EDITED, ntree); } return ret; @@ -1431,7 +1424,7 @@ static void rna_NodeTree_link_remove(bNodeTree *ntree, nodeRemLink(ntree, link); RNA_POINTER_INVALIDATE(link_ptr); - ntreeUpdateTree(bmain, ntree); + ED_node_tree_propagate_change(NULL, bmain, ntree); WM_main_add_notifier(NC_NODE | NA_EDITED, ntree); } @@ -1450,8 +1443,7 @@ static void rna_NodeTree_link_clear(bNodeTree *ntree, Main *bmain, ReportList *r link = next_link; } - ntreeUpdateTree(bmain, ntree); - + ED_node_tree_propagate_change(NULL, bmain, ntree); WM_main_add_notifier(NC_NODE | NA_EDITED, ntree); } @@ -1508,7 +1500,7 @@ static bNodeSocket *rna_NodeTree_inputs_new( bNodeSocket *sock = ntreeAddSocketInterface(ntree, SOCK_IN, type, name); - ntreeUpdateTree(bmain, ntree); + ED_node_tree_propagate_change(NULL, bmain, ntree); WM_main_add_notifier(NC_NODE | NA_EDITED, ntree); return sock; @@ -1523,7 +1515,7 @@ static bNodeSocket *rna_NodeTree_outputs_new( bNodeSocket *sock = ntreeAddSocketInterface(ntree, SOCK_OUT, type, name); - ntreeUpdateTree(bmain, ntree); + ED_node_tree_propagate_change(NULL, bmain, ntree); WM_main_add_notifier(NC_NODE | NA_EDITED, ntree); return sock; @@ -1544,8 +1536,7 @@ static void rna_NodeTree_socket_remove(bNodeTree *ntree, else { ntreeRemoveSocketInterface(ntree, sock); - ntreeUpdateTree(bmain, ntree); - DEG_id_tag_update(&ntree->id, 0); + ED_node_tree_propagate_change(NULL, bmain, ntree); WM_main_add_notifier(NC_NODE | NA_EDITED, ntree); } } @@ -1560,7 +1551,7 @@ static void rna_NodeTree_inputs_clear(bNodeTree *ntree, Main *bmain, ReportList ntreeRemoveSocketInterface(ntree, socket); } - ntreeUpdateTree(bmain, ntree); + ED_node_tree_propagate_change(NULL, bmain, ntree); WM_main_add_notifier(NC_NODE | NA_EDITED, ntree); } @@ -1574,7 +1565,7 @@ static void rna_NodeTree_outputs_clear(bNodeTree *ntree, Main *bmain, ReportList ntreeRemoveSocketInterface(ntree, socket); } - ntreeUpdateTree(bmain, ntree); + ED_node_tree_propagate_change(NULL, bmain, ntree); WM_main_add_notifier(NC_NODE | NA_EDITED, ntree); } @@ -1603,9 +1594,9 @@ static void rna_NodeTree_inputs_move(bNodeTree *ntree, Main *bmain, int from_ind } } - ntree->update |= NTREE_UPDATE_GROUP_IN; + BKE_ntree_update_tag_interface(ntree); - ntreeUpdateTree(bmain, ntree); + ED_node_tree_propagate_change(NULL, bmain, ntree); WM_main_add_notifier(NC_NODE | NA_EDITED, ntree); } @@ -1634,9 +1625,9 @@ static void rna_NodeTree_outputs_move(bNodeTree *ntree, Main *bmain, int from_in } } - ntree->update |= NTREE_UPDATE_GROUP_OUT; + BKE_ntree_update_tag_interface(ntree); - ntreeUpdateTree(bmain, ntree); + ED_node_tree_propagate_change(NULL, bmain, ntree); WM_main_add_notifier(NC_NODE | NA_EDITED, ntree); } @@ -1644,10 +1635,8 @@ static void rna_NodeTree_interface_update(bNodeTree *ntree, bContext *C) { Main *bmain = CTX_data_main(C); - ntree->update |= NTREE_UPDATE_GROUP; - ntreeUpdateTree(bmain, ntree); - - ED_node_tag_update_nodetree(bmain, ntree, NULL); + BKE_ntree_update_tag_interface(ntree); + ED_node_tree_propagate_change(NULL, bmain, ntree); } /* ******** NodeLink ******** */ @@ -2617,7 +2606,8 @@ static void rna_Node_update(Main *bmain, Scene *UNUSED(scene), PointerRNA *ptr) { bNodeTree *ntree = (bNodeTree *)ptr->owner_id; bNode *node = (bNode *)ptr->data; - ED_node_tag_update_nodetree(bmain, ntree, node); + BKE_ntree_update_tag_node_property(ntree, node); + ED_node_tree_propagate_change(NULL, bmain, ntree); } static void rna_Node_update_relations(Main *bmain, Scene *scene, PointerRNA *ptr) @@ -2626,9 +2616,10 @@ static void rna_Node_update_relations(Main *bmain, Scene *scene, PointerRNA *ptr DEG_relations_tag_update(bmain); } -static void rna_Node_socket_value_update(ID *id, bNode *node, bContext *C) +static void rna_Node_socket_value_update(ID *id, bNode *UNUSED(node), bContext *C) { - ED_node_tag_update_nodetree(CTX_data_main(C), (bNodeTree *)id, node); + BKE_ntree_update_tag_all((bNodeTree *)id); + ED_node_tree_propagate_change(C, CTX_data_main(C), (bNodeTree *)id); } static void rna_Node_select_set(PointerRNA *ptr, bool value) @@ -2682,7 +2673,7 @@ static bNodeSocket *rna_Node_inputs_new(ID *id, BKE_report(reports, RPT_ERROR, "Unable to create socket"); } else { - ntreeUpdateTree(bmain, ntree); + ED_node_tree_propagate_change(NULL, bmain, ntree); WM_main_add_notifier(NC_NODE | NA_EDITED, ntree); } @@ -2716,7 +2707,7 @@ static bNodeSocket *rna_Node_outputs_new(ID *id, BKE_report(reports, RPT_ERROR, "Unable to create socket"); } else { - ntreeUpdateTree(bmain, ntree); + ED_node_tree_propagate_change(NULL, bmain, ntree); WM_main_add_notifier(NC_NODE | NA_EDITED, ntree); } @@ -2734,7 +2725,7 @@ static void rna_Node_socket_remove( else { nodeRemoveSocket(ntree, node, sock); - ntreeUpdateTree(bmain, ntree); + ED_node_tree_propagate_change(NULL, bmain, ntree); WM_main_add_notifier(NC_NODE | NA_EDITED, ntree); } } @@ -2749,7 +2740,7 @@ static void rna_Node_inputs_clear(ID *id, bNode *node, Main *bmain) nodeRemoveSocket(ntree, node, sock); } - ntreeUpdateTree(bmain, ntree); + ED_node_tree_propagate_change(NULL, bmain, ntree); WM_main_add_notifier(NC_NODE | NA_EDITED, ntree); } @@ -2763,7 +2754,7 @@ static void rna_Node_outputs_clear(ID *id, bNode *node, Main *bmain) nodeRemoveSocket(ntree, node, sock); } - ntreeUpdateTree(bmain, ntree); + ED_node_tree_propagate_change(NULL, bmain, ntree); WM_main_add_notifier(NC_NODE | NA_EDITED, ntree); } @@ -2795,7 +2786,7 @@ static void rna_Node_inputs_move(ID *id, bNode *node, Main *bmain, int from_inde } } - ntreeUpdateTree(bmain, ntree); + ED_node_tree_propagate_change(NULL, bmain, ntree); WM_main_add_notifier(NC_NODE | NA_EDITED, ntree); } @@ -2827,7 +2818,7 @@ static void rna_Node_outputs_move(ID *id, bNode *node, Main *bmain, int from_ind } } - ntreeUpdateTree(bmain, ntree); + ED_node_tree_propagate_change(NULL, bmain, ntree); WM_main_add_notifier(NC_NODE | NA_EDITED, ntree); } @@ -3055,10 +3046,9 @@ static void rna_NodeSocket_update(Main *bmain, Scene *UNUSED(scene), PointerRNA { bNodeTree *ntree = (bNodeTree *)ptr->owner_id; bNodeSocket *sock = (bNodeSocket *)ptr->data; - bNode *node; - if (nodeFindNode(ntree, sock, &node, NULL)) { - ED_node_tag_update_nodetree(bmain, ntree, node); - } + + BKE_ntree_update_tag_socket_property(ntree, sock); + ED_node_tree_propagate_change(NULL, bmain, ntree); } static bool rna_NodeSocket_is_output_get(PointerRNA *ptr) @@ -3351,10 +3341,8 @@ static void rna_NodeSocketInterface_update(Main *bmain, Scene *UNUSED(scene), Po return; } - ntree->update |= NTREE_UPDATE_GROUP; - ntreeUpdateTree(bmain, ntree); - - ED_node_tag_update_nodetree(bmain, ntree, NULL); + BKE_ntree_update_tag_interface(ntree); + ED_node_tree_propagate_change(NULL, bmain, ntree); } /* ******** Standard Node Socket Base Types ******** */ @@ -3452,28 +3440,14 @@ static void rna_NodeSocketStandard_vector_range( /* using a context update function here, to avoid searching the node if possible */ static void rna_NodeSocketStandard_value_update(struct bContext *C, PointerRNA *ptr) { - bNode *node; - /* default update */ rna_NodeSocket_update(CTX_data_main(C), CTX_data_scene(C), ptr); - - /* try to use node from context, faster */ - node = CTX_data_pointer_get(C, "node").data; - if (!node) { - bNodeTree *ntree = (bNodeTree *)ptr->owner_id; - bNodeSocket *sock = ptr->data; - - /* fall back to searching node in the tree */ - nodeFindNode(ntree, sock, &node, NULL); - } } static void rna_NodeSocketStandard_value_and_relation_update(struct bContext *C, PointerRNA *ptr) { rna_NodeSocketStandard_value_update(C, ptr); - bNodeTree *ntree = (bNodeTree *)ptr->owner_id; Main *bmain = CTX_data_main(C); - ntreeUpdateTree(bmain, ntree); DEG_relations_tag_update(bmain); } @@ -3567,12 +3541,11 @@ static bool rna_NodeInternal_poll_instance(bNode *node, bNodeTree *ntree) } } -static void rna_NodeInternal_update(ID *id, bNode *node) +static void rna_NodeInternal_update(ID *id, bNode *node, Main *bmain) { bNodeTree *ntree = (bNodeTree *)id; - if (node->typeinfo->updatefunc) { - node->typeinfo->updatefunc(ntree, node); - } + BKE_ntree_update_tag_node_property(ntree, node); + ED_node_tree_propagate_change(NULL, bmain, ntree); } static void rna_NodeInternal_draw_buttons(ID *id, @@ -3721,7 +3694,8 @@ static void rna_Node_tex_image_update(Main *bmain, Scene *UNUSED(scene), Pointer bNodeTree *ntree = (bNodeTree *)ptr->owner_id; bNode *node = (bNode *)ptr->data; - ED_node_tag_update_nodetree(bmain, ntree, node); + BKE_ntree_update_tag_node_property(ntree, node); + ED_node_tree_propagate_change(NULL, bmain, ntree); WM_main_add_notifier(NC_IMAGE, NULL); } @@ -3730,11 +3704,8 @@ static void rna_NodeGroup_update(Main *bmain, Scene *UNUSED(scene), PointerRNA * bNodeTree *ntree = (bNodeTree *)ptr->owner_id; bNode *node = (bNode *)ptr->data; - if (node->id) { - ntreeUpdateTree(bmain, (bNodeTree *)node->id); - } - - ED_node_tag_update_nodetree(bmain, ntree, node); + BKE_ntree_update_tag_node_property(ntree, node); + ED_node_tree_propagate_change(NULL, bmain, ntree); DEG_relations_tag_update(bmain); } @@ -4142,13 +4113,12 @@ static const EnumPropertyItem *rna_Node_channel_itemf(bContext *UNUSED(C), return item; } -static void rna_Image_Node_update_id(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *ptr) +static void rna_Image_Node_update_id(Main *bmain, Scene *scene, PointerRNA *ptr) { - bNodeTree *ntree = (bNodeTree *)ptr->owner_id; bNode *node = (bNode *)ptr->data; node->update |= NODE_UPDATE_ID; - nodeUpdate(ntree, node); /* to update image node sockets */ + rna_Node_update(bmain, scene, ptr); } static void rna_NodeOutputFile_slots_begin(CollectionPropertyIterator *iter, PointerRNA *ptr) @@ -4395,7 +4365,7 @@ static bNodeSocket *rna_NodeOutputFile_slots_new( sock = ntreeCompositOutputFileAddSocket(ntree, node, name, im_format); - ntreeUpdateTree(CTX_data_main(C), ntree); + ED_node_tree_propagate_change(C, CTX_data_main(C), ntree); WM_main_add_notifier(NC_NODE | NA_EDITED, ntree); return sock; @@ -4504,42 +4474,27 @@ static void rna_ShaderNodeScript_update(Main *bmain, Scene *scene, PointerRNA *p RE_engine_free(engine); } - ED_node_tag_update_nodetree(bmain, ntree, node); + BKE_ntree_update_tag_node_property(ntree, node); + ED_node_tree_propagate_change(NULL, bmain, ntree); } static void rna_ShaderNode_socket_update(Main *bmain, Scene *scene, PointerRNA *ptr) { - bNodeTree *ntree = (bNodeTree *)ptr->owner_id; - bNode *node = (bNode *)ptr->data; - - nodeUpdate(ntree, node); rna_Node_update(bmain, scene, ptr); } static void rna_Node_socket_update(Main *bmain, Scene *scene, PointerRNA *ptr) { - bNodeTree *ntree = (bNodeTree *)ptr->owner_id; - bNode *node = (bNode *)ptr->data; - - nodeUpdate(ntree, node); rna_Node_update(bmain, scene, ptr); } static void rna_GeometryNode_socket_update(Main *bmain, Scene *scene, PointerRNA *ptr) { - bNodeTree *ntree = (bNodeTree *)ptr->owner_id; - bNode *node = (bNode *)ptr->data; - - nodeUpdate(ntree, node); rna_Node_update(bmain, scene, ptr); } static void rna_CompositorNodeScale_update(Main *bmain, Scene *scene, PointerRNA *ptr) { - bNodeTree *ntree = (bNodeTree *)ptr->owner_id; - bNode *node = (bNode *)ptr->data; - - nodeUpdate(ntree, node); rna_Node_update(bmain, scene, ptr); } @@ -12386,7 +12341,7 @@ static void rna_def_internal_node(BlenderRNA *brna) func = RNA_def_function(srna, "update", "rna_NodeInternal_update"); RNA_def_function_ui_description( func, "Update on node graph topology changes (adding or removing nodes and links)"); - RNA_def_function_flag(func, FUNC_USE_SELF_ID | FUNC_ALLOW_WRITE); + RNA_def_function_flag(func, FUNC_USE_SELF_ID | FUNC_USE_MAIN | FUNC_ALLOW_WRITE); /* draw buttons */ func = RNA_def_function(srna, "draw_buttons", "rna_NodeInternal_draw_buttons"); diff --git a/source/blender/makesrna/intern/rna_scene.c b/source/blender/makesrna/intern/rna_scene.c index d05f2a13c4b..5f1d9c5a5ce 100644 --- a/source/blender/makesrna/intern/rna_scene.c +++ b/source/blender/makesrna/intern/rna_scene.c @@ -5583,7 +5583,7 @@ static void rna_def_scene_ffmpeg_settings(BlenderRNA *brna) {FFMPEG_MPEG2, "MPEG2", 0, "MPEG-2", ""}, {FFMPEG_MPEG4, "MPEG4", 0, "MPEG-4", ""}, {FFMPEG_AVI, "AVI", 0, "AVI", ""}, - {FFMPEG_MOV, "QUICKTIME", 0, "Quicktime", ""}, + {FFMPEG_MOV, "QUICKTIME", 0, "QuickTime", ""}, {FFMPEG_DV, "DV", 0, "DV", ""}, {FFMPEG_OGG, "OGG", 0, "Ogg", ""}, {FFMPEG_MKV, "MKV", 0, "Matroska", ""}, diff --git a/source/blender/makesrna/intern/rna_texture.c b/source/blender/makesrna/intern/rna_texture.c index 5a74cfa9964..f66fb2653b5 100644 --- a/source/blender/makesrna/intern/rna_texture.c +++ b/source/blender/makesrna/intern/rna_texture.c @@ -199,7 +199,7 @@ static void rna_Texture_update(Main *bmain, Scene *UNUSED(scene), PointerRNA *pt } else if (GS(id->name) == ID_NT) { bNodeTree *ntree = (bNodeTree *)ptr->owner_id; - ED_node_tag_update_nodetree(bmain, ntree, NULL); + ED_node_tree_propagate_change(NULL, bmain, ntree); } } diff --git a/source/blender/makesrna/intern/rna_tracking.c b/source/blender/makesrna/intern/rna_tracking.c index c81588aa8b5..03f4acdae79 100644 --- a/source/blender/makesrna/intern/rna_tracking.c +++ b/source/blender/makesrna/intern/rna_tracking.c @@ -24,6 +24,7 @@ #include "MEM_guardedalloc.h" #include "BKE_movieclip.h" +#include "BKE_node_tree_update.h" #include "BKE_tracking.h" #include "RNA_access.h" @@ -415,11 +416,12 @@ static void rna_tracking_stabRotTracks_active_index_range( *max = max_ii(0, clip->tracking.stabilization.tot_rot_track - 1); } -static void rna_tracking_flushUpdate(Main *UNUSED(bmain), Scene *scene, PointerRNA *ptr) +static void rna_tracking_flushUpdate(Main *bmain, Scene *UNUSED(scene), PointerRNA *ptr) { MovieClip *clip = (MovieClip *)ptr->owner_id; - nodeUpdateID(scene->nodetree, &clip->id); + BKE_ntree_update_tag_id_changed(bmain, &clip->id); + BKE_ntree_update_main(bmain, NULL); WM_main_add_notifier(NC_SCENE | ND_NODES, NULL); WM_main_add_notifier(NC_SCENE, NULL); diff --git a/source/blender/makesrna/intern/rna_userdef.c b/source/blender/makesrna/intern/rna_userdef.c index dd1252ffebf..929cf94615b 100644 --- a/source/blender/makesrna/intern/rna_userdef.c +++ b/source/blender/makesrna/intern/rna_userdef.c @@ -2889,7 +2889,7 @@ static void rna_def_userdef_theme_space_node(BlenderRNA *brna) prop = RNA_def_property(srna, "group_node", PROP_FLOAT, PROP_COLOR_GAMMA); RNA_def_property_float_sdna(prop, NULL, "syntaxc"); - RNA_def_property_array(prop, 4); + RNA_def_property_array(prop, 3); RNA_def_property_ui_text(prop, "Group Node", ""); RNA_def_property_update(prop, 0, "rna_userdef_theme_update"); diff --git a/source/blender/modifiers/CMakeLists.txt b/source/blender/modifiers/CMakeLists.txt index a3ef26f619f..88ac06b25eb 100644 --- a/source/blender/modifiers/CMakeLists.txt +++ b/source/blender/modifiers/CMakeLists.txt @@ -114,7 +114,7 @@ set(SRC intern/MOD_weightvgedit.c intern/MOD_weightvgmix.c intern/MOD_weightvgproximity.c - intern/MOD_weld.c + intern/MOD_weld.cc intern/MOD_wireframe.c MOD_modifiertypes.h diff --git a/source/blender/modifiers/intern/MOD_nodes.cc b/source/blender/modifiers/intern/MOD_nodes.cc index ec6cbeb43bf..cee5d0be65d 100644 --- a/source/blender/modifiers/intern/MOD_nodes.cc +++ b/source/blender/modifiers/intern/MOD_nodes.cc @@ -60,6 +60,7 @@ #include "BKE_main.h" #include "BKE_mesh.h" #include "BKE_modifier.h" +#include "BKE_node_tree_update.h" #include "BKE_object.h" #include "BKE_pointcloud.h" #include "BKE_screen.h" @@ -237,7 +238,7 @@ static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphConte NodesModifierData *nmd = reinterpret_cast<NodesModifierData *>(md); DEG_add_modifier_to_transform_relation(ctx->node, "Nodes Modifier"); if (nmd->node_group != nullptr) { - DEG_add_node_tree_relation(ctx->node, nmd->node_group, "Nodes Modifier"); + DEG_add_node_tree_output_relation(ctx->node, nmd->node_group, "Nodes Modifier"); Set<ID *> used_ids; find_used_ids_from_settings(nmd->settings, used_ids); @@ -725,7 +726,7 @@ void MOD_nodes_init(Main *bmain, NodesModifierData *nmd) group_input_node, (bNodeSocket *)group_input_node->outputs.first); - ntreeUpdateTree(bmain, ntree); + BKE_ntree_update_main_tree(bmain, ntree, nullptr); } static void initialize_group_input(NodesModifierData &nmd, @@ -1294,7 +1295,7 @@ static void add_attribute_search_button(const bContext &C, return; } - AttributeSearchData *data = OBJECT_GUARDED_NEW(AttributeSearchData); + AttributeSearchData *data = MEM_new<AttributeSearchData>(__func__); data->object_session_uid = object->id.session_uuid; STRNCPY(data->modifier_name, nmd.modifier.name); STRNCPY(data->socket_identifier, socket.identifier); diff --git a/source/blender/modifiers/intern/MOD_weld.c b/source/blender/modifiers/intern/MOD_weld.cc index f842bef3298..7a7af4e61aa 100644 --- a/source/blender/modifiers/intern/MOD_weld.c +++ b/source/blender/modifiers/intern/MOD_weld.cc @@ -35,10 +35,13 @@ #include "BLI_utildefines.h" -#include "BLI_alloca.h" +#include "BLI_array.hh" #include "BLI_bitmap.h" +#include "BLI_index_range.hh" #include "BLI_kdtree.h" #include "BLI_math.h" +#include "BLI_span.hh" +#include "BLI_vector.hh" #include "BLT_translation.h" @@ -69,127 +72,133 @@ #include "MOD_modifiertypes.h" #include "MOD_ui_common.h" +using blender::Array; +using blender::IndexRange; +using blender::MutableSpan; +using blender::Span; +using blender::Vector; + /* Indicates when the element was not computed. */ -#define OUT_OF_CONTEXT (uint)(-1) +#define OUT_OF_CONTEXT (int)(-1) /* Indicates if the edge or face will be collapsed. */ -#define ELEM_COLLAPSED (uint)(-2) +#define ELEM_COLLAPSED (int)(-2) /* indicates whether an edge or vertex in groups_map will be merged. */ -#define ELEM_MERGED (uint)(-2) +#define ELEM_MERGED (int)(-2) /* Used to indicate a range in an array specifying a group. */ struct WeldGroup { - uint len; - uint ofs; + int len; + int ofs; }; /* Edge groups that will be merged. Final vertices are also indicated. */ struct WeldGroupEdge { struct WeldGroup group; - uint v1; - uint v2; + int v1; + int v2; }; -typedef struct WeldVert { +struct WeldVert { /* Indexes relative to the original Mesh. */ - uint vert_dest; - uint vert_orig; -} WeldVert; + int vert_dest; + int vert_orig; +}; -typedef struct WeldEdge { +struct WeldEdge { union { - uint flag; + int flag; struct { /* Indexes relative to the original Mesh. */ - uint edge_dest; - uint edge_orig; - uint vert_a; - uint vert_b; + int edge_dest; + int edge_orig; + int vert_a; + int vert_b; }; }; -} WeldEdge; +}; -typedef struct WeldLoop { +struct WeldLoop { union { - uint flag; + int flag; struct { /* Indexes relative to the original Mesh. */ - uint vert; - uint edge; - uint loop_orig; - uint loop_skip_to; + int vert; + int edge; + int loop_orig; + int loop_skip_to; }; }; -} WeldLoop; +}; -typedef struct WeldPoly { +struct WeldPoly { union { - uint flag; + int flag; struct { /* Indexes relative to the original Mesh. */ - uint poly_dst; - uint poly_orig; - uint loop_start; - uint loop_end; + int poly_dst; + int poly_orig; + int loop_start; + int loop_end; /* Final Polygon Size. */ - uint len; + int len; /* Group of loops that will be affected. */ struct WeldGroup loops; }; }; -} WeldPoly; +}; -typedef struct WeldMesh { +struct WeldMesh { /* Group of vertices to be merged. */ - struct WeldGroup *vert_groups; - uint *vert_groups_buffer; + Array<WeldGroup> vert_groups; + Array<int> vert_groups_buffer; /* Group of edges to be merged. */ - struct WeldGroupEdge *edge_groups; - uint *edge_groups_buffer; + Array<WeldGroupEdge> edge_groups; + Array<int> edge_groups_buffer; /* From the original index of the vertex, this indicates which group it is or is going to be * merged. */ - uint *edge_groups_map; + Array<int> edge_groups_map; /* References all polygons and loops that will be affected. */ - WeldLoop *wloop; - WeldPoly *wpoly; + Vector<WeldLoop> wloop; + Vector<WeldPoly> wpoly; WeldPoly *wpoly_new; - uint wloop_len; - uint wpoly_len; - uint wpoly_new_len; + int wloop_len; + int wpoly_len; + int wpoly_new_len; /* From the actual index of the element in the mesh, it indicates what is the index of the Weld * element above. */ - uint *loop_map; - uint *poly_map; + Array<int> loop_map; + Array<int> poly_map; - uint vert_kill_len; - uint edge_kill_len; - uint loop_kill_len; - uint poly_kill_len; /* Including the new polygons. */ + int vert_kill_len; + int edge_kill_len; + int loop_kill_len; + int poly_kill_len; /* Including the new polygons. */ /* Size of the affected polygon with more sides. */ - uint max_poly_len; -} WeldMesh; - -typedef struct WeldLoopOfPolyIter { - uint loop_start; - uint loop_end; - const WeldLoop *wloop; - const MLoop *mloop; - const uint *loop_map; + int max_poly_len; +}; + +struct WeldLoopOfPolyIter { + int loop_start; + int loop_end; + Span<WeldLoop> wloop; + Span<MLoop> mloop; + Span<int> loop_map; /* Weld group. */ - uint *group; + int *group; - uint l_curr; - uint l_next; + int l_curr; + int l_next; /* Return */ - uint group_len; - uint v; - uint e; + int group_len; + int v; + int e; char type; -} WeldLoopOfPolyIter; +}; /* -------------------------------------------------------------------- */ /** \name Debug Utils @@ -197,22 +206,20 @@ typedef struct WeldLoopOfPolyIter { #ifdef USE_WELD_DEBUG static bool weld_iter_loop_of_poly_begin(WeldLoopOfPolyIter *iter, - const WeldPoly *wp, - const WeldLoop *wloop, - const MLoop *mloop, - const uint *loop_map, - uint *group_buffer); + const WeldPoly &wp, + Span<WeldLoop> wloop, + Span<MLoop> mloop, + Span<int> loop_map, + int *group_buffer); static bool weld_iter_loop_of_poly_next(WeldLoopOfPolyIter *iter); -static void weld_assert_edge_kill_len(const WeldEdge *wedge, - const uint wedge_len, - const uint supposed_kill_len) +static void weld_assert_edge_kill_len(Span<WeldEdge> wedge, const int supposed_kill_len) { - uint kills = 0; + int kills = 0; const WeldEdge *we = &wedge[0]; - for (uint i = wedge_len; i--; we++) { - uint edge_dest = we->edge_dest; + for (int i = wedge.size(); i--; we++) { + int edge_dest = we->edge_dest; /* Magically includes collapsed edges. */ if (edge_dest != OUT_OF_CONTEXT) { kills++; @@ -221,28 +228,25 @@ static void weld_assert_edge_kill_len(const WeldEdge *wedge, BLI_assert(kills == supposed_kill_len); } -static void weld_assert_poly_and_loop_kill_len(const WeldPoly *wpoly, - const WeldPoly *wpoly_new, - const uint wpoly_new_len, - const WeldLoop *wloop, - const MLoop *mloop, - const uint *loop_map, - const uint *poly_map, - const MPoly *mpoly, - const uint mpoly_len, - const uint mloop_len, - const uint supposed_poly_kill_len, - const uint supposed_loop_kill_len) +static void weld_assert_poly_and_loop_kill_len(Span<WeldPoly> wpoly, + Span<WeldPoly> wpoly_new, + Span<WeldLoop> wloop, + Span<MLoop> mloop, + Span<int> loop_map, + Span<int> poly_map, + Span<MPoly> mpoly, + const int supposed_poly_kill_len, + const int supposed_loop_kill_len) { - uint poly_kills = 0; - uint loop_kills = mloop_len; + int poly_kills = 0; + int loop_kills = mloop.size(); const MPoly *mp = &mpoly[0]; - for (uint i = 0; i < mpoly_len; i++, mp++) { - uint poly_ctx = poly_map[i]; + for (int i = 0; i < mpoly.size(); i++, mp++) { + int poly_ctx = poly_map[i]; if (poly_ctx != OUT_OF_CONTEXT) { const WeldPoly *wp = &wpoly[poly_ctx]; WeldLoopOfPolyIter iter; - if (!weld_iter_loop_of_poly_begin(&iter, wp, wloop, mloop, loop_map, NULL)) { + if (!weld_iter_loop_of_poly_begin(&iter, *wp, wloop, mloop, loop_map, nullptr)) { poly_kills++; continue; } @@ -251,11 +255,11 @@ static void weld_assert_poly_and_loop_kill_len(const WeldPoly *wpoly, poly_kills++; continue; } - uint remain = wp->len; - uint l = wp->loop_start; + int remain = wp->len; + int l = wp->loop_start; while (remain) { - uint l_next = l + 1; - uint loop_ctx = loop_map[l]; + int l_next = l + 1; + int loop_ctx = loop_map[l]; if (loop_ctx != OUT_OF_CONTEXT) { const WeldLoop *wl = &wloop[loop_ctx]; if (wl->loop_skip_to != OUT_OF_CONTEXT) { @@ -279,17 +283,17 @@ static void weld_assert_poly_and_loop_kill_len(const WeldPoly *wpoly, } } - const WeldPoly *wp = &wpoly_new[0]; - for (uint i = wpoly_new_len; i--; wp++) { + const WeldPoly *wp = wpoly_new.data(); + for (int i = wpoly_new.size(); i--; wp++) { if (wp->poly_dst != OUT_OF_CONTEXT) { poly_kills++; continue; } - uint remain = wp->len; - uint l = wp->loop_start; + int remain = wp->len; + int l = wp->loop_start; while (remain) { - uint l_next = l + 1; - uint loop_ctx = loop_map[l]; + int l_next = l + 1; + int loop_ctx = loop_map[l]; if (loop_ctx != OUT_OF_CONTEXT) { const WeldLoop *wl = &wloop[loop_ctx]; if (wl->loop_skip_to != OUT_OF_CONTEXT) { @@ -312,46 +316,46 @@ static void weld_assert_poly_and_loop_kill_len(const WeldPoly *wpoly, BLI_assert(loop_kills == supposed_loop_kill_len); } -static void weld_assert_poly_no_vert_repetition(const WeldPoly *wp, - const WeldLoop *wloop, - const MLoop *mloop, - const uint *loop_map) +static void weld_assert_poly_no_vert_repetition(const WeldPoly &wp, + Span<WeldLoop> wloop, + Span<MLoop> mloop, + Span<int> loop_map) { - const uint len = wp->len; - uint *verts = BLI_array_alloca(verts, len); + const int len = wp.len; + Array<int, 64> verts(len); WeldLoopOfPolyIter iter; - if (!weld_iter_loop_of_poly_begin(&iter, wp, wloop, mloop, loop_map, NULL)) { + if (!weld_iter_loop_of_poly_begin(&iter, wp, wloop, mloop, loop_map, nullptr)) { return; } else { - uint i = 0; + int i = 0; while (weld_iter_loop_of_poly_next(&iter)) { verts[i++] = iter.v; } } - for (uint i = 0; i < len; i++) { - uint va = verts[i]; - for (uint j = i + 1; j < len; j++) { - uint vb = verts[j]; + for (int i = 0; i < len; i++) { + int va = verts[i]; + for (int j = i + 1; j < len; j++) { + int vb = verts[j]; BLI_assert(va != vb); } } } -static void weld_assert_poly_len(const WeldPoly *wp, const WeldLoop *wloop) +static void weld_assert_poly_len(const WeldPoly *wp, const Span<WeldLoop> wloop) { if (wp->flag == ELEM_COLLAPSED) { return; } - uint len = wp->len; + int len = wp->len; const WeldLoop *wl = &wloop[wp->loops.ofs]; BLI_assert(wp->loop_start <= wl->loop_orig); - uint end_wloop = wp->loops.ofs + wp->loops.len; + int end_wloop = wp->loops.ofs + wp->loops.len; const WeldLoop *wl_end = &wloop[end_wloop - 1]; - uint min_len = 0; + int min_len = 0; for (; wl <= wl_end; wl++) { BLI_assert(wl->loop_skip_to == OUT_OF_CONTEXT); /* Not for this case. */ if (wl->flag != ELEM_COLLAPSED) { @@ -360,7 +364,7 @@ static void weld_assert_poly_len(const WeldPoly *wp, const WeldLoop *wloop) } BLI_assert(len >= min_len); - uint max_len = wp->loop_end - wp->loop_start + 1; + int max_len = wp->loop_end - wp->loop_start + 1; BLI_assert(len <= max_len); } @@ -372,92 +376,70 @@ static void weld_assert_poly_len(const WeldPoly *wp, const WeldLoop *wloop) /** \name Weld Vert API * \{ */ -static void weld_vert_ctx_alloc_and_setup(const uint mvert_len, - uint *r_vert_dest_map, - WeldVert **r_wvert, - uint *r_wvert_len) +static Vector<WeldVert> weld_vert_ctx_alloc_and_setup(Span<int> vert_dest_map) { - /* Vert Context. */ - uint wvert_len = 0; - - WeldVert *wvert, *wv; - wvert = MEM_mallocN(sizeof(*wvert) * mvert_len, __func__); - wv = &wvert[0]; - - uint *v_dest_iter = &r_vert_dest_map[0]; - for (uint i = 0; i < mvert_len; i++, v_dest_iter++) { - if (*v_dest_iter != OUT_OF_CONTEXT) { - wv->vert_dest = *v_dest_iter; - wv->vert_orig = i; - wv++; - wvert_len++; + Vector<WeldVert> wvert; + wvert.reserve(vert_dest_map.size()); + + for (const int i : vert_dest_map.index_range()) { + if (vert_dest_map[i] != OUT_OF_CONTEXT) { + wvert.append({vert_dest_map[i], i}); } } - - *r_wvert = MEM_reallocN(wvert, sizeof(*wvert) * wvert_len); - *r_wvert_len = wvert_len; + return wvert; } -static void weld_vert_groups_setup(const uint mvert_len, - const uint wvert_len, - const WeldVert *wvert, - const uint *vert_dest_map, - uint *r_vert_groups_map, - uint **r_vert_groups_buffer, - struct WeldGroup **r_vert_groups) +static void weld_vert_groups_setup(Span<WeldVert> wvert, + Span<int> vert_dest_map, + MutableSpan<int> r_vert_groups_map, + Array<int> &r_vert_groups_buffer, + Array<WeldGroup> &r_vert_groups) { /* Get weld vert groups. */ - uint wgroups_len = 0; - const uint *vert_dest_iter = &vert_dest_map[0]; - uint *group_map_iter = &r_vert_groups_map[0]; - for (uint i = 0; i < mvert_len; i++, group_map_iter++, vert_dest_iter++) { - uint vert_dest = *vert_dest_iter; + int wgroups_len = 0; + for (const int i : vert_dest_map.index_range()) { + const int vert_dest = vert_dest_map[i]; if (vert_dest != OUT_OF_CONTEXT) { if (vert_dest != i) { - *group_map_iter = ELEM_MERGED; + r_vert_groups_map[i] = ELEM_MERGED; } else { - *group_map_iter = wgroups_len; + r_vert_groups_map[i] = wgroups_len; wgroups_len++; } } else { - *group_map_iter = OUT_OF_CONTEXT; + r_vert_groups_map[i] = OUT_OF_CONTEXT; } } - struct WeldGroup *wgroups = MEM_callocN(sizeof(*wgroups) * wgroups_len, __func__); + r_vert_groups.reinitialize(wgroups_len); + r_vert_groups.fill({0, 0}); + MutableSpan<WeldGroup> wgroups = r_vert_groups; - const WeldVert *wv = &wvert[0]; - for (uint i = wvert_len; i--; wv++) { - uint group_index = r_vert_groups_map[wv->vert_dest]; + for (const WeldVert &wv : wvert) { + int group_index = r_vert_groups_map[wv.vert_dest]; wgroups[group_index].len++; } - uint ofs = 0; - struct WeldGroup *wg_iter = &wgroups[0]; - for (uint i = wgroups_len; i--; wg_iter++) { - wg_iter->ofs = ofs; - ofs += wg_iter->len; + int ofs = 0; + for (WeldGroup &wg : wgroups) { + wg.ofs = ofs; + ofs += wg.len; } - BLI_assert(ofs == wvert_len); + BLI_assert(ofs == wvert.size()); - uint *groups_buffer = MEM_mallocN(sizeof(*groups_buffer) * ofs, __func__); - wv = &wvert[0]; - for (uint i = wvert_len; i--; wv++) { - uint group_index = r_vert_groups_map[wv->vert_dest]; - groups_buffer[wgroups[group_index].ofs++] = wv->vert_orig; + r_vert_groups_buffer.reinitialize(ofs); + for (const WeldVert &wv : wvert) { + int group_index = r_vert_groups_map[wv.vert_dest]; + r_vert_groups_buffer[wgroups[group_index].ofs++] = wv.vert_orig; } - wg_iter = &wgroups[0]; - for (uint i = wgroups_len; i--; wg_iter++) { - wg_iter->ofs -= wg_iter->len; + for (WeldGroup &wg : wgroups) { + wg.ofs -= wg.len; } - - *r_vert_groups = wgroups; - *r_vert_groups_buffer = groups_buffer; } /** \} */ @@ -466,31 +448,24 @@ static void weld_vert_groups_setup(const uint mvert_len, /** \name Weld Edge API * \{ */ -static void weld_edge_ctx_setup(const uint mvert_len, - const uint wedge_len, - struct WeldGroup *r_vlinks, - uint *r_edge_dest_map, - WeldEdge *r_wedge, - uint *r_edge_kiil_len) +static void weld_edge_ctx_setup(MutableSpan<WeldGroup> r_vlinks, + MutableSpan<int> r_edge_dest_map, + MutableSpan<WeldEdge> r_wedge, + int *r_edge_kiil_len) { - WeldEdge *we; - /* Setup Edge Overlap. */ - uint edge_kill_len = 0; + int edge_kill_len = 0; - struct WeldGroup *vl_iter, *v_links; - v_links = r_vlinks; - vl_iter = &v_links[0]; + MutableSpan<WeldGroup> v_links = r_vlinks; - we = &r_wedge[0]; - for (uint i = wedge_len; i--; we++) { - uint dst_vert_a = we->vert_a; - uint dst_vert_b = we->vert_b; + for (WeldEdge &we : r_wedge) { + int dst_vert_a = we.vert_a; + int dst_vert_b = we.vert_b; if (dst_vert_a == dst_vert_b) { - BLI_assert(we->edge_dest == OUT_OF_CONTEXT); - r_edge_dest_map[we->edge_orig] = ELEM_COLLAPSED; - we->flag = ELEM_COLLAPSED; + BLI_assert(we.edge_dest == OUT_OF_CONTEXT); + r_edge_dest_map[we.edge_orig] = ELEM_COLLAPSED; + we.flag = ELEM_COLLAPSED; edge_kill_len++; continue; } @@ -499,62 +474,60 @@ static void weld_edge_ctx_setup(const uint mvert_len, v_links[dst_vert_b].len++; } - uint link_len = 0; - vl_iter = &v_links[0]; - for (uint i = mvert_len; i--; vl_iter++) { - vl_iter->ofs = link_len; - link_len += vl_iter->len; + int link_len = 0; + for (WeldGroup &vl : r_vlinks) { + vl.ofs = link_len; + link_len += vl.len; } - if (link_len) { - uint *link_edge_buffer = MEM_mallocN(sizeof(*link_edge_buffer) * link_len, __func__); + if (link_len > 0) { + Array<int> link_edge_buffer(link_len); - we = &r_wedge[0]; - for (uint i = 0; i < wedge_len; i++, we++) { - if (we->flag == ELEM_COLLAPSED) { + for (const int i : r_wedge.index_range()) { + const WeldEdge &we = r_wedge[i]; + if (we.flag == ELEM_COLLAPSED) { continue; } - uint dst_vert_a = we->vert_a; - uint dst_vert_b = we->vert_b; + int dst_vert_a = we.vert_a; + int dst_vert_b = we.vert_b; link_edge_buffer[v_links[dst_vert_a].ofs++] = i; link_edge_buffer[v_links[dst_vert_b].ofs++] = i; } - vl_iter = &v_links[0]; - for (uint i = mvert_len; i--; vl_iter++) { + for (WeldGroup &vl : r_vlinks) { /* Fix offset */ - vl_iter->ofs -= vl_iter->len; + vl.ofs -= vl.len; } - we = &r_wedge[0]; - for (uint i = 0; i < wedge_len; i++, we++) { - if (we->edge_dest != OUT_OF_CONTEXT) { + for (const int i : r_wedge.index_range()) { + const WeldEdge &we = r_wedge[i]; + if (we.edge_dest != OUT_OF_CONTEXT) { /* No need to retest edges. * (Already includes collapsed edges). */ continue; } - uint dst_vert_a = we->vert_a; - uint dst_vert_b = we->vert_b; + int dst_vert_a = we.vert_a; + int dst_vert_b = we.vert_b; struct WeldGroup *link_a = &v_links[dst_vert_a]; struct WeldGroup *link_b = &v_links[dst_vert_b]; - uint edges_len_a = link_a->len; - uint edges_len_b = link_b->len; + int edges_len_a = link_a->len; + int edges_len_b = link_b->len; if (edges_len_a <= 1 || edges_len_b <= 1) { continue; } - uint *edges_ctx_a = &link_edge_buffer[link_a->ofs]; - uint *edges_ctx_b = &link_edge_buffer[link_b->ofs]; - uint edge_orig = we->edge_orig; + int *edges_ctx_a = &link_edge_buffer[link_a->ofs]; + int *edges_ctx_b = &link_edge_buffer[link_b->ofs]; + int edge_orig = we.edge_orig; for (; edges_len_a--; edges_ctx_a++) { - uint e_ctx_a = *edges_ctx_a; + int e_ctx_a = *edges_ctx_a; if (e_ctx_a == i) { continue; } @@ -565,7 +538,7 @@ static void weld_edge_ctx_setup(const uint mvert_len, if (edges_len_b == 0) { break; } - uint e_ctx_b = *edges_ctx_b; + int e_ctx_b = *edges_ctx_b; if (e_ctx_a == e_ctx_b) { WeldEdge *we_b = &r_wedge[e_ctx_b]; BLI_assert(ELEM(we_b->vert_a, dst_vert_a, dst_vert_b)); @@ -580,138 +553,118 @@ static void weld_edge_ctx_setup(const uint mvert_len, } #ifdef USE_WELD_DEBUG - weld_assert_edge_kill_len(r_wedge, wedge_len, edge_kill_len); + weld_assert_edge_kill_len(r_wedge, edge_kill_len); #endif - - MEM_freeN(link_edge_buffer); } *r_edge_kiil_len = edge_kill_len; } -static void weld_edge_ctx_alloc(const MEdge *medge, - const uint medge_len, - const uint *vert_dest_map, - uint *r_edge_dest_map, - uint **r_edge_ctx_map, - WeldEdge **r_wedge, - uint *r_wedge_len) +static Vector<WeldEdge> weld_edge_ctx_alloc(Span<MEdge> medge, + Span<int> vert_dest_map, + MutableSpan<int> r_edge_dest_map, + MutableSpan<int> r_edge_ctx_map) { /* Edge Context. */ - uint *edge_map = MEM_mallocN(sizeof(*edge_map) * medge_len, __func__); - uint wedge_len = 0; - - WeldEdge *wedge, *we; - wedge = MEM_mallocN(sizeof(*wedge) * medge_len, __func__); - we = &wedge[0]; - - const MEdge *me = &medge[0]; - uint *e_dest_iter = &r_edge_dest_map[0]; - uint *iter = &edge_map[0]; - for (uint i = 0; i < medge_len; i++, me++, iter++, e_dest_iter++) { - uint v1 = me->v1; - uint v2 = me->v2; - uint v_dest_1 = vert_dest_map[v1]; - uint v_dest_2 = vert_dest_map[v2]; + int wedge_len = 0; + + Vector<WeldEdge> wedge; + wedge.reserve(medge.size()); + + for (const int i : medge.index_range()) { + int v1 = medge[i].v1; + int v2 = medge[i].v2; + int v_dest_1 = vert_dest_map[v1]; + int v_dest_2 = vert_dest_map[v2]; if ((v_dest_1 != OUT_OF_CONTEXT) || (v_dest_2 != OUT_OF_CONTEXT)) { - we->vert_a = (v_dest_1 != OUT_OF_CONTEXT) ? v_dest_1 : v1; - we->vert_b = (v_dest_2 != OUT_OF_CONTEXT) ? v_dest_2 : v2; - we->edge_dest = OUT_OF_CONTEXT; - we->edge_orig = i; - we++; - *e_dest_iter = i; - *iter = wedge_len++; + WeldEdge we{}; + we.vert_a = (v_dest_1 != OUT_OF_CONTEXT) ? v_dest_1 : v1; + we.vert_b = (v_dest_2 != OUT_OF_CONTEXT) ? v_dest_2 : v2; + we.edge_dest = OUT_OF_CONTEXT; + we.edge_orig = i; + wedge.append(we); + r_edge_dest_map[i] = i; + r_edge_ctx_map[i] = wedge_len++; } else { - *e_dest_iter = OUT_OF_CONTEXT; - *iter = OUT_OF_CONTEXT; + r_edge_dest_map[i] = OUT_OF_CONTEXT; + r_edge_ctx_map[i] = OUT_OF_CONTEXT; } } - *r_wedge = MEM_reallocN(wedge, sizeof(*wedge) * wedge_len); - *r_wedge_len = wedge_len; - *r_edge_ctx_map = edge_map; + return wedge; } -static void weld_edge_groups_setup(const uint medge_len, - const uint edge_kill_len, - const uint wedge_len, - WeldEdge *wedge, - const uint *wedge_map, - uint *r_edge_groups_map, - uint **r_edge_groups_buffer, - struct WeldGroupEdge **r_edge_groups) +static void weld_edge_groups_setup(const int medge_len, + const int edge_kill_len, + MutableSpan<WeldEdge> wedge, + Span<int> wedge_map, + MutableSpan<int> r_edge_groups_map, + Array<int> &r_edge_groups_buffer, + Array<WeldGroupEdge> &r_edge_groups) { - /* Get weld edge groups. */ - struct WeldGroupEdge *wegroups, *wegrp_iter; + struct WeldGroupEdge *wegrp_iter; - uint wgroups_len = wedge_len - edge_kill_len; - wegroups = MEM_callocN(sizeof(*wegroups) * wgroups_len, __func__); - wegrp_iter = &wegroups[0]; + int wgroups_len = wedge.size() - edge_kill_len; + r_edge_groups.reinitialize(wgroups_len); + r_edge_groups.fill({0}); + MutableSpan<WeldGroupEdge> wegroups = r_edge_groups; + wegrp_iter = &r_edge_groups[0]; wgroups_len = 0; - const uint *edge_ctx_iter = &wedge_map[0]; - uint *group_map_iter = &r_edge_groups_map[0]; - for (uint i = medge_len; i--; edge_ctx_iter++, group_map_iter++) { - uint edge_ctx = *edge_ctx_iter; + for (const int i : IndexRange(medge_len)) { + int edge_ctx = wedge_map[i]; if (edge_ctx != OUT_OF_CONTEXT) { WeldEdge *we = &wedge[edge_ctx]; - uint edge_dest = we->edge_dest; + int edge_dest = we->edge_dest; if (edge_dest != OUT_OF_CONTEXT) { BLI_assert(edge_dest != we->edge_orig); - *group_map_iter = ELEM_MERGED; + r_edge_groups_map[i] = ELEM_MERGED; } else { we->edge_dest = we->edge_orig; wegrp_iter->v1 = we->vert_a; wegrp_iter->v2 = we->vert_b; - *group_map_iter = wgroups_len; + r_edge_groups_map[i] = wgroups_len; wgroups_len++; wegrp_iter++; } } else { - *group_map_iter = OUT_OF_CONTEXT; + r_edge_groups_map[i] = OUT_OF_CONTEXT; } } - BLI_assert(wgroups_len == wedge_len - edge_kill_len); + BLI_assert(wgroups_len == wedge.size() - edge_kill_len); - WeldEdge *we = &wedge[0]; - for (uint i = wedge_len; i--; we++) { - if (we->flag == ELEM_COLLAPSED) { + for (const WeldEdge &we : wedge) { + if (we.flag == ELEM_COLLAPSED) { continue; } - uint group_index = r_edge_groups_map[we->edge_dest]; + int group_index = r_edge_groups_map[we.edge_dest]; wegroups[group_index].group.len++; } - uint ofs = 0; - wegrp_iter = &wegroups[0]; - for (uint i = wgroups_len; i--; wegrp_iter++) { - wegrp_iter->group.ofs = ofs; - ofs += wegrp_iter->group.len; + int ofs = 0; + for (WeldGroupEdge &wegrp : wegroups) { + wegrp.group.ofs = ofs; + ofs += wegrp.group.len; } - uint *groups_buffer = MEM_mallocN(sizeof(*groups_buffer) * ofs, __func__); - we = &wedge[0]; - for (uint i = wedge_len; i--; we++) { - if (we->flag == ELEM_COLLAPSED) { + r_edge_groups_buffer.reinitialize(ofs); + for (const WeldEdge &we : wedge) { + if (we.flag == ELEM_COLLAPSED) { continue; } - uint group_index = r_edge_groups_map[we->edge_dest]; - groups_buffer[wegroups[group_index].group.ofs++] = we->edge_orig; + int group_index = r_edge_groups_map[we.edge_dest]; + r_edge_groups_buffer[wegroups[group_index].group.ofs++] = we.edge_orig; } - wegrp_iter = &wegroups[0]; - for (uint i = wgroups_len; i--; wegrp_iter++) { - wegrp_iter->group.ofs -= wegrp_iter->group.len; + for (WeldGroupEdge &wegrp : wegroups) { + wegrp.group.ofs -= wegrp.group.len; } - - *r_edge_groups_buffer = groups_buffer; - *r_edge_groups = wegroups; } /** \} */ @@ -721,31 +674,31 @@ static void weld_edge_groups_setup(const uint medge_len, * \{ */ static bool weld_iter_loop_of_poly_begin(WeldLoopOfPolyIter *iter, - const WeldPoly *wp, - const WeldLoop *wloop, - const MLoop *mloop, - const uint *loop_map, - uint *group_buffer) + const WeldPoly &wp, + Span<WeldLoop> wloop, + Span<MLoop> mloop, + Span<int> loop_map, + int *group_buffer) { - if (wp->flag == ELEM_COLLAPSED) { + if (wp.flag == ELEM_COLLAPSED) { return false; } - iter->loop_start = wp->loop_start; - iter->loop_end = wp->loop_end; + iter->loop_start = wp.loop_start; + iter->loop_end = wp.loop_end; iter->wloop = wloop; iter->mloop = mloop; iter->loop_map = loop_map; iter->group = group_buffer; - uint group_len = 0; + int group_len = 0; if (group_buffer) { /* First loop group needs more attention. */ - uint loop_start, loop_end, l; + int loop_start, loop_end, l; loop_start = iter->loop_start; loop_end = l = iter->loop_end; while (l >= loop_start) { - const uint loop_ctx = loop_map[l]; + const int loop_ctx = loop_map[l]; if (loop_ctx != OUT_OF_CONTEXT) { const WeldLoop *wl = &wloop[loop_ctx]; if (wl->flag == ELEM_COLLAPSED) { @@ -774,10 +727,10 @@ static bool weld_iter_loop_of_poly_begin(WeldLoopOfPolyIter *iter, static bool weld_iter_loop_of_poly_next(WeldLoopOfPolyIter *iter) { - uint loop_end = iter->loop_end; - const WeldLoop *wloop = iter->wloop; - const uint *loop_map = iter->loop_map; - uint l = iter->l_curr = iter->l_next; + int loop_end = iter->loop_end; + Span<WeldLoop> wloop = iter->wloop; + Span<int> loop_map = iter->loop_map; + int l = iter->l_curr = iter->l_next; if (l == iter->loop_start) { /* `grupo_len` is already calculated in the first loop */ } @@ -785,8 +738,8 @@ static bool weld_iter_loop_of_poly_next(WeldLoopOfPolyIter *iter) iter->group_len = 0; } while (l <= loop_end) { - uint l_next = l + 1; - const uint loop_ctx = loop_map[l]; + int l_next = l + 1; + const int loop_ctx = loop_map[l]; if (loop_ctx != OUT_OF_CONTEXT) { const WeldLoop *wl = &wloop[loop_ctx]; if (wl->loop_skip_to != OUT_OF_CONTEXT) { @@ -809,7 +762,7 @@ static bool weld_iter_loop_of_poly_next(WeldLoopOfPolyIter *iter) else { const MLoop *ml = &iter->mloop[l]; #ifdef USE_WELD_DEBUG - BLI_assert(iter->v != ml->v); + BLI_assert((uint)iter->v != ml->v); #endif iter->v = ml->v; iter->e = ml->e; @@ -825,129 +778,119 @@ static bool weld_iter_loop_of_poly_next(WeldLoopOfPolyIter *iter) return false; } -static void weld_poly_loop_ctx_alloc(const MPoly *mpoly, - const uint mpoly_len, - const MLoop *mloop, - const uint mloop_len, - const uint *vert_dest_map, - const uint *edge_dest_map, +static void weld_poly_loop_ctx_alloc(Span<MPoly> mpoly, + Span<MLoop> mloop, + Span<int> vert_dest_map, + Span<int> edge_dest_map, WeldMesh *r_weld_mesh) { /* Loop/Poly Context. */ - uint *loop_map = MEM_mallocN(sizeof(*loop_map) * mloop_len, __func__); - uint *poly_map = MEM_mallocN(sizeof(*poly_map) * mpoly_len, __func__); - uint wloop_len = 0; - uint wpoly_len = 0; - uint max_ctx_poly_len = 4; + Array<int> loop_map(mloop.size()); + Array<int> poly_map(mpoly.size()); + int wloop_len = 0; + int wpoly_len = 0; + int max_ctx_poly_len = 4; - WeldLoop *wloop, *wl; - wloop = MEM_mallocN(sizeof(*wloop) * mloop_len, __func__); - wl = &wloop[0]; + Vector<WeldLoop> wloop; + wloop.reserve(mloop.size()); - WeldPoly *wpoly, *wp; - wpoly = MEM_mallocN(sizeof(*wpoly) * mpoly_len, __func__); - wp = &wpoly[0]; + Vector<WeldPoly> wpoly; + wpoly.reserve(mpoly.size()); - uint maybe_new_poly = 0; + int maybe_new_poly = 0; - const MPoly *mp = &mpoly[0]; - uint *iter = &poly_map[0]; - uint *loop_map_iter = &loop_map[0]; - for (uint i = 0; i < mpoly_len; i++, mp++, iter++) { - const uint loopstart = mp->loopstart; - const uint totloop = mp->totloop; - - uint vert_ctx_len = 0; - - uint l = loopstart; - uint prev_wloop_len = wloop_len; - const MLoop *ml = &mloop[l]; - for (uint j = totloop; j--; l++, ml++, loop_map_iter++) { - uint v = ml->v; - uint e = ml->e; - uint v_dest = vert_dest_map[v]; - uint e_dest = edge_dest_map[e]; + for (const int i : mpoly.index_range()) { + const MPoly &mp = mpoly[i]; + const int loopstart = mp.loopstart; + const int totloop = mp.totloop; + + int vert_ctx_len = 0; + + int prev_wloop_len = wloop_len; + for (const int i_loop : mloop.index_range().slice(loopstart, totloop)) { + int v = mloop[i_loop].v; + int e = mloop[i_loop].e; + int v_dest = vert_dest_map[v]; + int e_dest = edge_dest_map[e]; bool is_vert_ctx = v_dest != OUT_OF_CONTEXT; bool is_edge_ctx = e_dest != OUT_OF_CONTEXT; if (is_vert_ctx) { vert_ctx_len++; } if (is_vert_ctx || is_edge_ctx) { - wl->vert = is_vert_ctx ? v_dest : v; - wl->edge = is_edge_ctx ? e_dest : e; - wl->loop_orig = l; - wl->loop_skip_to = OUT_OF_CONTEXT; - wl++; - - *loop_map_iter = wloop_len++; + WeldLoop wl{}; + wl.vert = is_vert_ctx ? v_dest : v; + wl.edge = is_edge_ctx ? e_dest : e; + wl.loop_orig = i_loop; + wl.loop_skip_to = OUT_OF_CONTEXT; + wloop.append(wl); + + loop_map[i_loop] = wloop_len++; } else { - *loop_map_iter = OUT_OF_CONTEXT; + loop_map[i_loop] = OUT_OF_CONTEXT; } } if (wloop_len != prev_wloop_len) { - uint loops_len = wloop_len - prev_wloop_len; - - wp->poly_dst = OUT_OF_CONTEXT; - wp->poly_orig = i; - wp->loops.len = loops_len; - wp->loops.ofs = prev_wloop_len; - wp->loop_start = loopstart; - wp->loop_end = loopstart + totloop - 1; - wp->len = totloop; - wp++; - - *iter = wpoly_len++; + int loops_len = wloop_len - prev_wloop_len; + WeldPoly wp{}; + wp.poly_dst = OUT_OF_CONTEXT; + wp.poly_orig = i; + wp.loops.len = loops_len; + wp.loops.ofs = prev_wloop_len; + wp.loop_start = loopstart; + wp.loop_end = loopstart + totloop - 1; + wp.len = totloop; + wpoly.append(wp); + + poly_map[i] = wpoly_len++; if (totloop > 5 && vert_ctx_len > 1) { - uint max_new = (totloop / 3) - 1; + int max_new = (totloop / 3) - 1; vert_ctx_len /= 2; maybe_new_poly += MIN2(max_new, vert_ctx_len); CLAMP_MIN(max_ctx_poly_len, totloop); } } else { - *iter = OUT_OF_CONTEXT; + poly_map[i] = OUT_OF_CONTEXT; } } - if (mpoly_len < (wpoly_len + maybe_new_poly)) { - WeldPoly *wpoly_tmp = wpoly; - wpoly = MEM_mallocN(sizeof(*wpoly) * ((size_t)wpoly_len + maybe_new_poly), __func__); - memcpy(wpoly, wpoly_tmp, sizeof(*wpoly) * wpoly_len); - MEM_freeN(wpoly_tmp); + if (mpoly.size() < (wpoly_len + maybe_new_poly)) { + wpoly.resize(wpoly_len + maybe_new_poly); } - WeldPoly *poly_new = &wpoly[wpoly_len]; + WeldPoly *poly_new = wpoly.data() + wpoly_len; - r_weld_mesh->wloop = MEM_reallocN(wloop, sizeof(*wloop) * wloop_len); - r_weld_mesh->wpoly = wpoly; + r_weld_mesh->wloop = std::move(wloop); + r_weld_mesh->wpoly = std::move(wpoly); r_weld_mesh->wpoly_new = poly_new; r_weld_mesh->wloop_len = wloop_len; r_weld_mesh->wpoly_len = wpoly_len; r_weld_mesh->wpoly_new_len = 0; - r_weld_mesh->loop_map = loop_map; - r_weld_mesh->poly_map = poly_map; + r_weld_mesh->loop_map = std::move(loop_map); + r_weld_mesh->poly_map = std::move(poly_map); r_weld_mesh->max_poly_len = max_ctx_poly_len; } -static void weld_poly_split_recursive(const uint *vert_dest_map, +static void weld_poly_split_recursive(Span<int> vert_dest_map, #ifdef USE_WELD_DEBUG - const MLoop *mloop, + const Span<MLoop> mloop, #endif - uint ctx_verts_len, + int ctx_verts_len, WeldPoly *r_wp, WeldMesh *r_weld_mesh, - uint *r_poly_kill, - uint *r_loop_kill) + int *r_poly_kill, + int *r_loop_kill) { - uint poly_len = r_wp->len; + int poly_len = r_wp->len; if (poly_len > 3 && ctx_verts_len > 1) { - const uint ctx_loops_len = r_wp->loops.len; - const uint ctx_loops_ofs = r_wp->loops.ofs; - WeldLoop *wloop = r_weld_mesh->wloop; + const int ctx_loops_len = r_wp->loops.len; + const int ctx_loops_ofs = r_wp->loops.ofs; + MutableSpan<WeldLoop> wloop = r_weld_mesh->wloop; WeldPoly *wpoly_new = r_weld_mesh->wpoly_new; - uint loop_kill = 0; + int loop_kill = 0; WeldLoop *poly_loops = &wloop[ctx_loops_ofs]; WeldLoop *wla = &poly_loops[0]; @@ -955,19 +898,19 @@ static void weld_poly_split_recursive(const uint *vert_dest_map, while (wla_prev->flag == ELEM_COLLAPSED) { wla_prev--; } - const uint la_len = ctx_loops_len - 1; - for (uint la = 0; la < la_len; la++, wla++) { + const int la_len = ctx_loops_len - 1; + for (int la = 0; la < la_len; la++, wla++) { wa_continue: if (wla->flag == ELEM_COLLAPSED) { continue; } - uint vert_a = wla->vert; + int vert_a = wla->vert; /* Only test vertices that will be merged. */ if (vert_dest_map[vert_a] != OUT_OF_CONTEXT) { - uint lb = la + 1; + int lb = la + 1; WeldLoop *wlb = wla + 1; WeldLoop *wlb_prev = wla; - uint killed_ab = 0; + int killed_ab = 0; ctx_verts_len = 1; for (; lb < ctx_loops_len; lb++, wlb++) { BLI_assert(wlb->loop_skip_to == OUT_OF_CONTEXT); @@ -975,13 +918,13 @@ static void weld_poly_split_recursive(const uint *vert_dest_map, killed_ab++; continue; } - uint vert_b = wlb->vert; + int vert_b = wlb->vert; if (vert_dest_map[vert_b] != OUT_OF_CONTEXT) { ctx_verts_len++; } if (vert_a == vert_b) { - const uint dist_a = wlb->loop_orig - wla->loop_orig - killed_ab; - const uint dist_b = poly_len - dist_a; + const int dist_a = wlb->loop_orig - wla->loop_orig - killed_ab; + const int dist_b = poly_len - dist_a; BLI_assert(dist_a != 0 && dist_b != 0); if (dist_a == 1 || dist_b == 1) { @@ -989,7 +932,7 @@ static void weld_poly_split_recursive(const uint *vert_dest_map, BLI_assert((wla->flag == ELEM_COLLAPSED) || (wlb->flag == ELEM_COLLAPSED)); } else { - WeldLoop *wl_tmp = NULL; + WeldLoop *wl_tmp = nullptr; if (dist_a == 2) { wl_tmp = wlb_prev; BLI_assert(wla->flag != ELEM_COLLAPSED); @@ -1000,7 +943,7 @@ static void weld_poly_split_recursive(const uint *vert_dest_map, poly_len -= 2; } if (dist_b == 2) { - if (wl_tmp != NULL) { + if (wl_tmp != nullptr) { r_wp->flag = ELEM_COLLAPSED; *r_poly_kill += 1; } @@ -1014,9 +957,9 @@ static void weld_poly_split_recursive(const uint *vert_dest_map, loop_kill += 2; poly_len -= 2; } - if (wl_tmp == NULL) { - const uint new_loops_len = lb - la; - const uint new_loops_ofs = ctx_loops_ofs + la; + if (wl_tmp == nullptr) { + const int new_loops_len = lb - la; + const int new_loops_ofs = ctx_loops_ofs + la; WeldPoly *new_wp = &wpoly_new[r_weld_mesh->wpoly_new_len++]; new_wp->poly_dst = OUT_OF_CONTEXT; @@ -1065,56 +1008,53 @@ static void weld_poly_split_recursive(const uint *vert_dest_map, *r_loop_kill += loop_kill; #ifdef USE_WELD_DEBUG - weld_assert_poly_no_vert_repetition(r_wp, wloop, mloop, r_weld_mesh->loop_map); + weld_assert_poly_no_vert_repetition(*r_wp, wloop, mloop, r_weld_mesh->loop_map); #endif } } -static void weld_poly_loop_ctx_setup(const MLoop *mloop, +static void weld_poly_loop_ctx_setup(Span<MLoop> mloop, #ifdef USE_WELD_DEBUG - const MPoly *mpoly, - const uint mpoly_len, - const uint mloop_len, + Span<MPoly> mpoly, #endif - const uint mvert_len, - const uint *vert_dest_map, - const uint remain_edge_ctx_len, - struct WeldGroup *r_vlinks, + const int mvert_len, + Span<int> vert_dest_map, + const int remain_edge_ctx_len, + MutableSpan<WeldGroup> r_vlinks, WeldMesh *r_weld_mesh) { - uint poly_kill_len, loop_kill_len, wpoly_len, wpoly_new_len; + int poly_kill_len, loop_kill_len, wpoly_len, wpoly_new_len; - WeldPoly *wpoly_new, *wpoly, *wp; - WeldLoop *wloop, *wl; + WeldPoly *wpoly_new; + WeldLoop *wl; - wpoly = r_weld_mesh->wpoly; - wloop = r_weld_mesh->wloop; + MutableSpan<WeldPoly> wpoly = r_weld_mesh->wpoly; + MutableSpan<WeldLoop> wloop = r_weld_mesh->wloop; wpoly_new = r_weld_mesh->wpoly_new; wpoly_len = r_weld_mesh->wpoly_len; wpoly_new_len = 0; poly_kill_len = 0; loop_kill_len = 0; - const uint *loop_map = r_weld_mesh->loop_map; + Span<int> loop_map = r_weld_mesh->loop_map; if (remain_edge_ctx_len) { /* Setup Poly/Loop. */ - wp = &wpoly[0]; - for (uint i = wpoly_len; i--; wp++) { - const uint ctx_loops_len = wp->loops.len; - const uint ctx_loops_ofs = wp->loops.ofs; + for (WeldPoly &wp : wpoly) { + const int ctx_loops_len = wp.loops.len; + const int ctx_loops_ofs = wp.loops.ofs; - uint poly_len = wp->len; - uint ctx_verts_len = 0; + int poly_len = wp.len; + int ctx_verts_len = 0; wl = &wloop[ctx_loops_ofs]; - for (uint l = ctx_loops_len; l--; wl++) { - const uint edge_dest = wl->edge; + for (int l = ctx_loops_len; l--; wl++) { + const int edge_dest = wl->edge; if (edge_dest == ELEM_COLLAPSED) { wl->flag = ELEM_COLLAPSED; if (poly_len == 3) { - wp->flag = ELEM_COLLAPSED; + wp.flag = ELEM_COLLAPSED; poly_kill_len++; loop_kill_len += 3; poly_len = 0; @@ -1124,7 +1064,7 @@ static void weld_poly_loop_ctx_setup(const MLoop *mloop, poly_len--; } else { - const uint vert_dst = wl->vert; + const int vert_dst = wl->vert; if (vert_dest_map[vert_dst] != OUT_OF_CONTEXT) { ctx_verts_len++; } @@ -1132,7 +1072,7 @@ static void weld_poly_loop_ctx_setup(const MLoop *mloop, } if (poly_len) { - wp->len = poly_len; + wp.len = poly_len; #ifdef USE_WELD_DEBUG weld_assert_poly_len(wp, wloop); #endif @@ -1142,7 +1082,7 @@ static void weld_poly_loop_ctx_setup(const MLoop *mloop, mloop, #endif ctx_verts_len, - wp, + &wp, r_weld_mesh, &poly_kill_len, &loop_kill_len); @@ -1153,75 +1093,70 @@ static void weld_poly_loop_ctx_setup(const MLoop *mloop, #ifdef USE_WELD_DEBUG weld_assert_poly_and_loop_kill_len(wpoly, - wpoly_new, - wpoly_new_len, + {wpoly_new, wpoly_new_len}, wloop, mloop, loop_map, r_weld_mesh->poly_map, mpoly, - mpoly_len, - mloop_len, poly_kill_len, loop_kill_len); #endif /* Setup Polygon Overlap. */ - uint wpoly_and_new_len = wpoly_len + wpoly_new_len; + int wpoly_and_new_len = wpoly_len + wpoly_new_len; - struct WeldGroup *vl_iter, *v_links = r_vlinks; - memset(v_links, 0, sizeof(*v_links) * mvert_len); + r_vlinks.fill({0, 0}); + MutableSpan<WeldGroup> v_links = r_vlinks; - wp = &wpoly[0]; - for (uint i = wpoly_and_new_len; i--; wp++) { + for (const int i : IndexRange(wpoly_and_new_len)) { + const WeldPoly &wp = wpoly[i]; WeldLoopOfPolyIter iter; - if (weld_iter_loop_of_poly_begin(&iter, wp, wloop, mloop, loop_map, NULL)) { + if (weld_iter_loop_of_poly_begin(&iter, wp, wloop, mloop, loop_map, nullptr)) { while (weld_iter_loop_of_poly_next(&iter)) { v_links[iter.v].len++; } } } - uint link_len = 0; - vl_iter = &v_links[0]; - for (uint i = mvert_len; i--; vl_iter++) { - vl_iter->ofs = link_len; - link_len += vl_iter->len; + int link_len = 0; + for (const int i : IndexRange(mvert_len)) { + v_links[i].ofs = link_len; + link_len += v_links[i].len; } if (link_len) { - uint *link_poly_buffer = MEM_mallocN(sizeof(*link_poly_buffer) * link_len, __func__); + Array<int> link_poly_buffer(link_len); - wp = &wpoly[0]; - for (uint i = 0; i < wpoly_and_new_len; i++, wp++) { + for (const int i : IndexRange(wpoly_and_new_len)) { + const WeldPoly &wp = wpoly[i]; WeldLoopOfPolyIter iter; - if (weld_iter_loop_of_poly_begin(&iter, wp, wloop, mloop, loop_map, NULL)) { + if (weld_iter_loop_of_poly_begin(&iter, wp, wloop, mloop, loop_map, nullptr)) { while (weld_iter_loop_of_poly_next(&iter)) { link_poly_buffer[v_links[iter.v].ofs++] = i; } } } - vl_iter = &v_links[0]; - for (uint i = mvert_len; i--; vl_iter++) { + for (WeldGroup &vl : r_vlinks) { /* Fix offset */ - vl_iter->ofs -= vl_iter->len; + vl.ofs -= vl.len; } - uint polys_len_a, polys_len_b, *polys_ctx_a, *polys_ctx_b, p_ctx_a, p_ctx_b; + int polys_len_a, polys_len_b, *polys_ctx_a, *polys_ctx_b, p_ctx_a, p_ctx_b; polys_len_b = p_ctx_b = 0; /* silence warnings */ - wp = &wpoly[0]; - for (uint i = 0; i < wpoly_and_new_len; i++, wp++) { - if (wp->poly_dst != OUT_OF_CONTEXT) { + for (const int i : IndexRange(wpoly_and_new_len)) { + const WeldPoly &wp = wpoly[i]; + if (wp.poly_dst != OUT_OF_CONTEXT) { /* No need to retest poly. * (Already includes collapsed polygons). */ continue; } WeldLoopOfPolyIter iter; - weld_iter_loop_of_poly_begin(&iter, wp, wloop, mloop, loop_map, NULL); + weld_iter_loop_of_poly_begin(&iter, wp, wloop, mloop, loop_map, nullptr); weld_iter_loop_of_poly_next(&iter); struct WeldGroup *link_a = &v_links[iter.v]; polys_len_a = link_a->len; @@ -1229,7 +1164,7 @@ static void weld_poly_loop_ctx_setup(const MLoop *mloop, BLI_assert(link_poly_buffer[link_a->ofs] == i); continue; } - uint wp_len = wp->len; + int wp_len = wp.len; polys_ctx_a = &link_poly_buffer[link_a->ofs]; for (; polys_len_a--; polys_ctx_a++) { p_ctx_a = *polys_ctx_a; @@ -1275,36 +1210,31 @@ static void weld_poly_loop_ctx_setup(const MLoop *mloop, BLI_assert(p_ctx_a > i); BLI_assert(p_ctx_a == p_ctx_b); BLI_assert(wp_tmp->poly_dst == OUT_OF_CONTEXT); - BLI_assert(wp_tmp != wp); - wp_tmp->poly_dst = wp->poly_orig; + BLI_assert(wp_tmp != &wp); + wp_tmp->poly_dst = wp.poly_orig; loop_kill_len += wp_tmp->len; poly_kill_len++; } } - MEM_freeN(link_poly_buffer); } } else { poly_kill_len = r_weld_mesh->wpoly_len; loop_kill_len = r_weld_mesh->wloop_len; - wp = &wpoly[0]; - for (uint i = wpoly_len; i--; wp++) { - wp->flag = ELEM_COLLAPSED; + for (WeldPoly &wp : wpoly) { + wp.flag = ELEM_COLLAPSED; } } #ifdef USE_WELD_DEBUG weld_assert_poly_and_loop_kill_len(wpoly, - wpoly_new, - wpoly_new_len, + {wpoly_new, wpoly_new_len}, wloop, mloop, loop_map, r_weld_mesh->poly_map, mpoly, - mpoly_len, - mloop_len, poly_kill_len, loop_kill_len); #endif @@ -1321,87 +1251,53 @@ static void weld_poly_loop_ctx_setup(const MLoop *mloop, * \{ */ static void weld_mesh_context_create(const Mesh *mesh, - uint *vert_dest_map, - const uint vert_kill_len, + MutableSpan<int> vert_dest_map, + const int vert_kill_len, WeldMesh *r_weld_mesh) { - const MEdge *medge = mesh->medge; - const MLoop *mloop = mesh->mloop; - const MPoly *mpoly = mesh->mpoly; - const uint mvert_len = mesh->totvert; - const uint medge_len = mesh->totedge; - const uint mloop_len = mesh->totloop; - const uint mpoly_len = mesh->totpoly; - - uint *edge_dest_map = MEM_mallocN(sizeof(*edge_dest_map) * medge_len, __func__); - struct WeldGroup *v_links = MEM_callocN(sizeof(*v_links) * mvert_len, __func__); - - WeldVert *wvert; - uint wvert_len; + Span<MEdge> medge{mesh->medge, mesh->totedge}; + Span<MPoly> mpoly{mesh->mpoly, mesh->totpoly}; + Span<MLoop> mloop{mesh->mloop, mesh->totloop}; + const int mvert_len = mesh->totvert; + + Vector<WeldVert> wvert = weld_vert_ctx_alloc_and_setup(vert_dest_map); r_weld_mesh->vert_kill_len = vert_kill_len; - weld_vert_ctx_alloc_and_setup(mvert_len, vert_dest_map, &wvert, &wvert_len); - uint *edge_ctx_map; - WeldEdge *wedge; - uint wedge_len; - weld_edge_ctx_alloc( - medge, medge_len, vert_dest_map, edge_dest_map, &edge_ctx_map, &wedge, &wedge_len); + Array<int> edge_dest_map(medge.size()); + Array<int> edge_ctx_map(medge.size()); + Vector<WeldEdge> wedge = weld_edge_ctx_alloc(medge, vert_dest_map, edge_dest_map, edge_ctx_map); - weld_edge_ctx_setup( - mvert_len, wedge_len, v_links, edge_dest_map, wedge, &r_weld_mesh->edge_kill_len); + Array<WeldGroup> v_links(mvert_len, {0, 0}); + weld_edge_ctx_setup(v_links, edge_dest_map, wedge, &r_weld_mesh->edge_kill_len); - weld_poly_loop_ctx_alloc( - mpoly, mpoly_len, mloop, mloop_len, vert_dest_map, edge_dest_map, r_weld_mesh); + weld_poly_loop_ctx_alloc(mpoly, mloop, vert_dest_map, edge_dest_map, r_weld_mesh); weld_poly_loop_ctx_setup(mloop, #ifdef USE_WELD_DEBUG mpoly, - mpoly_len, - mloop_len, + #endif mvert_len, vert_dest_map, - wedge_len - r_weld_mesh->edge_kill_len, + wedge.size() - r_weld_mesh->edge_kill_len, v_links, r_weld_mesh); - weld_vert_groups_setup(mvert_len, - wvert_len, - wvert, + weld_vert_groups_setup(wvert, vert_dest_map, vert_dest_map, - &r_weld_mesh->vert_groups_buffer, - &r_weld_mesh->vert_groups); + r_weld_mesh->vert_groups_buffer, + r_weld_mesh->vert_groups); - weld_edge_groups_setup(medge_len, + weld_edge_groups_setup(medge.size(), r_weld_mesh->edge_kill_len, - wedge_len, wedge, edge_ctx_map, edge_dest_map, - &r_weld_mesh->edge_groups_buffer, - &r_weld_mesh->edge_groups); - - r_weld_mesh->edge_groups_map = edge_dest_map; - MEM_freeN(v_links); - MEM_freeN(wvert); - MEM_freeN(edge_ctx_map); - MEM_freeN(wedge); -} + r_weld_mesh->edge_groups_buffer, + r_weld_mesh->edge_groups); -static void weld_mesh_context_free(WeldMesh *weld_mesh) -{ - MEM_freeN(weld_mesh->vert_groups); - MEM_freeN(weld_mesh->vert_groups_buffer); - - MEM_freeN(weld_mesh->edge_groups); - MEM_freeN(weld_mesh->edge_groups_buffer); - MEM_freeN(weld_mesh->edge_groups_map); - - MEM_freeN(weld_mesh->wloop); - MEM_freeN(weld_mesh->wpoly); - MEM_freeN(weld_mesh->loop_map); - MEM_freeN(weld_mesh->poly_map); + r_weld_mesh->edge_groups_map = std::move(edge_dest_map); } /** \} */ @@ -1411,14 +1307,14 @@ static void weld_mesh_context_free(WeldMesh *weld_mesh) * \{ */ static void customdata_weld( - const CustomData *source, CustomData *dest, const uint *src_indices, int count, int dest_index) + const CustomData *source, CustomData *dest, const int *src_indices, int count, int dest_index) { if (count == 1) { CustomData_copy_data(source, dest, src_indices[0], dest_index, 1); return; } - CustomData_interp(source, dest, (const int *)src_indices, NULL, NULL, count, dest_index); + CustomData_interp(source, dest, (const int *)src_indices, nullptr, nullptr, count, dest_index); int src_i, dest_i; int j; @@ -1427,8 +1323,8 @@ static void customdata_weld( #ifdef USE_WELD_NORMALS float no[3] = {0.0f, 0.0f, 0.0f}; #endif - uint crease = 0; - uint bweight = 0; + int crease = 0; + int bweight = 0; short flag = 0; /* interpolates a layer at a time */ @@ -1572,7 +1468,7 @@ static bool bvhtree_weld_overlap_cb(void *userdata, int index_a, int index_b, in /** Use for #MOD_WELD_MODE_CONNECTED calculation. */ struct WeldVertexCluster { float co[3]; - uint merged_verts; + int merged_verts; }; static Mesh *weldModifier_doWeld(WeldModifierData *wmd, @@ -1581,28 +1477,24 @@ static Mesh *weldModifier_doWeld(WeldModifierData *wmd, { Mesh *result = mesh; - BLI_bitmap *v_mask = NULL; + BLI_bitmap *v_mask = nullptr; int v_mask_act = 0; - const MVert *mvert; - const MLoop *mloop; - const MPoly *mpoly, *mp; - uint totvert, totedge, totloop, totpoly; + Span<MVert> mvert{mesh->mvert, mesh->totvert}; + int totvert, totedge, totloop, totpoly; - mvert = mesh->mvert; totvert = mesh->totvert; /* Vertex Group. */ const int defgrp_index = BKE_id_defgroup_name_index(&mesh->id, wmd->defgrp_name); if (defgrp_index != -1) { - MDeformVert *dvert, *dv; - dvert = CustomData_get_layer(&mesh->vdata, CD_MDEFORMVERT); + MDeformVert *dvert; + dvert = static_cast<MDeformVert *>(CustomData_get_layer(&mesh->vdata, CD_MDEFORMVERT)); if (dvert) { const bool invert_vgroup = (wmd->flag & MOD_WELD_INVERT_VGROUP) != 0; - dv = &dvert[0]; v_mask = BLI_BITMAP_NEW(totvert, __func__); - for (uint i = 0; i < totvert; i++, dv++) { - const bool found = BKE_defvert_find_weight(dv, defgrp_index) > 0.0f; + for (const int i : IndexRange(totvert)) { + const bool found = BKE_defvert_find_weight(&dvert[i], defgrp_index) > 0.0f; if (found != invert_vgroup) { BLI_BITMAP_ENABLE(v_mask, i); v_mask_act++; @@ -1613,15 +1505,15 @@ static Mesh *weldModifier_doWeld(WeldModifierData *wmd, /* From the original index of the vertex. * This indicates which vert it is or is going to be merged. */ - uint *vert_dest_map = MEM_malloc_arrayN(totvert, sizeof(*vert_dest_map), __func__); - uint vert_kill_len = 0; + Array<int> vert_dest_map(totvert); + int vert_kill_len = 0; if (wmd->mode == MOD_WELD_MODE_ALL) #ifdef USE_BVHTREEKDOP { /* Get overlap map. */ struct BVHTreeFromMesh treedata; BVHTree *bvhtree = bvhtree_from_mesh_verts_ex(&treedata, - mvert, + mvert.data(), totvert, false, v_mask, @@ -1630,8 +1522,8 @@ static Mesh *weldModifier_doWeld(WeldModifierData *wmd, 2, 6, 0, - NULL, - NULL); + nullptr, + nullptr); if (bvhtree) { struct WeldOverlapData data; @@ -1649,20 +1541,20 @@ static Mesh *weldModifier_doWeld(WeldModifierData *wmd, free_bvhtree_from_mesh(&treedata); if (overlap) { - range_vn_u(vert_dest_map, totvert, 0); + range_vn_i(vert_dest_map.data(), totvert, 0); const BVHTreeOverlap *overlap_iter = &overlap[0]; - for (uint i = 0; i < overlap_len; i++, overlap_iter++) { - uint indexA = overlap_iter->indexA; - uint indexB = overlap_iter->indexB; + for (int i = 0; i < overlap_len; i++, overlap_iter++) { + int indexA = overlap_iter->indexA; + int indexB = overlap_iter->indexB; BLI_assert(indexA < indexB); - uint va_dst = vert_dest_map[indexA]; + int va_dst = vert_dest_map[indexA]; while (va_dst != vert_dest_map[va_dst]) { va_dst = vert_dest_map[va_dst]; } - uint vb_dst = vert_dest_map[indexB]; + int vb_dst = vert_dest_map[indexB]; while (vb_dst != vert_dest_map[vb_dst]) { vb_dst = vert_dest_map[vb_dst]; } @@ -1670,19 +1562,19 @@ static Mesh *weldModifier_doWeld(WeldModifierData *wmd, continue; } if (va_dst > vb_dst) { - SWAP(uint, va_dst, vb_dst); + SWAP(int, va_dst, vb_dst); } vert_kill_len++; vert_dest_map[vb_dst] = va_dst; } /* Fix #r_vert_dest_map for next step. */ - for (uint i = 0; i < totvert; i++) { + for (int i = 0; i < totvert; i++) { if (i == vert_dest_map[i]) { vert_dest_map[i] = OUT_OF_CONTEXT; } else { - uint v = i; + int v = i; while (v != vert_dest_map[v] && vert_dest_map[v] != OUT_OF_CONTEXT) { v = vert_dest_map[v]; } @@ -1698,7 +1590,7 @@ static Mesh *weldModifier_doWeld(WeldModifierData *wmd, #else { KDTree_3d *tree = BLI_kdtree_3d_new(v_mask ? v_mask_act : totvert); - for (uint i = 0; i < totvert; i++) { + for (const int i : IndexRange(totvert)) { if (!v_mask || BLI_BITMAP_TEST(v_mask, i)) { BLI_kdtree_3d_insert(tree, i, mvert[i].co); } @@ -1707,37 +1599,34 @@ static Mesh *weldModifier_doWeld(WeldModifierData *wmd, BLI_kdtree_3d_balance(tree); vert_kill_len = BLI_kdtree_3d_calc_duplicates_fast( - tree, wmd->merge_dist, false, (int *)vert_dest_map); + tree, wmd->merge_dist, false, vert_dest_map.data()); BLI_kdtree_3d_free(tree); } #endif else { BLI_assert(wmd->mode == MOD_WELD_MODE_CONNECTED); - MEdge *medge, *me; + Span<MEdge> medge{mesh->medge, mesh->totedge}; - medge = mesh->medge; totvert = mesh->totvert; - totedge = mesh->totedge; - struct WeldVertexCluster *vert_clusters = MEM_malloc_arrayN( - totvert, sizeof(*vert_clusters), __func__); - struct WeldVertexCluster *vc = &vert_clusters[0]; - for (uint i = 0; i < totvert; i++, vc++) { - copy_v3_v3(vc->co, mvert[i].co); - vc->merged_verts = 0; + Array<WeldVertexCluster> vert_clusters(totvert); + + for (const int i : mvert.index_range()) { + WeldVertexCluster &vc = vert_clusters[i]; + copy_v3_v3(vc.co, mvert[i].co); + vc.merged_verts = 0; } const float merge_dist_sq = square_f(wmd->merge_dist); - range_vn_u(vert_dest_map, totvert, 0); + range_vn_i(vert_dest_map.data(), totvert, 0); /* Collapse Edges that are shorter than the threshold. */ - me = &medge[0]; - for (uint i = 0; i < totedge; i++, me++) { - uint v1 = me->v1; - uint v2 = me->v2; + for (const int i : medge.index_range()) { + int v1 = medge[i].v1; + int v2 = medge[i].v2; - if (wmd->flag & MOD_WELD_LOOSE_EDGES && (me->flag & ME_LOOSEEDGE) == 0) { + if (wmd->flag & MOD_WELD_LOOSE_EDGES && (medge[i].flag & ME_LOOSEEDGE) == 0) { continue; } while (v1 != vert_dest_map[v1]) { @@ -1753,10 +1642,10 @@ static Mesh *weldModifier_doWeld(WeldModifierData *wmd, continue; } if (v1 > v2) { - SWAP(uint, v1, v2); + SWAP(int, v1, v2); } - struct WeldVertexCluster *v1_cluster = &vert_clusters[v1]; - struct WeldVertexCluster *v2_cluster = &vert_clusters[v2]; + WeldVertexCluster *v1_cluster = &vert_clusters[v1]; + WeldVertexCluster *v2_cluster = &vert_clusters[v2]; float edgedir[3]; sub_v3_v3v3(edgedir, v2_cluster->co, v1_cluster->co); @@ -1772,14 +1661,12 @@ static Mesh *weldModifier_doWeld(WeldModifierData *wmd, } } - MEM_freeN(vert_clusters); - - for (uint i = 0; i < totvert; i++) { + for (const int i : IndexRange(totvert)) { if (i == vert_dest_map[i]) { vert_dest_map[i] = OUT_OF_CONTEXT; } else { - uint v = i; + int v = i; while ((v != vert_dest_map[v]) && (vert_dest_map[v] != OUT_OF_CONTEXT)) { v = vert_dest_map[v]; } @@ -1797,8 +1684,8 @@ static Mesh *weldModifier_doWeld(WeldModifierData *wmd, WeldMesh weld_mesh; weld_mesh_context_create(mesh, vert_dest_map, vert_kill_len, &weld_mesh); - mloop = mesh->mloop; - mpoly = mesh->mpoly; + Span<MLoop> mloop{mesh->mloop, mesh->totloop}; + Span<MPoly> mpoly{mesh->mpoly, mesh->totpoly}; totedge = mesh->totedge; totloop = mesh->totloop; @@ -1814,10 +1701,10 @@ static Mesh *weldModifier_doWeld(WeldModifierData *wmd, /* Vertices */ - uint *vert_final = vert_dest_map; - uint *index_iter = &vert_final[0]; + int *vert_final = vert_dest_map.data(); + int *index_iter = &vert_final[0]; int dest_index = 0; - for (uint i = 0; i < totvert; i++, index_iter++) { + for (int i = 0; i < totvert; i++, index_iter++) { int source_index = i; int count = 0; while (i < totvert && *index_iter == OUT_OF_CONTEXT) { @@ -1849,10 +1736,10 @@ static Mesh *weldModifier_doWeld(WeldModifierData *wmd, /* Edges */ - uint *edge_final = weld_mesh.edge_groups_map; + int *edge_final = weld_mesh.edge_groups_map.data(); index_iter = &edge_final[0]; dest_index = 0; - for (uint i = 0; i < totedge; i++, index_iter++) { + for (int i = 0; i < totedge; i++, index_iter++) { int source_index = i; int count = 0; while (i < totedge && *index_iter == OUT_OF_CONTEXT) { @@ -1894,18 +1781,18 @@ static Mesh *weldModifier_doWeld(WeldModifierData *wmd, /* Polys/Loops */ - mp = &mpoly[0]; MPoly *r_mp = &result->mpoly[0]; MLoop *r_ml = &result->mloop[0]; - uint r_i = 0; + int r_i = 0; int loop_cur = 0; - uint *group_buffer = BLI_array_alloca(group_buffer, weld_mesh.max_poly_len); - for (uint i = 0; i < totpoly; i++, mp++) { + Array<int, 64> group_buffer(weld_mesh.max_poly_len); + for (const int i : mpoly.index_range()) { + const MPoly &mp = mpoly[i]; int loop_start = loop_cur; - uint poly_ctx = weld_mesh.poly_map[i]; + int poly_ctx = weld_mesh.poly_map[i]; if (poly_ctx == OUT_OF_CONTEXT) { - uint mp_loop_len = mp->totloop; - CustomData_copy_data(&mesh->ldata, &result->ldata, mp->loopstart, loop_cur, mp_loop_len); + int mp_loop_len = mp.totloop; + CustomData_copy_data(&mesh->ldata, &result->ldata, mp.loopstart, loop_cur, mp_loop_len); loop_cur += mp_loop_len; for (; mp_loop_len--; r_ml++) { r_ml->v = vert_final[r_ml->v]; @@ -1913,20 +1800,21 @@ static Mesh *weldModifier_doWeld(WeldModifierData *wmd, } } else { - WeldPoly *wp = &weld_mesh.wpoly[poly_ctx]; + const WeldPoly &wp = weld_mesh.wpoly[poly_ctx]; WeldLoopOfPolyIter iter; if (!weld_iter_loop_of_poly_begin( - &iter, wp, weld_mesh.wloop, mloop, weld_mesh.loop_map, group_buffer)) { + &iter, wp, weld_mesh.wloop, mloop, weld_mesh.loop_map, group_buffer.data())) { continue; } - if (wp->poly_dst != OUT_OF_CONTEXT) { + if (wp.poly_dst != OUT_OF_CONTEXT) { continue; } while (weld_iter_loop_of_poly_next(&iter)) { - customdata_weld(&mesh->ldata, &result->ldata, group_buffer, iter.group_len, loop_cur); - uint v = vert_final[iter.v]; - uint e = edge_final[iter.e]; + customdata_weld( + &mesh->ldata, &result->ldata, group_buffer.data(), iter.group_len, loop_cur); + int v = vert_final[iter.v]; + int e = edge_final[iter.e]; r_ml->v = v; r_ml->e = e; r_ml++; @@ -1945,22 +1833,23 @@ static Mesh *weldModifier_doWeld(WeldModifierData *wmd, r_i++; } - WeldPoly *wp = &weld_mesh.wpoly_new[0]; - for (uint i = 0; i < weld_mesh.wpoly_new_len; i++, wp++) { + for (const int i : IndexRange(weld_mesh.wpoly_new_len)) { + const WeldPoly &wp = weld_mesh.wpoly_new[i]; int loop_start = loop_cur; WeldLoopOfPolyIter iter; if (!weld_iter_loop_of_poly_begin( - &iter, wp, weld_mesh.wloop, mloop, weld_mesh.loop_map, group_buffer)) { + &iter, wp, weld_mesh.wloop, mloop, weld_mesh.loop_map, group_buffer.data())) { continue; } - if (wp->poly_dst != OUT_OF_CONTEXT) { + if (wp.poly_dst != OUT_OF_CONTEXT) { continue; } while (weld_iter_loop_of_poly_next(&iter)) { - customdata_weld(&mesh->ldata, &result->ldata, group_buffer, iter.group_len, loop_cur); - uint v = vert_final[iter.v]; - uint e = edge_final[iter.e]; + customdata_weld( + &mesh->ldata, &result->ldata, group_buffer.data(), iter.group_len, loop_cur); + int v = vert_final[iter.v]; + int e = edge_final[iter.e]; r_ml->v = v; r_ml->e = e; r_ml++; @@ -1982,11 +1871,8 @@ static Mesh *weldModifier_doWeld(WeldModifierData *wmd, /* is this needed? */ BKE_mesh_normals_tag_dirty(result); - - weld_mesh_context_free(&weld_mesh); } - MEM_freeN(vert_dest_map); return result; } @@ -2027,12 +1913,12 @@ static void panel_draw(const bContext *UNUSED(C), Panel *panel) uiLayoutSetPropSep(layout, true); - uiItemR(layout, ptr, "mode", 0, NULL, ICON_NONE); + uiItemR(layout, ptr, "mode", 0, nullptr, ICON_NONE); uiItemR(layout, ptr, "merge_threshold", 0, IFACE_("Distance"), ICON_NONE); if (weld_mode == MOD_WELD_MODE_CONNECTED) { - uiItemR(layout, ptr, "loose_edges", 0, NULL, ICON_NONE); + uiItemR(layout, ptr, "loose_edges", 0, nullptr, ICON_NONE); } - modifier_vgroup_ui(layout, ptr, &ob_ptr, "vertex_group", "invert_vertex_group", NULL); + modifier_vgroup_ui(layout, ptr, &ob_ptr, "vertex_group", "invert_vertex_group", nullptr); modifier_panel_end(layout, ptr); } @@ -2048,34 +1934,35 @@ ModifierTypeInfo modifierType_Weld = { /* structSize */ sizeof(WeldModifierData), /* srna */ &RNA_WeldModifier, /* type */ eModifierTypeType_Constructive, - /* flags */ eModifierTypeFlag_AcceptsMesh | eModifierTypeFlag_SupportsMapping | - eModifierTypeFlag_SupportsEditmode | eModifierTypeFlag_EnableInEditmode | - eModifierTypeFlag_AcceptsCVs, + /* flags */ + (ModifierTypeFlag)(eModifierTypeFlag_AcceptsMesh | eModifierTypeFlag_SupportsMapping | + eModifierTypeFlag_SupportsEditmode | eModifierTypeFlag_EnableInEditmode | + eModifierTypeFlag_AcceptsCVs), /* icon */ ICON_AUTOMERGE_OFF, /* TODO: Use correct icon. */ /* copyData */ BKE_modifier_copydata_generic, - /* deformVerts */ NULL, - /* deformMatrices */ NULL, - /* deformVertsEM */ NULL, - /* deformMatricesEM */ NULL, + /* deformVerts */ nullptr, + /* deformMatrices */ nullptr, + /* deformVertsEM */ nullptr, + /* deformMatricesEM */ nullptr, /* modifyMesh */ modifyMesh, - /* modifyHair */ NULL, - /* modifyGeometrySet */ NULL, + /* modifyHair */ nullptr, + /* modifyGeometrySet */ nullptr, /* initData */ initData, /* requiredDataMask */ requiredDataMask, - /* freeData */ NULL, - /* isDisabled */ NULL, - /* updateDepsgraph */ NULL, - /* dependsOnTime */ NULL, - /* dependsOnNormals */ NULL, - /* foreachIDLink */ NULL, - /* foreachTexLink */ NULL, - /* freeRuntimeData */ NULL, + /* freeData */ nullptr, + /* isDisabled */ nullptr, + /* updateDepsgraph */ nullptr, + /* dependsOnTime */ nullptr, + /* dependsOnNormals */ nullptr, + /* foreachIDLink */ nullptr, + /* foreachTexLink */ nullptr, + /* freeRuntimeData */ nullptr, /* panelRegister */ panelRegister, - /* blendWrite */ NULL, - /* blendRead */ NULL, + /* blendWrite */ nullptr, + /* blendRead */ nullptr, }; /** \} */ diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt index 5f61d13a3af..2e2ff874f20 100644 --- a/source/blender/nodes/CMakeLists.txt +++ b/source/blender/nodes/CMakeLists.txt @@ -316,4 +316,8 @@ if(WITH_OPENVDB) add_definitions(-DWITH_OPENVDB ${OPENVDB_DEFINITIONS}) endif() +if(WITH_OPENIMAGEDENOISE) + add_definitions(-DWITH_OPENIMAGEDENOISE) +endif() + blender_add_lib(bf_nodes "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") diff --git a/source/blender/nodes/NOD_geometry.h b/source/blender/nodes/NOD_geometry.h index a0b8e237f19..c6efe6efb62 100644 --- a/source/blender/nodes/NOD_geometry.h +++ b/source/blender/nodes/NOD_geometry.h @@ -87,8 +87,8 @@ void register_node_type_geo_curve_resample(void); void register_node_type_geo_curve_reverse(void); void register_node_type_geo_curve_sample(void); void register_node_type_geo_curve_set_handles(void); -void register_node_type_geo_curve_spline_type(void); void register_node_type_geo_curve_spline_parameter(void); +void register_node_type_geo_curve_spline_type(void); void register_node_type_geo_curve_subdivide(void); void register_node_type_geo_curve_to_mesh(void); void register_node_type_geo_curve_to_points(void); diff --git a/source/blender/nodes/NOD_node_declaration.hh b/source/blender/nodes/NOD_node_declaration.hh index af2130ec452..4ad1bcad885 100644 --- a/source/blender/nodes/NOD_node_declaration.hh +++ b/source/blender/nodes/NOD_node_declaration.hh @@ -288,10 +288,13 @@ class NodeDeclarationBuilder { /** * All inputs support fields, and all outputs are fields if any of the inputs is a field. - * Calling field status definitions on each socket is unnecessary. + * Calling field status definitions on each socket is unnecessary. Must be called before adding + * any sockets. */ void is_function_node(bool value = true) { + BLI_assert_msg(declaration_.inputs().is_empty() && declaration_.outputs().is_empty(), + "is_function_node() must be called before any socket is created"); declaration_.is_function_node_ = value; } @@ -476,6 +479,10 @@ inline typename DeclType::Builder &NodeDeclarationBuilder::add_socket(StringRef socket_decl->name_ = name; socket_decl->identifier_ = identifier.is_empty() ? name : identifier; socket_decl->in_out_ = in_out; + if (declaration_.is_function_node()) { + socket_decl->input_field_type_ = InputSocketFieldType::IsSupported; + socket_decl->output_field_dependency_ = OutputFieldDependency::ForDependentField(); + } declarations.append(std::move(socket_decl)); Builder &socket_decl_builder_ref = *socket_decl_builder; builders_.append(std::move(socket_decl_builder)); diff --git a/source/blender/nodes/NOD_socket_declarations.hh b/source/blender/nodes/NOD_socket_declarations.hh index 3fb21a4263d..c0ba949e337 100644 --- a/source/blender/nodes/NOD_socket_declarations.hh +++ b/source/blender/nodes/NOD_socket_declarations.hh @@ -231,7 +231,7 @@ class Shader : public SocketDeclaration { bNodeSocket &build(bNodeTree &ntree, bNode &node) const override; bool matches(const bNodeSocket &socket) const override; - bool can_connect(const bNodeSocket &socket) const; + bool can_connect(const bNodeSocket &socket) const override; }; class ShaderBuilder : public SocketDeclarationBuilder<Shader> { diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h index 84bc7bf0ceb..0c6e42deb27 100644 --- a/source/blender/nodes/NOD_static_types.h +++ b/source/blender/nodes/NOD_static_types.h @@ -323,9 +323,9 @@ DefNode(GeometryNode, GEO_NODE_LEGACY_SELECT_BY_MATERIAL, 0, "LEGACY_SELECT_BY_M DefNode(GeometryNode, GEO_NODE_LEGACY_SUBDIVISION_SURFACE, def_geo_subdivision_surface, "LEGACY_SUBDIVISION_SURFACE", LegacySubdivisionSurface, "Subdivision Surface", "") DefNode(GeometryNode, GEO_NODE_LEGACY_VOLUME_TO_MESH, def_geo_volume_to_mesh, "LEGACY_VOLUME_TO_MESH", LegacyVolumeToMesh, "Volume to Mesh", "") +DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_DOMAIN_SIZE, def_geo_attribute_domain_size, "ATTRIBUTE_DOMAIN_SIZE", AttributeDomainSize, "Domain Size", "") DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_REMOVE, 0, "ATTRIBUTE_REMOVE", AttributeRemove, "Attribute Remove", "") DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_STATISTIC, def_geo_attribute_statistic, "ATTRIBUTE_STATISTIC", AttributeStatistic, "Attribute Statistic", "") -DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_DOMAIN_SIZE, def_geo_attribute_domain_size, "ATTRIBUTE_DOMAIN_SIZE", AttributeDomainSize, "Domain Size", "") DefNode(GeometryNode, GEO_NODE_BOUNDING_BOX, 0, "BOUNDING_BOX", BoundBox, "Bounding Box", "") DefNode(GeometryNode, GEO_NODE_CAPTURE_ATTRIBUTE, def_geo_attribute_capture, "CAPTURE_ATTRIBUTE", CaptureAttribute, "Capture Attribute", "") DefNode(GeometryNode, GEO_NODE_COLLECTION_INFO, def_geo_collection_info, "COLLECTION_INFO", CollectionInfo, "Collection Info", "") @@ -333,7 +333,6 @@ DefNode(GeometryNode, GEO_NODE_CONVEX_HULL, 0, "CONVEX_HULL", ConvexHull, "Conve DefNode(GeometryNode, GEO_NODE_CURVE_ENDPOINT_SELECTION, 0, "CURVE_ENDPOINT_SELECTION", CurveEndpointSelection, "Endpoint Selection", "") DefNode(GeometryNode, GEO_NODE_CURVE_HANDLE_TYPE_SELECTION, def_geo_curve_handle_type_selection, "CURVE_HANDLE_TYPE_SELECTION", CurveHandleTypeSelection, "Handle Type Selection", "") DefNode(GeometryNode, GEO_NODE_CURVE_LENGTH, 0, "CURVE_LENGTH", CurveLength, "Curve Length", "") -DefNode(GeometryNode, GEO_NODE_CURVE_SPLINE_PARAMETER, 0, "SPLINE_PARAMETER", SplineParameter, "Spline Parameter", "") DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_BEZIER_SEGMENT, def_geo_curve_primitive_bezier_segment, "CURVE_PRIMITIVE_BEZIER_SEGMENT", CurvePrimitiveBezierSegment, "Bezier Segment", "") DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_CIRCLE, def_geo_curve_primitive_circle, "CURVE_PRIMITIVE_CIRCLE", CurvePrimitiveCircle, "Curve Circle", "") DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_LINE, def_geo_curve_primitive_line, "CURVE_PRIMITIVE_LINE", CurvePrimitiveLine, "Curve Line", "") @@ -342,6 +341,7 @@ DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_QUADRILATERAL, def_geo_curve_prim DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_SPIRAL, 0, "CURVE_PRIMITIVE_SPIRAL", CurveSpiral, "Curve Spiral", "") DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_STAR, 0, "CURVE_PRIMITIVE_STAR", CurveStar, "Star", "") DefNode(GeometryNode, GEO_NODE_CURVE_SET_HANDLES, def_geo_curve_set_handles, "CURVE_SET_HANDLES", CurveSetHandles, "Set Handle Type", "") +DefNode(GeometryNode, GEO_NODE_CURVE_SPLINE_PARAMETER, 0, "SPLINE_PARAMETER", SplineParameter, "Spline Parameter", "") DefNode(GeometryNode, GEO_NODE_CURVE_SPLINE_TYPE, def_geo_curve_spline_type, "CURVE_SPLINE_TYPE", CurveSplineType, "Set Spline Type", "") DefNode(GeometryNode, GEO_NODE_CURVE_TO_MESH, 0, "CURVE_TO_MESH", CurveToMesh, "Curve to Mesh", "") DefNode(GeometryNode, GEO_NODE_CURVE_TO_POINTS, def_geo_curve_to_points, "CURVE_TO_POINTS", CurveToPoints, "Curve to Points", "") diff --git a/source/blender/nodes/composite/node_composite_tree.cc b/source/blender/nodes/composite/node_composite_tree.cc index 1326c9edab1..54967c82562 100644 --- a/source/blender/nodes/composite/node_composite_tree.cc +++ b/source/blender/nodes/composite/node_composite_tree.cc @@ -33,6 +33,7 @@ #include "BKE_global.h" #include "BKE_main.h" #include "BKE_node.h" +#include "BKE_node_tree_update.h" #include "BKE_tracking.h" #include "node_common.h" @@ -186,11 +187,6 @@ static void update(bNodeTree *ntree) ntreeSetOutput(ntree); ntree_update_reroute_nodes(ntree); - - if (ntree->update & NTREE_UPDATE_NODES) { - /* clean up preview cache, in case nodes have been removed */ - BKE_node_preview_remove_unused(ntree); - } } static void composite_node_add_init(bNodeTree *UNUSED(bnodetree), bNode *bnode) @@ -301,14 +297,15 @@ void ntreeCompositTagRender(Scene *scene) if (sce_iter->nodetree) { LISTBASE_FOREACH (bNode *, node, &sce_iter->nodetree->nodes) { if (node->id == (ID *)scene || node->type == CMP_NODE_COMPOSITE) { - nodeUpdate(sce_iter->nodetree, node); + BKE_ntree_update_tag_node_property(sce_iter->nodetree, node); } else if (node->type == CMP_NODE_TEXTURE) /* uses scene size_x/size_y */ { - nodeUpdate(sce_iter->nodetree, node); + BKE_ntree_update_tag_node_property(sce_iter->nodetree, node); } } } } + BKE_ntree_update_main(G_MAIN, nullptr); } /* XXX after render animation system gets a refresh, this call allows composite to end clean */ diff --git a/source/blender/nodes/composite/nodes/node_composite_denoise.cc b/source/blender/nodes/composite/nodes/node_composite_denoise.cc index f1e5388a7a3..17bad211dec 100644 --- a/source/blender/nodes/composite/nodes/node_composite_denoise.cc +++ b/source/blender/nodes/composite/nodes/node_composite_denoise.cc @@ -23,6 +23,8 @@ * \ingroup cmpnodes */ +#include "BLI_system.h" + #include "UI_interface.h" #include "UI_resources.h" diff --git a/source/blender/nodes/geometry/CMakeLists.txt b/source/blender/nodes/geometry/CMakeLists.txt index f9a64381981..c24f8849532 100644 --- a/source/blender/nodes/geometry/CMakeLists.txt +++ b/source/blender/nodes/geometry/CMakeLists.txt @@ -105,8 +105,8 @@ set(SRC nodes/node_geo_curve_reverse.cc nodes/node_geo_curve_sample.cc nodes/node_geo_curve_set_handles.cc - nodes/node_geo_curve_spline_type.cc nodes/node_geo_curve_spline_parameter.cc + nodes/node_geo_curve_spline_type.cc nodes/node_geo_curve_subdivide.cc nodes/node_geo_curve_to_mesh.cc nodes/node_geo_curve_to_points.cc diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_align_rotation_to_vector.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_align_rotation_to_vector.cc index 4366c6f9234..b4e1b671d15 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_align_rotation_to_vector.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_align_rotation_to_vector.cc @@ -53,8 +53,8 @@ static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) static void node_init(bNodeTree *UNUSED(ntree), bNode *node) { - NodeGeometryAlignRotationToVector *node_storage = (NodeGeometryAlignRotationToVector *) - MEM_callocN(sizeof(NodeGeometryAlignRotationToVector), __func__); + NodeGeometryAlignRotationToVector *node_storage = MEM_cnew<NodeGeometryAlignRotationToVector>( + __func__); node_storage->axis = GEO_NODE_ALIGN_ROTATION_TO_VECTOR_AXIS_X; node_storage->input_type_factor = GEO_NODE_ATTRIBUTE_INPUT_FLOAT; diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_clamp.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_clamp.cc index 7435152a966..a103709d284 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_clamp.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_clamp.cc @@ -46,8 +46,7 @@ static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) static void node_init(bNodeTree *UNUSED(tree), bNode *node) { - NodeAttributeClamp *data = (NodeAttributeClamp *)MEM_callocN(sizeof(NodeAttributeClamp), - __func__); + NodeAttributeClamp *data = MEM_cnew<NodeAttributeClamp>(__func__); data->data_type = CD_PROP_FLOAT; data->operation = NODE_CLAMP_MINMAX; node->storage = data; diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_color_ramp.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_color_ramp.cc index 4efdf786e4e..fe3fb872238 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_color_ramp.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_color_ramp.cc @@ -40,8 +40,7 @@ static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) static void node_init(bNodeTree *UNUSED(ntree), bNode *node) { - NodeAttributeColorRamp *node_storage = (NodeAttributeColorRamp *)MEM_callocN( - sizeof(NodeAttributeColorRamp), __func__); + NodeAttributeColorRamp *node_storage = MEM_cnew<NodeAttributeColorRamp>(__func__); BKE_colorband_init(&node_storage->color_ramp, true); node->storage = node_storage; } diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_combine_xyz.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_combine_xyz.cc index 7ba64c8db2b..3065ea3d7a1 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_combine_xyz.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_combine_xyz.cc @@ -46,8 +46,7 @@ static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) static void node_init(bNodeTree *UNUSED(tree), bNode *node) { - NodeAttributeCombineXYZ *data = (NodeAttributeCombineXYZ *)MEM_callocN( - sizeof(NodeAttributeCombineXYZ), __func__); + NodeAttributeCombineXYZ *data = MEM_cnew<NodeAttributeCombineXYZ>(__func__); data->input_type_x = GEO_NODE_ATTRIBUTE_INPUT_FLOAT; data->input_type_y = GEO_NODE_ATTRIBUTE_INPUT_FLOAT; diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_compare.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_compare.cc index 8aa1adb0cf3..3a973610cc4 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_compare.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_compare.cc @@ -50,8 +50,7 @@ static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) static void node_init(bNodeTree *UNUSED(tree), bNode *node) { - NodeAttributeCompare *data = (NodeAttributeCompare *)MEM_callocN(sizeof(NodeAttributeCompare), - __func__); + NodeAttributeCompare *data = MEM_cnew<NodeAttributeCompare>(__func__); data->operation = NODE_COMPARE_GREATER_THAN; data->input_type_a = GEO_NODE_ATTRIBUTE_INPUT_ATTRIBUTE; data->input_type_b = GEO_NODE_ATTRIBUTE_INPUT_ATTRIBUTE; diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_convert.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_convert.cc index 28c133871f7..b729eec87f8 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_convert.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_convert.cc @@ -39,8 +39,7 @@ static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) static void node_init(bNodeTree *UNUSED(tree), bNode *node) { - NodeAttributeConvert *data = (NodeAttributeConvert *)MEM_callocN(sizeof(NodeAttributeConvert), - __func__); + NodeAttributeConvert *data = MEM_cnew<NodeAttributeConvert>(__func__); data->data_type = CD_AUTO_FROM_NAME; data->domain = ATTR_DOMAIN_AUTO; diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_curve_map.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_curve_map.cc index 0a7b5dd8463..4bef8f658a9 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_curve_map.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_curve_map.cc @@ -75,8 +75,7 @@ static void node_copy_storage(bNodeTree *UNUSED(dest_ntree), static void node_init(bNodeTree *UNUSED(ntree), bNode *node) { - NodeAttributeCurveMap *data = (NodeAttributeCurveMap *)MEM_callocN(sizeof(NodeAttributeCurveMap), - __func__); + NodeAttributeCurveMap *data = MEM_cnew<NodeAttributeCurveMap>(__func__); data->data_type = CD_PROP_FLOAT; data->curve_vec = BKE_curvemapping_add(4, -1.0f, -1.0f, 1.0f, 1.0f); diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_map_range.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_map_range.cc index 8ebcf34ad0b..9bc1eaaa368 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_map_range.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_map_range.cc @@ -51,8 +51,7 @@ static void fn_attribute_map_range_layout(uiLayout *layout, bContext *UNUSED(C), static void node_init(bNodeTree *UNUSED(ntree), bNode *node) { - NodeAttributeMapRange *data = (NodeAttributeMapRange *)MEM_callocN(sizeof(NodeAttributeMapRange), - __func__); + NodeAttributeMapRange *data = MEM_cnew<NodeAttributeMapRange>(__func__); data->data_type = CD_PROP_FLOAT; data->interpolation_type = NODE_MAP_RANGE_LINEAR; diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_math.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_math.cc index e0a829b4100..3deaecad26d 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_math.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_math.cc @@ -121,7 +121,7 @@ static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) static void node_init(bNodeTree *UNUSED(tree), bNode *node) { - NodeAttributeMath *data = (NodeAttributeMath *)MEM_callocN(sizeof(NodeAttributeMath), __func__); + NodeAttributeMath *data = MEM_cnew<NodeAttributeMath>(__func__); data->operation = NODE_MATH_ADD; data->input_type_a = GEO_NODE_ATTRIBUTE_INPUT_ATTRIBUTE; diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_mix.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_mix.cc index 4a110e9690a..20cc285f3a5 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_mix.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_mix.cc @@ -61,8 +61,7 @@ static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) static void node_init(bNodeTree *UNUSED(ntree), bNode *node) { - NodeAttributeMix *data = (NodeAttributeMix *)MEM_callocN(sizeof(NodeAttributeMix), - "attribute mix node"); + NodeAttributeMix *data = MEM_cnew<NodeAttributeMix>("attribute mix node"); data->blend_type = MA_RAMP_BLEND; data->input_type_factor = GEO_NODE_ATTRIBUTE_INPUT_FLOAT; data->input_type_a = GEO_NODE_ATTRIBUTE_INPUT_ATTRIBUTE; diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_proximity.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_proximity.cc index 080bf38a740..e1b7f9452f3 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_proximity.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_proximity.cc @@ -44,8 +44,8 @@ static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) static void node_init(bNodeTree *UNUSED(ntree), bNode *node) { - NodeGeometryAttributeProximity *node_storage = (NodeGeometryAttributeProximity *)MEM_callocN( - sizeof(NodeGeometryAttributeProximity), __func__); + NodeGeometryAttributeProximity *node_storage = MEM_cnew<NodeGeometryAttributeProximity>( + __func__); node_storage->target_geometry_element = GEO_NODE_PROXIMITY_TARGET_FACES; node->storage = node_storage; diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_randomize.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_randomize.cc index ab2bc7b379c..f0aa8361e6e 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_randomize.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_randomize.cc @@ -81,8 +81,7 @@ static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) static void node_init(bNodeTree *UNUSED(tree), bNode *node) { - NodeAttributeRandomize *data = (NodeAttributeRandomize *)MEM_callocN( - sizeof(NodeAttributeRandomize), __func__); + NodeAttributeRandomize *data = MEM_cnew<NodeAttributeRandomize>(__func__); data->data_type = CD_PROP_FLOAT; data->domain = ATTR_DOMAIN_POINT; data->operation = GEO_NODE_ATTRIBUTE_RANDOMIZE_REPLACE_CREATE; diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_separate_xyz.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_separate_xyz.cc index c5aac118baf..c30a9d3a7dd 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_separate_xyz.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_separate_xyz.cc @@ -41,8 +41,7 @@ static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) static void node_init(bNodeTree *UNUSED(tree), bNode *node) { - NodeAttributeSeparateXYZ *data = (NodeAttributeSeparateXYZ *)MEM_callocN( - sizeof(NodeAttributeSeparateXYZ), __func__); + NodeAttributeSeparateXYZ *data = MEM_cnew<NodeAttributeSeparateXYZ>(__func__); data->input_type = GEO_NODE_ATTRIBUTE_INPUT_ATTRIBUTE; node->storage = data; } diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_transfer.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_transfer.cc index 686edc80f62..2552308a8c2 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_transfer.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_transfer.cc @@ -50,8 +50,7 @@ static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) static void node_init(bNodeTree *UNUSED(tree), bNode *node) { - NodeGeometryAttributeTransfer *data = (NodeGeometryAttributeTransfer *)MEM_callocN( - sizeof(NodeGeometryAttributeTransfer), __func__); + NodeGeometryAttributeTransfer *data = MEM_cnew<NodeGeometryAttributeTransfer>(__func__); data->domain = ATTR_DOMAIN_AUTO; node->storage = data; } diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_vector_math.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_vector_math.cc index 68051e81f57..c9e8ce88b25 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_vector_math.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_vector_math.cc @@ -103,8 +103,7 @@ static CustomDataType operation_get_read_type_c(const NodeVectorMathOperation op static void node_init(bNodeTree *UNUSED(tree), bNode *node) { - NodeAttributeVectorMath *data = (NodeAttributeVectorMath *)MEM_callocN( - sizeof(NodeAttributeVectorMath), __func__); + NodeAttributeVectorMath *data = MEM_cnew<NodeAttributeVectorMath>(__func__); data->operation = NODE_VECTOR_MATH_ADD; data->input_type_a = GEO_NODE_ATTRIBUTE_INPUT_ATTRIBUTE; diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_vector_rotate.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_vector_rotate.cc index 1ef50e69775..bdc09eb3408 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_vector_rotate.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_vector_rotate.cc @@ -112,8 +112,7 @@ static float3 vector_rotate_around_axis(const float3 vector, static void node_init(bNodeTree *UNUSED(ntree), bNode *node) { - NodeAttributeVectorRotate *node_storage = (NodeAttributeVectorRotate *)MEM_callocN( - sizeof(NodeAttributeVectorRotate), __func__); + NodeAttributeVectorRotate *node_storage = MEM_cnew<NodeAttributeVectorRotate>(__func__); node_storage->mode = GEO_NODE_VECTOR_ROTATE_TYPE_AXIS; node_storage->input_type_vector = GEO_NODE_ATTRIBUTE_INPUT_ATTRIBUTE; diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_select_by_handle_type.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_select_by_handle_type.cc index c702e9ff686..e86b0f225e3 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_select_by_handle_type.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_select_by_handle_type.cc @@ -40,8 +40,7 @@ static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) static void node_init(bNodeTree *UNUSED(tree), bNode *node) { - NodeGeometryCurveSelectHandles *data = (NodeGeometryCurveSelectHandles *)MEM_callocN( - sizeof(NodeGeometryCurveSelectHandles), __func__); + NodeGeometryCurveSelectHandles *data = MEM_cnew<NodeGeometryCurveSelectHandles>(__func__); data->handle_type = GEO_NODE_CURVE_HANDLE_AUTO; data->mode = GEO_NODE_CURVE_HANDLE_LEFT | GEO_NODE_CURVE_HANDLE_RIGHT; diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_set_handles.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_set_handles.cc index 1e476d01148..b4106e69bbd 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_set_handles.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_set_handles.cc @@ -38,8 +38,7 @@ static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) static void node_init(bNodeTree *UNUSED(tree), bNode *node) { - NodeGeometryCurveSetHandles *data = (NodeGeometryCurveSetHandles *)MEM_callocN( - sizeof(NodeGeometryCurveSetHandles), __func__); + NodeGeometryCurveSetHandles *data = MEM_cnew<NodeGeometryCurveSetHandles>(__func__); data->handle_type = GEO_NODE_CURVE_HANDLE_AUTO; data->mode = GEO_NODE_CURVE_HANDLE_LEFT | GEO_NODE_CURVE_HANDLE_RIGHT; diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_spline_type.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_spline_type.cc index f3599f4328f..cbd4e3b29f2 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_spline_type.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_spline_type.cc @@ -39,8 +39,7 @@ static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) static void node_init(bNodeTree *UNUSED(tree), bNode *node) { - NodeGeometryCurveSplineType *data = (NodeGeometryCurveSplineType *)MEM_callocN( - sizeof(NodeGeometryCurveSplineType), __func__); + NodeGeometryCurveSplineType *data = MEM_cnew<NodeGeometryCurveSplineType>(__func__); data->spline_type = GEO_NODE_SPLINE_TYPE_POLY; node->storage = data; diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_subdivide.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_subdivide.cc index 9878402dd35..e4db622ad52 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_subdivide.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_subdivide.cc @@ -44,8 +44,7 @@ static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) static void node_init(bNodeTree *UNUSED(tree), bNode *node) { - NodeGeometryCurveSubdivide *data = (NodeGeometryCurveSubdivide *)MEM_callocN( - sizeof(NodeGeometryCurveSubdivide), __func__); + NodeGeometryCurveSubdivide *data = MEM_cnew<NodeGeometryCurveSubdivide>(__func__); data->cuts_type = GEO_NODE_ATTRIBUTE_INPUT_INTEGER; node->storage = data; diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_to_points.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_to_points.cc index 3bd03f3cee0..a5d135582eb 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_to_points.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_to_points.cc @@ -95,8 +95,7 @@ static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) static void node_init(bNodeTree *UNUSED(tree), bNode *node) { - NodeGeometryCurveToPoints *data = (NodeGeometryCurveToPoints *)MEM_callocN( - sizeof(NodeGeometryCurveToPoints), __func__); + NodeGeometryCurveToPoints *data = MEM_cnew<NodeGeometryCurveToPoints>(__func__); data->mode = GEO_NODE_CURVE_RESAMPLE_COUNT; node->storage = data; diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_point_instance.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_point_instance.cc index 8915a58feb1..61fea9fee11 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_point_instance.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_point_instance.cc @@ -46,8 +46,7 @@ static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) static void node_init(bNodeTree *UNUSED(tree), bNode *node) { - NodeGeometryPointInstance *data = (NodeGeometryPointInstance *)MEM_callocN( - sizeof(NodeGeometryPointInstance), __func__); + NodeGeometryPointInstance *data = MEM_cnew<NodeGeometryPointInstance>(__func__); data->instance_type = GEO_NODE_POINT_INSTANCE_TYPE_OBJECT; data->flag |= GEO_NODE_POINT_INSTANCE_WHOLE_COLLECTION; node->storage = data; diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_point_rotate.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_point_rotate.cc index a0a7674797a..26fae8093a5 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_point_rotate.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_point_rotate.cc @@ -59,8 +59,7 @@ static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) static void node_init(bNodeTree *UNUSED(ntree), bNode *node) { - NodeGeometryRotatePoints *node_storage = (NodeGeometryRotatePoints *)MEM_callocN( - sizeof(NodeGeometryRotatePoints), __func__); + NodeGeometryRotatePoints *node_storage = MEM_cnew<NodeGeometryRotatePoints>(__func__); node_storage->type = GEO_NODE_POINT_ROTATE_TYPE_EULER; node_storage->space = GEO_NODE_POINT_ROTATE_SPACE_OBJECT; diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_point_scale.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_point_scale.cc index d38df124979..bf2db79515e 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_point_scale.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_point_scale.cc @@ -43,8 +43,7 @@ static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) static void node_init(bNodeTree *UNUSED(tree), bNode *node) { - NodeGeometryPointScale *data = (NodeGeometryPointScale *)MEM_callocN( - sizeof(NodeGeometryPointScale), __func__); + NodeGeometryPointScale *data = MEM_cnew<NodeGeometryPointScale>(__func__); data->input_type = GEO_NODE_ATTRIBUTE_INPUT_VECTOR; node->storage = data; diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_point_translate.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_point_translate.cc index c70478182ec..555bd228ac6 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_point_translate.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_point_translate.cc @@ -74,8 +74,7 @@ static void node_geo_exec(GeoNodeExecParams params) static void node_init(bNodeTree *UNUSED(tree), bNode *node) { - NodeGeometryPointTranslate *data = (NodeGeometryPointTranslate *)MEM_callocN( - sizeof(NodeGeometryPointTranslate), __func__); + NodeGeometryPointTranslate *data = MEM_cnew<NodeGeometryPointTranslate>(__func__); data->input_type = GEO_NODE_ATTRIBUTE_INPUT_VECTOR; node->storage = data; diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_points_to_volume.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_points_to_volume.cc index ec1ab67b530..a0cddab702b 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_points_to_volume.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_points_to_volume.cc @@ -51,8 +51,7 @@ static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) static void node_init(bNodeTree *UNUSED(ntree), bNode *node) { - NodeGeometryPointsToVolume *data = (NodeGeometryPointsToVolume *)MEM_callocN( - sizeof(NodeGeometryPointsToVolume), __func__); + NodeGeometryPointsToVolume *data = MEM_cnew<NodeGeometryPointsToVolume>(__func__); data->resolution_mode = GEO_NODE_POINTS_TO_VOLUME_RESOLUTION_MODE_AMOUNT; data->input_type_radius = GEO_NODE_ATTRIBUTE_INPUT_FLOAT; node->storage = data; diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_raycast.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_raycast.cc index 599ffd617a5..5a950114840 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_raycast.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_raycast.cc @@ -58,8 +58,7 @@ static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) static void node_init(bNodeTree *UNUSED(tree), bNode *node) { - NodeGeometryRaycast *data = (NodeGeometryRaycast *)MEM_callocN(sizeof(NodeGeometryRaycast), - __func__); + NodeGeometryRaycast *data = MEM_cnew<NodeGeometryRaycast>(__func__); data->input_type_ray_direction = GEO_NODE_ATTRIBUTE_INPUT_VECTOR; data->input_type_ray_length = GEO_NODE_ATTRIBUTE_INPUT_FLOAT; node->storage = data; diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_subdivision_surface.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_subdivision_surface.cc index 819ffb2c20c..dca5c58d164 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_subdivision_surface.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_subdivision_surface.cc @@ -47,8 +47,7 @@ static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) static void node_init(bNodeTree *UNUSED(ntree), bNode *node) { - NodeGeometrySubdivisionSurface *data = (NodeGeometrySubdivisionSurface *)MEM_callocN( - sizeof(NodeGeometrySubdivisionSurface), __func__); + NodeGeometrySubdivisionSurface *data = MEM_cnew<NodeGeometrySubdivisionSurface>(__func__); data->uv_smooth = SUBSURF_UV_SMOOTH_PRESERVE_BOUNDARIES; data->boundary_smooth = SUBSURF_BOUNDARY_SMOOTH_ALL; node->storage = data; diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_volume_to_mesh.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_volume_to_mesh.cc index acff0be7126..77ace8d072a 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_volume_to_mesh.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_volume_to_mesh.cc @@ -57,8 +57,7 @@ static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) static void node_init(bNodeTree *UNUSED(ntree), bNode *node) { - NodeGeometryVolumeToMesh *data = (NodeGeometryVolumeToMesh *)MEM_callocN( - sizeof(NodeGeometryVolumeToMesh), __func__); + NodeGeometryVolumeToMesh *data = MEM_cnew<NodeGeometryVolumeToMesh>(__func__); data->resolution_mode = VOLUME_TO_MESH_RESOLUTION_MODE_GRID; bNodeSocket *grid_socket = nodeFindSocket(node, SOCK_IN, "Density"); diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_capture.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_capture.cc index be0baa706af..6c4dd96ea9c 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_capture.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_capture.cc @@ -54,8 +54,7 @@ static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) static void node_init(bNodeTree *UNUSED(tree), bNode *node) { - NodeGeometryAttributeCapture *data = (NodeGeometryAttributeCapture *)MEM_callocN( - sizeof(NodeGeometryAttributeCapture), __func__); + NodeGeometryAttributeCapture *data = MEM_cnew<NodeGeometryAttributeCapture>(__func__); data->data_type = CD_PROP_FLOAT; data->domain = ATTR_DOMAIN_POINT; diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_fill.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_fill.cc index 3aabf8e21eb..92dbca060b2 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_fill.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_fill.cc @@ -48,8 +48,7 @@ static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) static void node_init(bNodeTree *UNUSED(ntree), bNode *node) { - NodeGeometryCurveFill *data = (NodeGeometryCurveFill *)MEM_callocN(sizeof(NodeGeometryCurveFill), - __func__); + NodeGeometryCurveFill *data = MEM_cnew<NodeGeometryCurveFill>(__func__); data->mode = GEO_NODE_CURVE_FILL_MODE_TRIANGULATED; node->storage = data; diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc index a438c1d6086..f69f773ce21 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc @@ -55,8 +55,7 @@ static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) static void node_init(bNodeTree *UNUSED(tree), bNode *node) { - NodeGeometryCurveFillet *data = (NodeGeometryCurveFillet *)MEM_callocN( - sizeof(NodeGeometryCurveFillet), __func__); + NodeGeometryCurveFillet *data = MEM_cnew<NodeGeometryCurveFillet>(__func__); data->mode = GEO_NODE_CURVE_FILLET_BEZIER; node->storage = data; diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_handle_type_selection.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_handle_type_selection.cc index 381bb0fc1d0..50eb5dfc1a1 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_handle_type_selection.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_handle_type_selection.cc @@ -38,8 +38,7 @@ static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) static void node_init(bNodeTree *UNUSED(tree), bNode *node) { - NodeGeometryCurveSelectHandles *data = (NodeGeometryCurveSelectHandles *)MEM_callocN( - sizeof(NodeGeometryCurveSelectHandles), __func__); + NodeGeometryCurveSelectHandles *data = MEM_cnew<NodeGeometryCurveSelectHandles>(__func__); data->handle_type = GEO_NODE_CURVE_HANDLE_AUTO; data->mode = GEO_NODE_CURVE_HANDLE_LEFT | GEO_NODE_CURVE_HANDLE_RIGHT; diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_bezier_segment.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_bezier_segment.cc index 4299b5cc022..a9829b64150 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_bezier_segment.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_bezier_segment.cc @@ -62,8 +62,8 @@ static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) static void node_init(bNodeTree *UNUSED(tree), bNode *node) { - NodeGeometryCurvePrimitiveBezierSegment *data = (NodeGeometryCurvePrimitiveBezierSegment *) - MEM_callocN(sizeof(NodeGeometryCurvePrimitiveBezierSegment), __func__); + NodeGeometryCurvePrimitiveBezierSegment *data = + MEM_cnew<NodeGeometryCurvePrimitiveBezierSegment>(__func__); data->mode = GEO_NODE_CURVE_PRIMITIVE_BEZIER_SEGMENT_POSITION; node->storage = data; diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_circle.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_circle.cc index 70abf4c64a7..6d689b907fc 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_circle.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_circle.cc @@ -68,8 +68,7 @@ static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) static void node_init(bNodeTree *UNUSED(tree), bNode *node) { - NodeGeometryCurvePrimitiveCircle *data = (NodeGeometryCurvePrimitiveCircle *)MEM_callocN( - sizeof(NodeGeometryCurvePrimitiveCircle), __func__); + NodeGeometryCurvePrimitiveCircle *data = MEM_cnew<NodeGeometryCurvePrimitiveCircle>(__func__); data->mode = GEO_NODE_CURVE_PRIMITIVE_CIRCLE_TYPE_RADIUS; node->storage = data; diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_line.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_line.cc index 6d71c97b15a..592374ddd81 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_line.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_line.cc @@ -52,8 +52,7 @@ static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) static void node_init(bNodeTree *UNUSED(tree), bNode *node) { - NodeGeometryCurvePrimitiveLine *data = (NodeGeometryCurvePrimitiveLine *)MEM_callocN( - sizeof(NodeGeometryCurvePrimitiveLine), __func__); + NodeGeometryCurvePrimitiveLine *data = MEM_cnew<NodeGeometryCurvePrimitiveLine>(__func__); data->mode = GEO_NODE_CURVE_PRIMITIVE_LINE_MODE_POINTS; node->storage = data; diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_quadrilateral.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_quadrilateral.cc index ff6294b9b6b..98480971fbd 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_quadrilateral.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_quadrilateral.cc @@ -89,8 +89,7 @@ static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) static void node_init(bNodeTree *UNUSED(tree), bNode *node) { - NodeGeometryCurvePrimitiveQuad *data = (NodeGeometryCurvePrimitiveQuad *)MEM_callocN( - sizeof(NodeGeometryCurvePrimitiveQuad), __func__); + NodeGeometryCurvePrimitiveQuad *data = MEM_cnew<NodeGeometryCurvePrimitiveQuad>(__func__); data->mode = GEO_NODE_CURVE_PRIMITIVE_QUAD_MODE_RECTANGLE; node->storage = data; } @@ -113,35 +112,26 @@ static void node_update(bNodeTree *ntree, bNode *node) bNodeSocket *p3 = p2->next; bNodeSocket *p4 = p3->next; - LISTBASE_FOREACH (bNodeSocket *, sock, &node->inputs) { - nodeSetSocketAvailability(ntree, sock, false); - } + Vector<bNodeSocket *> available_sockets; if (mode == GEO_NODE_CURVE_PRIMITIVE_QUAD_MODE_RECTANGLE) { - nodeSetSocketAvailability(ntree, width, true); - nodeSetSocketAvailability(ntree, height, true); + available_sockets.extend({width, height}); } else if (mode == GEO_NODE_CURVE_PRIMITIVE_QUAD_MODE_PARALLELOGRAM) { - nodeSetSocketAvailability(ntree, width, true); - nodeSetSocketAvailability(ntree, height, true); - nodeSetSocketAvailability(ntree, offset, true); + available_sockets.extend({width, height, offset}); } else if (mode == GEO_NODE_CURVE_PRIMITIVE_QUAD_MODE_TRAPEZOID) { - nodeSetSocketAvailability(ntree, bottom, true); - nodeSetSocketAvailability(ntree, top, true); - nodeSetSocketAvailability(ntree, offset, true); - nodeSetSocketAvailability(ntree, height, true); + available_sockets.extend({bottom, top, offset, height}); } else if (mode == GEO_NODE_CURVE_PRIMITIVE_QUAD_MODE_KITE) { - nodeSetSocketAvailability(ntree, width, true); - nodeSetSocketAvailability(ntree, bottom_height, true); - nodeSetSocketAvailability(ntree, top_height, true); + available_sockets.extend({width, bottom_height, top_height}); } else if (mode == GEO_NODE_CURVE_PRIMITIVE_QUAD_MODE_POINTS) { - nodeSetSocketAvailability(ntree, p1, true); - nodeSetSocketAvailability(ntree, p2, true); - nodeSetSocketAvailability(ntree, p3, true); - nodeSetSocketAvailability(ntree, p4, true); + available_sockets.extend({p1, p2, p3, p4}); + } + + LISTBASE_FOREACH (bNodeSocket *, sock, &node->inputs) { + nodeSetSocketAvailability(ntree, sock, available_sockets.contains(sock)); } } diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc index 7e465714265..741dad95362 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc @@ -50,8 +50,7 @@ static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) static void node_init(bNodeTree *UNUSED(tree), bNode *node) { - NodeGeometryCurveResample *data = (NodeGeometryCurveResample *)MEM_callocN( - sizeof(NodeGeometryCurveResample), __func__); + NodeGeometryCurveResample *data = MEM_cnew<NodeGeometryCurveResample>(__func__); data->mode = GEO_NODE_CURVE_RESAMPLE_COUNT; node->storage = data; diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_set_handles.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_set_handles.cc index 8c0827570c6..0124e9bd9dd 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_set_handles.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_set_handles.cc @@ -40,8 +40,7 @@ static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) static void node_init(bNodeTree *UNUSED(tree), bNode *node) { - NodeGeometryCurveSetHandles *data = (NodeGeometryCurveSetHandles *)MEM_callocN( - sizeof(NodeGeometryCurveSetHandles), __func__); + NodeGeometryCurveSetHandles *data = MEM_cnew<NodeGeometryCurveSetHandles>(__func__); data->handle_type = GEO_NODE_CURVE_HANDLE_AUTO; data->mode = GEO_NODE_CURVE_HANDLE_LEFT; diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_spline_type.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_spline_type.cc index eef8c1b0db5..2d07ac5411f 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_spline_type.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_spline_type.cc @@ -41,8 +41,7 @@ static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) static void node_init(bNodeTree *UNUSED(tree), bNode *node) { - NodeGeometryCurveSplineType *data = (NodeGeometryCurveSplineType *)MEM_callocN( - sizeof(NodeGeometryCurveSplineType), __func__); + NodeGeometryCurveSplineType *data = MEM_cnew<NodeGeometryCurveSplineType>(__func__); data->spline_type = GEO_NODE_SPLINE_TYPE_POLY; node->storage = data; diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc index 0e9425246cb..d20fd66017e 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc @@ -72,8 +72,7 @@ static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) static void node_init(bNodeTree *UNUSED(tree), bNode *node) { - NodeGeometryCurveToPoints *data = (NodeGeometryCurveToPoints *)MEM_callocN( - sizeof(NodeGeometryCurveToPoints), __func__); + NodeGeometryCurveToPoints *data = MEM_cnew<NodeGeometryCurveToPoints>(__func__); data->mode = GEO_NODE_CURVE_RESAMPLE_COUNT; node->storage = data; diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc index 746392a66cc..2f96696cee4 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc @@ -67,8 +67,7 @@ static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) static void node_init(bNodeTree *UNUSED(tree), bNode *node) { - NodeGeometryCurveTrim *data = (NodeGeometryCurveTrim *)MEM_callocN(sizeof(NodeGeometryCurveTrim), - __func__); + NodeGeometryCurveTrim *data = MEM_cnew<NodeGeometryCurveTrim>(__func__); data->mode = GEO_NODE_CURVE_SAMPLE_FACTOR; node->storage = data; diff --git a/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc b/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc index 1de809b30e4..a5197b23adb 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc @@ -979,8 +979,8 @@ static void do_mesh_separation(GeometrySet &geometry_set, /* Needed in all cases. */ Vector<int> selected_poly_indices; Vector<int> new_loop_starts; - int num_selected_polys; - int num_selected_loops; + int num_selected_polys = 0; + int num_selected_loops = 0; const Mesh &mesh_in = *in_component.get_for_read(); Mesh *mesh_out; @@ -1307,8 +1307,7 @@ static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) static void node_init(bNodeTree *UNUSED(tree), bNode *node) { - NodeGeometryDeleteGeometry *data = (NodeGeometryDeleteGeometry *)MEM_callocN( - sizeof(NodeGeometryDeleteGeometry), __func__); + NodeGeometryDeleteGeometry *data = MEM_cnew<NodeGeometryDeleteGeometry>(__func__); data->domain = ATTR_DOMAIN_POINT; data->mode = GEO_NODE_DELETE_GEOMETRY_MODE_ALL; diff --git a/source/blender/nodes/geometry/nodes/node_geo_dual_mesh.cc b/source/blender/nodes/geometry/nodes/node_geo_dual_mesh.cc index a3bbeca7af3..d1cbaa540d7 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_dual_mesh.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_dual_mesh.cc @@ -537,6 +537,77 @@ static void add_edge(const Mesh &mesh, loop_edges.append(new_edge_i); } +/* Returns true if the vertex is connected only to the two polygons and is not on the boundary. */ +static bool vertex_needs_dissolving(const int vertex, + const int first_poly_index, + const int second_poly_index, + const Span<VertexType> vertex_types, + const Span<Vector<int>> vertex_poly_indices) +{ + /* Order is guaranteed to be the same because 2poly verts that are not on the boundary are + * ignored in `sort_vertex_polys`. */ + return (vertex_types[vertex] != VertexType::Boundary && + vertex_poly_indices[vertex].size() == 2 && + vertex_poly_indices[vertex][0] == first_poly_index && + vertex_poly_indices[vertex][1] == second_poly_index); +} + +/** + * Finds 'normal' vertices which are connected to only two polygons and marks them to not be + * used in the datastructures derived from the mesh. For each pair of polygons which has such a + * vertex, an edge is created for the dual mesh between the centers of those two polygons. All + * edges in the input mesh which contain such a vertex are marked as 'done' to prevent duplicate + * edges being created. (See T94144) + */ +static void dissolve_redundant_verts(const Mesh &mesh, + const Span<Vector<int>> vertex_poly_indices, + MutableSpan<VertexType> vertex_types, + MutableSpan<int> old_to_new_edges_map, + Vector<MEdge> &new_edges, + Vector<int> &new_to_old_edges_map) +{ + for (const int vert_i : IndexRange(mesh.totvert)) { + if (vertex_poly_indices[vert_i].size() != 2 || vertex_types[vert_i] != VertexType::Normal) { + continue; + } + const int first_poly_index = vertex_poly_indices[vert_i][0]; + const int second_poly_index = vertex_poly_indices[vert_i][1]; + const int new_edge_index = new_edges.size(); + bool edge_created = false; + const MPoly &poly = mesh.mpoly[first_poly_index]; + for (const MLoop &loop : Span<MLoop>(&mesh.mloop[poly.loopstart], poly.totloop)) { + const MEdge &edge = mesh.medge[loop.e]; + const int v1 = edge.v1; + const int v2 = edge.v2; + bool mark_edge = false; + if (vertex_needs_dissolving( + v1, first_poly_index, second_poly_index, vertex_types, vertex_poly_indices)) { + /* This vertex is now 'removed' and should be ignored elsewhere. */ + vertex_types[v1] = VertexType::Loose; + mark_edge = true; + } + if (vertex_needs_dissolving( + v2, first_poly_index, second_poly_index, vertex_types, vertex_poly_indices)) { + /* This vertex is now 'removed' and should be ignored elsewhere. */ + vertex_types[v2] = VertexType::Loose; + mark_edge = true; + } + if (mark_edge) { + if (!edge_created) { + MEdge new_edge = MEdge(edge); + /* The vertex indices in the dual mesh are the polygon indices of the input mesh. */ + new_edge.v1 = first_poly_index; + new_edge.v2 = second_poly_index; + new_to_old_edges_map.append(loop.e); + new_edges.append(new_edge); + edge_created = true; + } + old_to_new_edges_map[loop.e] = new_edge_index; + } + } + } +} + /** * Calculate the barycentric dual of a mesh. The dual is only "dual" in terms of connectivity, * i.e. applying the function twice will give the same vertices, edges, and faces, but not the @@ -564,6 +635,9 @@ static void calc_dual_mesh(GeometrySet &geometry_set, Array<VertexType> vertex_types(mesh_in.totvert); Array<EdgeType> edge_types(mesh_in.totedge); calc_boundaries(mesh_in, vertex_types, edge_types); + /* Stores the indices of the polygons connected to the vertex. Because the polygons are looped + * over in order of their indices, the polygon's indices will be sorted in ascending order. + (This can change once they are sorted using `sort_vertex_polys`). */ Array<Vector<int>> vertex_poly_indices(mesh_in.totvert); Array<Array<int>> vertex_shared_edges(mesh_in.totvert); Array<Array<int>> vertex_corners(mesh_in.totvert); @@ -632,6 +706,16 @@ static void calc_dual_mesh(GeometrySet &geometry_set, * don't need a hashmap for that. */ Array<int> old_to_new_edges_map(mesh_in.totedge); old_to_new_edges_map.fill(-1); + + /* This is necessary to prevent duplicate edges from being created, but will likely not do + * anything for most meshes. */ + dissolve_redundant_verts(mesh_in, + vertex_poly_indices, + vertex_types, + old_to_new_edges_map, + new_edges, + new_to_old_edges_map); + for (const int i : IndexRange(mesh_in.totvert)) { if (vertex_types[i] == VertexType::Loose || vertex_types[i] >= VertexType::NonManifold || (!keep_boundaries && vertex_types[i] == VertexType::Boundary)) { diff --git a/source/blender/nodes/geometry/nodes/node_geo_image_texture.cc b/source/blender/nodes/geometry/nodes/node_geo_image_texture.cc index 0003f15854d..52ef48f147b 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_image_texture.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_image_texture.cc @@ -55,8 +55,7 @@ static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) static void node_init(bNodeTree *UNUSED(ntree), bNode *node) { - NodeGeometryImageTexture *tex = (NodeGeometryImageTexture *)MEM_callocN( - sizeof(NodeGeometryImageTexture), __func__); + NodeGeometryImageTexture *tex = MEM_cnew<NodeGeometryImageTexture>(__func__); node->storage = tex; } diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_circle.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_circle.cc index 7a1cb8a62a3..503915872d6 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_circle.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_circle.cc @@ -52,8 +52,7 @@ static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) static void node_init(bNodeTree *UNUSED(ntree), bNode *node) { - NodeGeometryMeshCircle *node_storage = (NodeGeometryMeshCircle *)MEM_callocN( - sizeof(NodeGeometryMeshCircle), __func__); + NodeGeometryMeshCircle *node_storage = MEM_cnew<NodeGeometryMeshCircle>(__func__); node_storage->fill_type = GEO_NODE_MESH_CIRCLE_FILL_NONE; @@ -214,8 +213,6 @@ static void node_geo_exec(GeoNodeExecParams params) Mesh *mesh = create_circle_mesh(radius, verts_num, fill); - BLI_assert(BKE_mesh_is_valid(mesh)); - params.set_output("Mesh", GeometrySet::create_with_mesh(mesh)); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cone.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cone.cc index 70b093798f8..b6ddbb4dd27 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cone.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cone.cc @@ -770,8 +770,7 @@ static void node_declare(NodeDeclarationBuilder &b) static void node_init(bNodeTree *UNUSED(ntree), bNode *node) { - NodeGeometryMeshCone *node_storage = (NodeGeometryMeshCone *)MEM_callocN( - sizeof(NodeGeometryMeshCone), __func__); + NodeGeometryMeshCone *node_storage = MEM_cnew<NodeGeometryMeshCone>(__func__); node_storage->fill_type = GEO_NODE_MESH_CIRCLE_FILL_NGON; diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cylinder.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cylinder.cc index b8d2ed3be92..ae270495b07 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cylinder.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cylinder.cc @@ -71,8 +71,7 @@ static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) static void node_init(bNodeTree *UNUSED(ntree), bNode *node) { - NodeGeometryMeshCylinder *node_storage = (NodeGeometryMeshCylinder *)MEM_callocN( - sizeof(NodeGeometryMeshCylinder), __func__); + NodeGeometryMeshCylinder *node_storage = MEM_cnew<NodeGeometryMeshCylinder>(__func__); node_storage->fill_type = GEO_NODE_MESH_CIRCLE_FILL_NGON; diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_grid.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_grid.cc index 77634a03af6..4a5668453bf 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_grid.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_grid.cc @@ -14,6 +14,8 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +#include "BLI_task.hh" + #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" @@ -38,11 +40,13 @@ static void calculate_uvs( const float dx = (size_x == 0.0f) ? 0.0f : 1.0f / size_x; const float dy = (size_y == 0.0f) ? 0.0f : 1.0f / size_y; - for (const int i : loops.index_range()) { - const float3 &co = verts[loops[i].v].co; - uvs[i].x = (co.x + size_x * 0.5f) * dx; - uvs[i].y = (co.y + size_y * 0.5f) * dy; - } + threading::parallel_for(loops.index_range(), 1024, [&](IndexRange range) { + for (const int i : range) { + const float3 &co = verts[loops[i].v].co; + uvs[i].x = (co.x + size_x * 0.5f) * dx; + uvs[i].y = (co.y + size_y * 0.5f) * dy; + } + }); uv_attribute.save(); } @@ -70,72 +74,95 @@ Mesh *create_grid_mesh(const int verts_x, const float dy = edges_y == 0 ? 0.0f : size_y / edges_y; const float x_shift = edges_x / 2.0f; const float y_shift = edges_y / 2.0f; - for (const int x_index : IndexRange(verts_x)) { - for (const int y_index : IndexRange(verts_y)) { - const int vert_index = x_index * verts_y + y_index; - verts[vert_index].co[0] = (x_index - x_shift) * dx; - verts[vert_index].co[1] = (y_index - y_shift) * dy; - verts[vert_index].co[2] = 0.0f; + threading::parallel_for(IndexRange(verts_x), 512, [&](IndexRange x_range) { + for (const int x : x_range) { + const int y_offset = x * verts_y; + threading::parallel_for(IndexRange(verts_y), 512, [&](IndexRange y_range) { + for (const int y : y_range) { + const int vert_index = y_offset + y; + verts[vert_index].co[0] = (x - x_shift) * dx; + verts[vert_index].co[1] = (y - y_shift) * dy; + verts[vert_index].co[2] = 0.0f; + } + }); } - } + }); } /* Point all vertex normals in the up direction. */ - const short up_normal[3] = {0, 0, SHRT_MAX}; - for (MVert &vert : verts) { - copy_v3_v3_short(vert.no, up_normal); + { + const short up_normal[3] = {0, 0, SHRT_MAX}; + for (MVert &vert : verts) { + copy_v3_v3_short(vert.no, up_normal); + } } - /* Build the horizontal edges in the X direction. */ const int y_edges_start = 0; + const int x_edges_start = verts_x * edges_y; const short edge_flag = (edges_x == 0 || edges_y == 0) ? ME_LOOSEEDGE : ME_EDGEDRAW | ME_EDGERENDER; - int edge_index = 0; - for (const int x : IndexRange(verts_x)) { - for (const int y : IndexRange(edges_y)) { - const int vert_index = x * verts_y + y; - MEdge &edge = edges[edge_index++]; - edge.v1 = vert_index; - edge.v2 = vert_index + 1; - edge.flag = edge_flag; + + /* Build the horizontal edges in the X direction. */ + threading::parallel_for(IndexRange(verts_x), 512, [&](IndexRange x_range) { + for (const int x : x_range) { + const int y_vert_offset = x * verts_y; + const int y_edge_offset = y_edges_start + x * edges_y; + threading::parallel_for(IndexRange(edges_y), 512, [&](IndexRange y_range) { + for (const int y : y_range) { + const int vert_index = y_vert_offset + y; + MEdge &edge = edges[y_edge_offset + y]; + edge.v1 = vert_index; + edge.v2 = vert_index + 1; + edge.flag = edge_flag; + } + }); } - } + }); /* Build the vertical edges in the Y direction. */ - const int x_edges_start = edge_index; - for (const int y : IndexRange(verts_y)) { - for (const int x : IndexRange(edges_x)) { - const int vert_index = x * verts_y + y; - MEdge &edge = edges[edge_index++]; - edge.v1 = vert_index; - edge.v2 = vert_index + verts_y; - edge.flag = edge_flag; + threading::parallel_for(IndexRange(verts_y), 512, [&](IndexRange y_range) { + for (const int y : y_range) { + const int x_edge_offset = x_edges_start + y * edges_x; + threading::parallel_for(IndexRange(edges_x), 512, [&](IndexRange x_range) { + for (const int x : x_range) { + const int vert_index = x * verts_y + y; + MEdge &edge = edges[x_edge_offset + x]; + edge.v1 = vert_index; + edge.v2 = vert_index + verts_y; + edge.flag = edge_flag; + } + }); } - } - - int loop_index = 0; - int poly_index = 0; - for (const int x : IndexRange(edges_x)) { - for (const int y : IndexRange(edges_y)) { - MPoly &poly = polys[poly_index++]; - poly.loopstart = loop_index; - poly.totloop = 4; - const int vert_index = x * verts_y + y; - - MLoop &loop_a = loops[loop_index++]; - loop_a.v = vert_index; - loop_a.e = x_edges_start + edges_x * y + x; - MLoop &loop_b = loops[loop_index++]; - loop_b.v = vert_index + verts_y; - loop_b.e = y_edges_start + edges_y * (x + 1) + y; - MLoop &loop_c = loops[loop_index++]; - loop_c.v = vert_index + verts_y + 1; - loop_c.e = x_edges_start + edges_x * (y + 1) + x; - MLoop &loop_d = loops[loop_index++]; - loop_d.v = vert_index + 1; - loop_d.e = y_edges_start + edges_y * x + y; + }); + + threading::parallel_for(IndexRange(edges_x), 512, [&](IndexRange x_range) { + for (const int x : x_range) { + const int y_offset = x * edges_y; + threading::parallel_for(IndexRange(edges_y), 512, [&](IndexRange y_range) { + for (const int y : y_range) { + const int poly_index = y_offset + y; + const int loop_index = poly_index * 4; + MPoly &poly = polys[poly_index]; + poly.loopstart = loop_index; + poly.totloop = 4; + const int vert_index = x * verts_y + y; + + MLoop &loop_a = loops[loop_index]; + loop_a.v = vert_index; + loop_a.e = x_edges_start + edges_x * y + x; + MLoop &loop_b = loops[loop_index + 1]; + loop_b.v = vert_index + verts_y; + loop_b.e = y_edges_start + edges_y * (x + 1) + y; + MLoop &loop_c = loops[loop_index + 2]; + loop_c.v = vert_index + verts_y + 1; + loop_c.e = x_edges_start + edges_x * (y + 1) + x; + MLoop &loop_d = loops[loop_index + 3]; + loop_d.v = vert_index + 1; + loop_d.e = y_edges_start + edges_y * x + y; + } + }); } - } + }); if (mesh->totpoly != 0) { calculate_uvs(mesh, verts, loops, size_x, size_y); @@ -185,7 +212,6 @@ static void node_geo_exec(GeoNodeExecParams params) } Mesh *mesh = create_grid_mesh(verts_x, verts_y, size_x, size_y); - BLI_assert(BKE_mesh_is_valid(mesh)); BKE_id_material_eval_ensure_default_slot(&mesh->id); params.set_output("Mesh", GeometrySet::create_with_mesh(mesh)); diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_line.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_line.cc index 9a87c040bdf..29f1e62b820 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_line.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_line.cc @@ -67,8 +67,7 @@ static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) static void node_init(bNodeTree *UNUSED(ntree), bNode *node) { - NodeGeometryMeshLine *node_storage = (NodeGeometryMeshLine *)MEM_callocN( - sizeof(NodeGeometryMeshLine), __func__); + NodeGeometryMeshLine *node_storage = MEM_cnew<NodeGeometryMeshLine>(__func__); node_storage->mode = GEO_NODE_MESH_LINE_MODE_OFFSET; node_storage->count_mode = GEO_NODE_MESH_LINE_COUNT_TOTAL; diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_uv_sphere.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_uv_sphere.cc index ce2e0923a30..f3213f791de 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_uv_sphere.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_uv_sphere.cc @@ -287,8 +287,6 @@ static Mesh *create_uv_sphere_mesh(const float radius, const int segments, const calculate_sphere_uvs(mesh, segments, rings); - BLI_assert(BKE_mesh_is_valid(mesh)); - return mesh; } diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_to_points.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_to_points.cc index 77314341fec..384ab43751f 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_to_points.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_to_points.cc @@ -50,8 +50,7 @@ static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) static void node_init(bNodeTree *UNUSED(tree), bNode *node) { - NodeGeometryMeshToPoints *data = (NodeGeometryMeshToPoints *)MEM_callocN( - sizeof(NodeGeometryMeshToPoints), __func__); + NodeGeometryMeshToPoints *data = MEM_cnew<NodeGeometryMeshToPoints>(__func__); data->mode = GEO_NODE_MESH_TO_POINTS_VERTICES; node->storage = data; } diff --git a/source/blender/nodes/geometry/nodes/node_geo_points_to_volume.cc b/source/blender/nodes/geometry/nodes/node_geo_points_to_volume.cc index 744cce6d445..351e0881fd4 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_points_to_volume.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_points_to_volume.cc @@ -66,8 +66,7 @@ static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) static void node_init(bNodeTree *UNUSED(ntree), bNode *node) { - NodeGeometryPointsToVolume *data = (NodeGeometryPointsToVolume *)MEM_callocN( - sizeof(NodeGeometryPointsToVolume), __func__); + NodeGeometryPointsToVolume *data = MEM_cnew<NodeGeometryPointsToVolume>(__func__); data->resolution_mode = GEO_NODE_POINTS_TO_VOLUME_RESOLUTION_MODE_AMOUNT; node->storage = data; } diff --git a/source/blender/nodes/geometry/nodes/node_geo_raycast.cc b/source/blender/nodes/geometry/nodes/node_geo_raycast.cc index d255fe482f6..82e96267a11 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_raycast.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_raycast.cc @@ -75,8 +75,7 @@ static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) static void node_init(bNodeTree *UNUSED(tree), bNode *node) { - NodeGeometryRaycast *data = (NodeGeometryRaycast *)MEM_callocN(sizeof(NodeGeometryRaycast), - __func__); + NodeGeometryRaycast *data = MEM_cnew<NodeGeometryRaycast>(__func__); data->mapping = GEO_NODE_RAYCAST_INTERPOLATED; data->data_type = CD_PROP_FLOAT; node->storage = data; diff --git a/source/blender/nodes/geometry/nodes/node_geo_separate_geometry.cc b/source/blender/nodes/geometry/nodes/node_geo_separate_geometry.cc index 7f1cc1be421..57e5eb63157 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_separate_geometry.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_separate_geometry.cc @@ -44,8 +44,7 @@ static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) static void node_init(bNodeTree *UNUSED(tree), bNode *node) { - NodeGeometrySeparateGeometry *data = (NodeGeometrySeparateGeometry *)MEM_callocN( - sizeof(NodeGeometrySeparateGeometry), __func__); + NodeGeometrySeparateGeometry *data = MEM_cnew<NodeGeometrySeparateGeometry>(__func__); data->domain = ATTR_DOMAIN_POINT; node->storage = data; diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_curve_handles.cc b/source/blender/nodes/geometry/nodes/node_geo_set_curve_handles.cc index 30a61574e19..bf8c71e1c91 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_set_curve_handles.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_set_curve_handles.cc @@ -41,8 +41,8 @@ static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) static void node_init(bNodeTree *UNUSED(tree), bNode *node) { - NodeGeometrySetCurveHandlePositions *data = (NodeGeometrySetCurveHandlePositions *)MEM_callocN( - sizeof(NodeGeometrySetCurveHandlePositions), __func__); + NodeGeometrySetCurveHandlePositions *data = MEM_cnew<NodeGeometrySetCurveHandlePositions>( + __func__); data->mode = GEO_NODE_CURVE_HANDLE_LEFT; node->storage = data; @@ -65,7 +65,7 @@ static void set_position_in_component(const GeometryNodeCurveHandleMode mode, evaluator.add(position_field); evaluator.add(offset_field); evaluator.evaluate(); - const IndexMask selection = evaluator.get_evaluated_as_mask(0); + const IndexMask selection = evaluator.get_evaluated_selection_as_mask(); CurveComponent *curve_component = static_cast<CurveComponent *>(&component); CurveEval *curve = curve_component->get_for_write(); diff --git a/source/blender/nodes/geometry/nodes/node_geo_string_to_curves.cc b/source/blender/nodes/geometry/nodes/node_geo_string_to_curves.cc index 33614eb3c46..910dfe261e8 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_string_to_curves.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_string_to_curves.cc @@ -88,8 +88,7 @@ static void node_layout(uiLayout *layout, struct bContext *C, PointerRNA *ptr) static void node_init(bNodeTree *UNUSED(ntree), bNode *node) { - NodeGeometryStringToCurves *data = (NodeGeometryStringToCurves *)MEM_callocN( - sizeof(NodeGeometryStringToCurves), __func__); + NodeGeometryStringToCurves *data = MEM_cnew<NodeGeometryStringToCurves>(__func__); data->overflow = GEO_NODE_STRING_TO_CURVES_MODE_OVERFLOW; data->align_x = GEO_NODE_STRING_TO_CURVES_ALIGN_X_LEFT; diff --git a/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc b/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc index 74e0560b32f..f5694ab10af 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc @@ -52,8 +52,7 @@ static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) static void node_init(bNodeTree *UNUSED(ntree), bNode *node) { - NodeGeometrySubdivisionSurface *data = (NodeGeometrySubdivisionSurface *)MEM_callocN( - sizeof(NodeGeometrySubdivisionSurface), __func__); + NodeGeometrySubdivisionSurface *data = MEM_cnew<NodeGeometrySubdivisionSurface>(__func__); data->uv_smooth = SUBSURF_UV_SMOOTH_PRESERVE_BOUNDARIES; data->boundary_smooth = SUBSURF_BOUNDARY_SMOOTH_ALL; node->storage = data; diff --git a/source/blender/nodes/geometry/nodes/node_geo_switch.cc b/source/blender/nodes/geometry/nodes/node_geo_switch.cc index d22522fe087..91aa1432ee2 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_switch.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_switch.cc @@ -94,7 +94,7 @@ static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) static void node_init(bNodeTree *UNUSED(tree), bNode *node) { - NodeSwitch *data = (NodeSwitch *)MEM_callocN(sizeof(NodeSwitch), __func__); + NodeSwitch *data = MEM_cnew<NodeSwitch>(__func__); data->input_type = SOCK_GEOMETRY; node->storage = data; } diff --git a/source/blender/nodes/geometry/nodes/node_geo_transfer_attribute.cc b/source/blender/nodes/geometry/nodes/node_geo_transfer_attribute.cc index 1f099dcd04d..285f7c6d42d 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_transfer_attribute.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_transfer_attribute.cc @@ -88,8 +88,7 @@ static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) static void node_init(bNodeTree *UNUSED(tree), bNode *node) { - NodeGeometryTransferAttribute *data = (NodeGeometryTransferAttribute *)MEM_callocN( - sizeof(NodeGeometryTransferAttribute), __func__); + NodeGeometryTransferAttribute *data = MEM_cnew<NodeGeometryTransferAttribute>(__func__); data->data_type = CD_PROP_FLOAT; data->mode = GEO_NODE_ATTRIBUTE_TRANSFER_NEAREST_FACE_INTERPOLATED; node->storage = data; diff --git a/source/blender/nodes/geometry/nodes/node_geo_viewer.cc b/source/blender/nodes/geometry/nodes/node_geo_viewer.cc index 18f2fa4cd36..6110ee42eea 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_viewer.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_viewer.cc @@ -42,8 +42,7 @@ static void node_declare(NodeDeclarationBuilder &b) static void node_init(bNodeTree *UNUSED(tree), bNode *node) { - NodeGeometryViewer *data = (NodeGeometryViewer *)MEM_callocN(sizeof(NodeGeometryViewer), - __func__); + NodeGeometryViewer *data = MEM_cnew<NodeGeometryViewer>(__func__); data->data_type = CD_PROP_FLOAT; node->storage = data; diff --git a/source/blender/nodes/geometry/nodes/node_geo_volume_to_mesh.cc b/source/blender/nodes/geometry/nodes/node_geo_volume_to_mesh.cc index 0819b401941..94e1dbfa4ad 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_volume_to_mesh.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_volume_to_mesh.cc @@ -69,8 +69,7 @@ static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) static void node_init(bNodeTree *UNUSED(ntree), bNode *node) { - NodeGeometryVolumeToMesh *data = (NodeGeometryVolumeToMesh *)MEM_callocN( - sizeof(NodeGeometryVolumeToMesh), __func__); + NodeGeometryVolumeToMesh *data = MEM_cnew<NodeGeometryVolumeToMesh>(__func__); data->resolution_mode = VOLUME_TO_MESH_RESOLUTION_MODE_GRID; node->storage = data; } diff --git a/source/blender/nodes/intern/node_common.cc b/source/blender/nodes/intern/node_common.cc index c302b1081af..a1fccc401a4 100644 --- a/source/blender/nodes/intern/node_common.cc +++ b/source/blender/nodes/intern/node_common.cc @@ -38,6 +38,7 @@ #include "BLT_translation.h" #include "BKE_node.h" +#include "BKE_node_tree_update.h" #include "RNA_types.h" @@ -153,8 +154,6 @@ static void update_socket_to_match_interface(bNodeTree &node_tree, /* Update socket type if necessary */ if (socket_to_update.typeinfo != interface_socket.typeinfo) { nodeModifySocketType(&node_tree, &node, &socket_to_update, interface_socket.idname); - /* Flag the tree to make sure link validity is updated after type changes. */ - node_tree.update |= NTREE_UPDATE_LINKS; } if (interface_socket.typeinfo->interface_verify_socket) { diff --git a/source/blender/nodes/intern/node_exec.cc b/source/blender/nodes/intern/node_exec.cc index 95070bf735e..f5b64f8499c 100644 --- a/source/blender/nodes/intern/node_exec.cc +++ b/source/blender/nodes/intern/node_exec.cc @@ -28,6 +28,7 @@ #include "BKE_global.h" #include "BKE_node.h" +#include "BKE_node_tree_update.h" #include "MEM_guardedalloc.h" @@ -170,7 +171,7 @@ bNodeTreeExec *ntree_exec_begin(bNodeExecContext *context, /* Using global main here is likely totally wrong, not sure what to do about that one though... * We cannot even check ntree is in global main, * since most of the time it won't be (thanks to ntree design)!!! */ - ntreeUpdateTree(G.main, ntree); + BKE_ntree_update_main_tree(G.main, ntree, nullptr); /* get a dependency-sorted list of nodes */ ntreeGetDependencyList(ntree, &nodelist, &totnodes); diff --git a/source/blender/nodes/intern/node_socket.cc b/source/blender/nodes/intern/node_socket.cc index d83c05b38a1..46cb9dcf891 100644 --- a/source/blender/nodes/intern/node_socket.cc +++ b/source/blender/nodes/intern/node_socket.cc @@ -236,6 +236,14 @@ static void refresh_socket_list(bNodeTree &ntree, link->tosock = new_socket; } } + LISTBASE_FOREACH (bNodeLink *, internal_link, &node.internal_links) { + if (internal_link->fromsock == old_socket_with_same_identifier) { + internal_link->fromsock = new_socket; + } + else if (internal_link->tosock == old_socket_with_same_identifier) { + internal_link->tosock = new_socket; + } + } } } } diff --git a/source/blender/nodes/intern/node_tree_ref.cc b/source/blender/nodes/intern/node_tree_ref.cc index 912d5e5322c..ffe0edb9762 100644 --- a/source/blender/nodes/intern/node_tree_ref.cc +++ b/source/blender/nodes/intern/node_tree_ref.cc @@ -70,6 +70,8 @@ NodeTreeRef::NodeTreeRef(bNodeTree *btree) : btree_(btree) break; } } + BLI_assert(internal_link.from_ != nullptr); + BLI_assert(internal_link.to_ != nullptr); node.internal_links_.append(&internal_link); } diff --git a/source/blender/nodes/intern/node_util.c b/source/blender/nodes/intern/node_util.c index 7620c8fa1a8..5c2d84cf605 100644 --- a/source/blender/nodes/intern/node_util.c +++ b/source/blender/nodes/intern/node_util.c @@ -35,6 +35,7 @@ #include "BKE_colortools.h" #include "BKE_node.h" +#include "BKE_node_tree_update.h" #include "RNA_access.h" #include "RNA_enum_types.h" @@ -340,188 +341,6 @@ void node_insert_link_default(bNodeTree *ntree, bNode *node, bNodeLink *link) /** \} */ /* -------------------------------------------------------------------- */ -/** \name Internal Links (mute and disconnect) - * \{ */ - -/** - * Common datatype priorities, works for compositor, shader and texture nodes alike - * defines priority of datatype connection based on output type (to): - * `< 0`: never connect these types. - * `>= 0`: priority of connection (higher values chosen first). - */ -static int node_datatype_priority(const bNodeSocketType *from, const bNodeSocketType *to) -{ - switch (to->type) { - case SOCK_RGBA: - switch (from->type) { - case SOCK_RGBA: - return 4; - case SOCK_FLOAT: - return 3; - case SOCK_INT: - return 2; - case SOCK_BOOLEAN: - return 1; - } - return -1; - case SOCK_VECTOR: - switch (from->type) { - case SOCK_VECTOR: - return 4; - case SOCK_FLOAT: - return 3; - case SOCK_INT: - return 2; - case SOCK_BOOLEAN: - return 1; - } - return -1; - case SOCK_FLOAT: - switch (from->type) { - case SOCK_FLOAT: - return 5; - case SOCK_INT: - return 4; - case SOCK_BOOLEAN: - return 3; - case SOCK_RGBA: - return 2; - case SOCK_VECTOR: - return 1; - } - return -1; - case SOCK_INT: - switch (from->type) { - case SOCK_INT: - return 5; - case SOCK_FLOAT: - return 4; - case SOCK_BOOLEAN: - return 3; - case SOCK_RGBA: - return 2; - case SOCK_VECTOR: - return 1; - } - return -1; - case SOCK_BOOLEAN: - switch (from->type) { - case SOCK_BOOLEAN: - return 5; - case SOCK_INT: - return 4; - case SOCK_FLOAT: - return 3; - case SOCK_RGBA: - return 2; - case SOCK_VECTOR: - return 1; - } - return -1; - } - - /* The rest of the socket types only allow an internal link if both the input and output socket - * have the same type. If the sockets are custom, we check the idname instead. */ - if (to->type == from->type && (to->type != SOCK_CUSTOM || STREQ(to->idname, from->idname))) { - return 1; - } - - return -1; -} - -/* select a suitable input socket for an output */ -static bNodeSocket *select_internal_link_input(bNode *node, bNodeSocket *output) -{ - if (node->type == NODE_REROUTE) { - return node->inputs.first; - } - - bNodeSocket *selected = NULL, *input; - int i; - int sel_priority = -1; - bool sel_is_linked = false; - - for (input = node->inputs.first, i = 0; input; input = input->next, i++) { - int priority = node_datatype_priority(input->typeinfo, output->typeinfo); - bool is_linked = (input->link != NULL); - bool preferred; - - if (nodeSocketIsHidden(input) || /* ignore hidden sockets */ - input->flag & - SOCK_NO_INTERNAL_LINK || /* ignore if input is not allowed for internal connections */ - priority < 0 || /* ignore incompatible types */ - priority < sel_priority) /* ignore if we already found a higher priority input */ - { - continue; - } - - /* determine if this input is preferred over the currently selected */ - preferred = (priority > sel_priority) || /* prefer higher datatype priority */ - (is_linked && !sel_is_linked); /* prefer linked over unlinked */ - - if (preferred) { - selected = input; - sel_is_linked = is_linked; - sel_priority = priority; - } - } - - return selected; -} - -void node_internal_links_create(bNodeTree *ntree, bNode *node) -{ - bNodeLink *link; - bNodeSocket *output, *input; - - /* sanity check */ - if (!ntree) { - return; - } - - /* use link pointer as a tag for handled sockets (for outputs is unused anyway) */ - for (output = node->outputs.first; output; output = output->next) { - output->link = NULL; - } - - for (link = ntree->links.first; link; link = link->next) { - if (nodeLinkIsHidden(link)) { - continue; - } - - output = link->fromsock; - if (link->fromnode != node || output->link) { - continue; - } - if (nodeSocketIsHidden(output) || output->flag & SOCK_NO_INTERNAL_LINK) { - continue; - } - output->link = link; /* not really used, just for tagging handled sockets */ - - /* look for suitable input */ - input = select_internal_link_input(node, output); - - if (input) { - bNodeLink *ilink = MEM_callocN(sizeof(bNodeLink), "internal node link"); - ilink->fromnode = node; - ilink->fromsock = input; - ilink->tonode = node; - ilink->tosock = output; - /* internal link is always valid */ - ilink->flag |= NODE_LINK_VALID; - BLI_addtail(&node->internal_links, ilink); - } - } - - /* clean up */ - for (output = node->outputs.first; output; output = output->next) { - output->link = NULL; - } -} - -/** \} */ - -/* -------------------------------------------------------------------- */ /** \name Default value RNA access * \{ */ diff --git a/source/blender/nodes/shader/node_shader_tree.c b/source/blender/nodes/shader/node_shader_tree.c index c3b5236373c..b19835fae19 100644 --- a/source/blender/nodes/shader/node_shader_tree.c +++ b/source/blender/nodes/shader/node_shader_tree.c @@ -44,6 +44,7 @@ #include "BKE_lib_id.h" #include "BKE_linestyle.h" #include "BKE_node.h" +#include "BKE_node_tree_update.h" #include "BKE_scene.h" #include "RNA_access.h" @@ -148,26 +149,11 @@ static void localize(bNodeTree *localtree, bNodeTree *UNUSED(ntree)) } } -static void local_sync(bNodeTree *localtree, bNodeTree *ntree) -{ - BKE_node_preview_sync_tree(ntree, localtree); -} - -static void local_merge(Main *UNUSED(bmain), bNodeTree *localtree, bNodeTree *ntree) -{ - BKE_node_preview_merge_tree(ntree, localtree, true); -} - static void update(bNodeTree *ntree) { ntreeSetOutput(ntree); ntree_update_reroute_nodes(ntree); - - if (ntree->update & NTREE_UPDATE_NODES) { - /* clean up preview cache, in case nodes have been removed */ - BKE_node_preview_remove_unused(ntree); - } } static bool shader_validate_link(eNodeSocketDatatype from, eNodeSocketDatatype to) @@ -202,8 +188,6 @@ void register_node_tree_type_sh(void) tt->foreach_nodeclass = foreach_nodeclass; tt->localize = localize; - tt->local_sync = local_sync; - tt->local_merge = local_merge; tt->update = update; tt->poll = shader_tree_poll; tt->get_from_context = shader_get_from_context; @@ -364,7 +348,7 @@ static void ntree_shader_unlink_hidden_value_sockets(bNode *group_node, bNodeSoc } if (removed_link) { - ntreeUpdateTree(G.main, group_ntree); + BKE_ntree_update_main_tree(G.main, group_ntree, NULL); } } @@ -423,7 +407,7 @@ static void ntree_shader_groups_expand_inputs(bNodeTree *localtree) } if (link_added) { - ntreeUpdateTree(G.main, localtree); + BKE_ntree_update_main_tree(G.main, localtree, NULL); } } @@ -503,7 +487,7 @@ static void flatten_group_do(bNodeTree *ntree, bNode *gnode) ntreeFreeLocalNode(ntree, node); } - ntree->update |= NTREE_UPDATE_NODES | NTREE_UPDATE_LINKS; + BKE_ntree_update_tag_all(ntree); } /* Flatten group to only have a simple single tree */ @@ -528,7 +512,7 @@ static void ntree_shader_groups_flatten(bNodeTree *localtree) } } - ntreeUpdateTree(G.main, localtree); + BKE_ntree_update_main_tree(G.main, localtree, NULL); } /* Check whether shader has a displacement. @@ -548,7 +532,7 @@ static bool ntree_shader_has_displacement(bNodeTree *ntree, return false; } /* Make sure sockets links pointers are correct. */ - ntreeUpdateTree(G.main, ntree); + BKE_ntree_update_main_tree(G.main, ntree, NULL); bNodeSocket *displacement = ntree_shader_node_find_input(output_node, "Displacement"); if (displacement == NULL) { @@ -637,7 +621,7 @@ static void ntree_shader_bypass_tagged_bump_nodes(bNodeTree *ntree) ntree_shader_bypass_bump_link(ntree, node, link); } } - ntreeUpdateTree(G.main, ntree); + BKE_ntree_update_main_tree(G.main, ntree, NULL); } static bool ntree_branch_count_and_tag_nodes(bNode *fromnode, bNode *tonode, void *userdata) @@ -722,7 +706,7 @@ static void ntree_shader_copy_branch_displacement(bNodeTree *ntree, nodeRemLink(ntree, displacement_link); nodeAddLink(ntree, displacement_node, displacement_socket, tonode, tosock); - ntreeUpdateTree(G.main, ntree); + BKE_ntree_update_main_tree(G.main, ntree, NULL); } /* Re-link displacement output to unconnected normal sockets via bump node. @@ -786,12 +770,12 @@ static void ntree_shader_relink_displacement(bNodeTree *ntree, bNode *output_nod geo_node->tmp_flag = -2; bump_node->tmp_flag = -2; - ntreeUpdateTree(G.main, ntree); + BKE_ntree_update_main_tree(G.main, ntree, NULL); /* Connect all free-standing Normal inputs and relink geometry/coordinate nodes. */ ntree_shader_link_builtin_normal(ntree, bump_node, bump_output_socket); /* We modified the tree, it needs to be updated now. */ - ntreeUpdateTree(G.main, ntree); + BKE_ntree_update_main_tree(G.main, ntree, NULL); } static void node_tag_branch_as_derivative(bNode *node, int dx) @@ -871,7 +855,7 @@ void ntree_shader_tag_nodes(bNodeTree *ntree, bNode *output_node, nTreeTags *tag return; } /* Make sure sockets links pointers are correct. */ - ntreeUpdateTree(G.main, ntree); + BKE_ntree_update_main_tree(G.main, ntree, NULL); nodeChainIterBackwards(ntree, output_node, ntree_tag_bsdf_cb, tags, 0); } diff --git a/source/blender/nodes/texture/node_texture_tree.c b/source/blender/nodes/texture/node_texture_tree.c index 14597050524..1125936aded 100644 --- a/source/blender/nodes/texture/node_texture_tree.c +++ b/source/blender/nodes/texture/node_texture_tree.c @@ -132,24 +132,9 @@ static void localize(bNodeTree *UNUSED(localtree), bNodeTree *UNUSED(ntree)) } #endif -static void local_sync(bNodeTree *localtree, bNodeTree *ntree) -{ - BKE_node_preview_sync_tree(ntree, localtree); -} - -static void local_merge(Main *UNUSED(bmain), bNodeTree *localtree, bNodeTree *ntree) -{ - BKE_node_preview_merge_tree(ntree, localtree, true); -} - static void update(bNodeTree *ntree) { ntree_update_reroute_nodes(ntree); - - if (ntree->update & NTREE_UPDATE_NODES) { - /* clean up preview cache, in case nodes have been removed */ - BKE_node_preview_remove_unused(ntree); - } } static bool texture_node_tree_socket_type_valid(bNodeTreeType *UNUSED(ntreetype), @@ -175,8 +160,6 @@ void register_node_tree_type_tex(void) tt->foreach_nodeclass = foreach_nodeclass; tt->update = update; tt->localize = localize; - tt->local_sync = local_sync; - tt->local_merge = local_merge; tt->get_from_context = texture_get_from_context; tt->valid_socket_type = texture_node_tree_socket_type_valid; diff --git a/source/blender/nodes/texture/node_texture_util.c b/source/blender/nodes/texture/node_texture_util.c index fe8e68bfc42..1ba693bf4a4 100644 --- a/source/blender/nodes/texture/node_texture_util.c +++ b/source/blender/nodes/texture/node_texture_util.c @@ -63,10 +63,6 @@ static void tex_call_delegate(TexDelegate *dg, float *out, TexParams *params, sh { if (dg->node->need_exec) { dg->fn(out, params, dg->node, dg->in, thread); - - if (dg->cdata->do_preview) { - tex_do_preview(dg->preview, params->previewco, out, dg->cdata->do_manage); - } } } @@ -123,19 +119,6 @@ void params_from_cdata(TexParams *out, TexCallData *in) out->mtex = in->mtex; } -void tex_do_preview(bNodePreview *preview, - const float coord[2], - const float col[4], - bool do_manage) -{ - if (preview) { - int xs = ((coord[0] + 1.0f) * 0.5f) * preview->xsize; - int ys = ((coord[1] + 1.0f) * 0.5f) * preview->ysize; - - BKE_node_preview_set_pixel(preview, col, xs, ys, do_manage); - } -} - void tex_output(bNode *node, bNodeExecData *execdata, bNodeStack **in, diff --git a/source/blender/nodes/texture/node_texture_util.h b/source/blender/nodes/texture/node_texture_util.h index 84d2c5c903a..473cef81359 100644 --- a/source/blender/nodes/texture/node_texture_util.h +++ b/source/blender/nodes/texture/node_texture_util.h @@ -121,10 +121,6 @@ void tex_output(bNode *node, bNodeStack *out, TexFn texfn, TexCallData *data); -void tex_do_preview(bNodePreview *preview, - const float coord[2], - const float col[4], - bool do_manage); void params_from_cdata(TexParams *out, TexCallData *in); diff --git a/source/blender/nodes/texture/nodes/node_texture_output.c b/source/blender/nodes/texture/nodes/node_texture_output.c index 19e24c9f82a..50f41239581 100644 --- a/source/blender/nodes/texture/nodes/node_texture_output.c +++ b/source/blender/nodes/texture/nodes/node_texture_output.c @@ -37,7 +37,7 @@ static bNodeSocketTemplate inputs[] = { static void exec(void *data, int UNUSED(thread), bNode *node, - bNodeExecData *execdata, + bNodeExecData *UNUSED(execdata), bNodeStack **in, bNodeStack **UNUSED(out)) { @@ -54,7 +54,6 @@ static void exec(void *data, else { tex_input_rgba(&target->tr, in[0], ¶ms, cdata->thread); } - tex_do_preview(execdata->preview, params.co, &target->tr, cdata->do_manage); } else { /* 0 means don't care, so just use first */ diff --git a/source/blender/nodes/texture/nodes/node_texture_viewer.c b/source/blender/nodes/texture/nodes/node_texture_viewer.c index 18b11b86d6f..1c22561bec4 100644 --- a/source/blender/nodes/texture/nodes/node_texture_viewer.c +++ b/source/blender/nodes/texture/nodes/node_texture_viewer.c @@ -36,7 +36,7 @@ static bNodeSocketTemplate outputs[] = { static void exec(void *data, int UNUSED(thread), bNode *UNUSED(node), - bNodeExecData *execdata, + bNodeExecData *UNUSED(execdata), bNodeStack **in, bNodeStack **UNUSED(out)) { @@ -48,7 +48,6 @@ static void exec(void *data, params_from_cdata(¶ms, cdata); tex_input_rgba(col, in[0], ¶ms, cdata->thread); - tex_do_preview(execdata->preview, params.previewco, col, cdata->do_manage); } } diff --git a/source/blender/render/intern/pipeline.c b/source/blender/render/intern/pipeline.c index a800361a41f..721abe45932 100644 --- a/source/blender/render/intern/pipeline.c +++ b/source/blender/render/intern/pipeline.c @@ -1131,7 +1131,7 @@ static void do_render_compositor_scenes(Render *re) render_scene_has_layers_to_render(scene, false)) { do_render_compositor_scene(re, scene, cfra); BLI_gset_add(scenes_rendered, scene); - nodeUpdate(restore_scene->nodetree, node); + node->typeinfo->updatefunc(restore_scene->nodetree, node); } } } @@ -1821,7 +1821,7 @@ void RE_RenderFrame(Render *re, render_callback_exec_id(re, re->main, &scene->id, BKE_CB_EVT_RENDER_INIT); /* Ugly global still... - * is to prevent preview events and signal subsurfs etc to make full resol. */ + * is to prevent preview events and signal subdivision-surface etc to make full resolution. */ G.is_rendering = true; scene->r.cfra = frame; @@ -2331,8 +2331,8 @@ void RE_RenderAnim(Render *re, } } - /* Ugly global still... is to prevent renderwin events and signal subsurfs etc to make full resol - * is also set by caller renderwin.c */ + /* Ugly global still... is to prevent renderwin events and signal subdivision-surface etc + * to make full resolution is also set by caller renderwin.c */ G.is_rendering = true; re->flag |= R_ANIMATION; diff --git a/source/blender/sequencer/intern/sequencer.c b/source/blender/sequencer/intern/sequencer.c index 908a5bd4f79..e4120a3cf95 100644 --- a/source/blender/sequencer/intern/sequencer.c +++ b/source/blender/sequencer/intern/sequencer.c @@ -140,6 +140,7 @@ Sequence *SEQ_sequence_alloc(ListBase *lb, int timeline_frame, int machine, int seq->pitch = 1.0f; seq->scene_sound = NULL; seq->type = type; + seq->blend_mode = SEQ_TYPE_ALPHAOVER; seq->strip = seq_strip_alloc(type); seq->stereo3d_format = MEM_callocN(sizeof(Stereo3dFormat), "Sequence Stereo Format"); @@ -418,6 +419,10 @@ void SEQ_meta_stack_free(Editing *ed, MetaStack *ms) MetaStack *SEQ_meta_stack_active_get(const Editing *ed) { + if (ed == NULL) { + return NULL; + } + return ed->metastack.last; } diff --git a/source/blender/sequencer/intern/strip_add.c b/source/blender/sequencer/intern/strip_add.c index a4b537b9074..f342765eec9 100644 --- a/source/blender/sequencer/intern/strip_add.c +++ b/source/blender/sequencer/intern/strip_add.c @@ -141,7 +141,6 @@ Sequence *SEQ_add_scene_strip(Scene *scene, ListBase *seqbase, struct SeqLoadDat { Sequence *seq = SEQ_sequence_alloc( seqbase, load_data->start_frame, load_data->channel, SEQ_TYPE_SCENE); - seq->blend_mode = SEQ_TYPE_ALPHAOVER; seq->scene = load_data->scene; seq->len = load_data->scene->r.efra - load_data->scene->r.sfra + 1; id_us_ensure_real((ID *)load_data->scene); @@ -154,7 +153,6 @@ Sequence *SEQ_add_movieclip_strip(Scene *scene, ListBase *seqbase, struct SeqLoa { Sequence *seq = SEQ_sequence_alloc( seqbase, load_data->start_frame, load_data->channel, SEQ_TYPE_MOVIECLIP); - seq->blend_mode = SEQ_TYPE_ALPHAOVER; seq->clip = load_data->clip; seq->len = BKE_movieclip_get_duration(load_data->clip); id_us_ensure_real((ID *)load_data->clip); @@ -167,7 +165,6 @@ Sequence *SEQ_add_mask_strip(Scene *scene, ListBase *seqbase, struct SeqLoadData { Sequence *seq = SEQ_sequence_alloc( seqbase, load_data->start_frame, load_data->channel, SEQ_TYPE_MASK); - seq->blend_mode = SEQ_TYPE_ALPHAOVER; seq->mask = load_data->mask; seq->len = BKE_mask_get_duration(load_data->mask); id_us_ensure_real((ID *)load_data->mask); @@ -191,9 +188,6 @@ Sequence *SEQ_add_effect_strip(Scene *scene, ListBase *seqbase, struct SeqLoadDa if (SEQ_effect_get_num_inputs(seq->type) == 1) { seq->blend_mode = seq->seq1->blend_mode; } - else { - seq->blend_mode = SEQ_TYPE_ALPHAOVER; - } if (!load_data->effect.seq1) { seq->len = 1; /* Effect is generator, set non zero length. */ @@ -250,7 +244,6 @@ Sequence *SEQ_add_image_strip(Main *bmain, Scene *scene, ListBase *seqbase, SeqL { Sequence *seq = SEQ_sequence_alloc( seqbase, load_data->start_frame, load_data->channel, SEQ_TYPE_IMAGE); - seq->blend_mode = SEQ_TYPE_ALPHAOVER; /* so alpha adjustment fade to the strip below */ seq->len = load_data->image.len; Strip *strip = seq->strip; strip->stripdata = MEM_callocN(load_data->image.len * sizeof(StripElem), "stripelem"); @@ -482,8 +475,6 @@ Sequence *SEQ_add_movie_strip( } } - seq->blend_mode = SEQ_TYPE_ALPHAOVER; /* so alpha adjustment fade to the strip below */ - if (anim_arr[0] != NULL) { seq->len = IMB_anim_get_duration(anim_arr[0], IMB_TC_RECORD_RUN); diff --git a/source/blender/sequencer/intern/strip_edit.c b/source/blender/sequencer/intern/strip_edit.c index 912ba9d41db..cf303e5be4e 100644 --- a/source/blender/sequencer/intern/strip_edit.c +++ b/source/blender/sequencer/intern/strip_edit.c @@ -489,21 +489,27 @@ Sequence *SEQ_edit_strip_split(Main *bmain, ListBase right_strips = {NULL, NULL}; SEQ_sequence_base_dupli_recursive(scene, scene, &right_strips, &left_strips, SEQ_DUPE_ALL, 0); - /* Split strips. */ Sequence *left_seq = left_strips.first; Sequence *right_seq = right_strips.first; - Sequence *return_seq = right_strips.first; + Sequence *return_seq = NULL; - /* Strips can't be tagged while in detached `seqbase`. Collect all strips which needs to be - * deleted and delay tagging until they are moved back to `seqbase` in `Editing`. */ - SeqCollection *strips_to_delete = SEQ_collection_create(__func__); + /* Move strips from detached `ListBase`, otherwise they can't be flagged for removal, + * SEQ_time_update_sequence can fail to update meta strips and they can't be renamed. + * This is because these functions check all strips in `Editing` to manage relationships. */ + BLI_movelisttolist(seqbase, &left_strips); + BLI_movelisttolist(seqbase, &right_strips); + /* Split strips. */ while (left_seq && right_seq) { if (left_seq->startdisp >= timeline_frame) { - SEQ_collection_append_strip(left_seq, strips_to_delete); + SEQ_edit_flag_for_removal(scene, seqbase, left_seq); } if (right_seq->enddisp <= timeline_frame) { - SEQ_collection_append_strip(right_seq, strips_to_delete); + SEQ_edit_flag_for_removal(scene, seqbase, right_seq); + } + else if (return_seq == NULL) { + /* Store return value - pointer to strip that will not be removed. */ + return_seq = right_seq; } seq_edit_split_handle_strip_offsets( @@ -512,20 +518,14 @@ Sequence *SEQ_edit_strip_split(Main *bmain, right_seq = right_seq->next; } - seq = right_strips.first; - BLI_movelisttolist(seqbase, &left_strips); - BLI_movelisttolist(seqbase, &right_strips); + SEQ_edit_remove_flagged_sequences(scene, seqbase); - for (; seq; seq = seq->next) { - SEQ_ensure_unique_name(seq, scene); + /* Rename duplicated strips. */ + Sequence *seq_rename = return_seq; + for (; seq_rename; seq_rename = seq_rename->next) { + SEQ_ensure_unique_name(seq_rename, scene); } - Sequence *seq_delete; - SEQ_ITERATOR_FOREACH (seq_delete, strips_to_delete) { - SEQ_edit_flag_for_removal(scene, seqbase, seq_delete); - } - SEQ_edit_remove_flagged_sequences(scene, seqbase); - SEQ_collection_free(strips_to_delete); return return_seq; } diff --git a/source/blender/sequencer/intern/strip_time.c b/source/blender/sequencer/intern/strip_time.c index 3228277ce72..31ee20cb6ca 100644 --- a/source/blender/sequencer/intern/strip_time.c +++ b/source/blender/sequencer/intern/strip_time.c @@ -193,6 +193,10 @@ static void seq_time_update_meta_strip(Scene *scene, Sequence *seq_meta) void SEQ_time_update_meta_strip_range(Scene *scene, Sequence *seq_meta) { + if (seq_meta == NULL) { + return; + } + seq_time_update_meta_strip(scene, seq_meta); /* Prevent meta-strip to move in timeline. */ diff --git a/source/blender/sequencer/intern/utils.c b/source/blender/sequencer/intern/utils.c index cd779b0b0c7..156c6ac4cb9 100644 --- a/source/blender/sequencer/intern/utils.c +++ b/source/blender/sequencer/intern/utils.c @@ -446,7 +446,7 @@ Sequence *SEQ_get_meta_by_seqbase(ListBase *seqbase_main, ListBase *meta_seqbase { SeqCollection *strips = SEQ_query_all_strips_recursive(seqbase_main); - Sequence *seq; + Sequence *seq = NULL; SEQ_ITERATOR_FOREACH (seq, strips) { if (seq->type == SEQ_TYPE_META && &seq->seqbase == meta_seqbase) { break; diff --git a/source/tools b/source/tools -Subproject b22d19e47f4d0353082f3d9f30ee8d244c5266d +Subproject 26bc78162ec89f21453ce3ded7b999bc6649f32 |