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:
authorHans Goudey <h.goudey@me.com>2021-02-17 02:15:08 +0300
committerHans Goudey <h.goudey@me.com>2021-02-17 02:15:08 +0300
commit461d4fc1aae200a6310a254b6e7c08070d9e94a7 (patch)
treef2ac642a29497855d879a022bc249a242a05b666 /source/blender/nodes
parentc9c4802c1c26d1125d9bb41ff9187b61e167cc42 (diff)
Geometry Nodes: Node error messages
This patch adds icons to the right side of nodes when they encounter a a problem. When hovered, a tooltip displays describing the encountered while evaluating the node. Some examples are: attribute doesn't exist, mesh has no faces, incorrect attribute type, etc. Exposing more messages to the system will be an ongoing process. Multiple warnings per node are supported. The system is implemented somewhat generically so that the basic structure can also be used to store more information from evaluation for the interface, like a list of available attributes. Currently the messages are just button tooltips. They could be styled differently in the future. Another limitation is that every instance of a node group in a parent node tree will have the same error messages, the "evaluation context" used to decide when to display the tooltips must be extended to support node tree paths. Differential Revision: https://developer.blender.org/D10290
Diffstat (limited to 'source/blender/nodes')
-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
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);