Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'source/blender/modifiers/intern')
-rw-r--r--source/blender/modifiers/intern/MOD_curve.c2
-rw-r--r--source/blender/modifiers/intern/MOD_mirror.c10
-rw-r--r--source/blender/modifiers/intern/MOD_nodes.cc73
-rw-r--r--source/blender/modifiers/intern/MOD_nodes_evaluator.cc414
-rw-r--r--source/blender/modifiers/intern/MOD_solidify_extrude.c1
-rw-r--r--source/blender/modifiers/intern/MOD_solidify_nonmanifold.c14
-rw-r--r--source/blender/modifiers/intern/MOD_ui_common.c19
-rw-r--r--source/blender/modifiers/intern/MOD_ui_common.h15
-rw-r--r--source/blender/modifiers/intern/MOD_util.c6
-rw-r--r--source/blender/modifiers/intern/MOD_util.h6
-rw-r--r--source/blender/modifiers/intern/MOD_weightvg_util.c21
-rw-r--r--source/blender/modifiers/intern/MOD_weightvg_util.h30
-rw-r--r--source/blender/modifiers/intern/MOD_weld.c4
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 &params_;
- const blender::nodes::DataTypeConversions &conversions_;
+ const blender::bke::DataTypeConversions &conversions_;
friend NodeParamsProvider;
@@ -441,7 +458,7 @@ class GeometryNodesEvaluator {
GeometryNodesEvaluator(GeometryNodesEvaluationParams &params)
: 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 &params)
{
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 */
+
/** \} */
/* -------------------------------------------------------------------- */