Geometry Nodes: Require modifier tag for add menu, show non-assets

Any geometry node group with the "Is Modifier" tag is exposed in the add
modifier menu. Local node groups that aren't assets are displayed in a
subsection of the "Unassigned" menu, as they are "asset wannabees"
that function similarly but aren't shared to other files.

Only modifier node groups can be assigned to the geometry nodes
modifier. Because of this, existing node groups are versioned to have
the tag if they have a geometry output.

Internally, this means the operator that used to only handle node group
assets has to also add local geometry node groups. That change isn't
so large though. The other changes are just UI, or changes to the
node group property poll functions.

Note: For assets, saving the file to generate asset meta-data may be
necessary for the versioning to affect the add modifier menu.

Pull Request: https://projects.blender.org/blender/blender/pulls/112918
This commit is contained in:
Hans Goudey 2023-09-26 21:53:20 +02:00 committed by Hans Goudey
parent 2d19e345cd
commit af3461c387
5 changed files with 169 additions and 74 deletions

View File

@ -161,8 +161,6 @@ class NODE_HT_header(Header):
row.template_ID(active_modifier, "node_group", new="node.new_geometry_node_group_assign")
else:
row.template_ID(snode, "node_tree", new="node.new_geometry_nodes_modifier")
if snode.node_tree and snode.node_tree.asset_data:
layout.popover(panel="NODE_PT_geometry_node_modifier")
else:
layout.template_ID(snode, "node_tree", new="node.new_geometry_node_group_tool")
if snode.node_tree and snode.node_tree.asset_data:
@ -484,22 +482,6 @@ class NODE_PT_geometry_node_tool_mode(Panel):
row_label.label(text=name, icon=icon)
class NODE_PT_geometry_node_modifier(Panel):
bl_space_type = 'NODE_EDITOR'
bl_region_type = 'HEADER'
bl_label = "Modifier"
def draw(self, context):
layout = self.layout
layout.use_property_split = True
layout.use_property_decorate = False
snode = context.space_data
group = snode.node_tree
layout.prop(group, "is_modifier")
class NODE_PT_node_color_presets(PresetPanel, Panel):
"""Predefined node color"""
bl_label = "Color Presets"
@ -976,6 +958,38 @@ class NODE_PT_node_tree_interface(Panel):
layout.use_property_split = False
class NODE_PT_node_tree_properties(Panel):
bl_space_type = 'NODE_EDITOR'
bl_region_type = 'UI'
bl_category = "Group"
bl_label = "Properties"
@classmethod
def poll(cls, context):
snode = context.space_data
if snode is None:
return False
group = snode.edit_tree
if group is None:
return False
if group.is_embedded_data:
return False
if group.bl_idname != "GeometryNodeTree":
return False
return True
def draw(self, context):
layout = self.layout
snode = context.space_data
group = snode.edit_tree
layout.use_property_split = True
layout.use_property_decorate = False
col = layout.column()
col.prop(group, "is_modifier")
col.prop(group, "is_tool")
class NODE_UL_simulation_zone_items(bpy.types.UIList):
def draw_item(self, context, layout, _data, item, icon, _active_data, _active_propname, _index):
if self.layout_type in {'DEFAULT', 'COMPACT'}:
@ -1180,12 +1194,12 @@ classes = (
NODE_MT_context_menu,
NODE_MT_view_pie,
NODE_PT_material_slots,
NODE_PT_geometry_node_modifier,
NODE_PT_geometry_node_tool_object_types,
NODE_PT_geometry_node_tool_mode,
NODE_PT_node_color_presets,
NODE_MT_node_tree_interface_context_menu,
NODE_PT_node_tree_interface,
NODE_PT_node_tree_properties,
NODE_PT_active_node_generic,
NODE_PT_active_node_color,
NODE_PT_texture_mapping,

View File

@ -977,6 +977,34 @@ static void version_node_group_split_socket(bNodeTreeInterface &tree_interface,
csocket->flag &= ~NODE_INTERFACE_SOCKET_OUTPUT;
}
static void enable_geometry_nodes_is_modifier(Main &bmain)
{
/* Any node group with a first socket geometry output can potentially be a modifier. Previously
* this wasn't an explicit option, so better to enable too many groups rather than too few. */
LISTBASE_FOREACH (bNodeTree *, group, &bmain.nodetrees) {
if (group->type != NTREE_GEOMETRY) {
continue;
}
group->tree_interface.foreach_item([&](const bNodeTreeInterfaceItem &item) {
if (item.item_type != NODE_INTERFACE_SOCKET) {
return true;
}
const auto &socket = reinterpret_cast<const bNodeTreeInterfaceSocket &>(item);
if ((socket.flag & NODE_INTERFACE_SOCKET_OUTPUT) == 0) {
return true;
}
if (!STREQ(socket.socket_type, "NodeSocketGeometry")) {
return true;
}
if (!group->geometry_node_asset_traits) {
group->geometry_node_asset_traits = MEM_new<GeometryNodeAssetTraits>(__func__);
}
group->geometry_node_asset_traits->flag |= GEO_NODE_ASSET_MODIFIER;
return false;
});
}
}
void blo_do_versions_400(FileData *fd, Library * /*lib*/, Main *bmain)
{
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 400, 1)) {
@ -1443,6 +1471,15 @@ void blo_do_versions_400(FileData *fd, Library * /*lib*/, Main *bmain)
FOREACH_NODETREE_END;
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 400, 26)) {
enable_geometry_nodes_is_modifier(*bmain);
LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) {
scene->simulation_frame_start = scene->r.sfra;
scene->simulation_frame_end = scene->r.efra;
}
}
/**
* Versioning code until next subversion bump goes here.
*
@ -1455,10 +1492,5 @@ void blo_do_versions_400(FileData *fd, Library * /*lib*/, Main *bmain)
*/
{
/* Keep this block, even when empty. */
LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) {
scene->simulation_frame_start = scene->r.sfra;
scene->simulation_frame_end = scene->r.efra;
}
}
}

