diff options
Diffstat (limited to 'source/blender/nodes')
5 files changed, 62 insertions, 3 deletions
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); |