Geometry Nodes: Node group operators initial phase #108947
|
@ -11,6 +11,7 @@ set(INC
|
|||
../../gpu
|
||||
../../makesdna
|
||||
../../makesrna
|
||||
../../nodes
|
||||
../../windowmanager
|
||||
../../../../intern/clog
|
||||
../../../../intern/guardedalloc
|
||||
|
|
|
@ -58,6 +58,8 @@
|
|||
|
||||
#include "GEO_reverse_uv_sampler.hh"
|
||||
|
||||
#include "curves_intern.hh"
|
||||
|
||||
/**
|
||||
* The code below uses a suffix naming convention to indicate the coordinate space:
|
||||
* `cu`: Local space of the curves object that is being edited.
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#include "WM_api.h"
|
||||
|
||||
#include "BKE_attribute_math.hh"
|
||||
#include "BKE_compute_contexts.hh"
|
||||
#include "BKE_context.h"
|
||||
#include "BKE_curves.hh"
|
||||
#include "BKE_geometry_set.hh"
|
||||
|
@ -36,8 +37,119 @@
|
|||
#include "UI_interface.h"
|
||||
#include "UI_resources.h"
|
||||
|
||||
#include "FN_lazy_function_execute.hh"
|
||||
|
||||
#include "NOD_geometry_nodes_execute.hh"
|
||||
#include "NOD_geometry_nodes_lazy_function.hh"
|
||||
|
||||
namespace blender::ed::curves {
|
||||
|
||||
static GeometrySet compute_geometry(const bNodeTree &btree,
|
||||
const nodes::GeometryNodesLazyFunctionGraphInfo &lf_graph_info,
|
||||
Object &object,
|
||||
Depsgraph &depsgraph,
|
||||
const IDProperty *properties,
|
||||
const bNode &output_node,
|
||||
GeometrySet input_geometry_set)
|
||||
{
|
||||
const nodes::GeometryNodeLazyFunctionGraphMapping &mapping = lf_graph_info.mapping;
|
||||
|
||||
Vector<const lf::OutputSocket *> graph_inputs = mapping.group_input_sockets;
|
||||
graph_inputs.extend(mapping.group_output_used_sockets);
|
||||
graph_inputs.extend(mapping.attribute_set_by_geometry_output.values().begin(),
|
||||
mapping.attribute_set_by_geometry_output.values().end());
|
||||
Vector<const lf::InputSocket *> graph_outputs = mapping.standard_group_output_sockets;
|
||||
|
||||
Array<GMutablePointer> param_inputs(graph_inputs.size());
|
||||
Array<GMutablePointer> param_outputs(graph_outputs.size());
|
||||
Array<std::optional<lf::ValueUsage>> param_input_usages(graph_inputs.size());
|
||||
Array<lf::ValueUsage> param_output_usages(graph_outputs.size(), lf::ValueUsage::Used);
|
||||
Array<bool> param_set_outputs(graph_outputs.size(), false);
|
||||
|
||||
nodes::GeometryNodesLazyFunctionLogger lf_logger(lf_graph_info);
|
||||
nodes::GeometryNodesLazyFunctionSideEffectProvider lf_side_effect_provider;
|
||||
|
||||
lf::GraphExecutor graph_executor{
|
||||
lf_graph_info.graph, graph_inputs, graph_outputs, &lf_logger, &lf_side_effect_provider};
|
||||
|
||||
nodes::GeoNodesModifierData geo_nodes_modifier_data;
|
||||
geo_nodes_modifier_data.depsgraph = &depsgraph;
|
||||
geo_nodes_modifier_data.self_object = &object;
|
||||
|
||||
Set<ComputeContextHash> socket_log_contexts;
|
||||
|
||||
MultiValueMap<ComputeContextHash, const lf::FunctionNode *> r_side_effect_nodes;
|
||||
nodes::GeoNodesLFUserData user_data;
|
||||
user_data.modifier_data = &geo_nodes_modifier_data;
|
||||
bke::ModifierComputeContext modifier_compute_context{nullptr, "actually a node group"};
|
||||
user_data.compute_context = &modifier_compute_context;
|
||||
|
||||
LinearAllocator<> allocator;
|
||||
Vector<GMutablePointer> inputs_to_destruct;
|
||||
|
||||
int input_index = -1;
|
||||
for (const int i : btree.interface_inputs().index_range()) {
|
||||
input_index++;
|
||||
const bNodeSocket &interface_socket = *btree.interface_inputs()[i];
|
||||
if (interface_socket.type == SOCK_GEOMETRY && input_index == 0) {
|
||||
param_inputs[input_index] = &input_geometry_set;
|
||||
continue;
|
||||
}
|
||||
|
||||
const CPPType *type = interface_socket.typeinfo->geometry_nodes_cpp_type;
|
||||
BLI_assert(type != nullptr);
|
||||
void *value = allocator.allocate(type->size(), type->alignment());
|
||||
nodes::initialize_group_input(btree, properties, i, value);
|
||||
param_inputs[input_index] = {type, value};
|
||||
inputs_to_destruct.append({type, value});
|
||||
}
|
||||
|
||||
Array<bool> output_used_inputs(btree.interface_outputs().size(), true);
|
||||
for (const int i : btree.interface_outputs().index_range()) {
|
||||
input_index++;
|
||||
param_inputs[input_index] = &output_used_inputs[i];
|
||||
}
|
||||
|
||||
Array<bke::AnonymousAttributeSet> attributes_to_propagate(
|
||||
mapping.attribute_set_by_geometry_output.size());
|
||||
for (const int i : attributes_to_propagate.index_range()) {
|
||||
input_index++;
|
||||
param_inputs[input_index] = &attributes_to_propagate[i];
|
||||
}
|
||||
|
||||
for (const int i : graph_outputs.index_range()) {
|
||||
const lf::InputSocket &socket = *graph_outputs[i];
|
||||
const CPPType &type = socket.type();
|
||||
void *buffer = allocator.allocate(type.size(), type.alignment());
|
||||
param_outputs[i] = {type, buffer};
|
||||
}
|
||||
|
||||
lf::Context lf_context;
|
||||
lf_context.storage = graph_executor.init_storage(allocator);
|
||||
lf_context.user_data = &user_data;
|
||||
lf::BasicParams lf_params{graph_executor,
|
||||
param_inputs,
|
||||
param_outputs,
|
||||
param_input_usages,
|
||||
param_output_usages,
|
||||
param_set_outputs};
|
||||
graph_executor.execute(lf_params, lf_context);
|
||||
graph_executor.destruct_storage(lf_context.storage);
|
||||
|
||||
for (GMutablePointer &ptr : inputs_to_destruct) {
|
||||
ptr.destruct();
|
||||
}
|
||||
|
||||
GeometrySet output_geometry_set = std::move(*static_cast<GeometrySet *>(param_outputs[0].get()));
|
||||
// store_output_attributes(output_geometry_set, btree, properties, output_node, param_outputs);
|
||||
|
||||
for (GMutablePointer &ptr : param_outputs) {
|
||||
ptr.destruct();
|
||||
}
|
||||
|
||||
return output_geometry_set;
|
||||
}
|
||||
|
||||
static int run_node_group_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
Scene *scene = CTX_data_scene(C);
|
||||
|
@ -55,7 +167,7 @@ static int run_node_group_exec(bContext *C, wmOperator *op)
|
|||
}
|
||||
const bNodeTree &node_tree = reinterpret_cast<const bNodeTree &>(*id);
|
||||
const nodes::GeometryNodesLazyFunctionGraphInfo *lf_graph_info =
|
||||
nodes::ensure_geometry_nodes_lazy_function_graph(tree);
|
||||
nodes::ensure_geometry_nodes_lazy_function_graph(node_tree);
|
||||
if (lf_graph_info == nullptr) {
|
||||
BKE_report(op->reports, RPT_ERROR, "Cannot evaluate node group");
|
||||
return;
|
||||
|
@ -69,7 +181,7 @@ static int run_node_group_exec(bContext *C, wmOperator *op)
|
|||
Curves &curves = *static_cast<Curves *>(object->data);
|
||||
GeometrySet geometry_set = GeometrySet::create_with_curves(&curves,
|
||||
GeometryOwnershipType::Editable);
|
||||
}
|
||||
}
|
||||
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
@ -78,9 +190,9 @@ void CURVES_OT_node_group(wmOperatorType *ot)
|
|||
{
|
||||
ot->name = "Run Node Group";
|
||||
ot->idname = __func__;
|
||||
ot->description = "Dummy"; // TODO: Retrieve from node group.
|
||||
ot->description = "Execute a node group on curves"; // TODO: Retrieve from node group.
|
||||
|
||||
ot->exec = node_group::run_node_group_exec;
|
||||
ot->exec = run_node_group_exec;
|
||||
ot->poll = editable_curves_poll;
|
||||
|
||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
||||
|
|
|
@ -385,234 +385,6 @@ static bool socket_type_has_attribute_toggle(const bNodeSocket &socket)
|
|||
return ELEM(socket.type, SOCK_FLOAT, SOCK_VECTOR, SOCK_BOOLEAN, SOCK_RGBA, SOCK_INT);
|
||||
}
|
||||
|
||||
/**
|
||||
* \return Whether using an attribute to input values of this type is supported, and the node
|
||||
* group's input for this socket accepts a field rather than just single values.
|
||||
*/
|
||||
static bool input_has_attribute_toggle(const bNodeTree &node_tree, const int socket_index)
|
||||
{
|
||||
BLI_assert(node_tree.runtime->field_inferencing_interface);
|
||||
const nodes::FieldInferencingInterface &field_interface =
|
||||
*node_tree.runtime->field_inferencing_interface;
|
||||
return field_interface.inputs[socket_index] != nodes::InputSocketFieldType::None;
|
||||
}
|
||||
|
||||
static std::unique_ptr<IDProperty, bke::idprop::IDPropertyDeleter> id_property_create_from_socket(
|
||||
const bNodeSocket &socket)
|
||||
{
|
||||
switch (socket.type) {
|
||||
case SOCK_FLOAT: {
|
||||
const bNodeSocketValueFloat *value = static_cast<const bNodeSocketValueFloat *>(
|
||||
socket.default_value);
|
||||
auto property = bke::idprop::create(socket.identifier, value->value);
|
||||
IDPropertyUIDataFloat *ui_data = (IDPropertyUIDataFloat *)IDP_ui_data_ensure(property.get());
|
||||
ui_data->base.rna_subtype = value->subtype;
|
||||
ui_data->soft_min = double(value->min);
|
||||
ui_data->soft_max = double(value->max);
|
||||
ui_data->default_value = value->value;
|
||||
return property;
|
||||
}
|
||||
case SOCK_INT: {
|
||||
const bNodeSocketValueInt *value = static_cast<const bNodeSocketValueInt *>(
|
||||
socket.default_value);
|
||||
auto property = bke::idprop::create(socket.identifier, value->value);
|
||||
IDPropertyUIDataInt *ui_data = (IDPropertyUIDataInt *)IDP_ui_data_ensure(property.get());
|
||||
ui_data->base.rna_subtype = value->subtype;
|
||||
ui_data->soft_min = value->min;
|
||||
ui_data->soft_max = value->max;
|
||||
ui_data->default_value = value->value;
|
||||
return property;
|
||||
}
|
||||
case SOCK_VECTOR: {
|
||||
const bNodeSocketValueVector *value = static_cast<const bNodeSocketValueVector *>(
|
||||
socket.default_value);
|
||||
auto property = bke::idprop::create(
|
||||
socket.identifier, Span<float>{value->value[0], value->value[1], value->value[2]});
|
||||
IDPropertyUIDataFloat *ui_data = (IDPropertyUIDataFloat *)IDP_ui_data_ensure(property.get());
|
||||
ui_data->base.rna_subtype = value->subtype;
|
||||
ui_data->soft_min = double(value->min);
|
||||
ui_data->soft_max = double(value->max);
|
||||
ui_data->default_array = (double *)MEM_mallocN(sizeof(double[3]), "mod_prop_default");
|
||||
ui_data->default_array_len = 3;
|
||||
for (const int i : IndexRange(3)) {
|
||||
ui_data->default_array[i] = double(value->value[i]);
|
||||
}
|
||||
return property;
|
||||
}
|
||||
case SOCK_RGBA: {
|
||||
const bNodeSocketValueRGBA *value = static_cast<const bNodeSocketValueRGBA *>(
|
||||
socket.default_value);
|
||||
auto property = bke::idprop::create(
|
||||
socket.identifier,
|
||||
Span<float>{value->value[0], value->value[1], value->value[2], value->value[3]});
|
||||
IDPropertyUIDataFloat *ui_data = (IDPropertyUIDataFloat *)IDP_ui_data_ensure(property.get());
|
||||
ui_data->base.rna_subtype = PROP_COLOR;
|
||||
ui_data->default_array = (double *)MEM_mallocN(sizeof(double[4]), __func__);
|
||||
ui_data->default_array_len = 4;
|
||||
ui_data->min = 0.0;
|
||||
ui_data->max = FLT_MAX;
|
||||
ui_data->soft_min = 0.0;
|
||||
ui_data->soft_max = 1.0;
|
||||
for (const int i : IndexRange(4)) {
|
||||
ui_data->default_array[i] = double(value->value[i]);
|
||||
}
|
||||
return property;
|
||||
}
|
||||
case SOCK_BOOLEAN: {
|
||||
const bNodeSocketValueBoolean *value = static_cast<const bNodeSocketValueBoolean *>(
|
||||
socket.default_value);
|
||||
auto property = bke::idprop::create_bool(socket.identifier, value->value);
|
||||
IDPropertyUIDataBool *ui_data = (IDPropertyUIDataBool *)IDP_ui_data_ensure(property.get());
|
||||
ui_data->default_value = value->value != 0;
|
||||
return property;
|
||||
}
|
||||
case SOCK_STRING: {
|
||||
const bNodeSocketValueString *value = static_cast<const bNodeSocketValueString *>(
|
||||
socket.default_value);
|
||||
auto property = bke::idprop::create(socket.identifier, value->value);
|
||||
IDPropertyUIDataString *ui_data = (IDPropertyUIDataString *)IDP_ui_data_ensure(
|
||||
property.get());
|
||||
ui_data->default_value = BLI_strdup(value->value);
|
||||
return property;
|
||||
}
|
||||
case SOCK_OBJECT: {
|
||||
const bNodeSocketValueObject *value = static_cast<const bNodeSocketValueObject *>(
|
||||
socket.default_value);
|
||||
auto property = bke::idprop::create(socket.identifier, reinterpret_cast<ID *>(value->value));
|
||||
IDPropertyUIDataID *ui_data = (IDPropertyUIDataID *)IDP_ui_data_ensure(property.get());
|
||||
ui_data->id_type = ID_OB;
|
||||
return property;
|
||||
}
|
||||
case SOCK_COLLECTION: {
|
||||
const bNodeSocketValueCollection *value = static_cast<const bNodeSocketValueCollection *>(
|
||||
socket.default_value);
|
||||
return bke::idprop::create(socket.identifier, reinterpret_cast<ID *>(value->value));
|
||||
}
|
||||
case SOCK_TEXTURE: {
|
||||
const bNodeSocketValueTexture *value = static_cast<const bNodeSocketValueTexture *>(
|
||||
socket.default_value);
|
||||
return bke::idprop::create(socket.identifier, reinterpret_cast<ID *>(value->value));
|
||||
}
|
||||
case SOCK_IMAGE: {
|
||||
const bNodeSocketValueImage *value = static_cast<const bNodeSocketValueImage *>(
|
||||
socket.default_value);
|
||||
return bke::idprop::create(socket.identifier, reinterpret_cast<ID *>(value->value));
|
||||
}
|
||||
case SOCK_MATERIAL: {
|
||||
const bNodeSocketValueMaterial *value = static_cast<const bNodeSocketValueMaterial *>(
|
||||
socket.default_value);
|
||||
return bke::idprop::create(socket.identifier, reinterpret_cast<ID *>(value->value));
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static bool id_property_type_matches_socket(const bNodeSocket &socket, const IDProperty &property)
|
||||
{
|
||||
switch (socket.type) {
|
||||
case SOCK_FLOAT:
|
||||
return ELEM(property.type, IDP_FLOAT, IDP_DOUBLE);
|
||||
case SOCK_INT:
|
||||
return property.type == IDP_INT;
|
||||
case SOCK_VECTOR:
|
||||
return property.type == IDP_ARRAY && property.subtype == IDP_FLOAT && property.len == 3;
|
||||
case SOCK_RGBA:
|
||||
return property.type == IDP_ARRAY && property.subtype == IDP_FLOAT && property.len == 4;
|
||||
case SOCK_BOOLEAN:
|
||||
return property.type == IDP_BOOLEAN;
|
||||
case SOCK_STRING:
|
||||
return property.type == IDP_STRING;
|
||||
case SOCK_OBJECT:
|
||||
case SOCK_COLLECTION:
|
||||
case SOCK_TEXTURE:
|
||||
case SOCK_IMAGE:
|
||||
case SOCK_MATERIAL:
|
||||
return property.type == IDP_ID;
|
||||
}
|
||||
BLI_assert_unreachable();
|
||||
return false;
|
||||
}
|
||||
|
||||
static void init_socket_cpp_value_from_property(const IDProperty &property,
|
||||
const eNodeSocketDatatype socket_value_type,
|
||||
void *r_value)
|
||||
{
|
||||
switch (socket_value_type) {
|
||||
case SOCK_FLOAT: {
|
||||
float value = 0.0f;
|
||||
if (property.type == IDP_FLOAT) {
|
||||
value = IDP_Float(&property);
|
||||
}
|
||||
else if (property.type == IDP_DOUBLE) {
|
||||
value = float(IDP_Double(&property));
|
||||
}
|
||||
new (r_value) fn::ValueOrField<float>(value);
|
||||
break;
|
||||
}
|
||||
case SOCK_INT: {
|
||||
int value = IDP_Int(&property);
|
||||
new (r_value) fn::ValueOrField<int>(value);
|
||||
break;
|
||||
}
|
||||
case SOCK_VECTOR: {
|
||||
float3 value;
|
||||
copy_v3_v3(value, (const float *)IDP_Array(&property));
|
||||
new (r_value) fn::ValueOrField<float3>(value);
|
||||
break;
|
||||
}
|
||||
case SOCK_RGBA: {
|
||||
ColorGeometry4f value;
|
||||
copy_v4_v4((float *)value, (const float *)IDP_Array(&property));
|
||||
new (r_value) fn::ValueOrField<ColorGeometry4f>(value);
|
||||
break;
|
||||
}
|
||||
case SOCK_BOOLEAN: {
|
||||
const bool value = IDP_Bool(&property);
|
||||
new (r_value) fn::ValueOrField<bool>(value);
|
||||
break;
|
||||
}
|
||||
case SOCK_STRING: {
|
||||
std::string value = IDP_String(&property);
|
||||
new (r_value) fn::ValueOrField<std::string>(std::move(value));
|
||||
break;
|
||||
}
|
||||
case SOCK_OBJECT: {
|
||||
ID *id = IDP_Id(&property);
|
||||
Object *object = (id && GS(id->name) == ID_OB) ? (Object *)id : nullptr;
|
||||
*(Object **)r_value = object;
|
||||
break;
|
||||
}
|
||||
case SOCK_COLLECTION: {
|
||||
ID *id = IDP_Id(&property);
|
||||
Collection *collection = (id && GS(id->name) == ID_GR) ? (Collection *)id : nullptr;
|
||||
*(Collection **)r_value = collection;
|
||||
break;
|
||||
}
|
||||
case SOCK_TEXTURE: {
|
||||
ID *id = IDP_Id(&property);
|
||||
Tex *texture = (id && GS(id->name) == ID_TE) ? (Tex *)id : nullptr;
|
||||
*(Tex **)r_value = texture;
|
||||
break;
|
||||
}
|
||||
case SOCK_IMAGE: {
|
||||
ID *id = IDP_Id(&property);
|
||||
Image *image = (id && GS(id->name) == ID_IM) ? (Image *)id : nullptr;
|
||||
*(Image **)r_value = image;
|
||||
break;
|
||||
}
|
||||
case SOCK_MATERIAL: {
|
||||
ID *id = IDP_Id(&property);
|
||||
Material *material = (id && GS(id->name) == ID_MA) ? (Material *)id : nullptr;
|
||||
*(Material **)r_value = material;
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
BLI_assert_unreachable();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void update_input_properties_from_node_tree(const bNodeTree &tree,
|
||||
const IDProperty *old_properties,
|
||||
IDProperty &properties)
|
||||
|
@ -1124,126 +896,6 @@ static void store_output_attributes(GeometrySet &geometry,
|
|||
/**
|
||||
* Evaluate a node group to compute the output geometry.
|
||||
*/
|
||||
static GeometrySet compute_geometry(const bNodeTree &btree,
|
||||
const nodes::GeometryNodesLazyFunctionGraphInfo &lf_graph_info,
|
||||
const bNode &output_node,
|
||||
GeometrySet input_geometry_set,
|
||||
NodesModifierData *nmd,
|
||||
const ModifierEvalContext *ctx)
|
||||
{
|
||||
const nodes::GeometryNodeLazyFunctionGraphMapping &mapping = lf_graph_info.mapping;
|
||||
|
||||
Vector<const lf::OutputSocket *> graph_inputs = mapping.group_input_sockets;
|
||||
graph_inputs.extend(mapping.group_output_used_sockets);
|
||||
graph_inputs.extend(mapping.attribute_set_by_geometry_output.values().begin(),
|
||||
mapping.attribute_set_by_geometry_output.values().end());
|
||||
Vector<const lf::InputSocket *> graph_outputs = mapping.standard_group_output_sockets;
|
||||
|
||||
Array<GMutablePointer> param_inputs(graph_inputs.size());
|
||||
Array<GMutablePointer> param_outputs(graph_outputs.size());
|
||||
Array<std::optional<lf::ValueUsage>> param_input_usages(graph_inputs.size());
|
||||
Array<lf::ValueUsage> param_output_usages(graph_outputs.size(), lf::ValueUsage::Used);
|
||||
Array<bool> param_set_outputs(graph_outputs.size(), false);
|
||||
|
||||
nodes::GeometryNodesLazyFunctionLogger lf_logger(lf_graph_info);
|
||||
nodes::GeometryNodesLazyFunctionSideEffectProvider lf_side_effect_provider;
|
||||
|
||||
lf::GraphExecutor graph_executor{
|
||||
lf_graph_info.graph, graph_inputs, graph_outputs, &lf_logger, &lf_side_effect_provider};
|
||||
|
||||
nodes::GeoNodesModifierData geo_nodes_modifier_data;
|
||||
geo_nodes_modifier_data.depsgraph = ctx->depsgraph;
|
||||
geo_nodes_modifier_data.self_object = ctx->object;
|
||||
auto eval_log = std::make_unique<geo_log::GeoModifierLog>();
|
||||
|
||||
Set<ComputeContextHash> socket_log_contexts;
|
||||
if (logging_enabled(ctx)) {
|
||||
geo_nodes_modifier_data.eval_log = eval_log.get();
|
||||
|
||||
find_socket_log_contexts(*nmd, *ctx, socket_log_contexts);
|
||||
geo_nodes_modifier_data.socket_log_contexts = &socket_log_contexts;
|
||||
}
|
||||
MultiValueMap<ComputeContextHash, const lf::FunctionNode *> r_side_effect_nodes;
|
||||
find_side_effect_nodes(*nmd, *ctx, r_side_effect_nodes);
|
||||
geo_nodes_modifier_data.side_effect_nodes = &r_side_effect_nodes;
|
||||
nodes::GeoNodesLFUserData user_data;
|
||||
user_data.modifier_data = &geo_nodes_modifier_data;
|
||||
bke::ModifierComputeContext modifier_compute_context{nullptr, nmd->modifier.name};
|
||||
user_data.compute_context = &modifier_compute_context;
|
||||
|
||||
LinearAllocator<> allocator;
|
||||
Vector<GMutablePointer> inputs_to_destruct;
|
||||
|
||||
const IDProperty *properties = nmd->settings.properties;
|
||||
int input_index = -1;
|
||||
for (const int i : btree.interface_inputs().index_range()) {
|
||||
input_index++;
|
||||
const bNodeSocket &interface_socket = *btree.interface_inputs()[i];
|
||||
if (interface_socket.type == SOCK_GEOMETRY && input_index == 0) {
|
||||
param_inputs[input_index] = &input_geometry_set;
|
||||
continue;
|
||||
}
|
||||
|
||||
const CPPType *type = interface_socket.typeinfo->geometry_nodes_cpp_type;
|
||||
BLI_assert(type != nullptr);
|
||||
void *value = allocator.allocate(type->size(), type->alignment());
|
||||
initialize_group_input(btree, properties, i, value);
|
||||
param_inputs[input_index] = {type, value};
|
||||
inputs_to_destruct.append({type, value});
|
||||
}
|
||||
|
||||
Array<bool> output_used_inputs(btree.interface_outputs().size(), true);
|
||||
for (const int i : btree.interface_outputs().index_range()) {
|
||||
input_index++;
|
||||
param_inputs[input_index] = &output_used_inputs[i];
|
||||
}
|
||||
|
||||
Array<bke::AnonymousAttributeSet> attributes_to_propagate(
|
||||
mapping.attribute_set_by_geometry_output.size());
|
||||
for (const int i : attributes_to_propagate.index_range()) {
|
||||
input_index++;
|
||||
param_inputs[input_index] = &attributes_to_propagate[i];
|
||||
}
|
||||
|
||||
for (const int i : graph_outputs.index_range()) {
|
||||
const lf::InputSocket &socket = *graph_outputs[i];
|
||||
const CPPType &type = socket.type();
|
||||
void *buffer = allocator.allocate(type.size(), type.alignment());
|
||||
param_outputs[i] = {type, buffer};
|
||||
}
|
||||
|
||||
lf::Context lf_context;
|
||||
lf_context.storage = graph_executor.init_storage(allocator);
|
||||
lf_context.user_data = &user_data;
|
||||
lf::BasicParams lf_params{graph_executor,
|
||||
param_inputs,
|
||||
param_outputs,
|
||||
param_input_usages,
|
||||
param_output_usages,
|
||||
param_set_outputs};
|
||||
graph_executor.execute(lf_params, lf_context);
|
||||
graph_executor.destruct_storage(lf_context.storage);
|
||||
|
||||
for (GMutablePointer &ptr : inputs_to_destruct) {
|
||||
ptr.destruct();
|
||||
}
|
||||
|
||||
GeometrySet output_geometry_set = std::move(*static_cast<GeometrySet *>(param_outputs[0].get()));
|
||||
store_output_attributes(output_geometry_set, btree, properties, output_node, param_outputs);
|
||||
|
||||
for (GMutablePointer &ptr : param_outputs) {
|
||||
ptr.destruct();
|
||||
}
|
||||
|
||||
if (logging_enabled(ctx)) {
|
||||
NodesModifierData *nmd_orig = reinterpret_cast<NodesModifierData *>(
|
||||
BKE_modifier_get_original(ctx->object, &nmd->modifier));
|
||||
delete static_cast<geo_log::GeoModifierLog *>(nmd_orig->runtime_eval_log);
|
||||
nmd_orig->runtime_eval_log = eval_log.release();
|
||||
}
|
||||
|
||||
return output_geometry_set;
|
||||
}
|
||||
|
||||
/**
|
||||
* \note This could be done in #initialize_group_input, though that would require adding the
|
||||
|
@ -1340,8 +992,7 @@ static void modifyGeometry(ModifierData *md,
|
|||
use_orig_index_polys = CustomData_has_layer(&mesh->pdata, CD_ORIGINDEX);
|
||||
}
|
||||
|
||||
geometry_set = compute_geometry(
|
||||
tree, *lf_graph_info, *output_node, std::move(geometry_set), nmd, ctx);
|
||||
geometry_set = compute_geometry(tree, *lf_graph_info, std::move(geometry_set), nmd, ctx);
|
||||
|
||||
if (use_orig_index_verts || use_orig_index_edges || use_orig_index_polys) {
|
||||
if (Mesh *mesh = geometry_set.get_mesh_for_write()) {
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
/**
|
||||
* For evaluation, geometry node groups are converted to a lazy-function graph. The generated graph
|
||||
* is cached per node group, so it only has to be generated once after a change.
|
||||
*
|
||||
* Node groups are *not* inlined into the lazy-function graph. This could be added in the future as
|
||||
* it might improve performance in some cases, but generally does not seem necessary. Inlining node
|
||||
* groups also has disadvantages like making per-node-group caches less useful, resulting in more
|
||||
* overhead.
|
||||
*
|
||||
* Instead, group nodes are just like all other nodes in the lazy-function graph. What makes them
|
||||
* special is that they reference the lazy-function graph of the group they reference.
|
||||
*
|
||||
* During lazy-function graph generation, a mapping between the #bNodeTree and
|
||||
* #lazy_function::Graph is build that can be used when evaluating the graph (e.g. for logging).
|
||||
*/
|
||||
|
||||
#include "FN_lazy_function_graph.hh"
|
||||
#include "FN_lazy_function_graph_executor.hh"
|
||||
|
||||
#include "NOD_geometry_nodes_log.hh"
|
||||
#include "NOD_multi_function.hh"
|
||||
|
||||
#include "BLI_compute_context.hh"
|
||||
|
||||
struct Object;
|
||||
struct Depsgraph;
|
||||
|
||||
namespace blender::nodes {
|
||||
|
||||
} // namespace blender::nodes
|
|
@ -62,6 +62,8 @@ struct GeoNodesModifierData {
|
|||
struct GeoNodesOperatorData {
|
||||
/** The object currently effected by the operator. */
|
||||
const Object *self_object = nullptr;
|
||||
/** Current evaluated depsgraph. */
|
||||
Depsgraph *depsgraph = nullptr;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -0,0 +1,444 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup nodes
|
||||
*
|
||||
* This file mainly converts a #bNodeTree into a lazy-function graph. This generally works by
|
||||
* creating a lazy-function for every node, which is then put into the lazy-function graph. Then
|
||||
* the nodes in the new graph are linked based on links in the original #bNodeTree. Some additional
|
||||
* nodes are inserted for things like type conversions and multi-input sockets.
|
||||
*
|
||||
* Currently, lazy-functions are even created for nodes that don't strictly require it, like
|
||||
* reroutes or muted nodes. In the future we could avoid that at the cost of additional code
|
||||
* complexity. So far, this does not seem to be a performance issue.
|
||||
*/
|
||||
|
||||
#include "NOD_geometry_exec.hh"
|
||||
#include "NOD_geometry_nodes_lazy_function.hh"
|
||||
#include "NOD_multi_function.hh"
|
||||
#include "NOD_node_declaration.hh"
|
||||
|
||||
#include "BLI_cpp_types.hh"
|
||||
#include "BLI_dot_export.hh"
|
||||
#include "BLI_hash.h"
|
||||
#include "BLI_lazy_threading.hh"
|
||||
#include "BLI_map.hh"
|
||||
|
||||
#include "DNA_ID.h"
|
||||
|
||||
#include "BKE_compute_contexts.hh"
|
||||
#include "BKE_geometry_set.hh"
|
||||
#include "BKE_idprop.hh"
|
||||
#include "BKE_type_conversions.hh"
|
||||
|
||||
#include "FN_field_cpp_type.hh"
|
||||
#include "FN_lazy_function_execute.hh"
|
||||
#include "FN_lazy_function_graph_executor.hh"
|
||||
|
||||
#include "DEG_depsgraph_query.h"
|
||||
|
||||
namespace blender::nodes {
|
||||
|
||||
/**
|
||||
* \return Whether using an attribute to input values of this type is supported, and the node
|
||||
* group's input for this socket accepts a field rather than just single values.
|
||||
*/
|
||||
static bool input_has_attribute_toggle(const bNodeTree &node_tree, const int socket_index)
|
||||
{
|
||||
BLI_assert(node_tree.runtime->field_inferencing_interface);
|
||||
const nodes::FieldInferencingInterface &field_interface =
|
||||
*node_tree.runtime->field_inferencing_interface;
|
||||
return field_interface.inputs[socket_index] != nodes::InputSocketFieldType::None;
|
||||
}
|
||||
|
||||
static std::unique_ptr<IDProperty, bke::idprop::IDPropertyDeleter> id_property_create_from_socket(
|
||||
const bNodeSocket &socket)
|
||||
{
|
||||
switch (socket.type) {
|
||||
case SOCK_FLOAT: {
|
||||
const bNodeSocketValueFloat *value = static_cast<const bNodeSocketValueFloat *>(
|
||||
socket.default_value);
|
||||
auto property = bke::idprop::create(socket.identifier, value->value);
|
||||
IDPropertyUIDataFloat *ui_data = (IDPropertyUIDataFloat *)IDP_ui_data_ensure(property.get());
|
||||
ui_data->base.rna_subtype = value->subtype;
|
||||
ui_data->soft_min = double(value->min);
|
||||
ui_data->soft_max = double(value->max);
|
||||
ui_data->default_value = value->value;
|
||||
return property;
|
||||
}
|
||||
case SOCK_INT: {
|
||||
const bNodeSocketValueInt *value = static_cast<const bNodeSocketValueInt *>(
|
||||
socket.default_value);
|
||||
auto property = bke::idprop::create(socket.identifier, value->value);
|
||||
IDPropertyUIDataInt *ui_data = (IDPropertyUIDataInt *)IDP_ui_data_ensure(property.get());
|
||||
ui_data->base.rna_subtype = value->subtype;
|
||||
ui_data->soft_min = value->min;
|
||||
ui_data->soft_max = value->max;
|
||||
ui_data->default_value = value->value;
|
||||
return property;
|
||||
}
|
||||
case SOCK_VECTOR: {
|
||||
const bNodeSocketValueVector *value = static_cast<const bNodeSocketValueVector *>(
|
||||
socket.default_value);
|
||||
auto property = bke::idprop::create(
|
||||
socket.identifier, Span<float>{value->value[0], value->value[1], value->value[2]});
|
||||
IDPropertyUIDataFloat *ui_data = (IDPropertyUIDataFloat *)IDP_ui_data_ensure(property.get());
|
||||
ui_data->base.rna_subtype = value->subtype;
|
||||
ui_data->soft_min = double(value->min);
|
||||
ui_data->soft_max = double(value->max);
|
||||
ui_data->default_array = (double *)MEM_mallocN(sizeof(double[3]), "mod_prop_default");
|
||||
ui_data->default_array_len = 3;
|
||||
for (const int i : IndexRange(3)) {
|
||||
ui_data->default_array[i] = double(value->value[i]);
|
||||
}
|
||||
return property;
|
||||
}
|
||||
case SOCK_RGBA: {
|
||||
const bNodeSocketValueRGBA *value = static_cast<const bNodeSocketValueRGBA *>(
|
||||
socket.default_value);
|
||||
auto property = bke::idprop::create(
|
||||
socket.identifier,
|
||||
Span<float>{value->value[0], value->value[1], value->value[2], value->value[3]});
|
||||
IDPropertyUIDataFloat *ui_data = (IDPropertyUIDataFloat *)IDP_ui_data_ensure(property.get());
|
||||
ui_data->base.rna_subtype = PROP_COLOR;
|
||||
ui_data->default_array = (double *)MEM_mallocN(sizeof(double[4]), __func__);
|
||||
ui_data->default_array_len = 4;
|
||||
ui_data->min = 0.0;
|
||||
ui_data->max = FLT_MAX;
|
||||
ui_data->soft_min = 0.0;
|
||||
ui_data->soft_max = 1.0;
|
||||
for (const int i : IndexRange(4)) {
|
||||
ui_data->default_array[i] = double(value->value[i]);
|
||||
}
|
||||
return property;
|
||||
}
|
||||
case SOCK_BOOLEAN: {
|
||||
const bNodeSocketValueBoolean *value = static_cast<const bNodeSocketValueBoolean *>(
|
||||
socket.default_value);
|
||||
auto property = bke::idprop::create_bool(socket.identifier, value->value);
|
||||
IDPropertyUIDataBool *ui_data = (IDPropertyUIDataBool *)IDP_ui_data_ensure(property.get());
|
||||
ui_data->default_value = value->value != 0;
|
||||
return property;
|
||||
}
|
||||
case SOCK_STRING: {
|
||||
const bNodeSocketValueString *value = static_cast<const bNodeSocketValueString *>(
|
||||
socket.default_value);
|
||||
auto property = bke::idprop::create(socket.identifier, value->value);
|
||||
IDPropertyUIDataString *ui_data = (IDPropertyUIDataString *)IDP_ui_data_ensure(
|
||||
property.get());
|
||||
ui_data->default_value = BLI_strdup(value->value);
|
||||
return property;
|
||||
}
|
||||
case SOCK_OBJECT: {
|
||||
const bNodeSocketValueObject *value = static_cast<const bNodeSocketValueObject *>(
|
||||
socket.default_value);
|
||||
auto property = bke::idprop::create(socket.identifier, reinterpret_cast<ID *>(value->value));
|
||||
IDPropertyUIDataID *ui_data = (IDPropertyUIDataID *)IDP_ui_data_ensure(property.get());
|
||||
ui_data->id_type = ID_OB;
|
||||
return property;
|
||||
}
|
||||
case SOCK_COLLECTION: {
|
||||
const bNodeSocketValueCollection *value = static_cast<const bNodeSocketValueCollection *>(
|
||||
socket.default_value);
|
||||
return bke::idprop::create(socket.identifier, reinterpret_cast<ID *>(value->value));
|
||||
}
|
||||
case SOCK_TEXTURE: {
|
||||
const bNodeSocketValueTexture *value = static_cast<const bNodeSocketValueTexture *>(
|
||||
socket.default_value);
|
||||
return bke::idprop::create(socket.identifier, reinterpret_cast<ID *>(value->value));
|
||||
}
|
||||
case SOCK_IMAGE: {
|
||||
const bNodeSocketValueImage *value = static_cast<const bNodeSocketValueImage *>(
|
||||
socket.default_value);
|
||||
return bke::idprop::create(socket.identifier, reinterpret_cast<ID *>(value->value));
|
||||
}
|
||||
case SOCK_MATERIAL: {
|
||||
const bNodeSocketValueMaterial *value = static_cast<const bNodeSocketValueMaterial *>(
|
||||
socket.default_value);
|
||||
return bke::idprop::create(socket.identifier, reinterpret_cast<ID *>(value->value));
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static bool id_property_type_matches_socket(const bNodeSocket &socket, const IDProperty &property)
|
||||
{
|
||||
switch (socket.type) {
|
||||
case SOCK_FLOAT:
|
||||
return ELEM(property.type, IDP_FLOAT, IDP_DOUBLE);
|
||||
case SOCK_INT:
|
||||
return property.type == IDP_INT;
|
||||
case SOCK_VECTOR:
|
||||
return property.type == IDP_ARRAY && property.subtype == IDP_FLOAT && property.len == 3;
|
||||
case SOCK_RGBA:
|
||||
return property.type == IDP_ARRAY && property.subtype == IDP_FLOAT && property.len == 4;
|
||||
case SOCK_BOOLEAN:
|
||||
return property.type == IDP_BOOLEAN;
|
||||
case SOCK_STRING:
|
||||
return property.type == IDP_STRING;
|
||||
case SOCK_OBJECT:
|
||||
case SOCK_COLLECTION:
|
||||
case SOCK_TEXTURE:
|
||||
case SOCK_IMAGE:
|
||||
case SOCK_MATERIAL:
|
||||
return property.type == IDP_ID;
|
||||
}
|
||||
BLI_assert_unreachable();
|
||||
return false;
|
||||
}
|
||||
|
||||
void initialize_group_input(const bNodeTree &tree,
|
||||
const IDProperty *properties,
|
||||
const int input_index,
|
||||
void *r_value)
|
||||
{
|
||||
const bNodeSocket &io_input = *tree.interface_inputs()[input_index];
|
||||
const bNodeSocketType &socket_type = *io_input.typeinfo;
|
||||
const eNodeSocketDatatype socket_data_type = static_cast<eNodeSocketDatatype>(io_input.type);
|
||||
if (properties == nullptr) {
|
||||
socket_type.get_geometry_nodes_cpp_value(io_input, r_value);
|
||||
return;
|
||||
}
|
||||
const IDProperty *property = IDP_GetPropertyFromGroup(properties, io_input.identifier);
|
||||
if (property == nullptr) {
|
||||
socket_type.get_geometry_nodes_cpp_value(io_input, r_value);
|
||||
return;
|
||||
}
|
||||
if (!id_property_type_matches_socket(io_input, *property)) {
|
||||
socket_type.get_geometry_nodes_cpp_value(io_input, r_value);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!input_has_attribute_toggle(tree, input_index)) {
|
||||
init_socket_cpp_value_from_property(*property, socket_data_type, r_value);
|
||||
return;
|
||||
}
|
||||
|
||||
const IDProperty *property_use_attribute = IDP_GetPropertyFromGroup(
|
||||
properties, (io_input.identifier + use_attribute_suffix).c_str());
|
||||
const IDProperty *property_attribute_name = IDP_GetPropertyFromGroup(
|
||||
properties, (io_input.identifier + attribute_name_suffix).c_str());
|
||||
if (property_use_attribute == nullptr || property_attribute_name == nullptr) {
|
||||
init_socket_cpp_value_from_property(*property, socket_data_type, r_value);
|
||||
return;
|
||||
}
|
||||
|
||||
const bool use_attribute = IDP_Int(property_use_attribute) != 0;
|
||||
if (use_attribute) {
|
||||
const StringRef attribute_name{IDP_String(property_attribute_name)};
|
||||
if (!bke::allow_procedural_attribute_access(attribute_name)) {
|
||||
init_socket_cpp_value_from_property(*property, socket_data_type, r_value);
|
||||
return;
|
||||
}
|
||||
fn::GField attribute_field = bke::AttributeFieldInput::Create(attribute_name,
|
||||
*socket_type.base_cpp_type);
|
||||
const auto *value_or_field_cpp_type = fn::ValueOrFieldCPPType::get_from_self(
|
||||
*socket_type.geometry_nodes_cpp_type);
|
||||
BLI_assert(value_or_field_cpp_type != nullptr);
|
||||
value_or_field_cpp_type->construct_from_field(r_value, std::move(attribute_field));
|
||||
}
|
||||
else {
|
||||
init_socket_cpp_value_from_property(*property, socket_data_type, r_value);
|
||||
}
|
||||
}
|
||||
|
||||
static void init_socket_cpp_value_from_property(const IDProperty &property,
|
||||
const eNodeSocketDatatype socket_value_type,
|
||||
void *r_value)
|
||||
{
|
||||
switch (socket_value_type) {
|
||||
case SOCK_FLOAT: {
|
||||
float value = 0.0f;
|
||||
if (property.type == IDP_FLOAT) {
|
||||
value = IDP_Float(&property);
|
||||
}
|
||||
else if (property.type == IDP_DOUBLE) {
|
||||
value = float(IDP_Double(&property));
|
||||
}
|
||||
new (r_value) fn::ValueOrField<float>(value);
|
||||
break;
|
||||
}
|
||||
case SOCK_INT: {
|
||||
int value = IDP_Int(&property);
|
||||
new (r_value) fn::ValueOrField<int>(value);
|
||||
break;
|
||||
}
|
||||
case SOCK_VECTOR: {
|
||||
float3 value = (const float *)IDP_Array(&property);
|
||||
new (r_value) fn::ValueOrField<float3>(value);
|
||||
break;
|
||||
}
|
||||
case SOCK_RGBA: {
|
||||
ColorGeometry4f value = (const float *)IDP_Array(&property);
|
||||
new (r_value) fn::ValueOrField<ColorGeometry4f>(value);
|
||||
break;
|
||||
}
|
||||
case SOCK_BOOLEAN: {
|
||||
const bool value = IDP_Bool(&property);
|
||||
new (r_value) fn::ValueOrField<bool>(value);
|
||||
break;
|
||||
}
|
||||
case SOCK_STRING: {
|
||||
std::string value = IDP_String(&property);
|
||||
new (r_value) fn::ValueOrField<std::string>(std::move(value));
|
||||
break;
|
||||
}
|
||||
case SOCK_OBJECT: {
|
||||
ID *id = IDP_Id(&property);
|
||||
Object *object = (id && GS(id->name) == ID_OB) ? (Object *)id : nullptr;
|
||||
*(Object **)r_value = object;
|
||||
break;
|
||||
}
|
||||
case SOCK_COLLECTION: {
|
||||
ID *id = IDP_Id(&property);
|
||||
Collection *collection = (id && GS(id->name) == ID_GR) ? (Collection *)id : nullptr;
|
||||
*(Collection **)r_value = collection;
|
||||
break;
|
||||
}
|
||||
case SOCK_TEXTURE: {
|
||||
ID *id = IDP_Id(&property);
|
||||
Tex *texture = (id && GS(id->name) == ID_TE) ? (Tex *)id : nullptr;
|
||||
*(Tex **)r_value = texture;
|
||||
break;
|
||||
}
|
||||
case SOCK_IMAGE: {
|
||||
ID *id = IDP_Id(&property);
|
||||
Image *image = (id && GS(id->name) == ID_IM) ? (Image *)id : nullptr;
|
||||
*(Image **)r_value = image;
|
||||
break;
|
||||
}
|
||||
case SOCK_MATERIAL: {
|
||||
ID *id = IDP_Id(&property);
|
||||
Material *material = (id && GS(id->name) == ID_MA) ? (Material *)id : nullptr;
|
||||
*(Material **)r_value = material;
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
BLI_assert_unreachable();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static GeometrySet compute_geometry(const bNodeTree &btree,
|
||||
const nodes::GeometryNodesLazyFunctionGraphInfo &lf_graph_info,
|
||||
GeometrySet input_geometry_set,
|
||||
NodesModifierData *nmd,
|
||||
const ModifierEvalContext *ctx)
|
||||
{
|
||||
|
||||
const nodes::GeometryNodeLazyFunctionGraphMapping &mapping = lf_graph_info.mapping;
|
||||
|
||||
Vector<const lf::OutputSocket *> graph_inputs = mapping.group_input_sockets;
|
||||
graph_inputs.extend(mapping.group_output_used_sockets);
|
||||
graph_inputs.extend(mapping.attribute_set_by_geometry_output.values().begin(),
|
||||
mapping.attribute_set_by_geometry_output.values().end());
|
||||
Vector<const lf::InputSocket *> graph_outputs = mapping.standard_group_output_sockets;
|
||||
|
||||
Array<GMutablePointer> param_inputs(graph_inputs.size());
|
||||
Array<GMutablePointer> param_outputs(graph_outputs.size());
|
||||
Array<std::optional<lf::ValueUsage>> param_input_usages(graph_inputs.size());
|
||||
Array<lf::ValueUsage> param_output_usages(graph_outputs.size(), lf::ValueUsage::Used);
|
||||
Array<bool> param_set_outputs(graph_outputs.size(), false);
|
||||
|
||||
nodes::GeometryNodesLazyFunctionLogger lf_logger(lf_graph_info);
|
||||
nodes::GeometryNodesLazyFunctionSideEffectProvider lf_side_effect_provider;
|
||||
|
||||
lf::GraphExecutor graph_executor{
|
||||
lf_graph_info.graph, graph_inputs, graph_outputs, &lf_logger, &lf_side_effect_provider};
|
||||
|
||||
nodes::GeoNodesModifierData geo_nodes_modifier_data;
|
||||
geo_nodes_modifier_data.depsgraph = ctx->depsgraph;
|
||||
geo_nodes_modifier_data.self_object = ctx->object;
|
||||
auto eval_log = std::make_unique<geo_log::GeoModifierLog>();
|
||||
|
||||
Set<ComputeContextHash> socket_log_contexts;
|
||||
if (logging_enabled(ctx)) {
|
||||
geo_nodes_modifier_data.eval_log = eval_log.get();
|
||||
|
||||
find_socket_log_contexts(*nmd, *ctx, socket_log_contexts);
|
||||
geo_nodes_modifier_data.socket_log_contexts = &socket_log_contexts;
|
||||
}
|
||||
MultiValueMap<ComputeContextHash, const lf::FunctionNode *> r_side_effect_nodes;
|
||||
find_side_effect_nodes(*nmd, *ctx, r_side_effect_nodes);
|
||||
geo_nodes_modifier_data.side_effect_nodes = &r_side_effect_nodes;
|
||||
nodes::GeoNodesLFUserData user_data;
|
||||
user_data.modifier_data = &geo_nodes_modifier_data;
|
||||
bke::ModifierComputeContext modifier_compute_context{nullptr, nmd->modifier.name};
|
||||
user_data.compute_context = &modifier_compute_context;
|
||||
|
||||
LinearAllocator<> allocator;
|
||||
Vector<GMutablePointer> inputs_to_destruct;
|
||||
|
||||
const IDProperty *properties = nmd->settings.properties;
|
||||
int input_index = -1;
|
||||
for (const int i : btree.interface_inputs().index_range()) {
|
||||
input_index++;
|
||||
const bNodeSocket &interface_socket = *btree.interface_inputs()[i];
|
||||
if (interface_socket.type == SOCK_GEOMETRY && input_index == 0) {
|
||||
param_inputs[input_index] = &input_geometry_set;
|
||||
continue;
|
||||
}
|
||||
|
||||
const CPPType *type = interface_socket.typeinfo->geometry_nodes_cpp_type;
|
||||
BLI_assert(type != nullptr);
|
||||
void *value = allocator.allocate(type->size(), type->alignment());
|
||||
initialize_group_input(btree, properties, i, value);
|
||||
param_inputs[input_index] = {type, value};
|
||||
inputs_to_destruct.append({type, value});
|
||||
}
|
||||
|
||||
Array<bool> output_used_inputs(btree.interface_outputs().size(), true);
|
||||
for (const int i : btree.interface_outputs().index_range()) {
|
||||
input_index++;
|
||||
param_inputs[input_index] = &output_used_inputs[i];
|
||||
}
|
||||
|
||||
Array<bke::AnonymousAttributeSet> attributes_to_propagate(
|
||||
mapping.attribute_set_by_geometry_output.size());
|
||||
for (const int i : attributes_to_propagate.index_range()) {
|
||||
input_index++;
|
||||
param_inputs[input_index] = &attributes_to_propagate[i];
|
||||
}
|
||||
|
||||
for (const int i : graph_outputs.index_range()) {
|
||||
const lf::InputSocket &socket = *graph_outputs[i];
|
||||
const CPPType &type = socket.type();
|
||||
void *buffer = allocator.allocate(type.size(), type.alignment());
|
||||
param_outputs[i] = {type, buffer};
|
||||
}
|
||||
|
||||
lf::Context lf_context;
|
||||
lf_context.storage = graph_executor.init_storage(allocator);
|
||||
lf_context.user_data = &user_data;
|
||||
lf::BasicParams lf_params{graph_executor,
|
||||
param_inputs,
|
||||
param_outputs,
|
||||
param_input_usages,
|
||||
param_output_usages,
|
||||
param_set_outputs};
|
||||
graph_executor.execute(lf_params, lf_context);
|
||||
graph_executor.destruct_storage(lf_context.storage);
|
||||
|
||||
for (GMutablePointer &ptr : inputs_to_destruct) {
|
||||
ptr.destruct();
|
||||
}
|
||||
|
||||
GeometrySet output_geometry_set = std::move(*static_cast<GeometrySet *>(param_outputs[0].get()));
|
||||
store_output_attributes(output_geometry_set, btree, properties, output_node, param_outputs);
|
||||
|
||||
for (GMutablePointer &ptr : param_outputs) {
|
||||
ptr.destruct();
|
||||
}
|
||||
|
||||
if (logging_enabled(ctx)) {
|
||||
NodesModifierData *nmd_orig = reinterpret_cast<NodesModifierData *>(
|
||||
BKE_modifier_get_original(ctx->object, &nmd->modifier));
|
||||
delete static_cast<geo_log::GeoModifierLog *>(nmd_orig->runtime_eval_log);
|
||||
nmd_orig->runtime_eval_log = eval_log.release();
|
||||
}
|
||||
|
||||
return output_geometry_set;
|
||||
}
|
||||
|
||||
} // namespace blender::nodes
|
|
@ -28,6 +28,7 @@
|
|||
|
||||
#include "BKE_compute_contexts.hh"
|
||||
#include "BKE_geometry_set.hh"
|
||||
#include "BKE_idprop.hh"
|
||||
#include "BKE_type_conversions.hh"
|
||||
|
||||
#include "FN_field_cpp_type.hh"
|
||||
|
|
Loading…
Reference in New Issue