Geometry Nodes: Support node tools in object mode #114819

Merged
Hans Goudey merged 1 commits from HooglyBoogly/blender:node-tools-object-mode into main 2023-11-15 17:01:27 +01:00
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");