Geometry Nodes: Initial tool-specific nodes

Add three new nodes for operations and inputs specific to
node group operators.
- **Selection** Whether elements are selected in the viewport
- **Set Selection** Sets the edit/sculpt selection, on the point,
  face, or curve domains
- **3D Cursor** Gives the location and rotation of the 3D cursor,
  in the local space of the modified object.
- **Face Set** The face set value from mesh sculpt mode,
  and whether the attribute exists.
- **Set Face Set** Set sculpt face set values.

In the add menu and search, the nodes are only visible in the
"Tool" context of the geometry node editor. They also give
errors when executed by a modifier.

Pull Request: https://projects.blender.org/blender/blender/pulls/109517
This commit is contained in:
Hans Goudey
2023-08-04 20:59:04 +02:00
committed by Hans Goudey
parent 9921c3532c
commit e3e6fb8ecf
24 changed files with 480 additions and 31 deletions
@@ -575,6 +575,20 @@ class NODE_MT_category_GEO_UTILITIES_MATH(Menu):
node_add_menu.draw_assets_for_catalog(layout, self.bl_label)
class NODE_MT_category_tool(Menu):
bl_idname = "NODE_MT_category_tool"
bl_label = "Tool"
def draw(self, _context):
layout = self.layout
node_add_menu.add_node_type(layout, "GeometryNodeTool3DCursor")
node_add_menu.add_node_type(layout, "GeometryNodeToolFaceSet")
node_add_menu.add_node_type(layout, "GeometryNodeToolSelection")
node_add_menu.add_node_type(layout, "GeometryNodeToolSetFaceSet")
node_add_menu.add_node_type(layout, "GeometryNodeToolSetSelection")
node_add_menu.draw_assets_for_catalog(layout, self.bl_label)
class NODE_MT_category_GEO_UV(Menu):
bl_idname = "NODE_MT_category_GEO_UV"
bl_label = "UV"
@@ -649,7 +663,8 @@ class NODE_MT_geometry_node_add_all(Menu):
bl_idname = "NODE_MT_geometry_node_add_all"
bl_label = ""
def draw(self, _context):
def draw(self, context):
snode = context.space_data
layout = self.layout
layout.menu("NODE_MT_geometry_node_GEO_ATTRIBUTE")
layout.menu("NODE_MT_geometry_node_GEO_INPUT")
@@ -669,6 +684,9 @@ class NODE_MT_geometry_node_add_all(Menu):
layout.menu("NODE_MT_category_GEO_TEXTURE")
layout.menu("NODE_MT_category_GEO_UTILITIES")
layout.separator()
if snode.geometry_nodes_type == 'TOOL':
layout.menu("NODE_MT_category_tool")
layout.separator()
layout.menu("NODE_MT_category_GEO_GROUP")
layout.menu("NODE_MT_category_GEO_LAYOUT")
node_add_menu.draw_root_assets(layout)
@@ -704,6 +722,7 @@ classes = (
NODE_MT_category_PRIMITIVES_MESH,
NODE_MT_geometry_node_mesh_topology,
NODE_MT_category_GEO_POINT,
NODE_MT_category_tool,
NODE_MT_category_simulation,
NODE_MT_category_GEO_VOLUME,
NODE_MT_geometry_node_GEO_MATERIAL,
@@ -225,6 +225,28 @@ class AttributeFieldInput : public GeometryFieldInput {
std::optional<eAttrDomain> preferred_domain(const GeometryComponent &component) const override;
};
class AttributeExistsFieldInput final : public bke::GeometryFieldInput {
private:
std::string name_;
public:
AttributeExistsFieldInput(std::string name, const CPPType &type)
: GeometryFieldInput(type, name), name_(std::move(name))
{
category_ = Category::Generated;
}
static fn::Field<bool> Create(std::string name)
{
const CPPType &type = CPPType::get<bool>();
auto field_input = std::make_shared<AttributeExistsFieldInput>(std::move(name), type);
return fn::Field<bool>(field_input);
}
GVArray get_varray_for_context(const bke::GeometryFieldContext &context,
const IndexMask &mask) const final;
};
class IDAttributeFieldInput : public GeometryFieldInput {
public:
IDAttributeFieldInput() : GeometryFieldInput(CPPType::get<int>())
+5
View File
@@ -1364,6 +1364,11 @@ void BKE_nodetree_remove_layer_n(struct bNodeTree *ntree, struct Scene *scene, i
* the repeat zone. */
#define GEO_NODE_REPEAT_INPUT 2107
#define GEO_NODE_REPEAT_OUTPUT 2108
#define GEO_NODE_TOOL_SELECTION 2109
#define GEO_NODE_TOOL_SET_SELECTION 2110
#define GEO_NODE_TOOL_3D_CURSOR 2111
#define GEO_NODE_TOOL_FACE_SET 2112
#define GEO_NODE_TOOL_SET_FACE_SET 2113
/** \} */
@@ -271,6 +271,14 @@ GVArray AttributeFieldInput::get_varray_for_context(const GeometryFieldContext &
return {};
}
GVArray AttributeExistsFieldInput::get_varray_for_context(const bke::GeometryFieldContext &context,
const IndexMask & /*mask*/) const
{
const bool exists = context.attributes()->contains(name_);
const int domain_size = context.attributes()->domain_size(context.domain());
return VArray<bool>::ForSingle(exists, domain_size);
}
std::string AttributeFieldInput::socket_inspection_name() const
{
return fmt::format(TIP_("\"{}\" attribute from geometry"), name_);
@@ -332,6 +332,7 @@ static int run_node_group_exec(bContext *C, wmOperator *op)
nodes::GeoNodesOperatorData operator_eval_data{};
operator_eval_data.depsgraph = depsgraph;
operator_eval_data.self_object = object;
operator_eval_data.scene = scene;
bke::GeometrySet geometry_orig = get_original_geometry_eval_copy(*object);
@@ -286,6 +286,7 @@ static void gather_socket_link_operations(const bContext &C,
const bNodeSocket &socket,
Vector<SocketLinkOperation> &search_link_ops)
{
const SpaceNode &snode = *CTX_wm_space_node(&C);
NODE_TYPES_BEGIN (node_type) {
const char *disabled_hint;
if (node_type->poll && !node_type->poll(node_type, &node_tree, &disabled_hint)) {
@@ -298,7 +299,8 @@ static void gather_socket_link_operations(const bContext &C,
continue;
}
if (node_type->gather_link_search_ops) {
nodes::GatherLinkSearchOpParams params{*node_type, node_tree, socket, search_link_ops};
nodes::GatherLinkSearchOpParams params{
*node_type, snode, node_tree, socket, search_link_ops};
node_type->gather_link_search_ops(params);
}
}
+1
View File
@@ -218,6 +218,7 @@ DEF_ENUM(rna_enum_color_attribute_type_items)
DEF_ENUM(rna_enum_attribute_type_with_auto_items)
DEF_ENUM(rna_enum_attribute_domain_items)
DEF_ENUM(rna_enum_attribute_domain_only_mesh_items)
DEF_ENUM(rna_enum_attribute_domain_point_face_curve_items)
DEF_ENUM(rna_enum_attribute_curves_domain_items)
DEF_ENUM(rna_enum_color_attribute_domain_items)
DEF_ENUM(rna_enum_attribute_domain_without_corner_items)
@@ -97,6 +97,13 @@ const EnumPropertyItem rna_enum_attribute_domain_only_mesh_items[] = {
{0, nullptr, 0, nullptr, nullptr},
};
const EnumPropertyItem rna_enum_attribute_domain_point_face_curve_items[] = {
{ATTR_DOMAIN_POINT, "POINT", 0, "Point", "Attribute on point"},
{ATTR_DOMAIN_FACE, "FACE", 0, "Face", "Attribute on mesh faces"},
{ATTR_DOMAIN_CURVE, "CURVE", 0, "Spline", "Attribute on spline"},
{0, NULL, 0, NULL, NULL},
};
const EnumPropertyItem rna_enum_attribute_domain_without_corner_items[] = {
{ATTR_DOMAIN_POINT, "POINT", 0, "Point", "Attribute on point"},
{ATTR_DOMAIN_EDGE, "EDGE", 0, "Edge", "Attribute on mesh edge"},
@@ -10103,6 +10103,16 @@ static void def_geo_points_to_volume(StructRNA *srna)
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
}
static void def_geo_tool_set_selection(StructRNA *srna)
{
PropertyRNA *prop = RNA_def_property(srna, "domain", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, nullptr, "custom1");
RNA_def_property_enum_items(prop, rna_enum_attribute_domain_point_face_curve_items);
RNA_def_property_enum_default(prop, ATTR_DOMAIN_POINT);
RNA_def_property_ui_text(prop, "Domain", "");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
}
static void def_geo_points_to_sdf_volume(StructRNA *srna)
{
PropertyRNA *prop;
@@ -33,6 +33,7 @@
struct Object;
struct Depsgraph;
struct Scene;
namespace blender::nodes {
@@ -85,6 +86,7 @@ struct GeoNodesOperatorData {
const Object *self_object = nullptr;
/** Current evaluated depsgraph. */
Depsgraph *depsgraph = nullptr;
Scene *scene = nullptr;
};
/**
+13 -1
View File
@@ -14,6 +14,7 @@
#include "NOD_node_declaration.hh"
struct bContext;
struct SpaceNode;
namespace blender::nodes {
@@ -73,6 +74,7 @@ class GatherLinkSearchOpParams {
/** The current node type. */
const bNodeType &node_type_;
const SpaceNode &snode_;
const bNodeTree &node_tree_;
const bNodeSocket &other_socket_;
@@ -82,10 +84,15 @@ class GatherLinkSearchOpParams {
public:
GatherLinkSearchOpParams(const bNodeType &node_type,
const SpaceNode &snode,
const bNodeTree &node_tree,
const bNodeSocket &other_socket,
Vector<SocketLinkOperation> &items)
: node_type_(node_type), node_tree_(node_tree), other_socket_(other_socket), items_(items)
: node_type_(node_type),
snode_(snode),
node_tree_(node_tree),
other_socket_(other_socket),
items_(items)
{
}
@@ -94,6 +101,11 @@ class GatherLinkSearchOpParams {
*/
const bNodeSocket &other_socket() const;
/**
* The currently active node editor.
*/
const SpaceNode &space_node() const;
/**
* The node tree the user is editing when the search menu is created.
*/
+5
View File
@@ -388,6 +388,11 @@ DefNode(GeometryNode, GEO_NODE_MESH_TOPOLOGY_VERTEX_OF_CORNER, 0, "VERTEX_OF_COR
DefNode(GeometryNode, GEO_NODE_OBJECT_INFO, def_geo_object_info, "OBJECT_INFO", ObjectInfo, "Object Info", "Retrieve information from an object")
DefNode(GeometryNode, GEO_NODE_OFFSET_POINT_IN_CURVE, 0, "OFFSET_POINT_IN_CURVE", OffsetPointInCurve, "Offset Point in Curve", "Offset a control point index within its curve")
DefNode(GeometryNode, GEO_NODE_OFFSET_SDF_VOLUME, 0, "OFFSET_SDF_VOLUME", OffsetSDFVolume, "Offset SDF Volume", "Move the surface of an SDF volume inwards or outwards")
DefNode(GeometryNode, GEO_NODE_TOOL_FACE_SET, 0, "TOOL_FACE_SET", ToolFaceSet, "Face Set", "Each face's sculpt face set value")
DefNode(GeometryNode, GEO_NODE_TOOL_3D_CURSOR, 0, "TOOL_3D_CURSOR", Tool3DCursor, "3D Cursor", "The scene's 3D cursor location and rotation")
DefNode(GeometryNode, GEO_NODE_TOOL_SELECTION, 0, "TOOL_SELECTION", ToolSelection, "Selection", "User selection of the edited geometry, for tool execution")
DefNode(GeometryNode, GEO_NODE_TOOL_SET_SELECTION, def_geo_tool_set_selection, "TOOL_SELECTION_SET", ToolSetSelection, "Set Selection", "Set selection of the edited geometry, for tool execution")
DefNode(GeometryNode, GEO_NODE_TOOL_SET_FACE_SET, 0, "TOOL_SET_FACE_SET", ToolSetFaceSet, "Set Face Set", "Set sculpt face set values for faces")
DefNode(GeometryNode, GEO_NODE_POINTS_TO_SDF_VOLUME, def_geo_points_to_sdf_volume, "POINTS_TO_SDF_VOLUME", PointsToSDFVolume, "Points to SDF Volume", "Generate an SDF volume sphere around every point")
DefNode(GeometryNode, GEO_NODE_POINTS_TO_VERTICES, 0, "POINTS_TO_VERTICES", PointsToVertices, "Points to Vertices", "Generate a mesh vertex for each point cloud point")
DefNode(GeometryNode, GEO_NODE_POINTS_TO_VOLUME, def_geo_points_to_volume, "POINTS_TO_VOLUME", PointsToVolume, "Points to Volume", "Generate a fog volume sphere around every point")
@@ -182,6 +182,11 @@ set(SRC
nodes/node_geo_string_to_curves.cc
nodes/node_geo_subdivision_surface.cc
nodes/node_geo_switch.cc
nodes/node_geo_tool_3d_cursor.cc
nodes/node_geo_tool_face_set.cc
nodes/node_geo_tool_selection.cc
nodes/node_geo_tool_set_face_set.cc
nodes/node_geo_tool_set_selection.cc
nodes/node_geo_transform_geometry.cc
nodes/node_geo_translate_instances.cc
nodes/node_geo_triangulate.cc
@@ -168,6 +168,11 @@ void register_geometry_nodes()
register_node_type_geo_string_to_curves();
register_node_type_geo_subdivision_surface();
register_node_type_geo_switch();
register_node_type_geo_tool_3d_cursor();
register_node_type_geo_tool_face_set();
register_node_type_geo_tool_selection();
register_node_type_geo_tool_set_face_set();
register_node_type_geo_tool_set_selection();
register_node_type_geo_transform_geometry();
register_node_type_geo_translate_instances();
register_node_type_geo_triangulate();
@@ -166,6 +166,11 @@ void register_node_type_geo_string_join();
void register_node_type_geo_string_to_curves();
void register_node_type_geo_subdivision_surface();
void register_node_type_geo_switch();
void register_node_type_geo_tool_3d_cursor();
void register_node_type_geo_tool_face_set();
void register_node_type_geo_tool_selection();
void register_node_type_geo_tool_set_face_set();
void register_node_type_geo_tool_set_selection();
void register_node_type_geo_transform_geometry();
void register_node_type_geo_translate_instances();
void register_node_type_geo_triangulate();
@@ -7,7 +7,9 @@
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
#include "DNA_space_types.h"
#include "BKE_context.h"
#include "BKE_mesh.hh"
#include "BKE_mesh_runtime.hh"
#include "BKE_pointcloud.h"
@@ -44,6 +46,30 @@ std::optional<eCustomDataType> node_socket_to_custom_data_type(const bNodeSocket
return node_data_type_to_custom_data_type(eNodeSocketDatatype(socket.type));
}
bool check_tool_context_and_error(GeoNodeExecParams &params)
{
if (!params.user_data()->operator_data) {
params.error_message_add(NodeWarningType::Error, "Node must be run as tool");
params.set_default_remaining_outputs();
return false;
}
return true;
}
void search_link_ops_for_for_tool_node(GatherAddNodeSearchParams &params)
{
const SpaceNode &snode = *CTX_wm_space_node(&params.context());
if (snode.geometry_nodes_type == SNODE_GEOMETRY_TOOL) {
search_node_add_ops_for_basic_node(params);
}
}
void search_link_ops_for_tool_node(GatherLinkSearchOpParams &params)
{
if (params.space_node().geometry_nodes_type == SNODE_GEOMETRY_TOOL) {
search_link_ops_for_basic_node(params);
}
}
} // namespace blender::nodes
bool geo_node_poll_default(const bNodeType * /*ntype*/,
@@ -32,6 +32,10 @@
#endif
struct BVHTreeFromMesh;
namespace blender::nodes {
class GatherAddNodeSearchParams;
class GatherLinkSearchOpParams;
} // namespace blender::nodes
void geo_node_type_base(bNodeType *ntype, int type, const char *name, short nclass);
bool geo_node_poll_default(const bNodeType *ntype,
@@ -40,6 +44,10 @@ bool geo_node_poll_default(const bNodeType *ntype,
namespace blender::nodes {
bool check_tool_context_and_error(GeoNodeExecParams &params);
void search_link_ops_for_for_tool_node(GatherAddNodeSearchParams &params);
void search_link_ops_for_tool_node(GatherLinkSearchOpParams &params);
void transform_mesh(Mesh &mesh,
const float3 translation,
const float3 rotation,
@@ -90,33 +90,6 @@ static void node_gather_link_searches(GatherLinkSearchOpParams &params)
}
}
class AttributeExistsFieldInput final : public bke::GeometryFieldInput {
private:
std::string name_;
public:
AttributeExistsFieldInput(std::string name, const CPPType &type)
: GeometryFieldInput(type, name), name_(std::move(name))
{
category_ = Category::Generated;
}
static Field<bool> Create(std::string name)
{
const CPPType &type = CPPType::get<bool>();
auto field_input = std::make_shared<AttributeExistsFieldInput>(std::move(name), type);
return Field<bool>(field_input);
}
GVArray get_varray_for_context(const bke::GeometryFieldContext &context,
const IndexMask & /*mask*/) const final
{
const bool exists = context.attributes()->contains(name_);
const int domain_size = context.attributes()->domain_size(context.domain());
return VArray<bool>::ForSingle(exists, domain_size);
}
};
static void node_geo_exec(GeoNodeExecParams params)
{
const NodeGeometryInputNamedAttribute &storage = node_storage(params.node());
@@ -159,7 +132,7 @@ static void node_geo_exec(GeoNodeExecParams params)
break;
}
params.set_output("Exists", AttributeExistsFieldInput::Create(std::move(name)));
params.set_output("Exists", bke::AttributeExistsFieldInput::Create(std::move(name)));
}
} // namespace blender::nodes::node_geo_input_named_attribute_cc
@@ -0,0 +1,58 @@
/* SPDX-FileCopyrightText: 2023 Blender Foundation
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "DNA_view3d_types.h"
#include "BLI_math_matrix.hh"
#include "node_geometry_util.hh"
namespace blender::nodes::node_geo_tool_3d_cursor_cc {
static void node_declare(NodeDeclarationBuilder &b)
{
b.add_output<decl::Vector>("Location")
.subtype(PROP_TRANSLATION)
.description(
"The location of the scene's 3D cursor, in the local space of the modified object");
b.add_output<decl::Rotation>("Rotation")
.description(
"The rotation of the scene's 3D cursor, in the local space of the modified object");
}
static void node_update(bNodeTree *tree, bNode *node)
{
bNodeSocket *rotation_socket = static_cast<bNodeSocket *>(node->outputs.last);
bke::nodeSetSocketAvailability(tree, rotation_socket, U.experimental.use_rotation_socket);
}
static void node_geo_exec(GeoNodeExecParams params)
{
if (!check_tool_context_and_error(params)) {
return;
}
const float4x4 world_to_object(params.user_data()->operator_data->self_object->world_to_object);
const View3DCursor &cursor = params.user_data()->operator_data->scene->cursor;
const float3 location_global(cursor.location);
const math::Quaternion rotation_global(float4(cursor.rotation_quaternion));
params.set_output("Location", math::transform_point(world_to_object, location_global));
if (U.experimental.use_rotation_socket) {
params.set_output("Rotation", math::to_quaternion(world_to_object) * rotation_global);
}
}
} // namespace blender::nodes::node_geo_tool_3d_cursor_cc
void register_node_type_geo_tool_3d_cursor()
{
namespace file_ns = blender::nodes::node_geo_tool_3d_cursor_cc;
static bNodeType ntype;
geo_node_type_base(&ntype, GEO_NODE_TOOL_3D_CURSOR, "3D Cursor", NODE_CLASS_INPUT);
ntype.declare = file_ns::node_declare;
ntype.geometry_node_execute = file_ns::node_geo_exec;
ntype.updatefunc = file_ns::node_update;
ntype.gather_add_node_search_ops = blender::nodes::search_link_ops_for_for_tool_node;
ntype.gather_link_search_ops = blender::nodes::search_link_ops_for_tool_node;
nodeRegisterType(&ntype);
}
@@ -0,0 +1,38 @@
/* SPDX-FileCopyrightText: 2023 Blender Foundation
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "BKE_geometry_fields.hh"
#include "node_geometry_util.hh"
namespace blender::nodes::node_geo_tool_face_set_cc {
static void node_declare(NodeDeclarationBuilder &b)
{
b.add_output<decl::Int>("Face Set").field_source();
b.add_output<decl::Bool>("Exists").field_source();
}
static void node_geo_exec(GeoNodeExecParams params)
{
if (!check_tool_context_and_error(params)) {
return;
}
params.set_output("Face Set", bke::AttributeFieldInput::Create<int>(".sculpt_face_set"));
params.set_output("Exists", bke::AttributeExistsFieldInput::Create(".sculpt_face_set"));
}
} // namespace blender::nodes::node_geo_tool_face_set_cc
void register_node_type_geo_tool_face_set()
{
namespace file_ns = blender::nodes::node_geo_tool_face_set_cc;
static bNodeType ntype;
geo_node_type_base(&ntype, GEO_NODE_TOOL_FACE_SET, "Face Set", NODE_CLASS_INPUT);
ntype.declare = file_ns::node_declare;
ntype.geometry_node_execute = file_ns::node_geo_exec;
ntype.gather_add_node_search_ops = blender::nodes::search_link_ops_for_for_tool_node;
ntype.gather_link_search_ops = blender::nodes::search_link_ops_for_tool_node;
nodeRegisterType(&ntype);
}
@@ -0,0 +1,71 @@
/* SPDX-FileCopyrightText: 2023 Blender Foundation
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "BKE_geometry_fields.hh"
#include "node_geometry_util.hh"
namespace blender::nodes::node_geo_tool_selection_cc {
static void node_declare(NodeDeclarationBuilder &b)
{
b.add_output<decl::Bool>("Selection").field_source();
}
class ToolSelectionFieldInput final : public bke::GeometryFieldInput {
public:
ToolSelectionFieldInput() : bke::GeometryFieldInput(CPPType::get<bool>(), "Operator Selection")
{
category_ = Category::NamedAttribute;
}
GVArray get_varray_for_context(const bke::GeometryFieldContext &context,
const IndexMask & /*mask*/) const final
{
const eAttrDomain domain = context.domain();
const AttributeAccessor attributes = *context.attributes();
switch (context.type()) {
case GeometryComponent::Type::Curve:
case GeometryComponent::Type::PointCloud:
return *attributes.lookup_or_default<bool>(".selection", domain, true);
case GeometryComponent::Type::Mesh:
switch (domain) {
case ATTR_DOMAIN_POINT:
return *attributes.lookup_or_default<bool>(".select_vert", domain, false);
case ATTR_DOMAIN_EDGE:
return *attributes.lookup_or_default<bool>(".select_edge", domain, false);
case ATTR_DOMAIN_FACE:
case ATTR_DOMAIN_CORNER:
return *attributes.lookup_or_default<bool>(".select_poly", domain, false);
default:
BLI_assert_unreachable();
return {};
}
default:
return {};
}
}
};
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>()));
}
} // namespace blender::nodes::node_geo_tool_selection_cc
void register_node_type_geo_tool_selection()
{
namespace file_ns = blender::nodes::node_geo_tool_selection_cc;
static bNodeType ntype;
geo_node_type_base(&ntype, GEO_NODE_TOOL_SELECTION, "Selection", NODE_CLASS_INPUT);
ntype.declare = file_ns::node_declare;
ntype.geometry_node_execute = file_ns::node_geo_exec;
ntype.gather_add_node_search_ops = blender::nodes::search_link_ops_for_for_tool_node;
ntype.gather_link_search_ops = blender::nodes::search_link_ops_for_tool_node;
nodeRegisterType(&ntype);
}
@@ -0,0 +1,69 @@
/* SPDX-FileCopyrightText: 2023 Blender Foundation
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "BKE_mesh.hh"
#include "UI_interface.h"
#include "UI_resources.h"
#include "node_geometry_util.hh"
namespace blender::nodes::node_geo_tool_set_face_set_cc {
static void node_declare(NodeDeclarationBuilder &b)
{
b.add_input<decl::Geometry>("Mesh");
b.add_input<decl::Bool>("Selection").default_value(true).hide_value().field_on_all();
b.add_input<decl::Int>("Face Set").hide_value().field_on_all();
b.add_output<decl::Geometry>("Mesh");
}
static bool is_constant_zero(const Field<int> &face_set)
{
if (face_set.node().depends_on_input()) {
return false;
}
return fn::evaluate_constant_field<int>(face_set) == 0;
}
static void node_geo_exec(GeoNodeExecParams params)
{
if (!check_tool_context_and_error(params)) {
return;
}
const Field<bool> selection = params.extract_input<Field<bool>>("Selection");
const Field<int> face_set = params.extract_input<Field<int>>("Face Set");
const bool is_zero = is_constant_zero(face_set);
GeometrySet geometry = params.extract_input<GeometrySet>("Mesh");
geometry.modify_geometry_sets([&](GeometrySet &geometry) {
if (Mesh *mesh = geometry.get_mesh_for_write()) {
if (is_zero) {
mesh->attributes_for_write().remove(".sculpt_face_set");
}
else {
bke::try_capture_field_on_geometry(geometry.get_component_for_write<MeshComponent>(),
".sculpt_face_set",
ATTR_DOMAIN_FACE,
selection,
face_set);
}
}
});
params.set_output("Mesh", std::move(geometry));
}
} // namespace blender::nodes::node_geo_tool_set_face_set_cc
void register_node_type_geo_tool_set_face_set()
{
namespace file_ns = blender::nodes::node_geo_tool_set_face_set_cc;
static bNodeType ntype;
geo_node_type_base(&ntype, GEO_NODE_TOOL_SET_FACE_SET, "Set Face Set", NODE_CLASS_GEOMETRY);
ntype.declare = file_ns::node_declare;
ntype.geometry_node_execute = file_ns::node_geo_exec;
ntype.gather_add_node_search_ops = blender::nodes::search_link_ops_for_for_tool_node;
ntype.gather_link_search_ops = blender::nodes::search_link_ops_for_tool_node;
nodeRegisterType(&ntype);
}
@@ -0,0 +1,92 @@
/* SPDX-FileCopyrightText: 2023 Blender Foundation
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "BKE_mesh.hh"
#include "UI_interface.h"
#include "UI_resources.h"
#include "node_geometry_util.hh"
namespace blender::nodes::node_geo_tool_set_selection_cc {
static void node_declare(NodeDeclarationBuilder &b)
{
b.add_input<decl::Geometry>("Geometry");
b.add_input<decl::Bool>("Selection").default_value(true).hide_value().field_on_all();
b.add_output<decl::Geometry>("Geometry");
}
static void node_layout(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr)
{
uiItemR(layout, ptr, "domain", UI_ITEM_NONE, "", ICON_NONE);
}
static void node_init(bNodeTree * /*tree*/, bNode *node)
{
node->custom1 = ATTR_DOMAIN_POINT;
}
static void node_geo_exec(GeoNodeExecParams params)
{
if (!check_tool_context_and_error(params)) {
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");
geometry.modify_geometry_sets([&](GeometrySet &geometry) {
if (Mesh *mesh = geometry.get_mesh_for_write()) {
switch (domain) {
case ATTR_DOMAIN_POINT:
bke::try_capture_field_on_geometry(geometry.get_component_for_write<MeshComponent>(),
".select_vert",
ATTR_DOMAIN_POINT,
selection);
BKE_mesh_flush_select_from_verts(mesh);
break;
case ATTR_DOMAIN_FACE:
bke::try_capture_field_on_geometry(geometry.get_component_for_write<MeshComponent>(),
".select_poly",
ATTR_DOMAIN_FACE,
selection);
BKE_mesh_flush_select_from_faces(mesh);
break;
default:
break;
}
}
if (geometry.has_curves()) {
if (ELEM(domain, ATTR_DOMAIN_POINT, ATTR_DOMAIN_CURVE)) {
bke::try_capture_field_on_geometry(
geometry.get_component_for_write<CurveComponent>(), ".selection", domain, selection);
}
}
if (geometry.has_pointcloud()) {
if (domain == ATTR_DOMAIN_POINT) {
bke::try_capture_field_on_geometry(geometry.get_component_for_write<PointCloudComponent>(),
".selection",
domain,
selection);
}
}
});
params.set_output("Geometry", std::move(geometry));
}
} // namespace blender::nodes::node_geo_tool_set_selection_cc
void register_node_type_geo_tool_set_selection()
{
namespace file_ns = blender::nodes::node_geo_tool_set_selection_cc;
static bNodeType ntype;
geo_node_type_base(&ntype, GEO_NODE_TOOL_SET_SELECTION, "Set Selection", NODE_CLASS_GEOMETRY);
ntype.declare = file_ns::node_declare;
ntype.initfunc = file_ns::node_init;
ntype.geometry_node_execute = file_ns::node_geo_exec;
ntype.draw_buttons = file_ns::node_layout;
ntype.gather_add_node_search_ops = blender::nodes::search_link_ops_for_for_tool_node;
ntype.gather_link_search_ops = blender::nodes::search_link_ops_for_tool_node;
nodeRegisterType(&ntype);
}
@@ -32,6 +32,11 @@ const bNodeSocket &GatherLinkSearchOpParams::other_socket() const
return other_socket_;
}
const SpaceNode &GatherLinkSearchOpParams::space_node() const
{
return snode_;
}
const bNodeTree &GatherLinkSearchOpParams::node_tree() const
{
return node_tree_;