Blender V5.0
add_modifier_assets.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
5#include "AS_asset_catalog.hh"
7#include "AS_asset_library.hh"
9
10#include "BLI_listbase.h"
12#include "BLI_string_utf8.h"
13
14#include "DNA_modifier_types.h"
15#include "DNA_screen_types.h"
16#include "DNA_space_types.h"
17
18#include "BKE_asset.hh"
19#include "BKE_context.hh"
20#include "BKE_idprop.hh"
21#include "BKE_lib_id.hh"
22#include "BKE_main.hh"
23#include "BKE_modifier.hh"
24#include "BKE_report.hh"
25#include "BKE_screen.hh"
26
27#include "BLT_translation.hh"
28
29#include "RNA_access.hh"
30
31#include "ED_asset.hh"
33#include "ED_object.hh"
34#include "ED_screen.hh"
35
36#include "MOD_nodes.hh"
37
38#include "UI_interface.hh"
40
41#include "WM_api.hh"
42
43#include "object_intern.hh"
44
45namespace blender::ed::object {
46
48{
50 return asset::list::is_loaded(&all_library_ref);
51}
52
54{
55 asset::AssetFilterSettings type_filter{};
56 type_filter.id_types = FILTER_ID_NT;
57 auto meta_data_filter = [&](const AssetMetaData &meta_data) {
58 const IDProperty *tree_type = BKE_asset_metadata_idprop_find(&meta_data, "type");
59 if (tree_type == nullptr || IDP_int_get(tree_type) != NTREE_GEOMETRY) {
60 return false;
61 }
62 const IDProperty *traits_flag = BKE_asset_metadata_idprop_find(
63 &meta_data, "geometry_node_asset_traits_flag");
64 if (traits_flag == nullptr || !(IDP_int_get(traits_flag) & GEO_NODE_ASSET_MODIFIER)) {
65 return false;
66 }
67 return true;
68 };
71 return asset::build_filtered_all_catalog_tree(library, C, type_filter, meta_data_filter);
72}
73
79
80static void catalog_assets_draw(const bContext *C, Menu *menu)
81{
83
84 const std::optional<StringRefNull> menu_path = CTX_data_string_get(C, "asset_catalog_path");
85 if (!menu_path) {
86 return;
87 }
88 const int skip_essentials = CTX_data_int_get(C, "skip_essentials").value_or(0);
89 const Span<asset_system::AssetRepresentation *> assets = tree.assets_per_path.lookup(
90 menu_path->data());
91 const asset_system::AssetCatalogTreeItem *catalog_item = tree.catalogs.find_item(
92 menu_path->data());
93 BLI_assert(catalog_item != nullptr);
94
95 if (assets.is_empty() && !catalog_item->has_children()) {
96 return;
97 }
98
99 uiLayout *layout = menu->layout;
100
101 bool first = true;
102 const auto ensure_separator = [&]() {
103 if (first) {
104 layout->separator();
105 first = false;
106 }
107 };
108
109 wmOperatorType *ot = WM_operatortype_find("OBJECT_OT_modifier_add_node_group", true);
110 for (const asset_system::AssetRepresentation *asset : assets) {
111 if (skip_essentials) {
112 if (asset->owner_asset_library().library_reference()->type == ASSET_LIBRARY_ESSENTIALS) {
113 continue;
114 }
115 }
116 ensure_separator();
117 PointerRNA props_ptr = layout->op(
118 ot, IFACE_(asset->get_name()), ICON_NONE, wm::OpCallContext::InvokeDefault, UI_ITEM_NONE);
120 }
121
122 catalog_item->foreach_child([&](const asset_system::AssetCatalogTreeItem &item) {
123 ensure_separator();
124 asset::draw_menu_for_catalog(item, "OBJECT_MT_add_modifier_catalog_assets", *layout);
125 });
126}
127
128static bool unassigned_local_poll(const Main &bmain)
129{
130 LISTBASE_FOREACH (const bNodeTree *, group, &bmain.nodetrees) {
131 /* Assets are displayed in other menus, and non-local data-blocks aren't added to this menu. */
132 if (group->id.library_weak_reference || group->id.asset_data) {
133 continue;
134 }
135 if (!group->geometry_node_asset_traits ||
136 !(group->geometry_node_asset_traits->flag & GEO_NODE_ASSET_MODIFIER))
137 {
138 continue;
139 }
140 return true;
141 }
142 return false;
143}
144
145static void unassigned_assets_draw(const bContext *C, Menu *menu)
146{
147 Main &bmain = *CTX_data_main(C);
149 uiLayout *layout = menu->layout;
150 wmOperatorType *ot = WM_operatortype_find("OBJECT_OT_modifier_add_node_group", true);
151 for (const asset_system::AssetRepresentation *asset : tree.unassigned_assets) {
152 PointerRNA props_ptr = layout->op(
153 ot, IFACE_(asset->get_name()), ICON_NONE, wm::OpCallContext::InvokeDefault, UI_ITEM_NONE);
155 }
156
157 bool first = true;
158 bool add_separator = !tree.unassigned_assets.is_empty();
159 LISTBASE_FOREACH (const bNodeTree *, group, &bmain.nodetrees) {
160 /* Assets are displayed in other menus, and non-local data-blocks aren't added to this menu. */
161 if (group->id.library_weak_reference || group->id.asset_data) {
162 continue;
163 }
164 if (!group->geometry_node_asset_traits ||
165 !(group->geometry_node_asset_traits->flag & GEO_NODE_ASSET_MODIFIER))
166 {
167 continue;
168 }
169
170 if (add_separator) {
171 layout->separator();
172 add_separator = false;
173 }
174 if (first) {
175 layout->label(IFACE_("Non-Assets"), ICON_NONE);
176 first = false;
177 }
178
179 PointerRNA props_ptr = layout->op(
180 ot, group->id.name + 2, ICON_NONE, wm::OpCallContext::InvokeDefault, UI_ITEM_NONE);
181 WM_operator_properties_id_lookup_set_from_id(&props_ptr, &group->id);
182 }
183}
184
185static void root_catalogs_draw(const bContext *C, Menu *menu)
186{
187 const Object *object = context_active_object(C);
188 if (!object) {
189 return;
190 }
191 uiLayout *layout = menu->layout;
192
193 const bool loading_finished = all_loading_finished();
194
197 if (tree.catalogs.is_empty() && loading_finished) {
198 return;
199 }
200
201 layout->separator();
202
203 if (!loading_finished) {
204 layout->label(IFACE_("Loading Asset Libraries"), ICON_INFO);
205 }
206
207 Set<std::string> all_builtin_menus = [&]() {
208 Set<std::string> menus;
210 menus.add_new("Edit");
211 }
213 menus.add_new("Generate");
214 }
216 menus.add_new("Deform");
217 }
218 if (ELEM(object->type, OB_MESH)) {
219 menus.add_new("Normals");
220 }
222 menus.add_new("Physics");
223 }
224 return menus;
225 }();
226
227 tree.catalogs.foreach_root_item([&](const asset_system::AssetCatalogTreeItem &item) {
228 if (!all_builtin_menus.contains(item.get_name())) {
229 asset::draw_menu_for_catalog(item, "OBJECT_MT_add_modifier_catalog_assets", *layout);
230 }
231 });
232
233 if (!tree.unassigned_assets.is_empty() || unassigned_local_poll(*CTX_data_main(C))) {
234 layout->separator();
235 layout->menu(
236 "OBJECT_MT_add_modifier_unassigned_assets", IFACE_("Unassigned"), ICON_FILE_HIDDEN);
237 }
238}
239
242 ReportList *reports)
243{
244 Main &bmain = *CTX_data_main(&C);
245 if (bNodeTree *group = reinterpret_cast<bNodeTree *>(
247 {
248 return group;
249 }
250
253 if (!asset) {
254 return nullptr;
255 }
256 return reinterpret_cast<bNodeTree *>(asset::asset_local_id_ensure_imported(bmain, *asset));
257}
258
260{
261 bNodeTree *node_group = get_asset_or_local_node_group(C, ptr, reports);
262 if (!node_group) {
263 return nullptr;
264 }
265 if (node_group->type != NTREE_GEOMETRY) {
266 if (reports) {
267 BKE_report(reports, RPT_ERROR, "Asset is not a geometry node group");
268 }
269 return nullptr;
270 }
271 return node_group;
272}
273
275{
276 Main *bmain = CTX_data_main(C);
277 Scene *scene = CTX_data_scene(C);
278
280 if (objects.is_empty()) {
281 return OPERATOR_CANCELLED;
282 }
283
284 bNodeTree *node_group = get_node_group(*C, *op->ptr, op->reports);
285 if (!node_group) {
286 return OPERATOR_CANCELLED;
287 }
288
289 bool changed = false;
290 for (const PointerRNA &ptr : objects) {
291 Object *object = static_cast<Object *>(ptr.data);
292 NodesModifierData *nmd = reinterpret_cast<NodesModifierData *>(
293 modifier_add(op->reports, bmain, scene, object, nullptr, eModifierType_Nodes));
294 if (!nmd) {
295 continue;
296 }
297 changed = true;
298 nmd->node_group = node_group;
299 id_us_plus(&node_group->id);
300 MOD_nodes_update_interface(object, nmd);
301
302 /* Don't show the data-block selector since it's not usually necessary for assets. */
305 node_group->geometry_node_asset_traits &&
306 (node_group->geometry_node_asset_traits->flag &
309
310 STRNCPY_UTF8(nmd->modifier.name, DATA_(node_group->id.name + 2));
311 BKE_modifier_unique_name(&object->modifiers, &nmd->modifier);
312
314 }
315
316 if (!changed) {
317 return OPERATOR_CANCELLED;
318 }
319
320 return OPERATOR_FINISHED;
321}
322
324 wmOperator *op,
325 const wmEvent *event)
326{
327 if (event->modifier & KM_ALT || CTX_wm_view3d(C)) {
328 RNA_boolean_set(op->ptr, "use_selected_objects", true);
329 }
330 return modifier_add_asset_exec(C, op);
331}
332
334 wmOperatorType * /*ot*/,
336{
339 if (!asset) {
340 return "";
341 }
342 if (!asset->get_metadata().description) {
343 return "";
344 }
345 return TIP_(asset->get_metadata().description);
346}
347
349{
350 ot->name = "Add Modifier";
351 ot->description = "Add a procedural operation/effect to the active object";
352 ot->idname = "OBJECT_OT_modifier_add_node_group";
353
357 ot->get_description = modifier_add_asset_get_description;
358
360
364}
365
367{
368 MenuType type{};
369 STRNCPY_UTF8(type.idname, "OBJECT_MT_add_modifier_unassigned_assets");
372 type.description = N_(
373 "Modifier node group assets not assigned to a catalog.\n"
374 "Catalogs can be assigned in the Asset Browser");
375 return type;
376}
377
379{
380 MenuType type{};
381 STRNCPY_UTF8(type.idname, "OBJECT_MT_add_modifier_catalog_assets");
385 return type;
386}
387
389{
390 MenuType type{};
391 STRNCPY_UTF8(type.idname, "OBJECT_MT_modifier_add_root_catalogs");
395 return type;
396}
397
405
407 const StringRef catalog_path,
408 const bool skip_essentials)
409{
411 const asset_system::AssetCatalogTreeItem *item = tree.catalogs.find_root_item(catalog_path);
412 if (!item) {
413 return;
414 }
417 if (!all_library) {
418 return;
419 }
420 uiLayout *col = &layout.column(false);
421 col->context_string_set("asset_catalog_path", item->catalog_path().str());
422 col->context_int_set("skip_essentials", skip_essentials);
423 col->menu_contents("OBJECT_MT_add_modifier_catalog_assets");
424}
425
426} // namespace blender::ed::object
Main runtime representation of an asset.
IDProperty * BKE_asset_metadata_idprop_find(const AssetMetaData *asset_data, const char *name) ATTR_WARN_UNUSED_RESULT
Definition asset.cc:179
std::optional< int64_t > CTX_data_int_get(const bContext *C, const char *member)
Scene * CTX_data_scene(const bContext *C)
Main * CTX_data_main(const bContext *C)
std::optional< blender::StringRefNull > CTX_data_string_get(const bContext *C, const char *member)
View3D * CTX_wm_view3d(const bContext *C)
#define IDP_int_get(prop)
void id_us_plus(ID *id)
Definition lib_id.cc:358
void BKE_modifier_unique_name(ListBase *modifiers, ModifierData *md)
@ RPT_ERROR
Definition BKE_report.hh:39
void BKE_report(ReportList *reports, eReportType type, const char *message)
Definition report.cc:153
#define BLI_assert(a)
Definition BLI_assert.h:46
#define LISTBASE_FOREACH(type, var, list)
#define STRNCPY_UTF8(dst, src)
#define SET_FLAG_FROM_TEST(value, test, flag)
#define ELEM(...)
#define TIP_(msgid)
#define IFACE_(msgid)
#define DATA_(msgid)
#define FILTER_ID_NT
Definition DNA_ID.h:1213
@ ID_NT
@ ASSET_LIBRARY_ESSENTIALS
@ NODES_MODIFIER_HIDE_DATABLOCK_SELECTOR
@ NODES_MODIFIER_HIDE_MANAGE_PANEL
@ eModifierType_Nodes
@ GEO_NODE_ASSET_MODIFIER
@ GEO_NODE_ASSET_HIDE_MODIFIER_MANAGE_PANEL
@ NTREE_GEOMETRY
@ OB_LATTICE
@ OB_SURF
@ OB_FONT
@ OB_MESH
@ OB_VOLUME
@ OB_CURVES_LEGACY
@ OPERATOR_CANCELLED
@ OPERATOR_FINISHED
bool ED_operator_object_active_editable(bContext *C)
void MOD_nodes_update_interface(Object *object, NodesModifierData *nmd)
Definition MOD_nodes.cc:449
#define C
Definition RandGen.cpp:29
#define UI_ITEM_NONE
@ KM_ALT
Definition WM_types.hh:280
@ OPTYPE_UNDO
Definition WM_types.hh:182
@ OPTYPE_REGISTER
Definition WM_types.hh:180
#define ND_MODIFIER
Definition WM_types.hh:462
#define NC_OBJECT
Definition WM_types.hh:379
bool contains(const Key &key) const
Definition BLI_set.hh:310
void add_new(const Key &key)
Definition BLI_set.hh:233
constexpr bool is_empty() const
Definition BLI_span.hh:260
bool is_empty() const
void foreach_child(ItemIterFn callback) const
KDTree_3d * tree
uint col
void * MEM_dupallocN(const void *vmemh)
Definition mallocn.cc:143
AssetLibraryReference all_library_reference()
void all_library_reload_catalogs_if_dirty()
asset_system::AssetLibrary * library_get_once_available(const AssetLibraryReference &library_reference)
bool is_loaded(const AssetLibraryReference *library_reference)
void asset_reading_region_listen_fn(const wmRegionListenerParams *params)
AssetItemTree build_filtered_all_catalog_tree(const AssetLibraryReference &library_ref, const bContext &C, const AssetFilterSettings &filter_settings, FunctionRef< bool(const AssetMetaData &)> meta_data_filter={})
void operator_asset_reference_props_register(StructRNA &srna)
void draw_menu_for_catalog(const asset_system::AssetCatalogTreeItem &item, const StringRefNull menu_name, uiLayout &layout)
const asset_system::AssetRepresentation * operator_asset_reference_props_get_asset_from_all_library(const bContext &C, PointerRNA &ptr, ReportList *reports)
void operator_asset_reference_props_set(const asset_system::AssetRepresentation &asset, PointerRNA &ptr)
ID * asset_local_id_ensure_imported(Main &bmain, const asset_system::AssetRepresentation &asset, const std::optional< eAssetImportMethod > import_method=std::nullopt)
static void root_catalogs_draw(const bContext *C, Menu *menu)
void modifier_register_use_selected_objects_prop(wmOperatorType *ot)
ModifierData * modifier_add(ReportList *reports, Main *bmain, Scene *scene, Object *ob, const char *name, int type)
static void catalog_assets_draw(const bContext *C, Menu *menu)
static wmOperatorStatus modifier_add_asset_invoke(bContext *C, wmOperator *op, const wmEvent *event)
static bNodeTree * get_asset_or_local_node_group(const bContext &C, PointerRNA &ptr, ReportList *reports)
static void OBJECT_OT_modifier_add_node_group(wmOperatorType *ot)
static wmOperatorStatus modifier_add_asset_exec(bContext *C, wmOperator *op)
static void unassigned_assets_draw(const bContext *C, Menu *menu)
static asset::AssetItemTree * get_static_item_tree()
static MenuType modifier_add_root_catalogs_menu_type()
static bNodeTree * get_node_group(const bContext &C, PointerRNA &ptr, ReportList *reports)
static bool unassigned_local_poll(const Main &bmain)
static MenuType modifier_add_unassigned_assets_menu_type()
static MenuType modifier_add_catalog_assets_menu_type()
static bool all_loading_finished()
static std::string modifier_add_asset_get_description(bContext *C, wmOperatorType *, PointerRNA *ptr)
static asset::AssetItemTree build_catalog_tree(const bContext &C)
Object * context_active_object(const bContext *C)
void ui_template_modifier_asset_menu_items(uiLayout &layout, StringRef catalog_path, bool skip_essentials)
Vector< PointerRNA > modifier_get_edit_objects(const bContext &C, const wmOperator &op)
void RNA_boolean_set(PointerRNA *ptr, const char *name, bool value)
The meta-data of an asset. By creating and giving this for a data-block (ID.asset_data),...
char name[258]
Definition DNA_ID.h:432
ListBase nodetrees
Definition BKE_main.hh:301
MenuTypeFlag flag
const char * description
char idname[BKE_ST_MAXNAME]
void(* draw)(const bContext *C, Menu *menu)
void(* listener)(const wmRegionListenerParams *params)
uiLayout * layout
struct bNodeTree * node_group
struct GeometryNodeAssetTraits * geometry_node_asset_traits
void label(blender::StringRef name, int icon)
uiLayout & column(bool align)
void separator(float factor=1.0f, LayoutSeparatorType type=LayoutSeparatorType::Auto)
PointerRNA op(wmOperatorType *ot, std::optional< blender::StringRef > name, int icon, blender::wm::OpCallContext context, eUI_Item_Flag flag)
void menu(MenuType *mt, std::optional< blender::StringRef > name, int icon)
wmEventModifierFlag modifier
Definition WM_types.hh:774
struct ReportList * reports
struct PointerRNA * ptr
#define N_(msgid)
void WM_event_add_notifier(const bContext *C, uint type, void *reference)
PointerRNA * ptr
Definition wm_files.cc:4238
wmOperatorType * ot
Definition wm_files.cc:4237
bool WM_menutype_add(MenuType *mt)
ID * WM_operator_properties_id_lookup_from_name_or_session_uid(Main *bmain, PointerRNA *ptr, const ID_Type type)
void WM_operator_properties_id_lookup(wmOperatorType *ot, const bool add_name_prop)
void WM_operator_properties_id_lookup_set_from_id(PointerRNA *ptr, const ID *id)
void WM_operatortype_append(void(*opfunc)(wmOperatorType *))
wmOperatorType * WM_operatortype_find(const char *idname, bool quiet)