View File

@ -18,6 +18,7 @@
#include "BKE_context.h"
#include "BKE_idprop.h"
#include "BKE_lib_id.h"
#include "BKE_main.h"
#include "BKE_report.h"
#include "BKE_screen.hh"
@ -95,7 +96,7 @@ static void catalog_assets_draw(const bContext *C, Menu *menu)
uiLayout *layout = menu->layout;
uiItemS(layout);
wmOperatorType *ot = WM_operatortype_find("OBJECT_OT_modifier_add_asset", true);
wmOperatorType *ot = WM_operatortype_find("OBJECT_OT_modifier_add_node_group", true);
for (const asset_system::AssetRepresentation *asset : assets) {
PointerRNA props_ptr;
uiItemFullO_ptr(layout,
@ -121,11 +122,29 @@ static void catalog_assets_draw(const bContext *C, Menu *menu)
});
}
static void unassigned_assets_draw(const bContext * /*C*/, Menu *menu)
static bool unassigned_local_poll(const Main &bmain)
{
LISTBASE_FOREACH (const bNodeTree *, group, &bmain.nodetrees) {
/* Assets are displayed in other menus, and non-local data-blocks aren't added to this menu. */
if (group->id.library_weak_reference || group->id.asset_data) {
continue;
}
if (!group->geometry_node_asset_traits ||
!(group->geometry_node_asset_traits->flag & GEO_NODE_ASSET_MODIFIER))
{
continue;
}
return true;
}
return false;
}
static void unassigned_assets_draw(const bContext *C, Menu *menu)
{
Main &bmain = *CTX_data_main(C);
asset::AssetItemTree &tree = *get_static_item_tree();
uiLayout *layout = menu->layout;
wmOperatorType *ot = WM_operatortype_find("OBJECT_OT_modifier_add_asset", true);
wmOperatorType *ot = WM_operatortype_find("OBJECT_OT_modifier_add_node_group", true);
for (const asset_system::AssetRepresentation *asset : tree.unassigned_assets) {
PointerRNA props_ptr;
uiItemFullO_ptr(layout,
@ -138,6 +157,37 @@ static void unassigned_assets_draw(const bContext * /*C*/, Menu *menu)
&props_ptr);
asset::operator_asset_reference_props_set(*asset, props_ptr);
}
bool add_separator = !tree.unassigned_assets.is_empty();
LISTBASE_FOREACH (const bNodeTree *, group, &bmain.nodetrees) {
/* Assets are displayed in other menus, and non-local data-blocks aren't added to this menu. */
if (group->id.library_weak_reference || group->id.asset_data) {
continue;
}
if (!group->geometry_node_asset_traits ||
!(group->geometry_node_asset_traits->flag & GEO_NODE_ASSET_MODIFIER))
{
continue;
}
if (add_separator) {
uiItemS(layout);
uiItemL(layout, IFACE_("Non-Assets"), ICON_NONE);
add_separator = false;
}
PointerRNA props_ptr;
uiItemFullO_ptr(layout,
ot,
group->id.name + 2,
ICON_NONE,
nullptr,
WM_OP_INVOKE_DEFAULT,
UI_ITEM_NONE,
&props_ptr);
WM_operator_properties_id_lookup_set_from_id(&props_ptr, &group->id);
}
}
static void root_catalogs_draw(const bContext *C, Menu *menu)
@ -193,7 +243,7 @@ static void root_catalogs_draw(const bContext *C, Menu *menu)
}
});
if (!tree.unassigned_assets.is_empty()) {
if (!tree.unassigned_assets.is_empty() || unassigned_local_poll(*CTX_data_main(C))) {
uiItemS(layout);
uiItemM(layout,
"OBJECT_MT_add_modifier_unassigned_assets",
@ -202,16 +252,28 @@ static void root_catalogs_draw(const bContext *C, Menu *menu)
}
}
static bNodeTree *get_node_group(const bContext &C, PointerRNA &ptr, ReportList *reports)
static bNodeTree *get_asset_or_local_node_group(const bContext &C,
PointerRNA &ptr,
ReportList *reports)
{
Main &bmain = *CTX_data_main(&C);
if (bNodeTree *group = reinterpret_cast<bNodeTree *>(
WM_operator_properties_id_lookup_from_name_or_session_uuid(&bmain, &ptr, ID_NT)))
{
return group;
}
const asset_system::AssetRepresentation *asset =
asset::operator_asset_reference_props_get_asset_from_all_library(C, ptr, reports);
if (!asset) {
return nullptr;
}
Main &bmain = *CTX_data_main(&C);
bNodeTree *node_group = reinterpret_cast<bNodeTree *>(
asset::asset_local_id_ensure_imported(bmain, *asset));
return reinterpret_cast<bNodeTree *>(asset::asset_local_id_ensure_imported(bmain, *asset));
}
static bNodeTree *get_node_group(const bContext &C, PointerRNA &ptr, ReportList *reports)
{
bNodeTree *node_group = get_asset_or_local_node_group(C, ptr, reports);
if (!node_group) {
return nullptr;
}
@ -270,11 +332,11 @@ static std::string modifier_add_asset_get_description(bContext *C,
return TIP_(asset->get_metadata().description);
}
static void OBJECT_OT_modifier_add_asset(wmOperatorType *ot)
static void OBJECT_OT_modifier_add_node_group(wmOperatorType *ot)
{
ot->name = "Add Modifier";
ot->description = "Add a procedural operation/effect to the active object";
ot->idname = "OBJECT_OT_modifier_add_asset";
ot->idname = "OBJECT_OT_modifier_add_node_group";
ot->exec = modifier_add_asset_exec;
ot->poll = ED_operator_object_active_editable;
@ -283,6 +345,7 @@ static void OBJECT_OT_modifier_add_asset(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
asset::operator_asset_reference_props_register(*ot->srna);
WM_operator_properties_id_lookup(ot, false);
}
static MenuType modifier_add_unassigned_assets_menu_type()
@ -291,7 +354,6 @@ static MenuType modifier_add_unassigned_assets_menu_type()
STRNCPY(type.idname, "OBJECT_MT_add_modifier_unassigned_assets");
type.draw = unassigned_assets_draw;
type.listener = asset::asset_reading_region_listen_fn;
type.flag = MenuTypeFlag::ContextDependent;
type.description = N_(
"Modifier node group assets not assigned to a catalog.\n"
"Catalogs can be assigned in the Asset Browser");
@ -323,7 +385,7 @@ void object_modifier_add_asset_register()
WM_menutype_add(MEM_new<MenuType>(__func__, modifier_add_catalog_assets_menu_type()));
WM_menutype_add(MEM_new<MenuType>(__func__, modifier_add_unassigned_assets_menu_type()));
WM_menutype_add(MEM_new<MenuType>(__func__, modifier_add_root_catalogs_menu_type()));
WM_operatortype_append(OBJECT_OT_modifier_add_asset);
WM_operatortype_append(OBJECT_OT_modifier_add_node_group);
}
void ui_template_modifier_asset_menu_items(uiLayout &layout,

View File

@ -1679,18 +1679,10 @@ static bool rna_NodesModifier_node_group_poll(PointerRNA * /*ptr*/, PointerRNA v
if (ntree->type != NTREE_GEOMETRY) {
return false;
}
if (ntree->id.asset_data) {
if (!ntree->geometry_node_asset_traits ||
(ntree->geometry_node_asset_traits->flag & GEO_NODE_ASSET_MODIFIER) == 0)
{
/* Only node group assets specially marked as modifiers can be modifiers. */
return false;
}
if (!ntree->geometry_node_asset_traits) {
return false;
}
if (ntree->geometry_node_asset_traits &&
ntree->geometry_node_asset_traits->flag & GEO_NODE_ASSET_TOOL)
{
/* Tool node groups cannot be modifiers. */
if ((ntree->geometry_node_asset_traits->flag & GEO_NODE_ASSET_MODIFIER) == 0) {
return false;
}
return true;

View File

@ -2507,30 +2507,27 @@ static void rna_SpaceNodeEditor_node_tree_set(PointerRNA *ptr,
ED_node_tree_start(snode, (bNodeTree *)value.data, nullptr, nullptr);
}
static bool space_node_node_geometry_nodes_tool_poll(const SpaceNode &snode,
const bNodeTree &ntree)
static bool space_node_node_geometry_nodes_poll(const SpaceNode &snode, const bNodeTree &ntree)
{
if (snode.geometry_nodes_type == SNODE_GEOMETRY_TOOL) {
if (!ntree.id.asset_data) {
/* Only assets can be tools. */
return false;
}
if (!ntree.geometry_node_asset_traits ||
(ntree.geometry_node_asset_traits->flag & GEO_NODE_ASSET_TOOL) == 0)
{
/* Only node groups specifically marked as tools can be tools. */
return false;
}
switch (SpaceNodeGeometryNodesType(snode.geometry_nodes_type)) {
case SNODE_GEOMETRY_MODIFIER:
if (!ntree.geometry_node_asset_traits) {
return false;
}
if ((ntree.geometry_node_asset_traits->flag & GEO_NODE_ASSET_MODIFIER) == 0) {
return false;
}
return true;
case SNODE_GEOMETRY_TOOL:
if (!ntree.geometry_node_asset_traits) {
return false;
}
if ((ntree.geometry_node_asset_traits->flag & GEO_NODE_ASSET_TOOL) == 0) {
return false;
}
return true;
}
else {
if (ntree.geometry_node_asset_traits &&
ntree.geometry_node_asset_traits->flag & GEO_NODE_ASSET_TOOL)
{
/* Tool node groups cannot be modifiers. */
return false;
}
}
return true;
return false;
}
static bool rna_SpaceNodeEditor_node_tree_poll(PointerRNA *ptr, const PointerRNA value)
@ -2543,10 +2540,8 @@ static bool rna_SpaceNodeEditor_node_tree_poll(PointerRNA *ptr, const PointerRNA
return false;
}
if (ntree->type == NTREE_GEOMETRY) {
if (snode->geometry_nodes_type == SNODE_GEOMETRY_TOOL) {
if (!space_node_node_geometry_nodes_tool_poll(*snode, *ntree)) {
return false;
}
if (!space_node_node_geometry_nodes_poll(*snode, *ntree)) {
return false;
}
}
return true;
@ -2559,7 +2554,7 @@ static void rna_SpaceNodeEditor_geometry_nodes_type_update(Main * /*bmain*/,
SpaceNode &snode = *static_cast<SpaceNode *>(ptr->data);
if (snode.nodetree) {
if (snode.nodetree->type == NTREE_GEOMETRY) {
if (!space_node_node_geometry_nodes_tool_poll(snode, *snode.nodetree)) {
if (!space_node_node_geometry_nodes_poll(snode, *snode.nodetree)) {
snode.nodetree = nullptr;
}
}