Geometry Nodes: allow naming geometry sets #114910

Merged
Jacques Lucke merged 20 commits from JacquesLucke/blender:geometry-names into main 2024-07-09 17:04:12 +02:00
19 changed files with 81 additions and 8 deletions

View File

@ -197,6 +197,7 @@ class NODE_MT_geometry_node_GEO_GEOMETRY_WRITE(Menu):
def draw(self, context):
layout = self.layout
node_add_menu.add_node_type(layout, "GeometryNodeSetGeometryName")
node_add_menu.add_node_type(layout, "GeometryNodeSetID")
node_add_menu.add_node_type(layout, "GeometryNodeSetPosition", search_weight=1.0)
if context.space_data.geometry_nodes_type == 'TOOL':

View File

@ -147,6 +147,13 @@ struct GeometrySet {
std::array<GeometryComponentPtr, GEO_COMPONENT_TYPE_ENUM_SIZE> components_;
public:
/**
* A user defined name for this geometry. It is not expected to be unique. Its main
* purpose is help debugging instance trees. It may eventually also be used when exporting
* instance trees or when creating separate objects from them.
*/
std::string name;

This string can not be shared and will be cause of large memory usage in common case with a lot of levels of nesting instances. In one level, here is just one instance handler so there is no a lot of geometry copy. But in case there is 2 levels, there is N copies of geometry. And since this string is not limited for its length here is can be really long text.

This string can not be shared and will be cause of large memory usage in common case with a lot of levels of nesting instances. In one level, here is just one instance handler so there is no a lot of geometry copy. But in case there is 2 levels, there is N copies of geometry. And since this string is not limited for its length here is can be really long text.
Review

It does use some extra memory, but I don't quite get the case that you describe. The GeometrySet is not stored for each instance separately, so it's not copied as often as you make it sound. I don't think this is something we need to be concerned about right now. There are ways to optimize it if we really need to in the future, but those also increase complexity.

It does use some extra memory, but I don't quite get the case that you describe. The `GeometrySet` is not stored for each instance separately, so it's not copied as often as you make it sound. I don't think this is something we need to be concerned about right now. There are ways to optimize it if we really need to in the future, but those also increase complexity.

The case that i describe is:
GeometrySet -> InstanceComponent -> InstanceHandle -> GeometrySet -> InstanceComponent -> InstanceHandle -> GeometrySet.
In this chain, 1 first geometry set, 1 second geometry set that is instantiated. So, last one is also just one.
But let there will be 2 different geometry at second level, with the same instances at 3 level. Now here is 2 copy of the most nested geometry.

The case that i describe is: GeometrySet -> InstanceComponent -> InstanceHandle -> GeometrySet -> InstanceComponent -> InstanceHandle -> GeometrySet. In this chain, 1 first geometry set, 1 second geometry set that is instantiated. So, last one is also just one. But let there will be 2 different geometry at second level, with the same instances at 3 level. Now here is 2 copy of the most nested geometry.
Review

Yeah, not a cause of concern for me currently.

Yeah, not a cause of concern for me currently.
/**
* The methods are defaulted here so that they are not instantiated in every translation unit.
*/
@ -420,7 +427,7 @@ struct GeometrySet {
friend bool operator==(const GeometrySet &a, const GeometrySet &b)
{
/* This compares only the component pointers, not the actual geometry data. */
return Span(a.components_) == Span(b.components_);
return Span(a.components_) == Span(b.components_) && a.name == b.name;
}
private:

View File

@ -1337,6 +1337,7 @@ void BKE_nodetree_remove_layer_n(bNodeTree *ntree, Scene *scene, int layer_index
#define GEO_NODE_INPUT_INSTANCE_TRANSFORM 2137
#define GEO_NODE_IMPORT_STL 2138
#define GEO_NODE_IMPORT_OBJ 2139
#define GEO_NODE_SET_GEOMETRY_NAME 2140
/** \} */

View File

@ -104,7 +104,7 @@ std::string InstanceReference::name() const
case Type::Collection:
return this->collection().id.name + 2;
case Type::GeometrySet:
return IFACE_("Geometry");
return this->geometry_set().name;
case Type::None:
break;
}

View File

@ -1500,6 +1500,9 @@ static void create_inspection_string_for_geometry_info(const geo_log::GeometryIn
}
fmt::format_to(fmt::appender(buf), TIP_("Geometry:"));
if (!value_log.name.empty()) {
fmt::format_to(fmt::appender(buf), " \"{}\"", value_log.name);
}
fmt::format_to(fmt::appender(buf), "\n");
for (bke::GeometryComponent::Type type : component_types) {
switch (type) {

View File

@ -194,6 +194,7 @@ GeometrySet join_geometries(const Span<GeometrySet> geometries,
const bke::AnonymousAttributePropagationInfo &propagation_info)
{
GeometrySet result;
result.name = geometries.is_empty() ? "" : geometries[0].name;
static const Array<GeometryComponent::Type> supported_types({GeometryComponent::Type::Mesh,
GeometryComponent::Type::PointCloud,
GeometryComponent::Type::Instance,

View File

@ -123,6 +123,7 @@ struct GeometryAttributeInfo {
*/
class GeometryInfoLog : public ValueLog {
public:
std::string name;
Vector<GeometryAttributeInfo> attributes;
Vector<bke::GeometryComponent::Type> component_types;

View File

@ -454,6 +454,7 @@ DefNode(GeometryNode, GEO_NODE_SET_CURVE_HANDLES, 0, "SET_CURVE_HANDLES", SetCur
DefNode(GeometryNode, GEO_NODE_SET_CURVE_NORMAL, 0, "SET_CURVE_NORMAL", SetCurveNormal, "Set Curve Normal", "Set the evaluation mode for curve normals")
DefNode(GeometryNode, GEO_NODE_SET_CURVE_RADIUS, 0, "SET_CURVE_RADIUS", SetCurveRadius, "Set Curve Radius", "Set the radius of the curve at each control point")
DefNode(GeometryNode, GEO_NODE_SET_CURVE_TILT, 0, "SET_CURVE_TILT", SetCurveTilt, "Set Curve Tilt", "Set the tilt angle at each curve control point")
DefNode(GeometryNode, GEO_NODE_SET_GEOMETRY_NAME, 0, "SET_GEOMETRY_NAME", SetGeometryName, "Set Geometry Name", "Set the name of a geometry for easier debugging")
DefNode(GeometryNode, GEO_NODE_SET_ID, 0, "SET_ID", SetID, "Set ID", "Set the id attribute on the input geometry, mainly used internally for randomizing")
DefNode(GeometryNode, GEO_NODE_SET_MATERIAL_INDEX, 0, "SET_MATERIAL_INDEX", SetMaterialIndex, "Set Material Index", "Set the material index for each selected geometry element")
DefNode(GeometryNode, GEO_NODE_SET_MATERIAL, 0, "SET_MATERIAL", SetMaterial, "Set Material", "Assign a material to geometry elements")

View File

@ -185,6 +185,7 @@ set(SRC
nodes/node_geo_set_curve_normal.cc
nodes/node_geo_set_curve_radius.cc
nodes/node_geo_set_curve_tilt.cc
nodes/node_geo_set_geometry_name.cc
nodes/node_geo_set_id.cc
nodes/node_geo_set_instance_transform.cc
nodes/node_geo_set_material.cc

View File

@ -206,10 +206,11 @@ static void node_geo_exec(GeoNodeExecParams params)
}
selection.finish();
}
geometry::debug_randomize_mesh_order(result);
params.set_output("Mesh", GeometrySet::from_mesh(result));
GeometrySet result_geometry = GeometrySet::from_mesh(result);
result_geometry.name = set_a.name;
params.set_output("Mesh", std::move(result_geometry));
#else
params.error_message_add(NodeWarningType::Error,
TIP_("Disabled, Blender was compiled without GMP"));

View File

@ -149,8 +149,10 @@ static void node_geo_exec(GeoNodeExecParams params)
const int handle = instances->add_reference(*collection);
instances->add_instance(handle, transform);
}
GeometrySet geometry = GeometrySet::from_instances(instances.release());
geometry.name = collection->id.name + 2;
params.set_output("Instances", GeometrySet::from_instances(instances.release()));
params.set_output("Instances", std::move(geometry));
}
static void node_rna(StructRNA *srna)

View File

@ -18,12 +18,15 @@ static void node_geo_exec(GeoNodeExecParams params)
{
Vector<GeometrySet> geometries = params.extract_input<Vector<GeometrySet>>("Geometry");
std::unique_ptr<bke::Instances> instances = std::make_unique<bke::Instances>();
for (GeometrySet &geometry : geometries) {
geometry.ensure_owns_direct_data();
const int handle = instances->add_reference(std::move(geometry));
instances->add_instance(handle, float4x4::identity());
}
params.set_output("Instances", GeometrySet::from_instances(instances.release()));
GeometrySet new_geometry = GeometrySet::from_instances(instances.release());
params.set_output("Instances", std::move(new_geometry));
}
static void node_register()

View File

@ -869,6 +869,7 @@ static void node_geo_exec(GeoNodeExecParams params)
{
new_curves.add(*curve_edit_data);
}
new_curves.name = guide_curves_geometry.name;
params.set_output("Curves", std::move(new_curves));
}

View File

@ -155,6 +155,7 @@ static void node_geo_exec(GeoNodeExecParams params)
}
}
geometry_set.name = object->id.name + 2;
params.set_output("Geometry", geometry_set);
}

View File

@ -77,8 +77,10 @@ static void node_geo_exec(GeoNodeExecParams params)
options.keep_original_ids = false;
options.realize_instance_attributes = true;
options.propagation_info = params.get_output_propagation_info("Geometry");
geometry_set = geometry::realize_instances(geometry_set, options, varied_depth_option);
params.set_output("Geometry", std::move(geometry_set));
GeometrySet new_geometry_set = geometry::realize_instances(
geometry_set, options, varied_depth_option);
new_geometry_set.name = geometry_set.name;
params.set_output("Geometry", std::move(new_geometry_set));
}
static void node_register()

View File

@ -30,6 +30,14 @@ static void node_geo_exec(GeoNodeExecParams params)
GeometrySet volumes;
GeometrySet instances;
const std::string &name = geometry_set.name;
meshes.name = name;
curves.name = name;
grease_pencil.name = name;
point_clouds.name = name;
volumes.name = name;
instances.name = name;
if (geometry_set.has<MeshComponent>()) {
meshes.add(*geometry_set.get_component<MeshComponent>());
}

View File

@ -0,0 +1,35 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "node_geometry_util.hh"
namespace blender::nodes::node_geo_set_geometry_name {
static void node_declare(NodeDeclarationBuilder &b)
{
b.add_input<decl::Geometry>("Geometry");
b.add_input<decl::String>("Name");
b.add_output<decl::Geometry>("Geometry").propagate_all();
}
static void node_geo_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
std::string name = params.extract_input<std::string>("Name");
geometry_set.name = std::move(name);
params.set_output("Geometry", std::move(geometry_set));
}
static void node_register()
{
static bke::bNodeType ntype;
geo_node_type_base(&ntype, GEO_NODE_SET_GEOMETRY_NAME, "Set Geometry Name", NODE_CLASS_GEOMETRY);
ntype.geometry_node_execute = node_geo_exec;
ntype.declare = node_declare;
bke::nodeRegisterType(&ntype);
}
NOD_REGISTER_NODE(node_register)
} // namespace blender::nodes::node_geo_set_geometry_name

View File

@ -330,6 +330,8 @@ static void node_geo_exec(GeoNodeExecParams params)
dst_instances->add_reference(std::move(group_geometry));
}
dst_geometry.name = src_geometry.name;
geometry::debug_randomize_instance_order(dst_instances);
params.set_output("Instances", std::move(dst_geometry));

View File

@ -62,6 +62,8 @@ FieldInfoLog::FieldInfoLog(const GField &field) : type(field.cpp_type())
GeometryInfoLog::GeometryInfoLog(const bke::GeometrySet &geometry_set)
{
this->name = geometry_set.name;
static std::array all_component_types = {bke::GeometryComponent::Type::Curve,
bke::GeometryComponent::Type::Instance,
bke::GeometryComponent::Type::Mesh,