Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--source/blender/blenkernel/BKE_node_ui_storage.hh95
-rw-r--r--source/blender/blenkernel/CMakeLists.txt1
-rw-r--r--source/blender/blenkernel/intern/node.cc10
-rw-r--r--source/blender/blenkernel/intern/node_ui_storage.cc104
-rw-r--r--source/blender/editors/space_node/node_draw.cc145
-rw-r--r--source/blender/makesdna/DNA_node_types.h3
-rw-r--r--source/blender/modifiers/intern/MOD_nodes.cc31
-rw-r--r--source/blender/nodes/NOD_geometry_exec.hh16
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_randomize.cc2
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_point_distribute.cc4
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_point_instance.cc6
-rw-r--r--source/blender/nodes/intern/node_geometry_exec.cc37
12 files changed, 447 insertions, 7 deletions
diff --git a/source/blender/blenkernel/BKE_node_ui_storage.hh b/source/blender/blenkernel/BKE_node_ui_storage.hh
new file mode 100644
index 00000000000..0b8ef60a603
--- /dev/null
+++ b/source/blender/blenkernel/BKE_node_ui_storage.hh
@@ -0,0 +1,95 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#pragma once
+
+#include "BLI_hash.hh"
+#include "BLI_map.hh"
+#include "BLI_session_uuid.h"
+
+#include "DNA_ID.h"
+#include "DNA_modifier_types.h"
+#include "DNA_session_uuid_types.h"
+
+struct bNode;
+struct bNodeTree;
+struct Object;
+struct ModifierData;
+
+using blender::Map;
+
+/**
+ * Contains the context necessary to determine when to display settings for a certain node tree
+ * that may be used for multiple modifiers and objects. The object name and modifier session UUID
+ * are used instead of pointers because they are re-allocated between evaluations.
+ *
+ * \note This does not yet handle the context of nested node trees.
+ */
+class NodeTreeEvaluationContext {
+ private:
+ std::string object_name_;
+ SessionUUID modifier_session_uuid_;
+
+ public:
+ NodeTreeEvaluationContext(const Object &object, const ModifierData &modifier)
+ {
+ object_name_ = reinterpret_cast<const ID &>(object).name;
+ modifier_session_uuid_ = modifier.session_uuid;
+ }
+
+ uint64_t hash() const
+ {
+ const uint64_t hash1 = blender::DefaultHash<std::string>{}(object_name_);
+ const uint64_t hash2 = BLI_session_uuid_hash_uint64(&modifier_session_uuid_);
+ return hash1 ^ (hash2 * 33); /* Copied from DefaultHash for std::pair. */
+ }
+
+ bool operator==(const NodeTreeEvaluationContext &other) const
+ {
+ return other.object_name_ == object_name_ &&
+ BLI_session_uuid_is_equal(&other.modifier_session_uuid_, &modifier_session_uuid_);
+ }
+};
+
+enum class NodeWarningType {
+ Error,
+ Warning,
+ Info,
+};
+
+struct NodeWarning {
+ NodeWarningType type;
+ std::string message;
+};
+
+struct NodeUIStorage {
+ blender::Vector<NodeWarning> warnings;
+};
+
+struct NodeTreeUIStorage {
+ Map<NodeTreeEvaluationContext, Map<std::string, NodeUIStorage>> context_map;
+};
+
+void BKE_nodetree_ui_storage_free_for_context(bNodeTree &ntree,
+ const NodeTreeEvaluationContext &context);
+
+void BKE_nodetree_ui_storage_ensure(bNodeTree &ntree);
+
+void BKE_nodetree_error_message_add(bNodeTree &ntree,
+ const NodeTreeEvaluationContext &context,
+ const bNode &node,
+ const NodeWarningType type,
+ std::string message);
diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt
index ead01dbd6cb..f288bf9aabc 100644
--- a/source/blender/blenkernel/CMakeLists.txt
+++ b/source/blender/blenkernel/CMakeLists.txt
@@ -200,6 +200,7 @@ set(SRC
intern/multires_versioning.c
intern/nla.c
intern/node.cc
+ intern/node_ui_storage.cc
intern/object.c
intern/object_deform.c
intern/object_dupli.c
diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc
index e34afd1ce17..f455f83f5c5 100644
--- a/source/blender/blenkernel/intern/node.cc
+++ b/source/blender/blenkernel/intern/node.cc
@@ -49,6 +49,7 @@
#include "BLI_ghash.h"
#include "BLI_listbase.h"
+#include "BLI_map.hh"
#include "BLI_math.h"
#include "BLI_path_util.h"
#include "BLI_string.h"
@@ -68,6 +69,7 @@
#include "BKE_lib_query.h"
#include "BKE_main.h"
#include "BKE_node.h"
+#include "BKE_node_ui_storage.hh"
#include "BLI_ghash.h"
#include "BLI_threads.h"
@@ -215,6 +217,10 @@ static void ntree_copy_data(Main *UNUSED(bmain), ID *id_dst, const ID *id_src, c
/* node tree will generate its own interface type */
ntree_dst->interface_type = nullptr;
+
+ /* Don't copy error messages in the runtime struct.
+ * They should be filled during execution anyway. */
+ ntree_dst->ui_storage = nullptr;
}
static void ntree_free_data(ID *id)
@@ -268,6 +274,8 @@ static void ntree_free_data(ID *id)
if (ntree->id.tag & LIB_TAG_LOCALIZED) {
BKE_libblock_free_data(&ntree->id, true);
}
+
+ delete ntree->ui_storage;
}
static void library_foreach_node_socket(LibraryForeachIDData *data, bNodeSocket *sock)
@@ -557,6 +565,7 @@ static void ntree_blend_write(BlendWriter *writer, ID *id, const void *id_addres
ntree->interface_type = nullptr;
ntree->progress = nullptr;
ntree->execdata = nullptr;
+ ntree->ui_storage = nullptr;
BLO_write_id_struct(writer, bNodeTree, id_address, &ntree->id);
@@ -588,6 +597,7 @@ void ntreeBlendReadData(BlendDataReader *reader, bNodeTree *ntree)
ntree->progress = nullptr;
ntree->execdata = nullptr;
+ ntree->ui_storage = nullptr;
BLO_read_data_address(reader, &ntree->adt);
BKE_animdata_blend_read_data(reader, ntree->adt);
diff --git a/source/blender/blenkernel/intern/node_ui_storage.cc b/source/blender/blenkernel/intern/node_ui_storage.cc
new file mode 100644
index 00000000000..4c8a5c824c4
--- /dev/null
+++ b/source/blender/blenkernel/intern/node_ui_storage.cc
@@ -0,0 +1,104 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "CLG_log.h"
+
+#include "BLI_map.hh"
+#include "BLI_string_ref.hh"
+#include "BLI_vector.hh"
+
+#include "DNA_node_types.h"
+#include "DNA_object_types.h"
+
+#include "BKE_node_ui_storage.hh"
+
+static CLG_LogRef LOG = {"bke.node_ui_storage"};
+
+using blender::Map;
+using blender::StringRef;
+using blender::Vector;
+
+void BKE_nodetree_ui_storage_ensure(bNodeTree &ntree)
+{
+ if (ntree.ui_storage == nullptr) {
+ ntree.ui_storage = new NodeTreeUIStorage();
+ }
+}
+
+/**
+ * Removes only the UI data associated with a particular evaluation context. The same node tree
+ * can be used for execution in multiple places, but the entire UI storage can't be removed when
+ * one execution starts, or all of the data associated with the node tree would be lost.
+ */
+void BKE_nodetree_ui_storage_free_for_context(bNodeTree &ntree,
+ const NodeTreeEvaluationContext &context)
+{
+ NodeTreeUIStorage *ui_storage = ntree.ui_storage;
+ if (ui_storage != nullptr) {
+ ui_storage->context_map.remove(context);
+ }
+}
+
+static void node_error_message_log(bNodeTree &ntree,
+ const bNode &node,
+ const StringRef message,
+ const NodeWarningType type)
+{
+ switch (type) {
+ case NodeWarningType::Error:
+ CLOG_ERROR(&LOG,
+ "Node Tree: \"%s\", Node: \"%s\", %s",
+ ntree.id.name + 2,
+ node.name,
+ message.data());
+ break;
+ case NodeWarningType::Warning:
+ CLOG_WARN(&LOG,
+ "Node Tree: \"%s\", Node: \"%s\", %s",
+ ntree.id.name + 2,
+ node.name,
+ message.data());
+ break;
+ case NodeWarningType::Info:
+ CLOG_INFO(&LOG,
+ 2,
+ "Node Tree: \"%s\", Node: \"%s\", %s",
+ ntree.id.name + 2,
+ node.name,
+ message.data());
+ break;
+ }
+}
+
+void BKE_nodetree_error_message_add(bNodeTree &ntree,
+ const NodeTreeEvaluationContext &context,
+ const bNode &node,
+ const NodeWarningType type,
+ std::string message)
+{
+ BLI_assert(ntree.ui_storage != nullptr);
+ NodeTreeUIStorage &ui_storage = *ntree.ui_storage;
+
+ node_error_message_log(ntree, node, message, type);
+
+ Map<std::string, NodeUIStorage> &node_tree_ui_storage =
+ ui_storage.context_map.lookup_or_add_default(context);
+
+ NodeUIStorage &node_ui_storage = node_tree_ui_storage.lookup_or_add_default_as(
+ StringRef(node.name));
+
+ node_ui_storage.warnings.append({type, std::move(message)});
+}
diff --git a/source/blender/editors/space_node/node_draw.cc b/source/blender/editors/space_node/node_draw.cc
index 8662217961c..f85d29f99d5 100644
--- a/source/blender/editors/space_node/node_draw.cc
+++ b/source/blender/editors/space_node/node_draw.cc
@@ -27,6 +27,7 @@
#include "DNA_light_types.h"
#include "DNA_linestyle_types.h"
#include "DNA_material_types.h"
+#include "DNA_modifier_types.h"
#include "DNA_node_types.h"
#include "DNA_screen_types.h"
#include "DNA_space_types.h"
@@ -34,7 +35,11 @@
#include "DNA_world_types.h"
#include "BLI_blenlib.h"
+#include "BLI_map.hh"
#include "BLI_math.h"
+#include "BLI_span.hh"
+#include "BLI_string_ref.hh"
+#include "BLI_vector.hh"
#include "BLT_translation.h"
@@ -42,6 +47,8 @@
#include "BKE_lib_id.h"
#include "BKE_main.h"
#include "BKE_node.h"
+#include "BKE_node_ui_storage.hh"
+#include "BKE_object.h"
#include "DEG_depsgraph.h"
@@ -74,6 +81,11 @@
# include "COM_compositor.h"
#endif
+using blender::Map;
+using blender::Span;
+using blender::StringRef;
+using blender::Vector;
+
extern "C" {
/* XXX interface.h */
extern void ui_draw_dropshadow(
@@ -1178,6 +1190,135 @@ void node_draw_sockets(const View2D *v2d,
}
}
+static int node_error_type_to_icon(const NodeWarningType type)
+{
+ switch (type) {
+ case NodeWarningType::Error:
+ return ICON_ERROR;
+ case NodeWarningType::Warning:
+ return ICON_ERROR;
+ case NodeWarningType::Info:
+ return ICON_INFO;
+ }
+
+ BLI_assert(false);
+ return ICON_ERROR;
+}
+
+static uint8_t node_error_type_priority(const NodeWarningType type)
+{
+ switch (type) {
+ case NodeWarningType::Error:
+ return 3;
+ case NodeWarningType::Warning:
+ return 2;
+ case NodeWarningType::Info:
+ return 1;
+ }
+
+ BLI_assert(false);
+ return 0;
+}
+
+static NodeWarningType node_error_highest_priority(Span<NodeWarning> warnings)
+{
+ uint8_t highest_priority = 0;
+ NodeWarningType highest_priority_type = NodeWarningType::Info;
+ for (const NodeWarning &warning : warnings) {
+ const uint8_t priority = node_error_type_priority(warning.type);
+ if (priority > highest_priority) {
+ highest_priority = priority;
+ highest_priority_type = warning.type;
+ }
+ }
+ return highest_priority_type;
+}
+
+static char *node_errrors_tooltip_fn(bContext *UNUSED(C), void *argN, const char *UNUSED(tip))
+{
+ const NodeUIStorage **storage_pointer_alloc = static_cast<const NodeUIStorage **>(argN);
+ const NodeUIStorage *node_ui_storage = *storage_pointer_alloc;
+ Span<NodeWarning> warnings = node_ui_storage->warnings;
+
+ std::string complete_string;
+
+ for (const NodeWarning &warning : warnings.drop_back(1)) {
+ complete_string += warning.message;
+ complete_string += '\n';
+ }
+
+ complete_string += warnings.last().message;
+
+ /* Remove the last period-- the tooltip system adds this automatically. */
+ if (complete_string.back() == '.') {
+ complete_string.pop_back();
+ }
+
+ return BLI_strdupn(complete_string.c_str(), complete_string.size());
+}
+
+#define NODE_HEADER_ICON_SIZE (0.8f * U.widget_unit)
+
+static const NodeUIStorage *node_ui_storage_get_from_context(const bContext *C,
+ const bNodeTree &ntree,
+ const bNode &node)
+{
+ const NodeTreeUIStorage *ui_storage = ntree.ui_storage;
+ if (ui_storage == nullptr) {
+ return nullptr;
+ }
+
+ const Object *active_object = CTX_data_active_object(C);
+ const ModifierData *active_modifier = BKE_object_active_modifier(active_object);
+ if (active_object == nullptr || active_modifier == nullptr) {
+ return nullptr;
+ }
+
+ const NodeTreeEvaluationContext context(*active_object, *active_modifier);
+ const Map<std::string, NodeUIStorage> *storage = ui_storage->context_map.lookup_ptr(context);
+ if (storage == nullptr) {
+ return nullptr;
+ }
+
+ return storage->lookup_ptr_as(StringRef(node.name));
+}
+
+static void node_add_error_message_button(
+ const bContext *C, bNodeTree &ntree, bNode &node, const rctf &rect, float &icon_offset)
+{
+ const NodeUIStorage *node_ui_storage = node_ui_storage_get_from_context(C, ntree, node);
+ if (node_ui_storage == nullptr) {
+ return;
+ }
+
+ /* The UI API forces us to allocate memory for each error button, because the
+ * ownership of #UI_but_func_tooltip_set's argument is transferred to the button. */
+ const NodeUIStorage **storage_pointer_alloc = (const NodeUIStorage **)MEM_mallocN(
+ sizeof(NodeUIStorage *), __func__);
+ *storage_pointer_alloc = node_ui_storage;
+
+ const NodeWarningType display_type = node_error_highest_priority(node_ui_storage->warnings);
+
+ icon_offset -= NODE_HEADER_ICON_SIZE;
+ UI_block_emboss_set(node.block, UI_EMBOSS_NONE);
+ uiBut *but = uiDefIconBut(node.block,
+ UI_BTYPE_BUT,
+ 0,
+ node_error_type_to_icon(display_type),
+ icon_offset,
+ rect.ymax - NODE_DY,
+ NODE_HEADER_ICON_SIZE,
+ UI_UNIT_Y,
+ nullptr,
+ 0,
+ 0,
+ 0,
+ 0,
+ nullptr);
+ UI_but_func_tooltip_set(but, node_errrors_tooltip_fn, storage_pointer_alloc);
+ UI_block_emboss_set(node.block, UI_EMBOSS);
+}
+
static void node_draw_basis(const bContext *C,
const View2D *v2d,
const SpaceNode *snode,
@@ -1186,7 +1327,7 @@ static void node_draw_basis(const bContext *C,
bNodeInstanceKey key)
{
/* float socket_size = NODE_SOCKSIZE*U.dpi/72; */ /* UNUSED */
- const float iconbutw = 0.8f * U.widget_unit;
+ const float iconbutw = NODE_HEADER_ICON_SIZE;
/* skip if out of view */
if (BLI_rctf_isect(&node->totr, &v2d->cur, nullptr) == false) {
@@ -1297,6 +1438,8 @@ static void node_draw_basis(const bContext *C,
UI_block_emboss_set(node->block, UI_EMBOSS);
}
+ node_add_error_message_button(C, *ntree, *node, *rct, iconofs);
+
/* title */
if (node->flag & SELECT) {
UI_GetThemeColor4fv(TH_SELECT, color);
diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h
index a7b5be753d1..3f1f8328cef 100644
--- a/source/blender/makesdna/DNA_node_types.h
+++ b/source/blender/makesdna/DNA_node_types.h
@@ -43,6 +43,7 @@ struct bNodeLink;
struct bNodePreview;
struct bNodeTreeExec;
struct bNodeType;
+struct NodeTreeUIStorage;
struct uiBlock;
#define NODE_MAXSTR 64
@@ -501,6 +502,8 @@ typedef struct bNodeTree {
int (*test_break)(void *);
void (*update_draw)(void *);
void *tbh, *prh, *sdh, *udh;
+
+ struct NodeTreeUIStorage *ui_storage;
} bNodeTree;
/* ntree->type, index */
diff --git a/source/blender/modifiers/intern/MOD_nodes.cc b/source/blender/modifiers/intern/MOD_nodes.cc
index 706ef8578ac..9ec7bdf3b80 100644
--- a/source/blender/modifiers/intern/MOD_nodes.cc
+++ b/source/blender/modifiers/intern/MOD_nodes.cc
@@ -50,6 +50,7 @@
#include "BKE_lib_query.h"
#include "BKE_mesh.h"
#include "BKE_modifier.h"
+#include "BKE_node_ui_storage.hh"
#include "BKE_pointcloud.h"
#include "BKE_screen.h"
#include "BKE_simulation.h"
@@ -244,6 +245,7 @@ class GeometryNodesEvaluator {
const blender::nodes::DataTypeConversions &conversions_;
const PersistentDataHandleMap &handle_map_;
const Object *self_object_;
+ const ModifierData *modifier_;
Depsgraph *depsgraph_;
public:
@@ -252,12 +254,14 @@ class GeometryNodesEvaluator {
blender::nodes::MultiFunctionByNode &mf_by_node,
const PersistentDataHandleMap &handle_map,
const Object *self_object,
+ const ModifierData *modifier,
Depsgraph *depsgraph)
: group_outputs_(std::move(group_outputs)),
mf_by_node_(mf_by_node),
conversions_(blender::nodes::get_implicit_type_conversions()),
handle_map_(handle_map),
self_object_(self_object),
+ modifier_(modifier),
depsgraph_(depsgraph)
{
for (auto item : group_input_data.items()) {
@@ -359,7 +363,7 @@ class GeometryNodesEvaluator {
/* Execute the node. */
GValueMap<StringRef> node_outputs_map{allocator_};
GeoNodeExecParams params{
- node, node_inputs_map, node_outputs_map, handle_map_, self_object_, depsgraph_};
+ node, node_inputs_map, node_outputs_map, handle_map_, self_object_, modifier_, depsgraph_};
this->execute_node(node, params);
/* Forward computed outputs to linked input sockets. */
@@ -946,6 +950,19 @@ static void fill_data_handle_map(const NodesModifierSettings &settings,
}
}
+static void reset_tree_ui_storage(Span<const blender::nodes::NodeTreeRef *> trees,
+ const Object &object,
+ const ModifierData &modifier)
+{
+ const NodeTreeEvaluationContext context = {object, modifier};
+
+ for (const blender::nodes::NodeTreeRef *tree : trees) {
+ bNodeTree *btree_cow = tree->btree();
+ bNodeTree *btree_original = (bNodeTree *)DEG_get_original_id((ID *)btree_cow);
+ BKE_nodetree_ui_storage_free_for_context(*btree_original, context);
+ }
+}
+
/**
* Evaluate a node group to compute the output geometry.
* Currently, this uses a fairly basic and inefficient algorithm that might compute things more
@@ -992,8 +1009,14 @@ static GeometrySet compute_geometry(const DerivedNodeTree &tree,
Vector<const DInputSocket *> group_outputs;
group_outputs.append(&socket_to_compute);
- GeometryNodesEvaluator evaluator{
- group_inputs, group_outputs, mf_by_node, handle_map, ctx->object, ctx->depsgraph};
+ GeometryNodesEvaluator evaluator{group_inputs,
+ group_outputs,
+ mf_by_node,
+ handle_map,
+ ctx->object,
+ (ModifierData *)nmd,
+ ctx->depsgraph};
+
Vector<GMutablePointer> results = evaluator.execute();
BLI_assert(results.size() == 1);
GMutablePointer result = results[0];
@@ -1091,6 +1114,8 @@ static void modifyGeometry(ModifierData *md,
return;
}
+ reset_tree_ui_storage(tree.used_node_tree_refs(), *ctx->object, *md);
+
geometry_set = compute_geometry(
tree, group_inputs, *group_outputs[0], std::move(geometry_set), nmd, ctx);
}
diff --git a/source/blender/nodes/NOD_geometry_exec.hh b/source/blender/nodes/NOD_geometry_exec.hh
index 18de52ed6d4..d5fd3ff0abb 100644
--- a/source/blender/nodes/NOD_geometry_exec.hh
+++ b/source/blender/nodes/NOD_geometry_exec.hh
@@ -21,6 +21,7 @@
#include "BKE_attribute_access.hh"
#include "BKE_geometry_set.hh"
#include "BKE_geometry_set_instances.hh"
+#include "BKE_node_ui_storage.hh"
#include "BKE_persistent_data_handle.hh"
#include "DNA_node_types.h"
@@ -28,6 +29,7 @@
#include "NOD_derived_node_tree.hh"
struct Depsgraph;
+struct ModifierData;
namespace blender::nodes {
@@ -62,6 +64,7 @@ class GeoNodeExecParams {
GValueMap<StringRef> &output_values_;
const PersistentDataHandleMap &handle_map_;
const Object *self_object_;
+ const ModifierData *modifier_;
Depsgraph *depsgraph_;
public:
@@ -70,12 +73,14 @@ class GeoNodeExecParams {
GValueMap<StringRef> &output_values,
const PersistentDataHandleMap &handle_map,
const Object *self_object,
+ const ModifierData *modifier,
Depsgraph *depsgraph)
: node_(node),
input_values_(input_values),
output_values_(output_values),
handle_map_(handle_map),
self_object_(self_object),
+ modifier_(modifier),
depsgraph_(depsgraph)
{
}
@@ -199,8 +204,17 @@ class GeoNodeExecParams {
}
/**
+ * Add an error message displayed at the top of the node when displaying the node tree,
+ * and potentially elsewhere in Blender.
+ */
+ void error_message_add(const NodeWarningType type, std::string message) const;
+
+ /**
* Creates a read-only attribute based on node inputs. The method automatically detects which
- * input with the given name is available.
+ * input socket with the given name is available.
+ *
+ * \note This will add an error message if the string socket is active and
+ * the input attribute does not exist.
*/
ReadAttributePtr get_input_attribute(const StringRef name,
const GeometryComponent &component,
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_randomize.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_randomize.cc
index 8a098c366a0..d7b85953a44 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_attribute_randomize.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_randomize.cc
@@ -214,6 +214,8 @@ static void randomize_attribute_on_component(GeometryComponent &component,
* doesn't already exist, don't do the operation. */
if (operation != GEO_NODE_ATTRIBUTE_RANDOMIZE_REPLACE_CREATE) {
if (!component.attribute_exists(attribute_name)) {
+ params.error_message_add(NodeWarningType::Error,
+ "No attribute with name '" + attribute_name + "'.");
return;
}
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_point_distribute.cc b/source/blender/nodes/geometry/nodes/node_geo_point_distribute.cc
index 581c356742b..40187490c23 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_point_distribute.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_point_distribute.cc
@@ -426,6 +426,7 @@ static void geo_node_point_distribute_exec(GeoNodeExecParams params)
static_cast<GeometryNodePointDistributeMethod>(params.node().custom1);
if (!geometry_set.has_mesh()) {
+ params.error_message_add(NodeWarningType::Error, "Geometry must contain a mesh.");
params.set_output("Geometry", std::move(geometry_set_out));
return;
}
@@ -441,7 +442,8 @@ static void geo_node_point_distribute_exec(GeoNodeExecParams params)
const MeshComponent &mesh_component = *geometry_set.get_component_for_read<MeshComponent>();
const Mesh *mesh_in = mesh_component.get_for_read();
- if (mesh_in == nullptr || mesh_in->mpoly == nullptr) {
+ if (mesh_in->mpoly == nullptr) {
+ params.error_message_add(NodeWarningType::Error, "Mesh has no faces.");
params.set_output("Geometry", std::move(geometry_set_out));
return;
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_point_instance.cc b/source/blender/nodes/geometry/nodes/node_geo_point_instance.cc
index 11921acdb68..669b5ee4614 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_point_instance.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_point_instance.cc
@@ -101,6 +101,12 @@ static void get_instanced_data__collection(
return;
}
+ if (BLI_listbase_is_empty(&collection->children) &&
+ BLI_listbase_is_empty(&collection->gobject)) {
+ params.error_message_add(NodeWarningType::Info, "Collection is empty.");
+ return;
+ }
+
const bool use_whole_collection = (node_storage->flag &
GEO_NODE_POINT_INSTANCE_WHOLE_COLLECTION) != 0;
if (use_whole_collection) {
diff --git a/source/blender/nodes/intern/node_geometry_exec.cc b/source/blender/nodes/intern/node_geometry_exec.cc
index 7f4f75c294f..ebbb6f60b78 100644
--- a/source/blender/nodes/intern/node_geometry_exec.cc
+++ b/source/blender/nodes/intern/node_geometry_exec.cc
@@ -14,6 +14,12 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+#include "DNA_modifier_types.h"
+
+#include "BKE_node_ui_storage.hh"
+
+#include "DEG_depsgraph_query.h"
+
#include "NOD_derived_node_tree.hh"
#include "NOD_geometry_exec.hh"
#include "NOD_type_callbacks.hh"
@@ -22,6 +28,23 @@
namespace blender::nodes {
+void GeoNodeExecParams::error_message_add(const NodeWarningType type, std::string message) const
+{
+ bNodeTree *btree_cow = node_.node_ref().tree().btree();
+ BLI_assert(btree_cow != nullptr);
+ if (btree_cow == nullptr) {
+ return;
+ }
+ bNodeTree *btree_original = (bNodeTree *)DEG_get_original_id((ID *)btree_cow);
+
+ BKE_nodetree_ui_storage_ensure(*btree_original);
+
+ const NodeTreeEvaluationContext context(*self_object_, *modifier_);
+
+ BKE_nodetree_error_message_add(
+ *btree_original, context, *node_.bnode(), type, std::move(message));
+}
+
const bNodeSocket *GeoNodeExecParams::find_available_socket(const StringRef name) const
{
for (const DSocket *socket : node_.inputs()) {
@@ -47,7 +70,19 @@ ReadAttributePtr GeoNodeExecParams::get_input_attribute(const StringRef name,
if (found_socket->type == SOCK_STRING) {
const std::string name = this->get_input<std::string>(found_socket->identifier);
- return component.attribute_get_for_read(name, domain, type, default_value);
+ /* Try getting the attribute without the default value. */
+ ReadAttributePtr attribute = component.attribute_try_get_for_read(name, domain, type);
+ if (attribute) {
+ return attribute;
+ }
+
+ /* If the attribute doesn't exist, use the default value and output an error message
+ * (except when the field is empty, to avoid spamming error messages). */
+ if (!name.empty()) {
+ this->error_message_add(NodeWarningType::Error,
+ std::string("No attribute with name '") + name + "'.");
+ }
+ return component.attribute_get_constant_for_read(domain, type, default_value);
}
if (found_socket->type == SOCK_FLOAT) {
const float value = this->get_input<float>(found_socket->identifier);