Geometry Nodes: Support node tools in object mode

Extend node tools to display tool assets in object mode as well
as edit and sculpt modes. For consistency with existing Blender
design, selection cannot be set and is just "true" in object mode
because it can't be visualized. The visibility of tools can be
customized per object type in object mode as well.

See #101778

Pull Request: https://projects.blender.org/blender/blender/pulls/114819
This commit is contained in:
Hans Goudey 2023-11-15 17:01:18 +01:00 committed by Hans Goudey
parent 6d18fe76b5
commit 3fcd9c9425
9 changed files with 220 additions and 67 deletions

View File

@ -55,11 +55,13 @@ def geometry_node_group_empty_tool_new(context):
else:
group.is_type_mesh = True
mode = context.object.mode if context.object else 'EDIT'
mode = context.object.mode if context.object else 'OBJECT'
if mode in {'SCULPT', 'SCULPT_CURVES'}:
group.is_mode_sculpt = True
else:
elif mode == 'EDIT':
group.is_mode_edit = True
else:
group.is_mode_object = True
return group

View File

@ -472,6 +472,7 @@ class NODE_PT_geometry_node_tool_mode(Panel):
group = snode.node_tree
modes = (
("is_mode_object", "Object Mode", 'OBJECT_DATAMODE'),
("is_mode_edit", "Edit Mode", 'EDITMODE_HLT'),
("is_mode_sculpt", "Sculpt Mode", 'SCULPTMODE_HLT'),
)

View File

@ -1169,9 +1169,12 @@ class VIEW3D_MT_editor_menus(Menu):
layout.menu("VIEW3D_MT_select_sculpt_curves")
layout.menu("VIEW3D_MT_sculpt_curves")
layout.template_node_operator_asset_root_items()
else:
layout.template_node_operator_asset_root_items()
else:
layout.menu("VIEW3D_MT_object")
layout.template_node_operator_asset_root_items()
# ********** Menu **********
@ -2743,6 +2746,8 @@ class VIEW3D_MT_object(Menu):
layout.operator("object.delete", text="Delete").use_global = False
layout.operator("object.delete", text="Delete Global").use_global = True
layout.template_node_operator_asset_menu_items(catalog_path="Object")
class VIEW3D_MT_object_animation(Menu):
bl_label = "Animation"
@ -3021,6 +3026,8 @@ class VIEW3D_MT_object_context_menu(Menu):
layout.operator_context = 'EXEC_REGION_WIN'
layout.operator("object.delete", text="Delete").use_global = False
layout.template_node_operator_asset_menu_items(catalog_path="Object")
class VIEW3D_MT_object_shading(Menu):
# XXX, this menu is a place to store shading operator in object mode
@ -3098,6 +3105,8 @@ class VIEW3D_MT_object_apply(Menu):
text="Parent Inverse",
text_ctxt=i18n_contexts.default)
layout.template_node_operator_asset_menu_items(catalog_path="Object/Apply")
class VIEW3D_MT_object_parent(Menu):
bl_label = "Parent"
@ -3180,6 +3189,7 @@ class VIEW3D_MT_object_quick_effects(Menu):
layout.operator("object.quick_explode")
layout.operator("object.quick_smoke")
layout.operator("object.quick_liquid")
layout.template_node_operator_asset_menu_items(catalog_path="Object/Quick Effects")
class VIEW3D_MT_object_showhide(Menu):
@ -3271,6 +3281,8 @@ class VIEW3D_MT_object_convert(Menu):
if ob and ob.type == 'CURVES':
layout.operator("curves.convert_to_particle_system", text="Particle System")
layout.template_node_operator_asset_menu_items(catalog_path="Object/Convert")
class VIEW3D_MT_make_links(Menu):

View File

