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:
Diffstat (limited to 'source/blender/modifiers/intern/MOD_nodes.cc')
-rw-r--r--source/blender/modifiers/intern/MOD_nodes.cc888
1 files changed, 389 insertions, 499 deletions
diff --git a/source/blender/modifiers/intern/MOD_nodes.cc b/source/blender/modifiers/intern/MOD_nodes.cc
index 6236dc87791..8fa80025790 100644
--- a/source/blender/modifiers/intern/MOD_nodes.cc
+++ b/source/blender/modifiers/intern/MOD_nodes.cc
@@ -29,12 +29,14 @@
#include "BLI_float3.hh"
#include "BLI_listbase.h"
+#include "BLI_multi_value_map.hh"
#include "BLI_set.hh"
#include "BLI_string.h"
#include "BLI_utildefines.h"
#include "DNA_collection_types.h"
#include "DNA_defaults.h"
+#include "DNA_material_types.h"
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
#include "DNA_modifier_types.h"
@@ -43,17 +45,23 @@
#include "DNA_pointcloud_types.h"
#include "DNA_scene_types.h"
#include "DNA_screen_types.h"
+#include "DNA_space_types.h"
+#include "DNA_windowmanager_types.h"
#include "BKE_customdata.h"
+#include "BKE_geometry_set_instances.hh"
#include "BKE_global.h"
#include "BKE_idprop.h"
#include "BKE_lib_query.h"
+#include "BKE_main.h"
#include "BKE_mesh.h"
#include "BKE_modifier.h"
#include "BKE_node_ui_storage.hh"
+#include "BKE_object.h"
#include "BKE_pointcloud.h"
#include "BKE_screen.h"
#include "BKE_simulation.h"
+#include "BKE_workspace.h"
#include "BLO_read_write.h"
@@ -68,13 +76,14 @@
#include "MOD_modifiertypes.h"
#include "MOD_nodes.h"
+#include "MOD_nodes_evaluator.hh"
#include "MOD_ui_common.h"
+#include "ED_spreadsheet.h"
+
#include "NOD_derived_node_tree.hh"
#include "NOD_geometry.h"
-#include "NOD_geometry_exec.hh"
#include "NOD_node_tree_multi_function.hh"
-#include "NOD_type_callbacks.hh"
using blender::float3;
using blender::FunctionRef;
@@ -85,11 +94,8 @@ using blender::Span;
using blender::StringRef;
using blender::StringRefNull;
using blender::Vector;
-using blender::bke::PersistentCollectionHandle;
-using blender::bke::PersistentDataHandleMap;
-using blender::bke::PersistentObjectHandle;
using blender::fn::GMutablePointer;
-using blender::fn::GValueMap;
+using blender::fn::GPointer;
using blender::nodes::GeoNodeExecParams;
using namespace blender::fn::multi_function_types;
using namespace blender::nodes::derived_node_tree_types;
@@ -118,6 +124,18 @@ static void addIdsUsedBySocket(const ListBase *sockets, Set<ID *> &ids)
ids.add(&collection->id);
}
}
+ else if (socket->type == SOCK_MATERIAL) {
+ Material *material = ((bNodeSocketValueMaterial *)socket->default_value)->value;
+ if (material != nullptr) {
+ ids.add(&material->id);
+ }
+ }
+ else if (socket->type == SOCK_TEXTURE) {
+ Tex *texture = ((bNodeSocketValueTexture *)socket->default_value)->value;
+ if (texture != nullptr) {
+ ids.add(&texture->id);
+ }
+ }
}
}
@@ -129,7 +147,7 @@ static void find_used_ids_from_nodes(const bNodeTree &tree, Set<ID *> &ids)
addIdsUsedBySocket(&node->inputs, ids);
addIdsUsedBySocket(&node->outputs, ids);
- if (node->type == NODE_GROUP) {
+ if (ELEM(node->type, NODE_GROUP, NODE_CUSTOM_GROUP)) {
const bNodeTree *group = (bNodeTree *)node->id;
if (group != nullptr && handled_groups.add(group)) {
find_used_ids_from_nodes(*group, ids);
@@ -153,40 +171,34 @@ static void find_used_ids_from_settings(const NodesModifierSettings &settings, S
&ids);
}
-static void add_collection_object_relations_recursive(const ModifierUpdateDepsgraphContext *ctx,
- Collection &collection);
+/* We don't know exactly what attributes from the other object we will need. */
+static const CustomData_MeshMasks dependency_data_mask{CD_MASK_PROP_ALL | CD_MASK_MDEFORMVERT,
+ CD_MASK_PROP_ALL,
+ CD_MASK_PROP_ALL,
+ CD_MASK_PROP_ALL,
+ CD_MASK_PROP_ALL};
+
+static void add_collection_relation(const ModifierUpdateDepsgraphContext *ctx,
+ Collection &collection)
+{
+ DEG_add_collection_geometry_relation(ctx->node, &collection, "Nodes Modifier");
+ DEG_add_collection_geometry_customdata_mask(ctx->node, &collection, &dependency_data_mask);
+}
static void add_object_relation(const ModifierUpdateDepsgraphContext *ctx, Object &object)
{
DEG_add_object_relation(ctx->node, &object, DEG_OB_COMP_TRANSFORM, "Nodes Modifier");
if (&(ID &)object != &ctx->object->id) {
- if (object.type == OB_EMPTY) {
- Collection *collection_instance = object.instance_collection;
- if (collection_instance != nullptr) {
- add_collection_object_relations_recursive(ctx, *collection_instance);
- }
+ if (object.type == OB_EMPTY && object.instance_collection != nullptr) {
+ add_collection_relation(ctx, *object.instance_collection);
}
- else {
+ else if (DEG_object_has_geometry_component(&object)) {
DEG_add_object_relation(ctx->node, &object, DEG_OB_COMP_GEOMETRY, "Nodes Modifier");
+ DEG_add_customdata_mask(ctx->node, &object, &dependency_data_mask);
}
}
}
-static void add_collection_object_relations_recursive(const ModifierUpdateDepsgraphContext *ctx,
- Collection &collection)
-{
- LISTBASE_FOREACH (CollectionObject *, collection_object, &collection.gobject) {
- BLI_assert(collection_object->ob != nullptr);
- Object &object = *collection_object->ob;
- add_object_relation(ctx, object);
- }
- LISTBASE_FOREACH (CollectionChild *, collection_child, &collection.children) {
- BLI_assert(collection_child->collection != nullptr);
- Collection &collection = *collection_child->collection;
- add_collection_object_relations_recursive(ctx, collection);
- }
-}
-
static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphContext *ctx)
{
NodesModifierData *nmd = reinterpret_cast<NodesModifierData *>(md);
@@ -198,18 +210,28 @@ static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphConte
find_used_ids_from_settings(nmd->settings, used_ids);
find_used_ids_from_nodes(*nmd->node_group, used_ids);
for (ID *id : used_ids) {
- if (GS(id->name) == ID_OB) {
- Object *object = reinterpret_cast<Object *>(id);
- add_object_relation(ctx, *object);
- }
- if (GS(id->name) == ID_GR) {
- Collection *collection = reinterpret_cast<Collection *>(id);
- add_collection_object_relations_recursive(ctx, *collection);
+ switch ((ID_Type)GS(id->name)) {
+ case ID_OB: {
+ Object *object = reinterpret_cast<Object *>(id);
+ add_object_relation(ctx, *object);
+ break;
+ }
+ case ID_GR: {
+ Collection *collection = reinterpret_cast<Collection *>(id);
+ add_collection_relation(ctx, *collection);
+ break;
+ }
+ case ID_TE: {
+ DEG_add_generic_id_relation(ctx->node, id, "Nodes Modifier");
+ }
+ default: {
+ /* Purposefully don't add relations for materials. While there are material sockets,
+ * the pointers are only passed around as handles rather than dereferenced. */
+ break;
+ }
}
}
}
-
- /* TODO: Add dependency for adding and removing objects in collections. */
}
static void foreachIDLink(ModifierData *md, Object *ob, IDWalkFunc walk, void *userData)
@@ -252,368 +274,16 @@ static bool isDisabled(const struct Scene *UNUSED(scene),
return false;
}
-class GeometryNodesEvaluator {
- private:
- blender::LinearAllocator<> allocator_;
- Map<std::pair<DInputSocket, DOutputSocket>, GMutablePointer> value_by_input_;
- Vector<DInputSocket> group_outputs_;
- blender::nodes::MultiFunctionByNode &mf_by_node_;
- const blender::nodes::DataTypeConversions &conversions_;
- const PersistentDataHandleMap &handle_map_;
- const Object *self_object_;
- const ModifierData *modifier_;
- Depsgraph *depsgraph_;
-
- public:
- GeometryNodesEvaluator(const Map<DOutputSocket, GMutablePointer> &group_input_data,
- Vector<DInputSocket> group_outputs,
- 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()) {
- this->forward_to_inputs(item.key, item.value);
- }
- }
-
- Vector<GMutablePointer> execute()
- {
- Vector<GMutablePointer> results;
- for (const DInputSocket &group_output : group_outputs_) {
- Vector<GMutablePointer> result = this->get_input_values(group_output);
- results.append(result[0]);
- }
- for (GMutablePointer value : value_by_input_.values()) {
- value.destruct();
- }
- return results;
- }
-
- private:
- Vector<GMutablePointer> get_input_values(const DInputSocket socket_to_compute)
- {
- Vector<DSocket> from_sockets;
- socket_to_compute.foreach_origin_socket([&](DSocket socket) { from_sockets.append(socket); });
-
- if (from_sockets.is_empty()) {
- /* The input is not connected, use the value from the socket itself. */
- const CPPType &type = *blender::nodes::socket_cpp_type_get(*socket_to_compute->typeinfo());
- return {get_unlinked_input_value(socket_to_compute, type)};
- }
-
- /* Multi-input sockets contain a vector of inputs. */
- if (socket_to_compute->is_multi_input_socket()) {
- return this->get_inputs_from_incoming_links(socket_to_compute, from_sockets);
- }
-
- const DSocket from_socket = from_sockets[0];
- GMutablePointer value = this->get_input_from_incoming_link(socket_to_compute, from_socket);
- return {value};
- }
-
- Vector<GMutablePointer> get_inputs_from_incoming_links(const DInputSocket socket_to_compute,
- const Span<DSocket> from_sockets)
- {
- Vector<GMutablePointer> values;
- for (const int i : from_sockets.index_range()) {
- const DSocket from_socket = from_sockets[i];
- const int first_occurence = from_sockets.take_front(i).first_index_try(from_socket);
- if (first_occurence == -1) {
- values.append(this->get_input_from_incoming_link(socket_to_compute, from_socket));
- }
- else {
- /* If the same from-socket occurs more than once, we make a copy of the first value. This
- * can happen when a node linked to a multi-input-socket is muted. */
- GMutablePointer value = values[first_occurence];
- const CPPType *type = value.type();
- void *copy_buffer = allocator_.allocate(type->size(), type->alignment());
- type->copy_to_uninitialized(value.get(), copy_buffer);
- values.append({type, copy_buffer});
- }
- }
- return values;
- }
-
- GMutablePointer get_input_from_incoming_link(const DInputSocket socket_to_compute,
- const DSocket from_socket)
- {
- if (from_socket->is_output()) {
- const DOutputSocket from_output_socket{from_socket};
- const std::pair<DInputSocket, DOutputSocket> key = std::make_pair(socket_to_compute,
- from_output_socket);
- std::optional<GMutablePointer> value = value_by_input_.pop_try(key);
- if (value.has_value()) {
- /* This input has been computed before, return it directly. */
- return {*value};
- }
-
- /* Compute the socket now. */
- this->compute_output_and_forward(from_output_socket);
- return {value_by_input_.pop(key)};
- }
-
- /* Get value from an unlinked input socket. */
- const CPPType &type = *blender::nodes::socket_cpp_type_get(*socket_to_compute->typeinfo());
- const DInputSocket from_input_socket{from_socket};
- return {get_unlinked_input_value(from_input_socket, type)};
- }
-
- void compute_output_and_forward(const DOutputSocket socket_to_compute)
- {
- const DNode node{socket_to_compute.context(), &socket_to_compute->node()};
-
- if (!socket_to_compute->is_available()) {
- /* If the output is not available, use a default value. */
- const CPPType &type = *blender::nodes::socket_cpp_type_get(*socket_to_compute->typeinfo());
- void *buffer = allocator_.allocate(type.size(), type.alignment());
- type.copy_to_uninitialized(type.default_value(), buffer);
- this->forward_to_inputs(socket_to_compute, {type, buffer});
- return;
- }
-
- /* Prepare inputs required to execute the node. */
- GValueMap<StringRef> node_inputs_map{allocator_};
- for (const InputSocketRef *input_socket : node->inputs()) {
- if (input_socket->is_available()) {
- Vector<GMutablePointer> values = this->get_input_values({node.context(), input_socket});
- for (int i = 0; i < values.size(); ++i) {
- /* Values from Multi Input Sockets are stored in input map with the format
- * <identifier>[<index>]. */
- blender::StringRefNull key = allocator_.copy_string(
- input_socket->identifier() + (i > 0 ? ("[" + std::to_string(i)) + "]" : ""));
- node_inputs_map.add_new_direct(key, std::move(values[i]));
- }
- }
- }
-
- /* Execute the node. */
- GValueMap<StringRef> node_outputs_map{allocator_};
- GeoNodeExecParams params{
- 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. */
- for (const OutputSocketRef *output_socket : node->outputs()) {
- if (output_socket->is_available()) {
- GMutablePointer value = node_outputs_map.extract(output_socket->identifier());
- this->forward_to_inputs({node.context(), output_socket}, value);
- }
- }
- }
-
- void execute_node(const DNode node, GeoNodeExecParams params)
- {
- const bNode &bnode = params.node();
-
- this->store_ui_hints(node, params);
-
- /* Use the geometry-node-execute callback if it exists. */
- if (bnode.typeinfo->geometry_node_execute != nullptr) {
- bnode.typeinfo->geometry_node_execute(params);
- return;
- }
-
- /* Use the multi-function implementation if it exists. */
- const MultiFunction *multi_function = mf_by_node_.lookup_default(node, nullptr);
- if (multi_function != nullptr) {
- this->execute_multi_function_node(node, params, *multi_function);
- return;
- }
-
- /* Just output default values if no implementation exists. */
- this->execute_unknown_node(node, params);
- }
-
- void store_ui_hints(const DNode node, GeoNodeExecParams params) const
- {
- for (const InputSocketRef *socket_ref : node->inputs()) {
- if (!socket_ref->is_available()) {
- continue;
- }
- if (socket_ref->bsocket()->type != SOCK_GEOMETRY) {
- continue;
- }
- if (socket_ref->is_multi_input_socket()) {
- /* Not needed currently. */
- continue;
- }
-
- bNodeTree *btree_cow = node->btree();
- bNodeTree *btree_original = (bNodeTree *)DEG_get_original_id((ID *)btree_cow);
- const NodeTreeEvaluationContext context(*self_object_, *modifier_);
-
- const GeometrySet &geometry_set = params.get_input<GeometrySet>(socket_ref->identifier());
- const Vector<const GeometryComponent *> components = geometry_set.get_components_for_read();
-
- for (const GeometryComponent *component : components) {
- component->attribute_foreach(
- [&](StringRefNull attribute_name, const AttributeMetaData &meta_data) {
- BKE_nodetree_attribute_hint_add(*btree_original,
- context,
- *node->bnode(),
- attribute_name,
- meta_data.domain,
- meta_data.data_type);
- return true;
- });
- }
- }
- }
-
- void execute_multi_function_node(const DNode node,
- GeoNodeExecParams params,
- const MultiFunction &fn)
- {
- MFContextBuilder fn_context;
- MFParamsBuilder fn_params{fn, 1};
- Vector<GMutablePointer> input_data;
- for (const InputSocketRef *socket_ref : node->inputs()) {
- if (socket_ref->is_available()) {
- GMutablePointer data = params.extract_input(socket_ref->identifier());
- fn_params.add_readonly_single_input(GSpan(*data.type(), data.get(), 1));
- input_data.append(data);
- }
- }
- Vector<GMutablePointer> output_data;
- for (const OutputSocketRef *socket_ref : node->outputs()) {
- if (socket_ref->is_available()) {
- const CPPType &type = *blender::nodes::socket_cpp_type_get(*socket_ref->typeinfo());
- void *buffer = allocator_.allocate(type.size(), type.alignment());
- fn_params.add_uninitialized_single_output(GMutableSpan(type, buffer, 1));
- output_data.append(GMutablePointer(type, buffer));
- }
- }
- fn.call(IndexRange(1), fn_params, fn_context);
- for (GMutablePointer value : input_data) {
- value.destruct();
- }
- int output_index = 0;
- for (const int i : node->outputs().index_range()) {
- if (node->output(i).is_available()) {
- GMutablePointer value = output_data[output_index];
- params.set_output_by_move(node->output(i).identifier(), value);
- value.destruct();
- output_index++;
- }
- }
- }
-
- void execute_unknown_node(const DNode node, GeoNodeExecParams params)
- {
- for (const OutputSocketRef *socket : node->outputs()) {
- if (socket->is_available()) {
- const CPPType &type = *blender::nodes::socket_cpp_type_get(*socket->typeinfo());
- params.set_output_by_copy(socket->identifier(), {type, type.default_value()});
- }
- }
- }
-
- void forward_to_inputs(const DOutputSocket from_socket, GMutablePointer value_to_forward)
- {
- /* For all sockets that are linked with the from_socket push the value to their node. */
- Vector<DInputSocket> to_sockets_all;
- from_socket.foreach_target_socket(
- [&](DInputSocket to_socket) { to_sockets_all.append_non_duplicates(to_socket); });
-
- const CPPType &from_type = *value_to_forward.type();
- Vector<DInputSocket> to_sockets_same_type;
- for (const DInputSocket &to_socket : to_sockets_all) {
- const CPPType &to_type = *blender::nodes::socket_cpp_type_get(*to_socket->typeinfo());
- const std::pair<DInputSocket, DOutputSocket> key = std::make_pair(to_socket, from_socket);
- if (from_type == to_type) {
- to_sockets_same_type.append(to_socket);
- }
- else {
- void *buffer = allocator_.allocate(to_type.size(), to_type.alignment());
- if (conversions_.is_convertible(from_type, to_type)) {
- conversions_.convert(from_type, to_type, value_to_forward.get(), buffer);
- }
- else {
- to_type.copy_to_uninitialized(to_type.default_value(), buffer);
- }
- add_value_to_input_socket(key, GMutablePointer{to_type, buffer});
- }
- }
-
- if (to_sockets_same_type.size() == 0) {
- /* This value is not further used, so destruct it. */
- value_to_forward.destruct();
- }
- else if (to_sockets_same_type.size() == 1) {
- /* This value is only used on one input socket, no need to copy it. */
- const DInputSocket to_socket = to_sockets_same_type[0];
- const std::pair<DInputSocket, DOutputSocket> key = std::make_pair(to_socket, from_socket);
-
- add_value_to_input_socket(key, value_to_forward);
- }
- else {
- /* Multiple inputs use the value, make a copy for every input except for one. */
- const DInputSocket first_to_socket = to_sockets_same_type[0];
- Span<DInputSocket> other_to_sockets = to_sockets_same_type.as_span().drop_front(1);
- const CPPType &type = *value_to_forward.type();
- const std::pair<DInputSocket, DOutputSocket> first_key = std::make_pair(first_to_socket,
- from_socket);
- add_value_to_input_socket(first_key, value_to_forward);
- for (const DInputSocket &to_socket : other_to_sockets) {
- const std::pair<DInputSocket, DOutputSocket> key = std::make_pair(to_socket, from_socket);
- void *buffer = allocator_.allocate(type.size(), type.alignment());
- type.copy_to_uninitialized(value_to_forward.get(), buffer);
- add_value_to_input_socket(key, GMutablePointer{type, buffer});
- }
- }
- }
-
- void add_value_to_input_socket(const std::pair<DInputSocket, DOutputSocket> key,
- GMutablePointer value)
- {
- value_by_input_.add_new(key, value);
+static bool logging_enabled(const ModifierEvalContext *ctx)
+{
+ if (!DEG_is_active(ctx->depsgraph)) {
+ return false;
}
-
- GMutablePointer get_unlinked_input_value(const DInputSocket &socket,
- const CPPType &required_type)
- {
- bNodeSocket *bsocket = socket->bsocket();
- const CPPType &type = *blender::nodes::socket_cpp_type_get(*socket->typeinfo());
- void *buffer = allocator_.allocate(type.size(), type.alignment());
-
- if (bsocket->type == SOCK_OBJECT) {
- Object *object = socket->default_value<bNodeSocketValueObject>()->value;
- PersistentObjectHandle object_handle = handle_map_.lookup(object);
- new (buffer) PersistentObjectHandle(object_handle);
- }
- else if (bsocket->type == SOCK_COLLECTION) {
- Collection *collection = socket->default_value<bNodeSocketValueCollection>()->value;
- PersistentCollectionHandle collection_handle = handle_map_.lookup(collection);
- new (buffer) PersistentCollectionHandle(collection_handle);
- }
- else {
- blender::nodes::socket_cpp_value_get(*bsocket, buffer);
- }
-
- if (type == required_type) {
- return {type, buffer};
- }
- if (conversions_.is_convertible(type, required_type)) {
- void *converted_buffer = allocator_.allocate(required_type.size(),
- required_type.alignment());
- conversions_.convert(type, required_type, buffer, converted_buffer);
- type.destruct(buffer);
- return {required_type, converted_buffer};
- }
- void *default_buffer = allocator_.allocate(required_type.size(), required_type.alignment());
- required_type.copy_to_uninitialized(required_type.default_value(), default_buffer);
- return {required_type, default_buffer};
+ if ((ctx->flag & MOD_APPLY_ORCO) != 0) {
+ return false;
}
-};
+ return true;
+}
/**
* This code is responsible for creating the new property and also creating the group of
@@ -634,9 +304,7 @@ struct SocketPropertyType {
IDProperty *(*create_default_ui_prop)(const bNodeSocket &socket, const char *name);
PropertyType (*rna_subtype_get)(const bNodeSocket &socket);
bool (*is_correct_type)(const IDProperty &property);
- void (*init_cpp_value)(const IDProperty &property,
- const PersistentDataHandleMap &handles,
- void *r_value);
+ void (*init_cpp_value)(const IDProperty &property, void *r_value);
};
static IDProperty *socket_add_property(IDProperty *settings_prop_group,
@@ -659,6 +327,14 @@ static IDProperty *socket_add_property(IDProperty *settings_prop_group,
IDP_AddToGroup(ui_container, prop_ui_group);
}
+ /* Set property description (tooltip). */
+ IDPropertyTemplate property_description_template;
+ property_description_template.string.str = socket.description;
+ property_description_template.string.len = BLI_strnlen(socket.description, MAX_NAME) + 1;
+ property_description_template.string.subtype = IDP_STRING_SUB_UTF8;
+ IDProperty *description = IDP_New(IDP_STRING, &property_description_template, "description");
+ IDP_AddToGroup(prop_ui_group, description);
+
/* Create the properties for the socket's UI settings. */
if (property_type.create_min_ui_prop != nullptr) {
IDP_AddToGroup(prop_ui_group, property_type.create_min_ui_prop(socket, "min"));
@@ -720,10 +396,15 @@ static const SocketPropertyType *get_socket_property_type(const bNodeSocket &bso
[](const bNodeSocket &socket) {
return (PropertyType)((bNodeSocketValueFloat *)socket.default_value)->subtype;
},
- [](const IDProperty &property) { return property.type == IDP_FLOAT; },
- [](const IDProperty &property,
- const PersistentDataHandleMap &UNUSED(handles),
- void *r_value) { *(float *)r_value = IDP_Float(&property); },
+ [](const IDProperty &property) { return ELEM(property.type, IDP_FLOAT, IDP_DOUBLE); },
+ [](const IDProperty &property, void *r_value) {
+ if (property.type == IDP_FLOAT) {
+ *(float *)r_value = IDP_Float(&property);
+ }
+ else if (property.type == IDP_DOUBLE) {
+ *(float *)r_value = (float)IDP_Double(&property);
+ }
+ },
};
return &float_type;
}
@@ -757,9 +438,7 @@ static const SocketPropertyType *get_socket_property_type(const bNodeSocket &bso
return (PropertyType)((bNodeSocketValueInt *)socket.default_value)->subtype;
},
[](const IDProperty &property) { return property.type == IDP_INT; },
- [](const IDProperty &property,
- const PersistentDataHandleMap &UNUSED(handles),
- void *r_value) { *(int *)r_value = IDP_Int(&property); },
+ [](const IDProperty &property, void *r_value) { *(int *)r_value = IDP_Int(&property); },
};
return &int_type;
}
@@ -802,9 +481,9 @@ static const SocketPropertyType *get_socket_property_type(const bNodeSocket &bso
return property.type == IDP_ARRAY && property.subtype == IDP_FLOAT &&
property.len == 3;
},
- [](const IDProperty &property,
- const PersistentDataHandleMap &UNUSED(handles),
- void *r_value) { copy_v3_v3((float *)r_value, (const float *)IDP_Array(&property)); },
+ [](const IDProperty &property, void *r_value) {
+ copy_v3_v3((float *)r_value, (const float *)IDP_Array(&property));
+ },
};
return &vector_type;
}
@@ -834,9 +513,9 @@ static const SocketPropertyType *get_socket_property_type(const bNodeSocket &bso
},
nullptr,
[](const IDProperty &property) { return property.type == IDP_INT; },
- [](const IDProperty &property,
- const PersistentDataHandleMap &UNUSED(handles),
- void *r_value) { *(bool *)r_value = IDP_Int(&property) != 0; },
+ [](const IDProperty &property, void *r_value) {
+ *(bool *)r_value = IDP_Int(&property) != 0;
+ },
};
return &boolean_type;
}
@@ -856,9 +535,9 @@ static const SocketPropertyType *get_socket_property_type(const bNodeSocket &bso
},
nullptr,
[](const IDProperty &property) { return property.type == IDP_STRING; },
- [](const IDProperty &property,
- const PersistentDataHandleMap &UNUSED(handles),
- void *r_value) { new (r_value) std::string(IDP_String(&property)); },
+ [](const IDProperty &property, void *r_value) {
+ new (r_value) std::string(IDP_String(&property));
+ },
};
return &string_type;
}
@@ -875,10 +554,10 @@ static const SocketPropertyType *get_socket_property_type(const bNodeSocket &bso
nullptr,
nullptr,
[](const IDProperty &property) { return property.type == IDP_ID; },
- [](const IDProperty &property, const PersistentDataHandleMap &handles, void *r_value) {
+ [](const IDProperty &property, void *r_value) {
ID *id = IDP_Id(&property);
Object *object = (id && GS(id->name) == ID_OB) ? (Object *)id : nullptr;
- new (r_value) PersistentObjectHandle(handles.lookup(object));
+ *(Object **)r_value = object;
},
};
return &object_type;
@@ -896,10 +575,52 @@ static const SocketPropertyType *get_socket_property_type(const bNodeSocket &bso
nullptr,
nullptr,
[](const IDProperty &property) { return property.type == IDP_ID; },
- [](const IDProperty &property, const PersistentDataHandleMap &handles, void *r_value) {
+ [](const IDProperty &property, void *r_value) {
ID *id = IDP_Id(&property);
Collection *collection = (id && GS(id->name) == ID_GR) ? (Collection *)id : nullptr;
- new (r_value) PersistentCollectionHandle(handles.lookup(collection));
+ *(Collection **)r_value = collection;
+ },
+ };
+ return &collection_type;
+ }
+ case SOCK_TEXTURE: {
+ static const SocketPropertyType collection_type = {
+ [](const bNodeSocket &socket, const char *name) {
+ bNodeSocketValueTexture *value = (bNodeSocketValueTexture *)socket.default_value;
+ IDPropertyTemplate idprop = {0};
+ idprop.id = (ID *)value->value;
+ return IDP_New(IDP_ID, &idprop, name);
+ },
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ [](const IDProperty &property) { return property.type == IDP_ID; },
+ [](const IDProperty &property, void *r_value) {
+ ID *id = IDP_Id(&property);
+ Tex *texture = (id && GS(id->name) == ID_TE) ? (Tex *)id : nullptr;
+ *(Tex **)r_value = texture;
+ },
+ };
+ return &collection_type;
+ }
+ case SOCK_MATERIAL: {
+ static const SocketPropertyType collection_type = {
+ [](const bNodeSocket &socket, const char *name) {
+ bNodeSocketValueMaterial *value = (bNodeSocketValueMaterial *)socket.default_value;
+ IDPropertyTemplate idprop = {0};
+ idprop.id = (ID *)value->value;
+ return IDP_New(IDP_ID, &idprop, name);
+ },
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ [](const IDProperty &property) { return property.type == IDP_ID; },
+ [](const IDProperty &property, void *r_value) {
+ ID *id = IDP_Id(&property);
+ Material *material = (id && GS(id->name) == ID_MA) ? (Material *)id : nullptr;
+ *(Material **)r_value = material;
},
};
return &collection_type;
@@ -987,7 +708,6 @@ void MOD_nodes_init(Main *bmain, NodesModifierData *nmd)
}
static void initialize_group_input(NodesModifierData &nmd,
- const PersistentDataHandleMap &handle_map,
const bNodeSocket &socket,
const CPPType &cpp_type,
void *r_value)
@@ -1011,22 +731,7 @@ static void initialize_group_input(NodesModifierData &nmd,
blender::nodes::socket_cpp_value_get(socket, r_value);
return;
}
- property_type->init_cpp_value(*property, handle_map, r_value);
-}
-
-static void fill_data_handle_map(const NodesModifierSettings &settings,
- const DerivedNodeTree &tree,
- PersistentDataHandleMap &handle_map)
-{
- Set<ID *> used_ids;
- find_used_ids_from_settings(settings, used_ids);
- find_used_ids_from_nodes(*tree.root_context().tree().btree(), used_ids);
-
- int current_handle = 0;
- for (ID *id : used_ids) {
- handle_map.add(current_handle, *id);
- current_handle++;
- }
+ property_type->init_cpp_value(*property, r_value);
}
static void reset_tree_ui_storage(Span<const blender::nodes::NodeTreeRef *> trees,
@@ -1042,29 +747,190 @@ static void reset_tree_ui_storage(Span<const blender::nodes::NodeTreeRef *> tree
}
}
+static Vector<SpaceSpreadsheet *> find_spreadsheet_editors(Main *bmain)
+{
+ wmWindowManager *wm = (wmWindowManager *)bmain->wm.first;
+ if (wm == nullptr) {
+ return {};
+ }
+ Vector<SpaceSpreadsheet *> spreadsheets;
+ LISTBASE_FOREACH (wmWindow *, window, &wm->windows) {
+ bScreen *screen = BKE_workspace_active_screen_get(window->workspace_hook);
+ LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
+ SpaceLink *sl = (SpaceLink *)area->spacedata.first;
+ if (sl->spacetype == SPACE_SPREADSHEET) {
+ spreadsheets.append((SpaceSpreadsheet *)sl);
+ }
+ }
+ }
+ return spreadsheets;
+}
+
+using PreviewSocketMap = blender::MultiValueMap<DSocket, uint64_t>;
+
+static DSocket try_find_preview_socket_in_node(const DNode node)
+{
+ for (const SocketRef *socket : node->outputs()) {
+ if (socket->bsocket()->type == SOCK_GEOMETRY) {
+ return {node.context(), socket};
+ }
+ }
+ for (const SocketRef *socket : node->inputs()) {
+ if (socket->bsocket()->type == SOCK_GEOMETRY &&
+ (socket->bsocket()->flag & SOCK_MULTI_INPUT) == 0) {
+ return {node.context(), socket};
+ }
+ }
+ return {};
+}
+
+static DSocket try_get_socket_to_preview_for_spreadsheet(SpaceSpreadsheet *sspreadsheet,
+ NodesModifierData *nmd,
+ const ModifierEvalContext *ctx,
+ const DerivedNodeTree &tree)
+{
+ Vector<SpreadsheetContext *> context_path = sspreadsheet->context_path;
+ if (context_path.size() < 3) {
+ return {};
+ }
+ if (context_path[0]->type != SPREADSHEET_CONTEXT_OBJECT) {
+ return {};
+ }
+ if (context_path[1]->type != SPREADSHEET_CONTEXT_MODIFIER) {
+ return {};
+ }
+ SpreadsheetContextObject *object_context = (SpreadsheetContextObject *)context_path[0];
+ if (object_context->object != DEG_get_original_object(ctx->object)) {
+ return {};
+ }
+ SpreadsheetContextModifier *modifier_context = (SpreadsheetContextModifier *)context_path[1];
+ if (StringRef(modifier_context->modifier_name) != nmd->modifier.name) {
+ return {};
+ }
+ for (SpreadsheetContext *context : context_path.as_span().drop_front(2)) {
+ if (context->type != SPREADSHEET_CONTEXT_NODE) {
+ return {};
+ }
+ }
+
+ Span<SpreadsheetContextNode *> nested_group_contexts =
+ context_path.as_span().drop_front(2).drop_back(1).cast<SpreadsheetContextNode *>();
+ SpreadsheetContextNode *last_context = (SpreadsheetContextNode *)context_path.last();
+
+ const DTreeContext *context = &tree.root_context();
+ for (SpreadsheetContextNode *node_context : nested_group_contexts) {
+ const NodeTreeRef &tree_ref = context->tree();
+ const NodeRef *found_node = nullptr;
+ for (const NodeRef *node_ref : tree_ref.nodes()) {
+ if (node_ref->name() == node_context->node_name) {
+ found_node = node_ref;
+ break;
+ }
+ }
+ if (found_node == nullptr) {
+ return {};
+ }
+ context = context->child_context(*found_node);
+ if (context == nullptr) {
+ return {};
+ }
+ }
+
+ const NodeTreeRef &tree_ref = context->tree();
+ for (const NodeRef *node_ref : tree_ref.nodes()) {
+ if (node_ref->name() == last_context->node_name) {
+ return try_find_preview_socket_in_node({context, node_ref});
+ }
+ }
+ return {};
+}
+
+static void find_sockets_to_preview(NodesModifierData *nmd,
+ const ModifierEvalContext *ctx,
+ const DerivedNodeTree &tree,
+ PreviewSocketMap &r_sockets_to_preview)
+{
+ Main *bmain = DEG_get_bmain(ctx->depsgraph);
+
+ /* Based on every visible spreadsheet context path, get a list of sockets that need to have their
+ * intermediate geometries cached for display. */
+ Vector<SpaceSpreadsheet *> spreadsheets = find_spreadsheet_editors(bmain);
+ for (SpaceSpreadsheet *sspreadsheet : spreadsheets) {
+ const DSocket socket = try_get_socket_to_preview_for_spreadsheet(sspreadsheet, nmd, ctx, tree);
+ if (socket) {
+ const uint64_t key = ED_spreadsheet_context_path_hash(sspreadsheet);
+ r_sockets_to_preview.add_non_duplicates(socket, key);
+ }
+ }
+}
+
+static void log_preview_socket_value(const Span<GPointer> values,
+ Object *object,
+ Span<uint64_t> keys)
+{
+ GeometrySet geometry_set = *(const GeometrySet *)values[0].get();
+ geometry_set.ensure_owns_direct_data();
+ for (uint64_t key : keys) {
+ BKE_object_preview_geometry_set_add(object, key, new GeometrySet(geometry_set));
+ }
+}
+
+static void log_ui_hints(const DSocket socket,
+ const Span<GPointer> values,
+ Object *self_object,
+ NodesModifierData *nmd)
+{
+ const DNode node = socket.node();
+ if (node->is_reroute_node() || socket->typeinfo()->type != SOCK_GEOMETRY) {
+ return;
+ }
+ bNodeTree *btree_cow = node->btree();
+ bNodeTree *btree_original = (bNodeTree *)DEG_get_original_id((ID *)btree_cow);
+ const NodeTreeEvaluationContext context{*self_object, nmd->modifier};
+ for (const GPointer &data : values) {
+ if (data.type() == &CPPType::get<GeometrySet>()) {
+ const GeometrySet &geometry_set = *(const GeometrySet *)data.get();
+ blender::bke::geometry_set_instances_attribute_foreach(
+ geometry_set,
+ [&](StringRefNull attribute_name, const AttributeMetaData &meta_data) {
+ BKE_nodetree_attribute_hint_add(*btree_original,
+ context,
+ *node->bnode(),
+ attribute_name,
+ meta_data.domain,
+ meta_data.data_type);
+ return true;
+ },
+ 8);
+ }
+ }
+}
+
/**
* Evaluate a node group to compute the output geometry.
* Currently, this uses a fairly basic and inefficient algorithm that might compute things more
* often than necessary. It's going to be replaced soon.
*/
static GeometrySet compute_geometry(const DerivedNodeTree &tree,
- Span<const OutputSocketRef *> group_input_sockets,
+ Span<const NodeRef *> group_input_nodes,
const InputSocketRef &socket_to_compute,
GeometrySet input_geometry_set,
NodesModifierData *nmd,
const ModifierEvalContext *ctx)
{
- blender::ResourceCollector resources;
- blender::LinearAllocator<> &allocator = resources.linear_allocator();
- blender::nodes::MultiFunctionByNode mf_by_node = get_multi_function_per_node(tree, resources);
-
- PersistentDataHandleMap handle_map;
- fill_data_handle_map(nmd->settings, tree, handle_map);
+ blender::ResourceScope scope;
+ blender::LinearAllocator<> &allocator = scope.linear_allocator();
+ blender::nodes::MultiFunctionByNode mf_by_node = get_multi_function_per_node(tree, scope);
Map<DOutputSocket, GMutablePointer> group_inputs;
const DTreeContext *root_context = &tree.root_context();
- if (group_input_sockets.size() > 0) {
+ for (const NodeRef *group_input_node : group_input_nodes) {
+ Span<const OutputSocketRef *> group_input_sockets = group_input_node->outputs().drop_back(1);
+ if (group_input_sockets.is_empty()) {
+ continue;
+ }
+
Span<const OutputSocketRef *> remaining_input_sockets = group_input_sockets;
/* If the group expects a geometry as first input, use the geometry that has been passed to
@@ -1072,7 +938,7 @@ static GeometrySet compute_geometry(const DerivedNodeTree &tree,
const OutputSocketRef *first_input_socket = group_input_sockets[0];
if (first_input_socket->bsocket()->type == SOCK_GEOMETRY) {
GeometrySet *geometry_set_in =
- allocator.construct<GeometrySet>(std::move(input_geometry_set)).release();
+ allocator.construct<GeometrySet>(input_geometry_set).release();
group_inputs.add_new({root_context, first_input_socket}, geometry_set_in);
remaining_input_sockets = remaining_input_sockets.drop_front(1);
}
@@ -1081,28 +947,44 @@ static GeometrySet compute_geometry(const DerivedNodeTree &tree,
for (const OutputSocketRef *socket : remaining_input_sockets) {
const CPPType &cpp_type = *blender::nodes::socket_cpp_type_get(*socket->typeinfo());
void *value_in = allocator.allocate(cpp_type.size(), cpp_type.alignment());
- initialize_group_input(*nmd, handle_map, *socket->bsocket(), cpp_type, value_in);
+ initialize_group_input(*nmd, *socket->bsocket(), cpp_type, value_in);
group_inputs.add_new({root_context, socket}, {cpp_type, value_in});
}
}
+ /* Don't keep a reference to the input geometry components to avoid copies during evaluation. */
+ input_geometry_set.clear();
+
Vector<DInputSocket> group_outputs;
group_outputs.append({root_context, &socket_to_compute});
- GeometryNodesEvaluator evaluator{group_inputs,
- group_outputs,
- mf_by_node,
- handle_map,
- ctx->object,
- (ModifierData *)nmd,
- ctx->depsgraph};
+ PreviewSocketMap preview_sockets;
+ find_sockets_to_preview(nmd, ctx, tree, preview_sockets);
- Vector<GMutablePointer> results = evaluator.execute();
- BLI_assert(results.size() == 1);
- GMutablePointer result = results[0];
-
- GeometrySet output_geometry = std::move(*(GeometrySet *)result.get());
- return output_geometry;
+ auto log_socket_value = [&](const DSocket socket, const Span<GPointer> values) {
+ if (!logging_enabled(ctx)) {
+ return;
+ }
+ Span<uint64_t> keys = preview_sockets.lookup(socket);
+ if (!keys.is_empty()) {
+ log_preview_socket_value(values, ctx->object, keys);
+ }
+ log_ui_hints(socket, values, ctx->object, nmd);
+ };
+
+ blender::modifiers::geometry_nodes::GeometryNodesEvaluationParams eval_params;
+ eval_params.input_values = group_inputs;
+ eval_params.output_sockets = group_outputs;
+ eval_params.mf_by_node = &mf_by_node;
+ eval_params.modifier_ = nmd;
+ eval_params.depsgraph = ctx->depsgraph;
+ eval_params.self_object = ctx->object;
+ eval_params.log_socket_value_fn = log_socket_value;
+ blender::modifiers::geometry_nodes::evaluate_geometry_nodes(eval_params);
+
+ BLI_assert(eval_params.r_output_values.size() == 1);
+ GMutablePointer result = eval_params.r_output_values[0];
+ return result.relocate_out<GeometrySet>();
}
/**
@@ -1174,18 +1056,10 @@ static void modifyGeometry(ModifierData *md,
Span<const NodeRef *> input_nodes = root_tree_ref.nodes_by_type("NodeGroupInput");
Span<const NodeRef *> output_nodes = root_tree_ref.nodes_by_type("NodeGroupOutput");
- if (input_nodes.size() > 1) {
- return;
- }
if (output_nodes.size() != 1) {
return;
}
- Span<const OutputSocketRef *> group_inputs;
- if (input_nodes.size() == 1) {
- group_inputs = input_nodes[0]->outputs().drop_back(1);
- }
-
Span<const InputSocketRef *> group_outputs = output_nodes[0]->inputs().drop_back(1);
if (group_outputs.size() == 0) {
@@ -1197,10 +1071,12 @@ static void modifyGeometry(ModifierData *md,
return;
}
- reset_tree_ui_storage(tree.used_node_tree_refs(), *ctx->object, *md);
+ if (logging_enabled(ctx)) {
+ 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);
+ tree, input_nodes, *group_outputs[0], std::move(geometry_set), nmd, ctx);
}
static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *mesh)
@@ -1209,6 +1085,11 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *
geometry_set.get_component_for_write<MeshComponent>().copy_vertex_group_names_from_object(
*ctx->object);
modifyGeometry(md, ctx, geometry_set);
+
+ /* This function is only called when applying modifiers. In this case it makes sense to realize
+ * instances, otherwise in some cases there might be no results when applying the modifier. */
+ geometry_set = blender::bke::geometry_set_realize_mesh_for_modifier(geometry_set);
+
Mesh *new_mesh = geometry_set.get_component_for_write<MeshComponent>().release();
if (new_mesh == nullptr) {
return BKE_mesh_new_nomain(0, 0, 0, 0, 0);
@@ -1242,36 +1123,45 @@ static void draw_property_for_socket(uiLayout *layout,
/* IDProperties can be removed with python, so there could be a situation where
* there isn't a property for a socket or it doesn't have the correct type. */
- if (property != nullptr && property_type->is_correct_type(*property)) {
+ if (property == nullptr || !property_type->is_correct_type(*property)) {
+ return;
+ }
- char socket_id_esc[sizeof(socket.identifier) * 2];
- BLI_str_escape(socket_id_esc, socket.identifier, sizeof(socket_id_esc));
+ char socket_id_esc[sizeof(socket.identifier) * 2];
+ BLI_str_escape(socket_id_esc, socket.identifier, sizeof(socket_id_esc));
- char rna_path[sizeof(socket_id_esc) + 4];
- BLI_snprintf(rna_path, ARRAY_SIZE(rna_path), "[\"%s\"]", socket_id_esc);
+ char rna_path[sizeof(socket_id_esc) + 4];
+ BLI_snprintf(rna_path, ARRAY_SIZE(rna_path), "[\"%s\"]", socket_id_esc);
- /* Use #uiItemPointerR to draw pointer properties because #uiItemR would not have enough
- * information about what type of ID to select for editing the values. This is because
- * pointer IDProperties contain no information about their type. */
- switch (socket.type) {
- case SOCK_OBJECT: {
- uiItemPointerR(
- layout, md_ptr, rna_path, bmain_ptr, "objects", socket.name, ICON_OBJECT_DATA);
- break;
- }
- case SOCK_COLLECTION: {
- uiItemPointerR(layout,
- md_ptr,
- rna_path,
- bmain_ptr,
- "collections",
- socket.name,
- ICON_OUTLINER_COLLECTION);
- break;
- }
- default:
- uiItemR(layout, md_ptr, rna_path, 0, socket.name, ICON_NONE);
+ /* Use #uiItemPointerR to draw pointer properties because #uiItemR would not have enough
+ * information about what type of ID to select for editing the values. This is because
+ * pointer IDProperties contain no information about their type. */
+ switch (socket.type) {
+ case SOCK_OBJECT: {
+ uiItemPointerR(
+ layout, md_ptr, rna_path, bmain_ptr, "objects", socket.name, ICON_OBJECT_DATA);
+ break;
+ }
+ case SOCK_COLLECTION: {
+ uiItemPointerR(layout,
+ md_ptr,
+ rna_path,
+ bmain_ptr,
+ "collections",
+ socket.name,
+ ICON_OUTLINER_COLLECTION);
+ break;
+ }
+ case SOCK_MATERIAL: {
+ uiItemPointerR(layout, md_ptr, rna_path, bmain_ptr, "materials", socket.name, ICON_MATERIAL);
+ break;
+ }
+ case SOCK_TEXTURE: {
+ uiItemPointerR(layout, md_ptr, rna_path, bmain_ptr, "textures", socket.name, ICON_TEXTURE);
+ break;
}
+ default:
+ uiItemR(layout, md_ptr, rna_path, 0, socket.name, ICON_NONE);
}
}
@@ -1359,6 +1249,7 @@ static void requiredDataMask(Object *UNUSED(ob),
/* We don't know what the node tree will need. If there are vertex groups, it is likely that the
* node tree wants to access them. */
r_cddata_masks->vmask |= CD_MASK_MDEFORMVERT;
+ r_cddata_masks->vmask |= CD_MASK_PROP_ALL;
}
ModifierTypeInfo modifierType_Nodes = {
@@ -1382,7 +1273,6 @@ ModifierTypeInfo modifierType_Nodes = {
/* modifyMesh */ modifyMesh,
/* modifyHair */ nullptr,
/* modifyGeometrySet */ modifyGeometrySet,
- /* modifyVolume */ nullptr,
/* initData */ initData,
/* requiredDataMask */ requiredDataMask,