diff options
Diffstat (limited to 'source/blender/modifiers/intern')
-rw-r--r-- | source/blender/modifiers/intern/MOD_curve.c | 2 | ||||
-rw-r--r-- | source/blender/modifiers/intern/MOD_mirror.c | 10 | ||||
-rw-r--r-- | source/blender/modifiers/intern/MOD_nodes.cc | 73 | ||||
-rw-r--r-- | source/blender/modifiers/intern/MOD_nodes_evaluator.cc | 414 | ||||
-rw-r--r-- | source/blender/modifiers/intern/MOD_solidify_extrude.c | 1 | ||||
-rw-r--r-- | source/blender/modifiers/intern/MOD_solidify_nonmanifold.c | 14 | ||||
-rw-r--r-- | source/blender/modifiers/intern/MOD_ui_common.c | 19 | ||||
-rw-r--r-- | source/blender/modifiers/intern/MOD_ui_common.h | 15 | ||||
-rw-r--r-- | source/blender/modifiers/intern/MOD_util.c | 6 | ||||
-rw-r--r-- | source/blender/modifiers/intern/MOD_util.h | 6 | ||||
-rw-r--r-- | source/blender/modifiers/intern/MOD_weightvg_util.c | 21 | ||||
-rw-r--r-- | source/blender/modifiers/intern/MOD_weightvg_util.h | 30 | ||||
-rw-r--r-- | source/blender/modifiers/intern/MOD_weld.c | 4 |
13 files changed, 434 insertions, 181 deletions
diff --git a/source/blender/modifiers/intern/MOD_curve.c b/source/blender/modifiers/intern/MOD_curve.c index aae6d257766..20dbb299767 100644 --- a/source/blender/modifiers/intern/MOD_curve.c +++ b/source/blender/modifiers/intern/MOD_curve.c @@ -167,7 +167,7 @@ static void deformVertsEM(ModifierData *md, int defgrp_index = -1; if (ctx->object->type == OB_MESH && cmd->name[0] != '\0') { - defgrp_index = BKE_id_defgroup_name_index(&mesh->id, cmd->name); + defgrp_index = BKE_object_defgroup_name_index(ctx->object, cmd->name); if (defgrp_index != -1) { use_dverts = true; } diff --git a/source/blender/modifiers/intern/MOD_mirror.c b/source/blender/modifiers/intern/MOD_mirror.c index 7fd90c71c9f..bbac6589577 100644 --- a/source/blender/modifiers/intern/MOD_mirror.c +++ b/source/blender/modifiers/intern/MOD_mirror.c @@ -84,14 +84,17 @@ static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphConte static Mesh *mirrorModifier__doMirror(MirrorModifierData *mmd, Object *ob, Mesh *mesh) { Mesh *result = mesh; + const bool use_correct_order_on_merge = mmd->use_correct_order_on_merge; /* check which axes have been toggled and mirror accordingly */ if (mmd->flag & MOD_MIR_AXIS_X) { - result = BKE_mesh_mirror_apply_mirror_on_axis_for_modifier(mmd, ob, result, 0); + result = BKE_mesh_mirror_apply_mirror_on_axis_for_modifier( + mmd, ob, result, 0, use_correct_order_on_merge); } if (mmd->flag & MOD_MIR_AXIS_Y) { Mesh *tmp = result; - result = BKE_mesh_mirror_apply_mirror_on_axis_for_modifier(mmd, ob, result, 1); + result = BKE_mesh_mirror_apply_mirror_on_axis_for_modifier( + mmd, ob, result, 1, use_correct_order_on_merge); if (tmp != mesh) { /* free intermediate results */ BKE_id_free(NULL, tmp); @@ -99,7 +102,8 @@ static Mesh *mirrorModifier__doMirror(MirrorModifierData *mmd, Object *ob, Mesh } if (mmd->flag & MOD_MIR_AXIS_Z) { Mesh *tmp = result; - result = BKE_mesh_mirror_apply_mirror_on_axis_for_modifier(mmd, ob, result, 2); + result = BKE_mesh_mirror_apply_mirror_on_axis_for_modifier( + mmd, ob, result, 2, use_correct_order_on_merge); if (tmp != mesh) { /* free intermediate results */ BKE_id_free(NULL, tmp); diff --git a/source/blender/modifiers/intern/MOD_nodes.cc b/source/blender/modifiers/intern/MOD_nodes.cc index c1cdfa43920..ec6cbeb43bf 100644 --- a/source/blender/modifiers/intern/MOD_nodes.cc +++ b/source/blender/modifiers/intern/MOD_nodes.cc @@ -98,6 +98,7 @@ #include "NOD_node_declaration.hh" #include "FN_field.hh" +#include "FN_field_cpp_type.hh" #include "FN_multi_function.hh" using blender::Array; @@ -113,9 +114,11 @@ using blender::StringRef; using blender::StringRefNull; using blender::Vector; using blender::bke::OutputAttribute; +using blender::fn::Field; using blender::fn::GField; using blender::fn::GMutablePointer; using blender::fn::GPointer; +using blender::fn::ValueOrField; using blender::nodes::FieldInferencingInterface; using blender::nodes::GeoNodeExecParams; using blender::nodes::InputSocketFieldType; @@ -265,6 +268,39 @@ static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphConte } } +static bool check_tree_for_time_node(const bNodeTree &tree, + Set<const bNodeTree *> &r_checked_trees) +{ + if (!r_checked_trees.add(&tree)) { + return false; + } + LISTBASE_FOREACH (const bNode *, node, &tree.nodes) { + if (node->type == GEO_NODE_INPUT_SCENE_TIME) { + return true; + } + if (node->type == NODE_GROUP) { + const bNodeTree *sub_tree = reinterpret_cast<const bNodeTree *>(node->id); + if (sub_tree && check_tree_for_time_node(*sub_tree, r_checked_trees)) { + return true; + } + } + } + return false; +} + +static bool dependsOnTime(struct Scene *UNUSED(scene), + ModifierData *md, + const int UNUSED(dag_eval_mode)) +{ + const NodesModifierData *nmd = reinterpret_cast<NodesModifierData *>(md); + const bNodeTree *tree = nmd->node_group; + if (tree == nullptr) { + return false; + } + Set<const bNodeTree *> checked_trees; + return check_tree_for_time_node(*tree, checked_trees); +} + static void foreachIDLink(ModifierData *md, Object *ob, IDWalkFunc walk, void *userData) { NodesModifierData *nmd = reinterpret_cast<NodesModifierData *>(md); @@ -491,35 +527,34 @@ static void init_socket_cpp_value_from_property(const IDProperty &property, else if (property.type == IDP_DOUBLE) { value = (float)IDP_Double(&property); } - new (r_value) blender::fn::Field<float>(blender::fn::make_constant_field(value)); + new (r_value) ValueOrField<float>(value); break; } case SOCK_INT: { int value = IDP_Int(&property); - new (r_value) blender::fn::Field<int>(blender::fn::make_constant_field(value)); + new (r_value) ValueOrField<int>(value); break; } case SOCK_VECTOR: { float3 value; copy_v3_v3(value, (const float *)IDP_Array(&property)); - new (r_value) blender::fn::Field<float3>(blender::fn::make_constant_field(value)); + new (r_value) ValueOrField<float3>(value); break; } case SOCK_RGBA: { blender::ColorGeometry4f value; copy_v4_v4((float *)value, (const float *)IDP_Array(&property)); - new (r_value) blender::fn::Field<ColorGeometry4f>(blender::fn::make_constant_field(value)); + new (r_value) ValueOrField<ColorGeometry4f>(value); break; } case SOCK_BOOLEAN: { bool value = IDP_Int(&property) != 0; - new (r_value) blender::fn::Field<bool>(blender::fn::make_constant_field(value)); + new (r_value) ValueOrField<bool>(value); break; } case SOCK_STRING: { std::string value = IDP_String(&property); - new (r_value) - blender::fn::Field<std::string>(blender::fn::make_constant_field(std::move(value))); + new (r_value) ValueOrField<std::string>(std::move(value)); break; } case SOCK_OBJECT: { @@ -559,11 +594,6 @@ static void init_socket_cpp_value_from_property(const IDProperty &property, } } -/** - * Rebuild the list of properties based on the sockets exposed as the modifier's node group - * inputs. If any properties correspond to the old properties by name and type, carry over - * the values. - */ void MOD_nodes_update_interface(Object *object, NodesModifierData *nmd) { if (nmd->node_group == nullptr) { @@ -739,8 +769,13 @@ static void initialize_group_input(NodesModifierData &nmd, if (use_attribute) { const StringRef attribute_name{IDP_String(property_attribute_name)}; auto attribute_input = std::make_shared<blender::bke::AttributeFieldInput>( - attribute_name, *socket_type.get_base_cpp_type()); - new (r_value) blender::fn::GField(std::move(attribute_input), 0); + attribute_name, *socket_type.base_cpp_type); + GField attribute_field{std::move(attribute_input), 0}; + const blender::fn::ValueOrFieldCPPType *cpp_type = + dynamic_cast<const blender::fn::ValueOrFieldCPPType *>( + socket_type.geometry_nodes_cpp_type); + BLI_assert(cpp_type != nullptr); + cpp_type->construct_from_field(r_value, std::move(attribute_field)); } else { init_socket_cpp_value_from_property( @@ -904,7 +939,11 @@ static void store_output_value_in_geometry(GeometrySet &geometry_set, if (attribute_name.is_empty()) { return; } - const GField &field = *(const GField *)value.get(); + const blender::fn::ValueOrFieldCPPType *cpp_type = + dynamic_cast<const blender::fn::ValueOrFieldCPPType *>(value.type()); + BLI_assert(cpp_type != nullptr); + + const GField field = cpp_type->as_field(value.get()); const bNodeSocket *interface_socket = (bNodeSocket *)BLI_findlink(&nmd->node_group->outputs, socket.index()); const AttributeDomain domain = (AttributeDomain)interface_socket->attribute_domain; @@ -963,7 +1002,7 @@ static GeometrySet compute_geometry(const DerivedNodeTree &tree, /* Initialize remaining group inputs. */ for (const OutputSocketRef *socket : remaining_input_sockets) { - const CPPType &cpp_type = *socket->typeinfo()->get_geometry_nodes_cpp_type(); + const CPPType &cpp_type = *socket->typeinfo()->geometry_nodes_cpp_type; void *value_in = allocator.allocate(cpp_type.size(), cpp_type.alignment()); initialize_group_input(*nmd, *socket, value_in); group_inputs.add_new({root_context, socket}, {cpp_type, value_in}); @@ -1584,7 +1623,7 @@ ModifierTypeInfo modifierType_Nodes = { /* freeData */ freeData, /* isDisabled */ isDisabled, /* updateDepsgraph */ updateDepsgraph, - /* dependsOnTime */ nullptr, + /* dependsOnTime */ dependsOnTime, /* dependsOnNormals */ nullptr, /* foreachIDLink */ foreachIDLink, /* foreachTexLink */ foreachTexLink, diff --git a/source/blender/modifiers/intern/MOD_nodes_evaluator.cc b/source/blender/modifiers/intern/MOD_nodes_evaluator.cc index badd633f648..4e808120f4a 100644 --- a/source/blender/modifiers/intern/MOD_nodes_evaluator.cc +++ b/source/blender/modifiers/intern/MOD_nodes_evaluator.cc @@ -16,9 +16,10 @@ #include "MOD_nodes_evaluator.hh" +#include "BKE_type_conversions.hh" + #include "NOD_geometry_exec.hh" #include "NOD_socket_declarations.hh" -#include "NOD_type_conversions.hh" #include "DEG_depsgraph_query.h" @@ -35,13 +36,17 @@ #include "BLI_task.hh" #include "BLI_vector_set.hh" +#include <chrono> + namespace blender::modifiers::geometry_nodes { using fn::CPPType; using fn::Field; -using fn::FieldCPPType; using fn::GField; using fn::GValueMap; +using fn::GVArray; +using fn::ValueOrField; +using fn::ValueOrFieldCPPType; using nodes::GeoNodeExecParams; using namespace fn::multi_function_types; @@ -309,10 +314,10 @@ class LockedNode : NonCopyable, NonMovable { static const CPPType *get_socket_cpp_type(const SocketRef &socket) { const bNodeSocketType *typeinfo = socket.typeinfo(); - if (typeinfo->get_geometry_nodes_cpp_type == nullptr) { + if (typeinfo->geometry_nodes_cpp_type == nullptr) { return nullptr; } - const CPPType *type = typeinfo->get_geometry_nodes_cpp_type(); + const CPPType *type = typeinfo->geometry_nodes_cpp_type; if (type == nullptr) { return nullptr; } @@ -348,18 +353,19 @@ static bool get_implicit_socket_input(const SocketRef &socket, void *r_value) GEO_NODE_CURVE_HANDLE_LEFT ? "handle_left" : "handle_right"; - new (r_value) Field<float3>(bke::AttributeFieldInput::Create<float3>(side)); + new (r_value) ValueOrField<float3>(bke::AttributeFieldInput::Create<float3>(side)); return true; } - new (r_value) Field<float3>(bke::AttributeFieldInput::Create<float3>("position")); + new (r_value) ValueOrField<float3>(bke::AttributeFieldInput::Create<float3>("position")); return true; } if (socket.typeinfo()->type == SOCK_INT) { if (ELEM(bnode.type, FN_NODE_RANDOM_VALUE, GEO_NODE_INSTANCE_ON_POINTS)) { - new (r_value) Field<int>(std::make_shared<bke::IDAttributeFieldInput>()); + new (r_value) + ValueOrField<int>(Field<int>(std::make_shared<bke::IDAttributeFieldInput>())); return true; } - new (r_value) Field<int>(std::make_shared<fn::IndexFieldInput>()); + new (r_value) ValueOrField<int>(Field<int>(std::make_shared<fn::IndexFieldInput>())); return true; } } @@ -381,14 +387,23 @@ static bool node_supports_laziness(const DNode node) return node->typeinfo()->geometry_node_execute_supports_laziness; } +struct NodeTaskRunState { + /** The node that should be run on the same thread after the current node finished. */ + DNode next_node_to_run; +}; + /** Implements the callbacks that might be called when a node is executed. */ class NodeParamsProvider : public nodes::GeoNodeExecParamsProvider { private: GeometryNodesEvaluator &evaluator_; NodeState &node_state_; + NodeTaskRunState *run_state_; public: - NodeParamsProvider(GeometryNodesEvaluator &evaluator, DNode dnode, NodeState &node_state); + NodeParamsProvider(GeometryNodesEvaluator &evaluator, + DNode dnode, + NodeState &node_state, + NodeTaskRunState *run_state); bool can_get_input(StringRef identifier) const override; bool can_set_output(StringRef identifier) const override; @@ -402,6 +417,8 @@ class NodeParamsProvider : public nodes::GeoNodeExecParamsProvider { bool lazy_require_input(StringRef identifier) override; bool lazy_output_is_required(StringRef identifier) const override; + + void set_default_remaining_outputs() override; }; class GeometryNodesEvaluator { @@ -433,7 +450,7 @@ class GeometryNodesEvaluator { TaskPool *task_pool_ = nullptr; GeometryNodesEvaluationParams ¶ms_; - const blender::nodes::DataTypeConversions &conversions_; + const blender::bke::DataTypeConversions &conversions_; friend NodeParamsProvider; @@ -441,7 +458,7 @@ class GeometryNodesEvaluator { GeometryNodesEvaluator(GeometryNodesEvaluationParams ¶ms) : outer_allocator_(params.allocator), params_(params), - conversions_(blender::nodes::get_implicit_type_conversions()) + conversions_(blender::bke::get_implicit_type_conversions()) { } @@ -640,7 +657,7 @@ class GeometryNodesEvaluator { value.destruct(); continue; } - this->forward_output(socket, value); + this->forward_output(socket, value, nullptr); } } @@ -649,7 +666,7 @@ class GeometryNodesEvaluator { for (const DInputSocket &socket : params_.output_sockets) { const DNode node = socket.node(); NodeState &node_state = this->get_node_state(node); - this->with_locked_node(node, node_state, [&](LockedNode &locked_node) { + this->with_locked_node(node, node_state, nullptr, [&](LockedNode &locked_node) { /* Setting an input as required will schedule any linked node. */ this->set_input_required(locked_node, socket); }); @@ -657,7 +674,7 @@ class GeometryNodesEvaluator { for (const DSocket socket : params_.force_compute_sockets) { const DNode node = socket.node(); NodeState &node_state = this->get_node_state(node); - this->with_locked_node(node, node_state, [&](LockedNode &locked_node) { + this->with_locked_node(node, node_state, nullptr, [&](LockedNode &locked_node) { if (socket->is_input()) { this->set_input_required(locked_node, DInputSocket(socket)); } @@ -702,12 +719,24 @@ class GeometryNodesEvaluator { { void *user_data = BLI_task_pool_user_data(task_pool); GeometryNodesEvaluator &evaluator = *(GeometryNodesEvaluator *)user_data; - const NodeWithState *node_with_state = (const NodeWithState *)task_data; - - evaluator.node_task_run(node_with_state->node, *node_with_state->state); + const NodeWithState *root_node_with_state = (const NodeWithState *)task_data; + + /* First, the node provided by the task pool is executed. During the execution other nodes + * might be scheduled. One of those nodes is not added to the task pool but is executed in the + * loop below directly. This has two main benefits: + * - Fewer round trips through the task pool which add threading overhead. + * - Helps with cpu cache efficiency, because a thread is more likely to process data that it + * has processed shortly before. + */ + DNode next_node_to_run = root_node_with_state->node; + while (next_node_to_run) { + NodeTaskRunState run_state; + evaluator.node_task_run(next_node_to_run, &run_state); + next_node_to_run = run_state.next_node_to_run; + } } - void node_task_run(const DNode node, NodeState &node_state) + void node_task_run(const DNode node, NodeTaskRunState *run_state) { /* These nodes are sometimes scheduled. We could also check for them in other places, but * it's the easiest to do it here. */ @@ -715,21 +744,25 @@ class GeometryNodesEvaluator { return; } - const bool do_execute_node = this->node_task_preprocessing(node, node_state); + NodeState &node_state = *node_states_.lookup_key_as(node).state; + + const bool do_execute_node = this->node_task_preprocessing(node, node_state, run_state); /* Only execute the node if all prerequisites are met. There has to be an output that is * required and all required inputs have to be provided already. */ if (do_execute_node) { - this->execute_node(node, node_state); + this->execute_node(node, node_state, run_state); } - this->node_task_postprocessing(node, node_state, do_execute_node); + this->node_task_postprocessing(node, node_state, do_execute_node, run_state); } - bool node_task_preprocessing(const DNode node, NodeState &node_state) + bool node_task_preprocessing(const DNode node, + NodeState &node_state, + NodeTaskRunState *run_state) { bool do_execute_node = false; - this->with_locked_node(node, node_state, [&](LockedNode &locked_node) { + this->with_locked_node(node, node_state, run_state, [&](LockedNode &locked_node) { BLI_assert(node_state.schedule_state == NodeScheduleState::Scheduled); node_state.schedule_state = NodeScheduleState::Running; @@ -888,7 +921,7 @@ class GeometryNodesEvaluator { * Actually execute the node. All the required inputs are available and at least one output is * required. */ - void execute_node(const DNode node, NodeState &node_state) + void execute_node(const DNode node, NodeState &node_state, NodeTaskRunState *run_state) { const bNode &bnode = *node->bnode(); @@ -902,40 +935,49 @@ class GeometryNodesEvaluator { /* Use the geometry node execute callback if it exists. */ if (bnode.typeinfo->geometry_node_execute != nullptr) { - this->execute_geometry_node(node, node_state); + this->execute_geometry_node(node, node_state, run_state); return; } /* Use the multi-function implementation if it exists. */ const nodes::NodeMultiFunctions::Item &fn_item = params_.mf_by_node->try_get(node); if (fn_item.fn != nullptr) { - this->execute_multi_function_node(node, fn_item, node_state); + this->execute_multi_function_node(node, fn_item, node_state, run_state); return; } - this->execute_unknown_node(node, node_state); + this->execute_unknown_node(node, node_state, run_state); } - void execute_geometry_node(const DNode node, NodeState &node_state) + void execute_geometry_node(const DNode node, NodeState &node_state, NodeTaskRunState *run_state) { const bNode &bnode = *node->bnode(); - NodeParamsProvider params_provider{*this, node, node_state}; + NodeParamsProvider params_provider{*this, node, node_state, run_state}; GeoNodeExecParams params{params_provider}; if (node->idname().find("Legacy") != StringRef::not_found) { params.error_message_add(geo_log::NodeWarningType::Legacy, TIP_("Legacy node will be removed before Blender 4.0")); } + using Clock = std::chrono::steady_clock; + Clock::time_point begin = Clock::now(); bnode.typeinfo->geometry_node_execute(params); + Clock::time_point end = Clock::now(); + const std::chrono::microseconds duration = + std::chrono::duration_cast<std::chrono::microseconds>(end - begin); + if (params_.geo_logger != nullptr) { + params_.geo_logger->local().log_execution_time(node, duration); + } } void execute_multi_function_node(const DNode node, const nodes::NodeMultiFunctions::Item &fn_item, - NodeState &node_state) + NodeState &node_state, + NodeTaskRunState *run_state) { if (node->idname().find("Legacy") != StringRef::not_found) { /* Create geometry nodes params just for creating an error message. */ - NodeParamsProvider params_provider{*this, node, node_state}; + NodeParamsProvider params_provider{*this, node, node_state, run_state}; GeoNodeExecParams params{params_provider}; params.error_message_add(geo_log::NodeWarningType::Legacy, TIP_("Legacy node will be removed before Blender 4.0")); @@ -943,8 +985,9 @@ class GeometryNodesEvaluator { LinearAllocator<> &allocator = local_allocators_.local(); - /* Prepare the inputs for the multi function. */ - Vector<GField> input_fields; + bool any_input_is_field = false; + Vector<const void *, 16> input_values; + Vector<const ValueOrFieldCPPType *, 16> input_types; for (const int i : node->inputs().index_range()) { const InputSocketRef &socket_ref = node->input(i); if (!socket_ref.is_available()) { @@ -955,7 +998,38 @@ class GeometryNodesEvaluator { BLI_assert(input_state.was_ready_for_execution); SingleInputValue &single_value = *input_state.value.single; BLI_assert(single_value.value != nullptr); - input_fields.append(std::move(*(GField *)single_value.value)); + const ValueOrFieldCPPType &field_cpp_type = static_cast<const ValueOrFieldCPPType &>( + *input_state.type); + input_values.append(single_value.value); + input_types.append(&field_cpp_type); + if (field_cpp_type.is_field(single_value.value)) { + any_input_is_field = true; + } + } + + if (any_input_is_field) { + this->execute_multi_function_node__field( + node, fn_item, node_state, allocator, input_values, input_types, run_state); + } + else { + this->execute_multi_function_node__value( + node, *fn_item.fn, node_state, allocator, input_values, input_types, run_state); + } + } + + void execute_multi_function_node__field(const DNode node, + const nodes::NodeMultiFunctions::Item &fn_item, + NodeState &node_state, + LinearAllocator<> &allocator, + Span<const void *> input_values, + Span<const ValueOrFieldCPPType *> input_types, + NodeTaskRunState *run_state) + { + Vector<GField> input_fields; + for (const int i : input_values.index_range()) { + const void *input_value_or_field = input_values[i]; + const ValueOrFieldCPPType &field_cpp_type = *input_types[i]; + input_fields.append(field_cpp_type.as_field(input_value_or_field)); } std::shared_ptr<fn::FieldOperation> operation; @@ -966,7 +1040,6 @@ class GeometryNodesEvaluator { operation = std::make_shared<fn::FieldOperation>(*fn_item.fn, std::move(input_fields)); } - /* Forward outputs. */ int output_index = 0; for (const int i : node->outputs().index_range()) { const OutputSocketRef &socket_ref = node->output(i); @@ -975,17 +1048,70 @@ class GeometryNodesEvaluator { } OutputState &output_state = node_state.outputs[i]; const DOutputSocket socket{node.context(), &socket_ref}; - const CPPType *cpp_type = get_socket_cpp_type(socket_ref); + const ValueOrFieldCPPType *cpp_type = static_cast<const ValueOrFieldCPPType *>( + get_socket_cpp_type(socket_ref)); GField new_field{operation, output_index}; - new_field = fn::make_field_constant_if_possible(std::move(new_field)); - GField &field_to_forward = *allocator.construct<GField>(std::move(new_field)).release(); - this->forward_output(socket, {cpp_type, &field_to_forward}); + void *buffer = allocator.allocate(cpp_type->size(), cpp_type->alignment()); + cpp_type->construct_from_field(buffer, std::move(new_field)); + this->forward_output(socket, {cpp_type, buffer}, run_state); output_state.has_been_computed = true; output_index++; } } - void execute_unknown_node(const DNode node, NodeState &node_state) + void execute_multi_function_node__value(const DNode node, + const MultiFunction &fn, + NodeState &node_state, + LinearAllocator<> &allocator, + Span<const void *> input_values, + Span<const ValueOrFieldCPPType *> input_types, + NodeTaskRunState *run_state) + { + MFParamsBuilder params{fn, 1}; + for (const int i : input_values.index_range()) { + const void *input_value_or_field = input_values[i]; + const ValueOrFieldCPPType &field_cpp_type = *input_types[i]; + const CPPType &base_type = field_cpp_type.base_type(); + const void *input_value = field_cpp_type.get_value_ptr(input_value_or_field); + params.add_readonly_single_input(GVArray::ForSingleRef(base_type, 1, input_value)); + } + + Vector<GMutablePointer, 16> output_buffers; + for (const int i : node->outputs().index_range()) { + const DOutputSocket socket = node.output(i); + if (!socket->is_available()) { + output_buffers.append({}); + continue; + } + const ValueOrFieldCPPType *value_or_field_type = static_cast<const ValueOrFieldCPPType *>( + get_socket_cpp_type(socket)); + const CPPType &base_type = value_or_field_type->base_type(); + void *value_or_field_buffer = allocator.allocate(value_or_field_type->size(), + value_or_field_type->alignment()); + value_or_field_type->default_construct(value_or_field_buffer); + void *value_buffer = value_or_field_type->get_value_ptr(value_or_field_buffer); + base_type.destruct(value_buffer); + params.add_uninitialized_single_output(GMutableSpan{base_type, value_buffer, 1}); + output_buffers.append({value_or_field_type, value_or_field_buffer}); + } + + MFContextBuilder context; + fn.call(IndexRange(1), params, context); + + for (const int i : output_buffers.index_range()) { + GMutablePointer buffer = output_buffers[i]; + if (buffer.get() == nullptr) { + continue; + } + const DOutputSocket socket = node.output(i); + this->forward_output(socket, buffer, run_state); + + OutputState &output_state = node_state.outputs[i]; + output_state.has_been_computed = true; + } + } + + void execute_unknown_node(const DNode node, NodeState &node_state, NodeTaskRunState *run_state) { LinearAllocator<> &allocator = local_allocators_.local(); for (const OutputSocketRef *socket : node->outputs()) { @@ -1002,13 +1128,16 @@ class GeometryNodesEvaluator { output_state.has_been_computed = true; void *buffer = allocator.allocate(type->size(), type->alignment()); this->construct_default_value(*type, buffer); - this->forward_output({node.context(), socket}, {*type, buffer}); + this->forward_output({node.context(), socket}, {*type, buffer}, run_state); } } - void node_task_postprocessing(const DNode node, NodeState &node_state, bool was_executed) + void node_task_postprocessing(const DNode node, + NodeState &node_state, + bool was_executed, + NodeTaskRunState *run_state) { - this->with_locked_node(node, node_state, [&](LockedNode &locked_node) { + this->with_locked_node(node, node_state, run_state, [&](LockedNode &locked_node) { const bool node_has_finished = this->finish_node_if_possible(locked_node); const bool reschedule_requested = node_state.schedule_state == NodeScheduleState::RunningAndRescheduled; @@ -1089,10 +1218,9 @@ class GeometryNodesEvaluator { /** * Load the required input from the socket or trigger nodes to the left to compute the value. - * When this function is called, the node will always be executed again eventually (either - * immediately, or when all required inputs have been computed by other nodes). + * \return True when the node will be triggered by another node again when the value is computed. */ - void set_input_required(LockedNode &locked_node, const DInputSocket input_socket) + bool set_input_required(LockedNode &locked_node, const DInputSocket input_socket) { BLI_assert(locked_node.node == input_socket.node()); InputState &input_state = locked_node.node_state.inputs[input_socket->index()]; @@ -1100,19 +1228,16 @@ class GeometryNodesEvaluator { /* Value set as unused cannot become used again. */ BLI_assert(input_state.usage != ValueUsage::Unused); - if (input_state.usage == ValueUsage::Required) { - /* The value is already required, but the node might expect to be evaluated again. */ - this->schedule_node(locked_node); - /* Returning here also ensure that the code below is executed at most once per input. */ - return; + if (input_state.was_ready_for_execution) { + return false; } - input_state.usage = ValueUsage::Required; - if (input_state.was_ready_for_execution) { - /* The value was already ready, but the node might expect to be evaluated again. */ - this->schedule_node(locked_node); - return; + if (input_state.usage == ValueUsage::Required) { + /* If the input was not ready for execution but is required, the node will be triggered again + * once the input has been computed. */ + return true; } + input_state.usage = ValueUsage::Required; /* Count how many values still have to be added to this input until it is "complete". */ int missing_values = 0; @@ -1127,9 +1252,7 @@ class GeometryNodesEvaluator { } } if (missing_values == 0) { - /* The input is fully available already, but the node might expect to be evaluated again. */ - this->schedule_node(locked_node); - return; + return false; } /* Increase the total number of missing required inputs. This ensures that the node will be * scheduled correctly when all inputs have been provided. */ @@ -1144,30 +1267,28 @@ class GeometryNodesEvaluator { /* If there are no origin sockets, just load the value from the socket directly. */ this->load_unlinked_input_value(locked_node, input_socket, input_state, input_socket); locked_node.node_state.missing_required_inputs -= 1; - this->schedule_node(locked_node); - return; + return false; } - bool will_be_triggered_by_other_node = false; + bool requested_from_other_node = false; for (const DSocket &origin_socket : origin_sockets) { if (origin_socket->is_input()) { /* Load the value directly from the origin socket. In most cases this is an unlinked * group input. */ this->load_unlinked_input_value(locked_node, input_socket, input_state, origin_socket); locked_node.node_state.missing_required_inputs -= 1; - this->schedule_node(locked_node); } else { /* The value has not been computed yet, so when it will be forwarded by another node, this * node will be triggered. */ - will_be_triggered_by_other_node = true; - + requested_from_other_node = true; locked_node.delayed_required_outputs.append(DOutputSocket(origin_socket)); } } /* If this node will be triggered by another node, we don't have to schedule it now. */ - if (!will_be_triggered_by_other_node) { - this->schedule_node(locked_node); + if (requested_from_other_node) { + return true; } + return false; } void set_input_unused(LockedNode &locked_node, const DInputSocket socket) @@ -1203,13 +1324,13 @@ class GeometryNodesEvaluator { }); } - void send_output_required_notification(const DOutputSocket socket) + void send_output_required_notification(const DOutputSocket socket, NodeTaskRunState *run_state) { const DNode node = socket.node(); NodeState &node_state = this->get_node_state(node); OutputState &output_state = node_state.outputs[socket->index()]; - this->with_locked_node(node, node_state, [&](LockedNode &locked_node) { + this->with_locked_node(node, node_state, run_state, [&](LockedNode &locked_node) { if (output_state.output_usage == ValueUsage::Required) { /* Output is marked as required already. So the node is scheduled already. */ return; @@ -1221,13 +1342,13 @@ class GeometryNodesEvaluator { }); } - void send_output_unused_notification(const DOutputSocket socket) + void send_output_unused_notification(const DOutputSocket socket, NodeTaskRunState *run_state) { const DNode node = socket.node(); NodeState &node_state = this->get_node_state(node); OutputState &output_state = node_state.outputs[socket->index()]; - this->with_locked_node(node, node_state, [&](LockedNode &locked_node) { + this->with_locked_node(node, node_state, run_state, [&](LockedNode &locked_node) { output_state.potential_users -= 1; if (output_state.potential_users == 0) { /* The socket might be required even though the output is not used by other sockets. That @@ -1253,8 +1374,11 @@ class GeometryNodesEvaluator { /** * Moves a newly computed value from an output socket to all the inputs that might need it. + * Takes ownership of the value and destructs if it is unused. */ - void forward_output(const DOutputSocket from_socket, GMutablePointer value_to_forward) + void forward_output(const DOutputSocket from_socket, + GMutablePointer value_to_forward, + NodeTaskRunState *run_state) { BLI_assert(value_to_forward.get() != nullptr); @@ -1307,12 +1431,12 @@ class GeometryNodesEvaluator { } else { /* The value has been converted. */ - this->add_value_to_input_socket(to_socket, from_socket, current_value); + this->add_value_to_input_socket(to_socket, from_socket, current_value, run_state); } }); this->log_socket_value(log_original_value_sockets, value_to_forward); this->forward_to_sockets_with_same_type( - allocator, forward_original_value_sockets, value_to_forward, from_socket); + allocator, forward_original_value_sockets, value_to_forward, from_socket, run_state); } bool should_forward_to_socket(const DInputSocket socket) @@ -1334,7 +1458,8 @@ class GeometryNodesEvaluator { void forward_to_sockets_with_same_type(LinearAllocator<> &allocator, Span<DInputSocket> to_sockets, GMutablePointer value_to_forward, - const DOutputSocket from_socket) + const DOutputSocket from_socket, + NodeTaskRunState *run_state) { if (to_sockets.is_empty()) { /* Value is not used anymore, so it can be destructed. */ @@ -1343,7 +1468,7 @@ class GeometryNodesEvaluator { else if (to_sockets.size() == 1) { /* Value is only used by one input socket, no need to copy it. */ const DInputSocket to_socket = to_sockets[0]; - this->add_value_to_input_socket(to_socket, from_socket, value_to_forward); + this->add_value_to_input_socket(to_socket, from_socket, value_to_forward, run_state); } else { /* Multiple inputs use the value, make a copy for every input except for one. */ @@ -1353,17 +1478,18 @@ class GeometryNodesEvaluator { for (const DInputSocket &to_socket : to_sockets.drop_front(1)) { void *buffer = allocator.allocate(type.size(), type.alignment()); type.copy_construct(value_to_forward.get(), buffer); - this->add_value_to_input_socket(to_socket, from_socket, {type, buffer}); + this->add_value_to_input_socket(to_socket, from_socket, {type, buffer}, run_state); } /* Forward the original value to one of the targets. */ const DInputSocket to_socket = to_sockets[0]; - this->add_value_to_input_socket(to_socket, from_socket, value_to_forward); + this->add_value_to_input_socket(to_socket, from_socket, value_to_forward, run_state); } } void add_value_to_input_socket(const DInputSocket socket, const DOutputSocket origin, - GMutablePointer value) + GMutablePointer value, + NodeTaskRunState *run_state) { BLI_assert(socket->is_available()); @@ -1371,7 +1497,7 @@ class GeometryNodesEvaluator { NodeState &node_state = this->get_node_state(node); InputState &input_state = node_state.inputs[socket->index()]; - this->with_locked_node(node, node_state, [&](LockedNode &locked_node) { + this->with_locked_node(node, node_state, run_state, [&](LockedNode &locked_node) { if (socket->is_multi_input_socket()) { /* Add a new value to the multi-input. */ MultiInputValue &multi_value = *input_state.value.multi; @@ -1398,6 +1524,14 @@ class GeometryNodesEvaluator { }); } + /** + * Loads the value of a socket that is not computed by another node. Note that the socket may + * still be linked to e.g. a Group Input node, but the socket on the outside is not connected to + * anything. + * + * \param input_socket: The socket of the node that wants to use the value. + * \param origin_socket: The socket that we want to load the value from. + */ void load_unlinked_input_value(LockedNode &locked_node, const DInputSocket input_socket, InputState &input_state, @@ -1417,7 +1551,15 @@ class GeometryNodesEvaluator { else { SingleInputValue &single_value = *input_state.value.single; single_value.value = value.get(); - this->log_socket_value({input_socket}, value); + Vector<DSocket> sockets_to_log_to = {input_socket}; + if (origin_socket != input_socket) { + /* This might log the socket value for the #origin_socket more than once, but this is + * handled by the logging system gracefully. */ + sockets_to_log_to.append(origin_socket); + } + /* TODO: Log to the intermediate sockets between the group input and where the value is + * actually used as well. */ + this->log_socket_value(sockets_to_log_to, value); } } @@ -1466,19 +1608,28 @@ class GeometryNodesEvaluator { from_type.copy_construct(from_value, to_value); return; } - - const FieldCPPType *from_field_type = dynamic_cast<const FieldCPPType *>(&from_type); - const FieldCPPType *to_field_type = dynamic_cast<const FieldCPPType *>(&to_type); + const ValueOrFieldCPPType *from_field_type = dynamic_cast<const ValueOrFieldCPPType *>( + &from_type); + const ValueOrFieldCPPType *to_field_type = dynamic_cast<const ValueOrFieldCPPType *>(&to_type); if (from_field_type != nullptr && to_field_type != nullptr) { - const CPPType &from_base_type = from_field_type->field_type(); - const CPPType &to_base_type = to_field_type->field_type(); + const CPPType &from_base_type = from_field_type->base_type(); + const CPPType &to_base_type = to_field_type->base_type(); if (conversions_.is_convertible(from_base_type, to_base_type)) { - const MultiFunction &fn = *conversions_.get_conversion_multi_function( - MFDataType::ForSingle(from_base_type), MFDataType::ForSingle(to_base_type)); - const GField &from_field = *(const GField *)from_value; - auto operation = std::make_shared<fn::FieldOperation>(fn, Vector<GField>{from_field}); - new (to_value) GField(std::move(operation), 0); + if (from_field_type->is_field(from_value)) { + const GField &from_field = *from_field_type->get_field_ptr(from_value); + const MultiFunction &fn = *conversions_.get_conversion_multi_function( + MFDataType::ForSingle(from_base_type), MFDataType::ForSingle(to_base_type)); + auto operation = std::make_shared<fn::FieldOperation>(fn, Vector<GField>{from_field}); + to_field_type->construct_from_field(to_value, GField(std::move(operation), 0)); + } + else { + to_field_type->default_construct(to_value); + const void *from_value_ptr = from_field_type->get_value_ptr(from_value); + void *to_value_ptr = to_field_type->get_value_ptr(to_value); + conversions_.get_conversion_functions(from_base_type, to_base_type) + ->convert_single_to_initialized(from_value_ptr, to_value_ptr); + } return; } } @@ -1494,14 +1645,6 @@ class GeometryNodesEvaluator { void construct_default_value(const CPPType &type, void *r_value) { - if (const FieldCPPType *field_cpp_type = dynamic_cast<const FieldCPPType *>(&type)) { - const CPPType &base_type = field_cpp_type->field_type(); - auto constant_fn = std::make_unique<fn::CustomMF_GenericConstant>( - base_type, base_type.default_value(), false); - auto operation = std::make_shared<fn::FieldOperation>(std::move(constant_fn)); - new (r_value) GField(std::move(operation), 0); - return; - } type.copy_construct(type.default_value(), r_value); } @@ -1533,10 +1676,21 @@ class GeometryNodesEvaluator { params_.geo_logger->local().log_value_for_sockets(sockets, value); } + void log_debug_message(DNode node, std::string message) + { + if (params_.geo_logger == nullptr) { + return; + } + params_.geo_logger->local().log_debug_message(node, std::move(message)); + } + /* In most cases when `NodeState` is accessed, the node has to be locked first to avoid race * conditions. */ template<typename Function> - void with_locked_node(const DNode node, NodeState &node_state, const Function &function) + void with_locked_node(const DNode node, + NodeState &node_state, + NodeTaskRunState *run_state, + const Function &function) { LockedNode locked_node{node, node_state}; @@ -1549,21 +1703,32 @@ class GeometryNodesEvaluator { /* Then send notifications to the other nodes after the node state is unlocked. This avoids * locking two nodes at the same time on this thread and helps to prevent deadlocks. */ for (const DOutputSocket &socket : locked_node.delayed_required_outputs) { - this->send_output_required_notification(socket); + this->send_output_required_notification(socket, run_state); } for (const DOutputSocket &socket : locked_node.delayed_unused_outputs) { - this->send_output_unused_notification(socket); - } - for (const DNode &node : locked_node.delayed_scheduled_nodes) { - this->add_node_to_task_pool(node); + this->send_output_unused_notification(socket, run_state); + } + for (const DNode &node_to_schedule : locked_node.delayed_scheduled_nodes) { + if (run_state != nullptr && !run_state->next_node_to_run) { + /* Execute the node on the same thread after the current node finished. */ + /* Currently, this assumes that it is always best to run the first node that is scheduled + * on the same thread. That is usually correct, because the geometry socket which carries + * the most data usually comes first in nodes. */ + run_state->next_node_to_run = node_to_schedule; + } + else { + /* Push the node to the task pool so that another thread can start working on it. */ + this->add_node_to_task_pool(node_to_schedule); + } } } }; NodeParamsProvider::NodeParamsProvider(GeometryNodesEvaluator &evaluator, DNode dnode, - NodeState &node_state) - : evaluator_(evaluator), node_state_(node_state) + NodeState &node_state, + NodeTaskRunState *run_state) + : evaluator_(evaluator), node_state_(node_state), run_state_(run_state) { this->dnode = dnode; this->self_object = evaluator.params_.self_object; @@ -1671,7 +1836,7 @@ void NodeParamsProvider::set_output(StringRef identifier, GMutablePointer value) OutputState &output_state = node_state_.outputs[socket->index()]; BLI_assert(!output_state.has_been_computed); - evaluator_.forward_output(socket, value); + evaluator_.forward_output(socket, value, run_state_); output_state.has_been_computed = true; } @@ -1685,8 +1850,12 @@ bool NodeParamsProvider::lazy_require_input(StringRef identifier) if (input_state.was_ready_for_execution) { return false; } - evaluator_.with_locked_node(this->dnode, node_state_, [&](LockedNode &locked_node) { - evaluator_.set_input_required(locked_node, socket); + evaluator_.with_locked_node(this->dnode, node_state_, run_state_, [&](LockedNode &locked_node) { + if (!evaluator_.set_input_required(locked_node, socket)) { + /* Schedule the currently executed node again because the value is available now but was not + * ready for the current execution. */ + evaluator_.schedule_node(locked_node); + } }); return true; } @@ -1696,7 +1865,7 @@ void NodeParamsProvider::set_input_unused(StringRef identifier) const DInputSocket socket = this->dnode.input_by_identifier(identifier); BLI_assert(socket); - evaluator_.with_locked_node(this->dnode, node_state_, [&](LockedNode &locked_node) { + evaluator_.with_locked_node(this->dnode, node_state_, run_state_, [&](LockedNode &locked_node) { evaluator_.set_input_unused(locked_node, socket); }); } @@ -1726,6 +1895,29 @@ bool NodeParamsProvider::lazy_output_is_required(StringRef identifier) const return output_state.output_usage_for_execution == ValueUsage::Required; } +void NodeParamsProvider::set_default_remaining_outputs() +{ + LinearAllocator<> &allocator = evaluator_.local_allocators_.local(); + + for (const int i : this->dnode->outputs().index_range()) { + OutputState &output_state = node_state_.outputs[i]; + if (output_state.has_been_computed) { + continue; + } + if (output_state.output_usage_for_execution == ValueUsage::Unused) { + continue; + } + + const DOutputSocket socket = this->dnode.output(i); + const CPPType *type = get_socket_cpp_type(socket); + BLI_assert(type != nullptr); + void *buffer = allocator.allocate(type->size(), type->alignment()); + type->copy_construct(type->default_value(), buffer); + evaluator_.forward_output(socket, {type, buffer}, run_state_); + output_state.has_been_computed = true; + } +} + void evaluate_geometry_nodes(GeometryNodesEvaluationParams ¶ms) { GeometryNodesEvaluator evaluator{params}; diff --git a/source/blender/modifiers/intern/MOD_solidify_extrude.c b/source/blender/modifiers/intern/MOD_solidify_extrude.c index 54a508ff5e2..20d65b9d9f8 100644 --- a/source/blender/modifiers/intern/MOD_solidify_extrude.c +++ b/source/blender/modifiers/intern/MOD_solidify_extrude.c @@ -183,6 +183,7 @@ static void mesh_calc_hq_normal(Mesh *mesh, float (*poly_nors)[3], float (*r_ver /* -------------------------------------------------------------------- */ /** \name Main Solidify Function * \{ */ + /* NOLINTNEXTLINE: readability-function-size */ Mesh *MOD_solidify_extrude_modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *mesh) { diff --git a/source/blender/modifiers/intern/MOD_solidify_nonmanifold.c b/source/blender/modifiers/intern/MOD_solidify_nonmanifold.c index d4aaefcfe05..997f5943060 100644 --- a/source/blender/modifiers/intern/MOD_solidify_nonmanifold.c +++ b/source/blender/modifiers/intern/MOD_solidify_nonmanifold.c @@ -74,6 +74,7 @@ static float angle_signed_on_axis_normalized_v3v3_v3(const float n[3], static float clamp_nonzero(const float value, const float epsilon) { BLI_assert(!(epsilon < 0.0f)); + /* Return closest value with `abs(value) >= epsilon`. */ if (value < 0.0f) { return min_ff(value, -epsilon); } @@ -171,15 +172,22 @@ Mesh *MOD_solidify_nonmanifold_modifyMesh(ModifierData *md, float(*poly_nors)[3] = NULL; + /* #ofs_front and #ofs_back are the offset from the original + * surface along the normal, where #oft_front is along the positive + * and #oft_back is along the negative normal. */ const float ofs_front = (smd->offset_fac + 1.0f) * 0.5f * smd->offset; const float ofs_back = ofs_front - smd->offset * smd->offset_fac; - const float ofs_front_clamped = clamp_nonzero(smd->offset > 0 ? ofs_front : ofs_back, 1e-5f); - const float ofs_back_clamped = clamp_nonzero(smd->offset > 0 ? ofs_back : ofs_front, 1e-5f); + /* #ofs_front_clamped and #ofs_back_clamped are the same as + * #ofs_front and #ofs_back, but never zero. */ + const float ofs_front_clamped = clamp_nonzero(ofs_front, 1e-5f); + const float ofs_back_clamped = clamp_nonzero(ofs_back, 1e-5f); const float offset_fac_vg = smd->offset_fac_vg; const float offset_fac_vg_inv = 1.0f - smd->offset_fac_vg; const float offset = fabsf(smd->offset) * smd->offset_clamp; const bool do_angle_clamp = smd->flag & MOD_SOLIDIFY_OFFSET_ANGLE_CLAMP; - const bool do_flip = (smd->flag & MOD_SOLIDIFY_FLIP) != 0; + /* #do_flip, flips the normals of the result. This is inverted if negative thickness + * is used, since simple solidify with negative thickness keeps the faces facing outside. */ + const bool do_flip = ((smd->flag & MOD_SOLIDIFY_FLIP) != 0) == (smd->offset > 0); const bool do_rim = smd->flag & MOD_SOLIDIFY_RIM; const bool do_shell = ((smd->flag & MOD_SOLIDIFY_RIM) && (smd->flag & MOD_SOLIDIFY_NOSHELL)) == 0; diff --git a/source/blender/modifiers/intern/MOD_ui_common.c b/source/blender/modifiers/intern/MOD_ui_common.c index 9937a2342c3..fc1f6d33b25 100644 --- a/source/blender/modifiers/intern/MOD_ui_common.c +++ b/source/blender/modifiers/intern/MOD_ui_common.c @@ -100,9 +100,6 @@ static void set_modifier_expand_flag(const bContext *UNUSED(C), Panel *panel, sh /** \name Modifier Panel Layouts * \{ */ -/** - * Draw modifier error message. - */ void modifier_panel_end(uiLayout *layout, PointerRNA *ptr) { ModifierData *md = ptr->data; @@ -132,14 +129,11 @@ PointerRNA *modifier_panel_get_property_pointers(Panel *panel, PointerRNA *r_ob_ uiBlock *block = uiLayoutGetBlock(panel->layout); UI_block_lock_set(block, ID_IS_LINKED((Object *)ptr->owner_id), ERROR_LIBDATA_MESSAGE); - uiLayoutSetContextPointer(panel->layout, "modifier", ptr); + UI_panel_context_pointer_set(panel, "modifier", ptr); return ptr; } -/** - * Helper function for modifier layouts to draw vertex group settings. - */ void modifier_vgroup_ui(uiLayout *layout, PointerRNA *ptr, PointerRNA *ob_ptr, @@ -304,7 +298,7 @@ static void modifier_panel_header(const bContext *C, Panel *panel) ModifierData *md = (ModifierData *)ptr->data; Object *ob = (Object *)ptr->owner_id; - uiLayoutSetContextPointer(panel->layout, "modifier", ptr); + UI_panel_context_pointer_set(panel, "modifier", ptr); const ModifierTypeInfo *mti = BKE_modifier_get_info(md->type); Scene *scene = CTX_data_scene(C); @@ -429,9 +423,6 @@ static void modifier_panel_header(const bContext *C, Panel *panel) /** \name Modifier Registration Helpers * \{ */ -/** - * Create a panel in the context's region - */ PanelType *modifier_panel_register(ARegionType *region_type, ModifierType type, PanelDrawFn draw) { PanelType *panel_type = MEM_callocN(sizeof(PanelType), __func__); @@ -458,12 +449,6 @@ PanelType *modifier_panel_register(ARegionType *region_type, ModifierType type, return panel_type; } -/** - * Add a child panel to the parent. - * - * \note To create the panel type's idname, it appends the \a name argument to the \a parent's - * idname. - */ PanelType *modifier_subpanel_register(ARegionType *region_type, const char *name, const char *label, diff --git a/source/blender/modifiers/intern/MOD_ui_common.h b/source/blender/modifiers/intern/MOD_ui_common.h index e7f801049b8..9662b181013 100644 --- a/source/blender/modifiers/intern/MOD_ui_common.h +++ b/source/blender/modifiers/intern/MOD_ui_common.h @@ -37,6 +37,9 @@ typedef void (*PanelDrawFn)(const bContext *, struct Panel *); void modifier_panel_buttons(const struct bContext *C, struct Panel *panel); +/** + * Helper function for modifier layouts to draw vertex group settings. + */ void modifier_vgroup_ui(struct uiLayout *layout, struct PointerRNA *ptr, struct PointerRNA *ob_ptr, @@ -44,15 +47,27 @@ void modifier_vgroup_ui(struct uiLayout *layout, const char *invert_vgroup_prop, const char *text); +/** + * Draw modifier error message. + */ void modifier_panel_end(struct uiLayout *layout, PointerRNA *ptr); struct PointerRNA *modifier_panel_get_property_pointers(struct Panel *panel, struct PointerRNA *r_ob_ptr); +/** + * Create a panel in the context's region + */ struct PanelType *modifier_panel_register(struct ARegionType *region_type, ModifierType type, PanelDrawFn draw); +/** + * Add a child panel to the parent. + * + * \note To create the panel type's idname, it appends the \a name argument to the \a parent's + * idname. + */ struct PanelType *modifier_subpanel_register(struct ARegionType *region_type, const char *name, const char *label, diff --git a/source/blender/modifiers/intern/MOD_util.c b/source/blender/modifiers/intern/MOD_util.c index d57e92b4b35..16ef65f7838 100644 --- a/source/blender/modifiers/intern/MOD_util.c +++ b/source/blender/modifiers/intern/MOD_util.c @@ -72,7 +72,6 @@ void MOD_init_texture(MappingInfoModifierData *dmd, const ModifierEvalContext *c } /* TODO: to be renamed to get_texture_coords once we are done with moving modifiers to Mesh. */ -/** \param cos: may be NULL, in which case we use directly mesh vertices' coordinates. */ void MOD_get_texture_coords(MappingInfoModifierData *dmd, const ModifierEvalContext *UNUSED(ctx), Object *ob, @@ -182,7 +181,6 @@ void MOD_previous_vcos_store(ModifierData *md, const float (*vert_coords)[3]) /* lattice/mesh modifier too */ } -/* returns a mesh if mesh == NULL, for deforming modifiers that need it */ Mesh *MOD_deform_mesh_eval_get(Object *ob, struct BMEditMesh *em, Mesh *mesh, @@ -219,8 +217,7 @@ Mesh *MOD_deform_mesh_eval_get(Object *ob, } if (use_orco) { - CustomData_add_layer( - &mesh->vdata, CD_ORCO, CD_ASSIGN, BKE_mesh_orco_verts_get(ob), mesh->totvert); + BKE_mesh_orco_ensure(ob, mesh); } } else if (ELEM(ob->type, OB_FONT, OB_CURVE, OB_SURF)) { @@ -289,7 +286,6 @@ void MOD_depsgraph_update_object_bone_relation(struct DepsNodeHandle *node, } } -/* only called by BKE_modifier.h/modifier.c */ void modifier_type_init(ModifierTypeInfo *types[]) { #define INIT_TYPE(typeName) (types[eModifierType_##typeName] = &modifierType_##typeName) diff --git a/source/blender/modifiers/intern/MOD_util.h b/source/blender/modifiers/intern/MOD_util.h index a3db848874e..a6fc2749e71 100644 --- a/source/blender/modifiers/intern/MOD_util.h +++ b/source/blender/modifiers/intern/MOD_util.h @@ -32,6 +32,9 @@ struct ModifierEvalContext; struct Object; void MOD_init_texture(struct MappingInfoModifierData *dmd, const struct ModifierEvalContext *ctx); +/** + * \param cos: may be NULL, in which case we use directly mesh vertices' coordinates. + */ void MOD_get_texture_coords(struct MappingInfoModifierData *dmd, const struct ModifierEvalContext *ctx, struct Object *ob, @@ -41,6 +44,9 @@ void MOD_get_texture_coords(struct MappingInfoModifierData *dmd, void MOD_previous_vcos_store(struct ModifierData *md, const float (*vert_coords)[3]); +/** + * \returns a mesh if mesh == NULL, for deforming modifiers that need it. + */ struct Mesh *MOD_deform_mesh_eval_get(struct Object *ob, struct BMEditMesh *em, struct Mesh *mesh, diff --git a/source/blender/modifiers/intern/MOD_weightvg_util.c b/source/blender/modifiers/intern/MOD_weightvg_util.c index e403051d1be..cd9e5162527 100644 --- a/source/blender/modifiers/intern/MOD_weightvg_util.c +++ b/source/blender/modifiers/intern/MOD_weightvg_util.c @@ -57,12 +57,6 @@ #include "MOD_weightvg_util.h" #include "RE_texture.h" /* Texture masking. */ -/* Maps new_w weights in place, using either one of the predefined functions, or a custom curve. - * Return values are in new_w. - * If indices is not NULL, it must be a table of same length as org_w and new_w, - * mapping to the real vertex index (in case the weight tables do not cover the whole vertices...). - * cmap might be NULL, in which case curve mapping mode will return unmodified data. - */ void weightvg_do_map( int num, float *new_w, short falloff_type, const bool do_invert, CurveMapping *cmap, RNG *rng) { @@ -125,13 +119,6 @@ void weightvg_do_map( } } -/* Applies new_w weights to org_w ones, using either a texture, vgroup or constant value as factor. - * Return values are in org_w. - * If indices is not NULL, it must be a table of same length as org_w and new_w, - * mapping to the real vertex index (in case the weight tables do not cover the whole vertices...). - * XXX The standard "factor" value is assumed in [0.0, 1.0] range. - * Else, weird results might appear. - */ void weightvg_do_mask(const ModifierEvalContext *ctx, const int num, const int *indices, @@ -267,12 +254,6 @@ void weightvg_do_mask(const ModifierEvalContext *ctx, } } -/* Applies weights to given vgroup (defgroup), and optionally add/remove vertices from the group. - * If dws is not NULL, it must be an array of MDeformWeight pointers of same length as weights (and - * defgrp_idx can then have any value). - * If indices is not NULL, it must be an array of same length as weights, mapping to the real - * vertex index (in case the weight array does not cover the whole vertices...). - */ void weightvg_update_vg(MDeformVert *dvert, int defgrp_idx, MDeformWeight **dws, @@ -340,8 +321,6 @@ void weightvg_update_vg(MDeformVert *dvert, } } -/* Common vertex weight mask interface elements for the modifier panels. - */ void weightvg_ui_common(const bContext *C, PointerRNA *ob_ptr, PointerRNA *ptr, uiLayout *layout) { PointerRNA mask_texture_ptr = RNA_pointer_get(ptr, "mask_texture"); diff --git a/source/blender/modifiers/intern/MOD_weightvg_util.h b/source/blender/modifiers/intern/MOD_weightvg_util.h index 796603289ca..00aecd7342c 100644 --- a/source/blender/modifiers/intern/MOD_weightvg_util.h +++ b/source/blender/modifiers/intern/MOD_weightvg_util.h @@ -46,13 +46,21 @@ struct uiLayout; * Util functions. * **************************************/ -/* We cannot divide by zero (what a surprise...). - * So if -MOD_WEIGHTVGROUP_DIVMODE_ZEROFLOOR < weightf < MOD_WEIGHTVGROUP_DIVMODE_ZEROFLOOR, +/** + * We cannot divide by zero (what a surprise...). + * So if `-MOD_WEIGHTVGROUP_DIVMODE_ZEROFLOOR < weightf < MOD_WEIGHTVGROUP_DIVMODE_ZEROFLOOR`, * we clamp weightf to this value (or its negative version). * Also used to avoid null power factor. */ #define MOD_WVG_ZEROFLOOR 1.0e-32f +/** + * Maps new_w weights in place, using either one of the predefined functions, or a custom curve. + * Return values are in new_w. + * If indices is not NULL, it must be a table of same length as org_w and new_w, + * mapping to the real vertex index (in case the weight tables do not cover the whole vertices...). + * cmap might be NULL, in which case curve mapping mode will return unmodified data. + */ void weightvg_do_map(int num, float *new_w, short falloff_type, @@ -60,6 +68,14 @@ void weightvg_do_map(int num, struct CurveMapping *cmap, struct RNG *rng); +/** + * Applies new_w weights to org_w ones, using either a texture, vgroup or constant value as factor. + * Return values are in org_w. + * If indices is not NULL, it must be a table of same length as org_w and new_w, + * mapping to the real vertex index (in case the weight tables do not cover the whole vertices...). + * XXX The standard "factor" value is assumed in [0.0, 1.0] range. + * Else, weird results might appear. + */ void weightvg_do_mask(const ModifierEvalContext *ctx, const int num, const int *indices, @@ -78,6 +94,13 @@ void weightvg_do_mask(const ModifierEvalContext *ctx, const char *tex_uvlayer_name, const bool invert_vgroup_mask); +/** + * Applies weights to given vgroup (defgroup), and optionally add/remove vertices from the group. + * If dws is not NULL, it must be an array of #MDeformWeight pointers of same length as weights + * (and defgrp_idx can then have any value). + * If indices is not NULL, it must be an array of same length as weights, mapping to the real + * vertex index (in case the weight array does not cover the whole vertices...). + */ void weightvg_update_vg(struct MDeformVert *dvert, int defgrp_idx, struct MDeformWeight **dws, @@ -90,4 +113,7 @@ void weightvg_update_vg(struct MDeformVert *dvert, const float rem_thresh, const bool do_normalize); +/** + * Common vertex weight mask interface elements for the modifier panels. + */ void weightvg_ui_common(const bContext *C, PointerRNA *ob_ptr, PointerRNA *ptr, uiLayout *layout); diff --git a/source/blender/modifiers/intern/MOD_weld.c b/source/blender/modifiers/intern/MOD_weld.c index 503297d5985..f842bef3298 100644 --- a/source/blender/modifiers/intern/MOD_weld.c +++ b/source/blender/modifiers/intern/MOD_weld.c @@ -363,7 +363,9 @@ static void weld_assert_poly_len(const WeldPoly *wp, const WeldLoop *wloop) uint max_len = wp->loop_end - wp->loop_start + 1; BLI_assert(len <= max_len); } -#endif + +#endif /* USE_WELD_DEBUG */ + /** \} */ /* -------------------------------------------------------------------- */ |