diff options
Diffstat (limited to 'source/blender/modifiers/intern/MOD_nodes.cc')
-rw-r--r-- | source/blender/modifiers/intern/MOD_nodes.cc | 888 |
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, |