@ -287,6 +287,39 @@ static IDProperty *replace_inputs_evaluated_data_blocks(const IDProperty &op_pro
return properties;
}
static bool object_has_editable_data(const Main &bmain, const Object &object)
{
if (!ELEM(object.type, OB_CURVES, OB_POINTCLOUD, OB_MESH)) {
return false;
}
if (!BKE_id_is_editable(&bmain, static_cast<const ID *>(object.data))) {
return false;
}
return true;
}
static Vector<Object *> gather_supported_objects(const bContext &C,
const Main &bmain,
const eObjectMode mode)
{
Vector<Object *> objects;
Set<const ID *> unique_object_data;
CTX_DATA_BEGIN (&C, Object *, object, selected_objects) {
if (object->mode != mode) {
continue;
}
if (!unique_object_data.add(static_cast<const ID *>(object->data))) {
continue;
}
if (!object_has_editable_data(bmain, *object)) {
continue;
}
objects.append(object);
}
CTX_DATA_END;
return objects;
}
static int run_node_group_exec(bContext *C, wmOperator *op)
{
Main *bmain = CTX_data_main(C);
@ -296,9 +329,6 @@ static int run_node_group_exec(bContext *C, wmOperator *op)
if (!active_object) {
return OPERATOR_CANCELLED;
}
if (active_object->mode == OB_MODE_OBJECT) {
return OPERATOR_CANCELLED;
}
const eObjectMode mode = eObjectMode(active_object->mode);
const bNodeTree *node_tree_orig = get_node_group(*C, *op->ptr, op->reports);
@ -306,13 +336,10 @@ static int run_node_group_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_mode_unique_data(
scene, view_layer, CTX_wm_view3d(C), &objects_len, mode);
BLI_SCOPED_DEFER([&]() { MEM_SAFE_FREE(objects); });
const Vector<Object *> objects = gather_supported_objects(*C, *bmain, mode);
Depsgraph *depsgraph = build_depsgraph_from_indirect_ids(
*bmain, *scene, *view_layer, *node_tree_orig, {objects, objects_len}, *op->properties);
*bmain, *scene, *view_layer, *node_tree_orig, objects, *op->properties);
DEG_evaluate_on_refresh(depsgraph);
BLI_SCOPED_DEFER([&]() { DEG_graph_free(depsgraph); });
@ -348,11 +375,9 @@ static int run_node_group_exec(bContext *C, wmOperator *op)
OperatorComputeContext compute_context(op->type->idname);
for (Object *object : Span(objects, objects_len)) {
if (!ELEM(object->type, OB_CURVES, OB_POINTCLOUD, OB_MESH)) {
continue;
}
for (Object *object : objects) {
nodes::GeoNodesOperatorData operator_eval_data{};
operator_eval_data.mode = mode;
operator_eval_data.depsgraph = depsgraph;
operator_eval_data.self_object = DEG_get_evaluated_object(depsgraph, object);
operator_eval_data.scene = DEG_get_evaluated_scene(depsgraph);
@ -600,72 +625,138 @@ static bool asset_menu_poll(const bContext *C, MenuType * /*mt*/)
return CTX_wm_view3d(C);
}
static GeometryNodeAssetTraitFlag asset_flag_for_context(const eContextObjectMode ctx_mode)
static GeometryNodeAssetTraitFlag asset_flag_for_context(const ObjectType type,
const eObjectMode mode)
{
switch (ctx_mode) {
case CTX_MODE_EDIT_MESH:
return (GEO_NODE_ASSET_TOOL | GEO_NODE_ASSET_EDIT | GEO_NODE_ASSET_MESH);
case CTX_MODE_EDIT_CURVES:
return (GEO_NODE_ASSET_TOOL | GEO_NODE_ASSET_EDIT | GEO_NODE_ASSET_CURVE);
case CTX_MODE_EDIT_POINT_CLOUD:
return (GEO_NODE_ASSET_TOOL | GEO_NODE_ASSET_EDIT | GEO_NODE_ASSET_POINT_CLOUD);
case CTX_MODE_SCULPT:
return (GEO_NODE_ASSET_TOOL | GEO_NODE_ASSET_SCULPT | GEO_NODE_ASSET_MESH);
case CTX_MODE_SCULPT_CURVES:
return (GEO_NODE_ASSET_TOOL | GEO_NODE_ASSET_SCULPT | GEO_NODE_ASSET_CURVE);
switch (type) {
case OB_MESH: {
switch (mode) {
case OB_MODE_OBJECT:
return (GEO_NODE_ASSET_TOOL | GEO_NODE_ASSET_OBJECT | GEO_NODE_ASSET_MESH);
case OB_MODE_EDIT:
return (GEO_NODE_ASSET_TOOL | GEO_NODE_ASSET_EDIT | GEO_NODE_ASSET_MESH);
case OB_MODE_SCULPT:
return (GEO_NODE_ASSET_TOOL | GEO_NODE_ASSET_SCULPT | GEO_NODE_ASSET_MESH);
default:
break;
}
break;
}
case OB_CURVES: {
switch (mode) {
case OB_MODE_OBJECT:
return (GEO_NODE_ASSET_TOOL | GEO_NODE_ASSET_OBJECT | GEO_NODE_ASSET_CURVE);
case OB_MODE_EDIT:
return (GEO_NODE_ASSET_TOOL | GEO_NODE_ASSET_EDIT | GEO_NODE_ASSET_CURVE);
case OB_MODE_SCULPT_CURVES:
return (GEO_NODE_ASSET_TOOL | GEO_NODE_ASSET_SCULPT | GEO_NODE_ASSET_CURVE);
default:
break;
}
break;
}
case OB_POINTCLOUD: {
switch (mode) {
case OB_MODE_OBJECT:
return (GEO_NODE_ASSET_TOOL | GEO_NODE_ASSET_OBJECT | GEO_NODE_ASSET_POINT_CLOUD);
case OB_MODE_EDIT:
return (GEO_NODE_ASSET_TOOL | GEO_NODE_ASSET_EDIT | GEO_NODE_ASSET_POINT_CLOUD);
default:
break;
}
break;
}
default:
BLI_assert_unreachable();
return GeometryNodeAssetTraitFlag(0);
break;
}
BLI_assert_unreachable();
return GeometryNodeAssetTraitFlag(0);
}
static asset::AssetItemTree *get_static_item_tree(const eContextObjectMode mode)
static GeometryNodeAssetTraitFlag asset_flag_for_context(const Object &active_object)
{
switch (mode) {
case CTX_MODE_EDIT_MESH: {
static asset::AssetItemTree tree;
return &tree;
return asset_flag_for_context(ObjectType(active_object.type), eObjectMode(active_object.mode));
}
static asset::AssetItemTree *get_static_item_tree(const ObjectType type, const eObjectMode mode)
{
switch (type) {
case OB_MESH: {
switch (mode) {
case OB_MODE_OBJECT: {
static asset::AssetItemTree tree;
return &tree;
}
case OB_MODE_EDIT: {
static asset::AssetItemTree tree;
return &tree;
}
case OB_MODE_SCULPT: {
static asset::AssetItemTree tree;
return &tree;
}
default:
return nullptr;
}
}
case CTX_MODE_EDIT_CURVES: {
static asset::AssetItemTree tree;
return &tree;
case OB_CURVES: {
switch (mode) {
case OB_MODE_OBJECT: {
static asset::AssetItemTree tree;
return &tree;
}
case OB_MODE_EDIT: {
static asset::AssetItemTree tree;
return &tree;
}
case OB_MODE_SCULPT_CURVES: {
static asset::AssetItemTree tree;
return &tree;
}
default:
return nullptr;
}
}
case CTX_MODE_EDIT_POINT_CLOUD: {
static asset::AssetItemTree tree;
return &tree;
}
case CTX_MODE_SCULPT: {
static asset::AssetItemTree tree;
return &tree;
}
case CTX_MODE_SCULPT_CURVES: {
static asset::AssetItemTree tree;
return &tree;
case OB_POINTCLOUD: {
switch (mode) {
case OB_MODE_OBJECT: {
static asset::AssetItemTree tree;
return &tree;
}
case OB_MODE_EDIT: {
static asset::AssetItemTree tree;
return &tree;
}
default:
return nullptr;
}
}
default:
return nullptr;
}
}
static asset::AssetItemTree *get_static_item_tree(const bContext &C)
static asset::AssetItemTree *get_static_item_tree(const Object &active_object)
{
return get_static_item_tree(eContextObjectMode(CTX_data_mode_enum(&C)));
return get_static_item_tree(ObjectType(active_object.type), eObjectMode(active_object.mode));
}
void clear_operator_asset_trees()
{
for (const int mode : IndexRange(CTX_MODE_NUM)) {
if (asset::AssetItemTree *tree = get_static_item_tree(eContextObjectMode(mode)))
*tree = {};
for (const ObjectType type : {OB_MESH, OB_CURVES, OB_POINTCLOUD}) {
for (const eObjectMode mode : {OB_MODE_OBJECT, OB_MODE_EDIT, OB_MODE_SCULPT_CURVES}) {
if (asset::AssetItemTree *tree = get_static_item_tree(type, mode)) {
*tree = {};
}
}
}
}
static asset::AssetItemTree build_catalog_tree(const bContext &C)
static asset::AssetItemTree build_catalog_tree(const bContext &C, const Object &active_object)
{
const eContextObjectMode ctx_mode = eContextObjectMode(CTX_data_mode_enum(&C));
AssetFilterSettings type_filter{};
type_filter.id_types = FILTER_ID_NT;
const GeometryNodeAssetTraitFlag flag = asset_flag_for_context(ctx_mode);
const GeometryNodeAssetTraitFlag flag = asset_flag_for_context(active_object);
auto meta_data_filter = [&](const AssetMetaData &meta_data) {
const IDProperty *tree_type = BKE_asset_metadata_idprop_find(&meta_data, "type");
if (tree_type == nullptr || IDP_Int(tree_type) != NTREE_GEOMETRY) {
@ -703,6 +794,15 @@ static Set<std::string> get_builtin_menus(const ObjectType object_type, const eO
break;
case OB_MESH:
switch (mode) {
case OB_MODE_OBJECT:
menus.add_new("View");
menus.add_new("Select");
menus.add_new("Add");
menus.add_new("Object");
menus.add_new("Object/Apply");
menus.add_new("Object/Convert");
menus.add_new("Object/Quick Effects");
break;
case OB_MODE_EDIT:
menus.add_new("View");
menus.add_new("Select");
@ -752,7 +852,7 @@ static void catalog_assets_draw(const bContext *C, Menu *menu)
if (!active_object) {
return;
}
asset::AssetItemTree *tree = get_static_item_tree(*C);
asset::AssetItemTree *tree = get_static_item_tree(*active_object);
if (!tree) {
return;
}
@ -822,9 +922,11 @@ MenuType node_group_operator_assets_menu()
static bool unassigned_local_poll(const bContext &C)
{
Main &bmain = *CTX_data_main(&C);
const GeometryNodeAssetTraitFlag flag = asset_flag_for_context(
eContextObjectMode(CTX_data_mode_enum(&C)));
const Object *active_object = CTX_data_active_object(&C);
if (!active_object) {
return false;
}
const GeometryNodeAssetTraitFlag flag = asset_flag_for_context(*active_object);
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) {
@ -841,7 +943,11 @@ static bool unassigned_local_poll(const bContext &C)
static void catalog_assets_draw_unassigned(const bContext *C, Menu *menu)
{
asset::AssetItemTree *tree = get_static_item_tree(*C);
const Object *active_object = CTX_data_active_object(C);
if (!active_object) {
return;
}
asset::AssetItemTree *tree = get_static_item_tree(*active_object);
if (!tree) {
return;
}
@ -860,8 +966,7 @@ static void catalog_assets_draw_unassigned(const bContext *C, Menu *menu)
asset::operator_asset_reference_props_set(*asset, props_ptr);
}
const GeometryNodeAssetTraitFlag flag = asset_flag_for_context(
eContextObjectMode(CTX_data_mode_enum(C)));
const GeometryNodeAssetTraitFlag flag = asset_flag_for_context(*active_object);
bool first = true;
bool add_separator = !tree->unassigned_assets.is_empty();
@ -919,7 +1024,11 @@ void ui_template_node_operator_asset_menu_items(uiLayout &layout,
const StringRef catalog_path)
{
bScreen &screen = *CTX_wm_screen(&C);
asset::AssetItemTree *tree = get_static_item_tree(C);
const Object *active_object = CTX_data_active_object(&C);
if (!active_object) {
return;
}
asset::AssetItemTree *tree = get_static_item_tree(*active_object);
if (!tree) {
return;
}
@ -948,12 +1057,12 @@ void ui_template_node_operator_asset_root_items(uiLayout &layout, const bContext
if (!active_object) {
return;
}
asset::AssetItemTree *tree = get_static_item_tree(C);
asset::AssetItemTree *tree = get_static_item_tree(*active_object);
if (!tree) {
return;
}
if (tree->assets_per_path.size() == 0) {
*tree = build_catalog_tree(C);
*tree = build_catalog_tree(C, *active_object);
}
asset_system::AssetLibrary *all_library = ED_assetlist_library_get_once_available(

View File

@ -931,8 +931,9 @@ typedef enum GeometryNodeAssetTraitFlag {
GEO_NODE_ASSET_CURVE = (1 << 4),
GEO_NODE_ASSET_POINT_CLOUD = (1 << 5),
GEO_NODE_ASSET_MODIFIER = (1 << 6),
GEO_NODE_ASSET_OBJECT = (1 << 7),
} GeometryNodeAssetTraitFlag;
ENUM_OPERATORS(GeometryNodeAssetTraitFlag, GEO_NODE_ASSET_MODIFIER);
ENUM_OPERATORS(GeometryNodeAssetTraitFlag, GEO_NODE_ASSET_OBJECT);
/* Data structs, for `node->storage`. */

View File

@ -1802,6 +1802,15 @@ static void rna_GeometryNodeTree_is_modifier_set(PointerRNA *ptr, bool value)
geometry_node_asset_trait_flag_set(ptr, GEO_NODE_ASSET_MODIFIER, value);
}
static bool rna_GeometryNodeTree_is_mode_object_get(PointerRNA *ptr)
{
return geometry_node_asset_trait_flag_get(ptr, GEO_NODE_ASSET_OBJECT);
}
static void rna_GeometryNodeTree_is_mode_object_set(PointerRNA *ptr, bool value)
{
geometry_node_asset_trait_flag_set(ptr, GEO_NODE_ASSET_OBJECT, value);
}
static bool rna_GeometryNodeTree_is_mode_edit_get(PointerRNA *ptr)
{
return geometry_node_asset_trait_flag_get(ptr, GEO_NODE_ASSET_EDIT);
@ -10272,6 +10281,14 @@ static void rna_def_geometry_nodetree(BlenderRNA *brna)
prop, "rna_GeometryNodeTree_is_modifier_get", "rna_GeometryNodeTree_is_modifier_set");
RNA_def_property_update(prop, NC_NODE | ND_DISPLAY, "rna_NodeTree_update_asset");
prop = RNA_def_property(srna, "is_mode_object", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, nullptr, "flag", GEO_NODE_ASSET_EDIT);
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
RNA_def_property_ui_text(prop, "Edit", "The node group is used in object mode");
RNA_def_property_boolean_funcs(
prop, "rna_GeometryNodeTree_is_mode_object_get", "rna_GeometryNodeTree_is_mode_object_set");
RNA_def_property_update(prop, NC_NODE | ND_DISPLAY, "rna_NodeTree_update_asset");
prop = RNA_def_property(srna, "is_mode_edit", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, nullptr, "flag", GEO_NODE_ASSET_EDIT);
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);

View File

@ -166,6 +166,7 @@ struct GeoNodesModifierData {
};
struct GeoNodesOperatorData {
eObjectMode mode;
/** The object currently effected by the operator. */
const Object *self_object = nullptr;
/** Current evaluated depsgraph. */

View File

@ -53,7 +53,12 @@ static void node_geo_exec(GeoNodeExecParams params)
if (!check_tool_context_and_error(params)) {
return;
}
params.set_output("Selection", Field<bool>(std::make_shared<ToolSelectionFieldInput>()));
if (params.user_data()->operator_data->mode == OB_MODE_OBJECT) {
params.set_output("Selection", true);
}
else {
params.set_output("Selection", Field<bool>(std::make_shared<ToolSelectionFieldInput>()));
}
}
static void node_register()

View File

@ -37,6 +37,11 @@ static void node_geo_exec(GeoNodeExecParams params)
if (!check_tool_context_and_error(params)) {
return;
}
if (params.user_data()->operator_data->mode == OB_MODE_OBJECT) {
params.error_message_add(NodeWarningType::Error,
"Selection control is not supported in object mode");
return;
}
const Field<bool> selection = params.extract_input<Field<bool>>("Selection");
const eAttrDomain domain = eAttrDomain(params.node().custom1);
GeometrySet geometry = params.extract_input<GeometrySet>("Geometry");