Geometry Nodes: Runtime RNA for node tree interface

This PR implements the changes described in #132129. The main goal is to
use actual RNA types to store node group inputs on the Geometry Nodes
modifier and node tools, rather than using custom properties. This makes
the API much friendlier, as well as resolving long-standing technical
debt.

The API change looks like this:
```python
# Before
modifier["identifier"] = 5.0
modifier["identifier_use_attribute"] = True
modifier["identifier_attribute_name"] = "some_input_attribute"
modifier["identifier_attribute_name"] = "some_output_attribute"
# After
modifier.inputs.identifier.value = 5.0
modifier.inputs.identifier.type = "ATTRIBUTE"
modifier.inputs.identifier.attribute_name = "some_input_attribute"
modifier.outputs.identifier.attribute_name = "some_output_attribute"
```

The `identifier` is the identifier of the interface socket (e.g.
`Socket_2` currently). This PR does not allow changing this property
name, that can be done as separately later.

Another benefit of this change is that combined with the work from
#148763, now the node tool inputs are remembered between invocations
like properties from any other operator. To get node tool registration
working, we have to include all the information necessary for the RNA
property registration in the asset meta-data.

Backward and forward compatibility are supported (though animation paths
are only backward compatible), but this does break the current Python API
for accessing Geometry Nodes modifier properties.

Co-authored-by: Hans Goudey <hans@blender.org>
Pull Request: https://projects.blender.org/blender/blender/pulls/138117
This commit is contained in:
Jacques Lucke 2026-03-31 17:55:38 +02:00 • committed by Hans Goudey
parent 2da517ec11
commit 1561c1ea4a
40 changed files with 2690 additions and 1211 deletions

View file

@ -9,6 +9,7 @@
*/
#include <memory>
#include <optional>
#include "DNA_ID.h"
#include "DNA_ID_enums.h"
@ -376,6 +377,14 @@ const IDProperty *_IDP_assert_type_mask(const IDProperty *prop, int ty_mask);
#define IDP_ID_get(prop) ((void)0, ((ID *)_IDP_assert_type(prop, IDP_ID)->data.pointer))
/* No `IDP_ID_set` needed. */
std::optional<StringRefNull> IDP_group_lookup_string(const IDProperty &group, StringRef name);
std::optional<float> IDP_group_lookup_float(const IDProperty &group, StringRef name);
std::optional<int> IDP_group_lookup_int(const IDProperty &group, StringRef name);
std::optional<bool> IDP_group_lookup_bool(const IDProperty &group, StringRef name);
std::optional<Span<float>> IDP_group_lookup_float_array(const IDProperty &group,
StringRef name,
int required_size);
/**
* Return an int from an #IDProperty with a compatible type. This should be avoided, but
* it's sometimes necessary, for example when legacy files have incorrect property types.

View file

@ -0,0 +1,26 @@
/* SPDX-FileCopyrightText: 2026 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
/** \file
* \ingroup bke
*/
#include <xxhash.h>
namespace blender {
struct IDProperty;
namespace bke::idprop {
/**
* Hash the value of the given IDProperty recursively. This only hashes the actual values, not e.g.
* the ui data.
*/
void hash(const IDProperty &base_prop, XXH3_state_t *hash_state);
} // namespace bke::idprop
} // namespace blender

View file

@ -58,6 +58,7 @@ struct bNodeTreeExec;
class CPPType;
namespace nodes {
struct GeneratedTreeSrnaData;
class NodeMultiFunctionBuilder;
class GeoNodeExecParams;
class NodeDeclaration;
@ -149,6 +150,12 @@ using NodeBlendWriteFunction = void (*)(const bNodeTree &tree,
BlendWriter &writer);
using NodeBlendDataReadFunction = void (*)(bNodeTree &tree, bNode &node, BlendDataReader &reader);
using SocketMakeGeometryNodesInputSrnaFunction =
void (*)(const bNodeTree &tree,
StructRNA &srna,
const bNodeTreeInterfaceSocket &io_socket,
nodes::GeneratedTreeSrnaData &r_generated);
/**
* \brief Defines a socket type.
*
@ -208,6 +215,8 @@ struct bNodeSocketType {
SocketGetGeometryNodesCPPValueFunction get_geometry_nodes_cpp_value = nullptr;
/* Default value for this socket type. */
const SocketValueVariant *geometry_nodes_default_value = nullptr;
SocketMakeGeometryNodesInputSrnaFunction make_geometry_nodes_input_srna = nullptr;
};
using NodeInitExecFunction = void *(*)(bNodeExecContext * context,
@ -928,6 +937,7 @@ void node_tree_free_local_node(bNodeTree &ntree, bNode &node);
void node_tree_update_all_new(Main &main);
/** Update asset meta-data cache of data-block properties. */
IDProperty *node_create_asset_meta_data_properties(const bNodeTree &node_tree);
void node_update_asset_metadata(bNodeTree &node_tree);
void node_tree_node_flag_set(bNodeTree &ntree, int flag, bool enable);

View file

@ -36,6 +36,7 @@ struct bNodeTree;
namespace nodes {
struct FieldInferencingInterface;
struct EvalDependencies;
struct GeneratedTreeSrnaData;
class NodeDeclaration;
struct GeometryNodesLazyFunctionGraphInfo;
struct StructureTypeInterface;
@ -160,6 +161,9 @@ class bNodeTreeRuntime : NonCopyable, NonMovable {
*/
bNodeTreeExec *execdata = nullptr;
/** Contains RNA types generated for the geometry nodes modifier interface. */
std::shared_ptr<nodes::GeneratedTreeSrnaData> geometry_nodes_srna_data;
/** Information about how inputs and outputs of the node group interact with fields. */
std::unique_ptr<nodes::FieldInferencingInterface> field_inferencing_interface;
/** Field status for every socket, accessed with #bNodeSocket::index_in_tree(). */

View file

@ -417,6 +417,7 @@ set(SRC
BKE_icons.hh
BKE_id_hash.hh
BKE_idprop.hh
BKE_idprop_hash.hh
BKE_idtype.hh
BKE_image.hh
BKE_image_format.hh

View file

@ -13,6 +13,8 @@
#include <cstdlib>
#include <cstring>
#include <xxhash.h>
#include <fmt/format.h>
#include "BLI_listbase.h"
@ -22,6 +24,7 @@
#include "BLI_utildefines.h"
#include "BKE_idprop.hh"
#include "BKE_idprop_hash.hh"
#include "BKE_lib_id.hh"
#include "CLG_log.h"
@ -793,6 +796,56 @@ static void IDP_FreeGroup(IDProperty *prop, const bool do_id_user)
BLI_freelistN(&prop->data.group);
}
std::optional<StringRefNull> IDP_group_lookup_string(const IDProperty &group, StringRef name)
{
const IDProperty *prop = IDP_GetPropertyFromGroup(&group, name);
if (!prop || prop->type != IDP_STRING) {
return std::nullopt;
}
return IDP_string_get(prop);
}
std::optional<float> IDP_group_lookup_float(const IDProperty &group, StringRef name)
{
const IDProperty *prop = IDP_GetPropertyFromGroup(&group, name);
if (!prop || prop->type != IDP_FLOAT) {
return std::nullopt;
}
return IDP_float_get(prop);
}
std::optional<int> IDP_group_lookup_int(const IDProperty &group, StringRef name)
{
const IDProperty *prop = IDP_GetPropertyFromGroup(&group, name);
if (!prop || prop->type != IDP_INT) {
return std::nullopt;
}
return IDP_int_get(prop);
}
std::optional<bool> IDP_group_lookup_bool(const IDProperty &group, StringRef name)
{
const IDProperty *prop = IDP_GetPropertyFromGroup(&group, name);
if (!prop || prop->type != IDP_BOOLEAN) {
return std::nullopt;
}
return IDP_bool_get(prop);
}
std::optional<Span<float>> IDP_group_lookup_float_array(const IDProperty &group,
StringRef name,
int required_size)
{
const IDProperty *prop = IDP_GetPropertyFromGroup(&group, name);
if (!prop || prop->type != IDP_FLOAT) {
return std::nullopt;
}
if (prop->len != required_size) {
return std::nullopt;
}
return Span(IDP_array_float_get(prop), prop->len);
}
/** \} */
/* -------------------------------------------------------------------- */
@ -1985,6 +2038,59 @@ IDPropertyUIData *IDP_TryConvertUIData(IDPropertyUIData *src,
return nullptr;
}
namespace bke::idprop {
void hash(const IDProperty &base_prop, XXH3_state_t *hash_state)
{
IDP_foreach_property(&const_cast<IDProperty &>(base_prop), 0, [&](const IDProperty *prop) {
XXH3_64bits_update(hash_state, prop->name, strlen(prop->name));
XXH3_64bits_update(hash_state, &prop->type, sizeof(prop->type));
switch (eIDPropertyType(prop->type)) {
case IDP_INT:
case IDP_FLOAT:
case IDP_BOOLEAN: {
XXH3_64bits_update(hash_state, &prop->data.val, sizeof(prop->data.val));
break;
}
case IDP_DOUBLE: {
const double val = IDP_double_get(prop);
XXH3_64bits_update(hash_state, &val, sizeof(double));
break;
}
case IDP_STRING: {
if (prop->data.pointer) {
XXH3_64bits_update(hash_state, prop->data.pointer, size_t(prop->len));
}
break;
}
case IDP_ARRAY: {
if (prop->data.pointer) {
XXH3_64bits_update(hash_state, &prop->subtype, sizeof(prop->subtype));
const size_t elem_size = idp_size_table[int(prop->subtype)];
XXH3_64bits_update(hash_state, prop->data.pointer, elem_size * size_t(prop->len));
}
break;
}
case IDP_ID: {
/* NOTE: Hashing the session_uid of the ID makes the hash session-specific. An alternative
* hash wouldn't be complete though, because data-block names aren't necessarily unique. */
if (const ID *id = static_cast<const ID *>(prop->data.pointer)) {
XXH3_64bits_update(hash_state, &id->session_uid, sizeof(ID::session_uid));
}
break;
}
case IDP_GROUP:
case IDP_IDPARRAY: {
/* Handled by the recursion in #IDP_foreach_property. */
break;
}
}
});
}
} // namespace bke::idprop
/** \} */
/* -------------------------------------------------------------------- */

View file

@ -50,6 +50,8 @@
#include "BKE_node_runtime.hh"
#include "BKE_node_tree_update.hh"
#include "NOD_geometry_nodes_srna.hh"
#include "BLT_translation.hh"
namespace blender {
@ -2265,15 +2267,22 @@ static ModifierData *create_auto_smooth_modifier(
md->node_group = get_node_group(object.id.lib);
id_us_plus(&md->node_group->id);
md->settings.properties = idprop::create_group("Nodes Modifier Settings").release();
IDProperty *angle_prop = idprop::create("Socket_2", angle).release();
auto *ui_data = reinterpret_cast<IDPropertyUIDataFloat *>(IDP_ui_data_ensure(angle_prop));
ui_data->base.rna_subtype = PROP_ANGLE;
ui_data->soft_min = 0.0f;
ui_data->soft_max = DEG2RADF(180.0f);
IDP_AddToGroup(md->settings.properties, angle_prop);
IDP_AddToGroup(md->settings.properties, idprop::create("Socket_2_use_attribute", 0).release());
IDP_AddToGroup(md->settings.properties, idprop::create("Socket_2_attribute_name", "").release());
IDProperty *system_props = bke::idprop::create_group("NodesModifierProperties").release();
IDProperty *inputs = bke::idprop::create_group("inputs").release();
IDP_AddToGroup(system_props, inputs);
IDProperty *angle_prop_group = bke::idprop::create_group("Socket_2").release();
IDP_AddToGroup(inputs, angle_prop_group);
IDProperty *type_prop =
idprop::create("type", int(nodes::GeometryNodesInputType::Value)).release();
IDP_AddToGroup(angle_prop_group, type_prop);
IDProperty *angle_prop = idprop::create("value", angle).release();
IDP_AddToGroup(angle_prop_group, angle_prop);
md->modifier.system_properties = system_props;
BKE_modifiers_persistent_uid_init(object, md->modifier);
return &md->modifier;

View file

@ -53,6 +53,7 @@
#include "BKE_fluid.h"
#include "BKE_geometry_set.hh"
#include "BKE_global.hh"
#include "BKE_idprop.hh"
#include "BKE_idtype.hh"
#include "BKE_key.hh"
#include "BKE_lib_id.hh"
@ -198,6 +199,9 @@ void BKE_modifier_free_ex(ModifierData *md, const int flag)
if (md->error) {
MEM_delete(md->error);
}
if (md->system_properties != nullptr) {
IDP_FreeProperty_ex(md->system_properties, false);
}
MEM_delete(md);
}
@ -289,7 +293,9 @@ void BKE_modifiers_foreach_ID_link(Object *ob, IDWalkFunc walk, void *user_data)
{
for (ModifierData &md : ob->modifiers) {
const ModifierTypeInfo *mti = BKE_modifier_get_info(ModifierType(md.type));
IDP_foreach_property(md.system_properties, IDP_TYPE_FILTER_ID, [&](IDProperty *id_prop) {
walk(user_data, ob, (ID **)&id_prop->data.pointer, IDWALK_CB_USER);
});
if (mti->foreach_ID_link) {
mti->foreach_ID_link(&md, ob, walk, user_data);
}
@ -368,6 +374,10 @@ void BKE_modifier_copydata_ex(const ModifierData *md, ModifierData *target, cons
mti->foreach_ID_link(target, nullptr, modifier_copy_data_id_us_cb, nullptr);
}
}
if (md->system_properties) {
target->system_properties = IDP_CopyProperty_ex(md->system_properties, flag);
}
}
void BKE_modifier_copydata(const ModifierData *md, ModifierData *target)
@ -1127,6 +1137,10 @@ void BKE_modifier_blend_write(BlendWriter *writer,
continue;
}
if (md.system_properties) {
IDP_BlendWrite(writer, md.system_properties);
}
/* If the blend_write callback is defined, it should handle the whole writing process. */
if (mti->blend_write != nullptr) {
mti->blend_write(writer, id_owner, &md);
@ -1348,6 +1362,9 @@ void BKE_modifier_blend_read_data(BlendDataReader *reader, ListBaseT<ModifierDat
md->error = nullptr;
md->runtime = nullptr;
BLO_read_struct(reader, IDProperty, &md->system_properties);
IDP_BlendDataRead(reader, &md->system_properties);
/* If linking from a library, clear 'local' library override flag. */
if (ID_IS_LINKED(ob)) {
md->flag &= ~eModifierFlag_OverrideLibrary_Local;

View file

@ -99,6 +99,7 @@
#include "NOD_geo_simulation.hh"
#include "NOD_geometry_nodes_gizmos.hh"
#include "NOD_geometry_nodes_lazy_function.hh"
#include "NOD_geometry_nodes_srna.hh"
#include "NOD_menu_value.hh"
#include "NOD_node_declaration.hh"
#include "NOD_register.hh"
@ -239,6 +240,7 @@ static void ntree_copy_data(Main * /*bmain*/,
}
}
}
dst_runtime.geometry_nodes_srna_data = ntree_src->runtime->geometry_nodes_srna_data;
if (ntree_src->geometry_node_asset_traits) {
ntree_dst->geometry_node_asset_traits = MEM_new<GeometryNodeAssetTraits>(
@ -2096,6 +2098,159 @@ static void ntree_blend_read_after_liblink(BlendLibReader *reader, ID *id)
}
}
IDProperty *node_create_asset_meta_data_properties(const bNodeTree &node_tree)
{
auto properties = idprop::create_group("properties");
auto inputs = idprop::create_group("inputs");
node_tree.ensure_interface_cache();
for (const bNodeTreeInterfaceSocket *socket : node_tree.interface_inputs()) {
const bNodeSocketType *base_typeinfo = node_socket_type_find(socket->socket_type);
auto input = idprop::create_group(socket->identifier);
IDP_AddToGroup(input.get(),
idprop::create("name", socket->name ? socket->name : "").release());
IDP_AddToGroup(input.get(), idprop::create("type", base_typeinfo->type).release());
IDP_AddToGroup(
input.get(),
idprop::create("description", socket->description ? socket->description : "").release());
switch (base_typeinfo->type) {
case SOCK_FLOAT: {
const auto &value = node_interface::get_socket_data_as<bNodeSocketValueFloat>(*socket);
IDP_AddToGroup(input.get(), idprop::create("subtype", value.subtype).release());
IDP_AddToGroup(input.get(), idprop::create("min", value.min).release());
IDP_AddToGroup(input.get(), idprop::create("max", value.max).release());
IDP_AddToGroup(input.get(), idprop::create("default_value", value.value).release());
if (socket->default_attribute_name) {
IDP_AddToGroup(
input.get(),
idprop::create("default_attribute_name", socket->default_attribute_name).release());
}
break;
}
case SOCK_VECTOR: {
const auto &value = node_interface::get_socket_data_as<bNodeSocketValueVector>(*socket);
IDP_AddToGroup(input.get(), idprop::create("subtype", value.subtype).release());
IDP_AddToGroup(
input.get(),
idprop::create("default_value", Span(value.value, value.dimensions)).release());
if (value.dimensions != 3) {
IDP_AddToGroup(input.get(), idprop::create("dimensions", value.dimensions).release());
}
IDP_AddToGroup(input.get(), idprop::create("min", value.min).release());
IDP_AddToGroup(input.get(), idprop::create("max", value.max).release());
if (socket->default_attribute_name) {
IDP_AddToGroup(
input.get(),
idprop::create("default_attribute_name", socket->default_attribute_name).release());
}
break;
}
case SOCK_RGBA: {
const auto &value = node_interface::get_socket_data_as<bNodeSocketValueRGBA>(*socket);
IDP_AddToGroup(input.get(),
idprop::create("default_value", Span(value.value, 4)).release());
if (socket->default_attribute_name) {
IDP_AddToGroup(
input.get(),
idprop::create("default_attribute_name", socket->default_attribute_name).release());
}
break;
}
case SOCK_BOOLEAN: {
const auto &value = node_interface::get_socket_data_as<bNodeSocketValueBoolean>(*socket);
IDP_AddToGroup(input.get(), idprop::create_bool("default_value", value.value).release());
if (socket->default_attribute_name) {
IDP_AddToGroup(
input.get(),
idprop::create("default_attribute_name", socket->default_attribute_name).release());
}
break;
}
case SOCK_ROTATION: {
const auto &value = node_interface::get_socket_data_as<bNodeSocketValueRotation>(*socket);
const float3 default_value = float3(math::EulerXYZ(float3(value.value_euler)));
IDP_AddToGroup(input.get(),
idprop::create("default_value", Span(&default_value.x, 3)).release());
if (socket->default_attribute_name) {
IDP_AddToGroup(
input.get(),
idprop::create("default_attribute_name", socket->default_attribute_name).release());
}
break;
}
case SOCK_INT: {
const auto &value = node_interface::get_socket_data_as<bNodeSocketValueInt>(*socket);
IDP_AddToGroup(input.get(), idprop::create("subtype", value.subtype).release());
IDP_AddToGroup(input.get(), idprop::create("min", value.min).release());
IDP_AddToGroup(input.get(), idprop::create("max", value.max).release());
IDP_AddToGroup(input.get(), idprop::create("default_value", value.value).release());
if (socket->default_attribute_name) {
IDP_AddToGroup(
input.get(),
idprop::create("default_attribute_name", socket->default_attribute_name).release());
}
break;
}
case SOCK_STRING: {
const auto &value = node_interface::get_socket_data_as<bNodeSocketValueString>(*socket);
IDP_AddToGroup(input.get(), idprop::create("subtype", value.subtype).release());
IDP_AddToGroup(input.get(), idprop::create("default_value", value.value).release());
break;
}
case SOCK_MENU: {
const auto &value = node_interface::get_socket_data_as<bNodeSocketValueMenu>(*socket);
IDP_AddToGroup(input.get(), idprop::create("default_value", value.value).release());
auto items = idprop::create_group("items");
for (const RuntimeNodeEnumItem &enum_item : value.enum_items->items) {
auto item = idprop::create_group(std::to_string(enum_item.identifier));
IDP_AddToGroup(item.get(), idprop::create("name", enum_item.name).release());
IDP_AddToGroup(item.get(),
idprop::create("description", enum_item.description).release());
}
IDP_AddToGroup(input.get(), items.release());
break;
}
case SOCK_SHADER:
case SOCK_MATRIX:
case SOCK_OBJECT:
case SOCK_IMAGE:
case SOCK_GEOMETRY:
case SOCK_COLLECTION:
case SOCK_TEXTURE:
case SOCK_MATERIAL:
case SOCK_BUNDLE:
case SOCK_CLOSURE:
case SOCK_CUSTOM:
case SOCK_FONT:
case SOCK_SCENE:
case SOCK_TEXT_ID:
case SOCK_MASK:
case SOCK_SOUND:
case SOCK_INT_VECTOR:
break;
}
IDP_AddToGroup(inputs.get(), input.release());
}
IDP_AddToGroup(properties.get(), inputs.release());
auto panels = idprop::create_group("panels");
for (const bNodeTreeInterfaceItem *item : node_tree.interface_items()) {
if (item->item_type != NODE_INTERFACE_PANEL) {
continue;
}
const auto &panel = *reinterpret_cast<const bNodeTreeInterfacePanel *>(item);
std::string open_identifier = fmt::format("open_{}", panel.identifier);
auto panel_props = idprop::create_group(open_identifier);
IDP_AddToGroup(
panels.get(),
idprop::create_bool(open_identifier, panel.flag & NODE_INTERFACE_PANEL_DEFAULT_CLOSED)
.release());
}
IDP_AddToGroup(properties.get(), panels.release());
return properties.release();
}
void node_update_asset_metadata(bNodeTree &node_tree)
{
AssetMetaData *asset_data = node_tree.id.asset_data;
@ -2104,22 +2259,14 @@ void node_update_asset_metadata(bNodeTree &node_tree)
}
BKE_asset_metadata_idprop_ensure(asset_data, idprop::create("type", node_tree.type).release());
auto inputs = idprop::create_group("inputs");
auto outputs = idprop::create_group("outputs");
node_tree.ensure_interface_cache();
for (const bNodeTreeInterfaceSocket *socket : node_tree.interface_inputs()) {
auto *prop = idprop::create(socket->name ? socket->name : "", socket->socket_type).release();
if (!IDP_AddToGroup(inputs.get(), prop)) {
IDP_FreeProperty(prop);
}
}
for (const bNodeTreeInterfaceSocket *socket : node_tree.interface_outputs()) {
auto *prop = idprop::create(socket->name ? socket->name : "", socket->socket_type).release();
if (!IDP_AddToGroup(outputs.get(), prop)) {
IDP_FreeProperty(prop);
}
}
BKE_asset_metadata_idprop_ensure(asset_data, inputs.release());
BKE_asset_metadata_idprop_ensure(asset_data, node_create_asset_meta_data_properties(node_tree));
BKE_asset_metadata_idprop_ensure(asset_data, outputs.release());
if (node_tree.geometry_node_asset_traits) {
auto property = idprop::create("geometry_node_asset_traits_flag",

View file

@ -37,6 +37,7 @@
#include "NOD_geo_viewer.hh"
#include "NOD_geometry_nodes_gizmos.hh"
#include "NOD_geometry_nodes_lazy_function.hh"
#include "NOD_geometry_nodes_srna.hh"
#include "NOD_node_declaration.hh"
#include "NOD_socket.hh"
#include "NOD_socket_declarations.hh"
@ -47,6 +48,9 @@
#include "BLT_translation.hh"
#include "RNA_access.hh"
#include "RNA_define.hh"
namespace blender {
using namespace blender::nodes;
@ -580,6 +584,13 @@ class NodeTreeMainUpdater {
result.interface_changed = true;
}
if (result.interface_changed) {
if (ntree.type == NTREE_GEOMETRY) {
ntree.runtime->geometry_nodes_srna_data = nodes::create_geometry_nodes_rna_for_modifier(
ntree);
}
}
#ifndef NDEBUG
/* Check the uniqueness of node identifiers. */
Set<int32_t> node_identifiers;

View file

@ -143,6 +143,8 @@
#include "ANIM_action_legacy.hh"
#include "ANIM_animdata.hh"
#include "NOD_geometry_nodes_srna.hh"
#include "RNA_prototypes.hh"
#ifdef WITH_PYTHON
@ -667,6 +669,97 @@ static void object_foreach_working_space_color(ID *id,
}
}
namespace forward_compat {
/**
* \note We do this at the object level so that pointers for temporarily allocated IDProperties
* aren't reused between modifiers.
*/
static void create_legacy_geometry_nodes_properties(Object &ob)
{
for (ModifierData &md : ob.modifiers) {
if (md.type != eModifierType_Nodes) {
continue;
}
NodesModifierData &nmd = reinterpret_cast<NodesModifierData &>(md);
const IDProperty *system_props = nmd.modifier.system_properties;
if (!system_props) {
return;
}
IDProperty *legacy_props = bke::idprop::create_group("Nodes Modifier Settings").release();
const IDProperty *inputs = IDP_GetPropertyFromGroup(system_props, "inputs");
if (inputs && inputs->type == IDP_GROUP) {
for (const IDProperty &prop : inputs->data.group) {
const StringRefNull identifier = prop.name;
const IDProperty *type_prop = IDP_GetPropertyFromGroup(&prop, "type");
if (!type_prop) {
continue;
}
const int type = IDP_int_get(type_prop);
if (type == int(nodes::GeometryNodesInputType::Layer)) {
if (const IDProperty *name = IDP_GetPropertyFromGroup(&prop, "layer_name")) {
IDProperty *legacy_prop = IDP_CopyProperty(name);
STRNCPY(legacy_prop->name, identifier.c_str());
IDP_AddToGroup(legacy_props, legacy_prop);
}
}
else {
if (const IDProperty *value = IDP_GetPropertyFromGroup(&prop, "value")) {
IDProperty *legacy_prop = IDP_CopyProperty(value);
STRNCPY(legacy_prop->name, identifier.c_str());
IDP_AddToGroup(legacy_props, legacy_prop);
}
const bool use_attribute = type == int(nodes::GeometryNodesInputType::Attribute);
IDP_AddToGroup(
legacy_props,
bke::idprop::create(identifier + "_use_attribute", int(use_attribute)).release());
if (const IDProperty *name = IDP_GetPropertyFromGroup(&prop, "attribute_name")) {
IDProperty *legacy_attr_prop = IDP_CopyProperty(name);
SNPRINTF(legacy_attr_prop->name, "%s_attribute_name", identifier.c_str());
IDP_AddToGroup(legacy_props, legacy_attr_prop);
}
}
}
}
const IDProperty *outputs = IDP_GetPropertyFromGroup(system_props, "outputs");
if (outputs && outputs->type == IDP_GROUP) {
for (const IDProperty &prop : outputs->data.group) {
const StringRefNull identifier = prop.name;
if (const IDProperty *name = IDP_GetPropertyFromGroup(&prop, "attribute_name")) {
IDProperty *legacy_prop = IDP_CopyProperty(name);
SNPRINTF(legacy_prop->name, "%s_attribute_name", identifier.c_str());
IDP_AddToGroup(legacy_props, legacy_prop);
}
}
}
BLI_assert(!nmd.settings.properties);
nmd.settings.properties = legacy_props;
}
}
static void free_legacy_geometry_nodes_properties(Object &ob)
{
for (ModifierData &md : ob.modifiers) {
if (md.type != eModifierType_Nodes) {
continue;
}
NodesModifierData &nmd = reinterpret_cast<NodesModifierData &>(md);
if (!nmd.settings.properties) {
continue;
}
IDP_FreeProperty_ex(nmd.settings.properties, false);
nmd.settings.properties = nullptr;
}
}
} // namespace forward_compat
static void object_blend_write(BlendWriter *writer, ID *id, const void *id_address)
{
Object *ob = id_cast<Object *>(id);
@ -686,6 +779,10 @@ static void object_blend_write(BlendWriter *writer, ID *id, const void *id_addre
writer->write_id_struct(id_address, ob);
BKE_id_blend_write(writer, &ob->id);
if (!is_undo) {
forward_compat::create_legacy_geometry_nodes_properties(*ob);
}
/* direct data */
writer->write_pointer_array(ob->totcol, ob->mat);
writer->write_char_array(ob->totcol, ob->matbits);
@ -739,6 +836,10 @@ static void object_blend_write(BlendWriter *writer, ID *id, const void *id_addre
writer->write_struct(ob->lightprobe_cache);
BKE_lightprobe_cache_blend_write(writer, ob->lightprobe_cache);
}
if (!is_undo) {
forward_compat::free_legacy_geometry_nodes_properties(*ob);
}
}
static void object_blend_read_data(BlendDataReader *reader, ID *id)

View file

@ -8,20 +8,29 @@
#define DNA_DEPRECATED_ALLOW
#include "NOD_geometry_nodes_srna.hh"
#include "DNA_ID.h"
#include "DNA_brush_types.h"
#include "DNA_curve_types.h"
#include "DNA_modifier_types.h"
#include "DNA_node_tree_interface_types.h"
#include "DNA_node_types.h"
#include "DNA_scene_types.h"
#include "DNA_screen_types.h"
#include "BLI_listbase_iterator.hh"
#include "BLI_string.h"
#include "BLI_sys_types.h"
#include "BKE_animsys.h"
#include "BKE_curves.hh"
#include "BKE_idprop.hh"
#include "BKE_main.hh"
#include "BKE_mesh_legacy_convert.hh"
#include "BKE_node.hh"
#include "BKE_node_legacy_types.hh"
#include "BKE_node_runtime.hh"
#include "SEQ_iterator.hh"
#include "SEQ_sequencer.hh"
@ -36,6 +45,137 @@ namespace blender {
// static CLG_LogRef LOG = {"blend.doversion"};
static void version_geometry_nodes_properties(Main &bmain, Object &object, NodesModifierData &nmd)
{
const IDProperty *old_props = nmd.settings.properties;
if (!old_props) {
/* Versioning has already been done, this check makes the function idempotent. */
return;
}
if (!nmd.node_group) {
IDP_FreeProperty(nmd.settings.properties);
nmd.settings.properties = nullptr;
return;
}
if (ID_MISSING(&nmd.node_group->id)) {
return;
}
const bNodeTree &ntree = *nmd.node_group;
ntree.ensure_interface_cache();
IDProperty *system_props = bke::idprop::create_group("NodesModifierProperties").release();
IDProperty *inputs = bke::idprop::create_group("inputs").release();
IDP_AddToGroup(system_props, inputs);
const std::string inputs_path_prefix = fmt::format("modifiers[\"{}\"]", nmd.modifier.name);
for (const bNodeTreeInterfaceSocket *input : ntree.interface_inputs()) {
const StringRefNull identifier = input->identifier;
IDProperty *old_value_prop = IDP_GetPropertyFromGroup(old_props, identifier);
if (!old_value_prop) {
continue;
}
IDProperty *group = bke::idprop::create_group(identifier).release();
IDP_AddToGroup(inputs, group);
if (input->flag & NODE_INTERFACE_SOCKET_LAYER_SELECTION) {
IDP_AddToGroup(
group, bke::idprop::create("type", int(nodes::GeometryNodesInputType::Layer)).release());
const StringRefNull layer_name = [&]() {
const IDProperty *layer_name = IDP_GetPropertyFromGroup(old_props, identifier);
if (layer_name) {
return StringRefNull(IDP_string_get(layer_name));
}
return StringRefNull();
}();
IDP_AddToGroup(group, bke::idprop::create("layer_name", layer_name).release());
continue;
}
IDProperty *new_value_prop = IDP_CopyProperty(old_value_prop);
STRNCPY(new_value_prop->name, "value");
IDP_AddToGroup(group, new_value_prop);
const std::string old_value_path = fmt::format("[\"{}\"]", identifier);
const std::string new_value_path = fmt::format(".properties.inputs.{}.value", identifier);
BKE_animdata_fix_paths_rename_all_ex(&bmain,
&object.id,
inputs_path_prefix.c_str(),
old_value_path.c_str(),
new_value_path.c_str(),
0,
0,
false,
false);
if (IDOverrideLibrary *override_library = object.id.override_library) {
for (IDOverrideLibraryProperty &prop : override_library->properties) {
const StringRef path = prop.rna_path;
const int64_t i = path.find(inputs_path_prefix);
if (i == StringRef::not_found) {
continue;
}
if (path.drop_known_prefix(inputs_path_prefix) != old_value_path) {
continue;
}
MEM_delete(prop.rna_path);
prop.rna_path = BLI_sprintfN("%s%s", inputs_path_prefix.c_str(), new_value_path.c_str());
}
}
bool use_attribute = false;
if (const IDProperty *use_attribute_prop = IDP_GetPropertyFromGroup(
old_props, identifier + "_use_attribute"))
{
/* This property changed to an enum property and animation is not versioned. */
if (use_attribute_prop->type == IDP_INT) {
use_attribute = bool(IDP_int_get(use_attribute_prop));
}
else {
use_attribute = bool(IDP_bool_get(use_attribute_prop));
}
}
const auto input_type = use_attribute ? nodes::GeometryNodesInputType::Attribute :
nodes::GeometryNodesInputType::Value;
IDP_AddToGroup(group, bke::idprop::create("type", int(input_type)).release());
const StringRefNull attribute_name = [&]() {
const IDProperty *attribute_name = IDP_GetPropertyFromGroup(old_props,
identifier + "_attribute_name");
if (attribute_name) {
return StringRefNull(IDP_string_get(attribute_name));
}
return StringRefNull();
}();
IDP_AddToGroup(group, bke::idprop::create("attribute_name", attribute_name).release());
}
IDProperty *outputs = bke::idprop::create_group("outputs").release();
IDP_AddToGroup(system_props, outputs);
for (const bNodeTreeInterfaceSocket *output : ntree.interface_outputs()) {
const StringRef identifier = output->identifier;
IDProperty *old_name_prop = IDP_GetPropertyFromGroup(old_props,
identifier + "_attribute_name");
if (!old_name_prop) {
continue;
}
IDProperty *group = bke::idprop::create_group(identifier).release();
IDP_AddToGroup(outputs, group);
IDProperty *new_value_prop = IDP_CopyProperty(old_name_prop);
STRNCPY(new_value_prop->name, "attribute_name");
IDP_AddToGroup(group, new_value_prop);
}
if (nmd.modifier.system_properties) {
IDP_FreeProperty(nmd.modifier.system_properties);
}
nmd.modifier.system_properties = system_props;
IDP_FreeProperty(nmd.settings.properties);
nmd.settings.properties = nullptr;
}
/* Saving file extension is now a property of the File Output node. So inherit this
* setting from the active scene to restore the old behavior.
* Note: One limitation is that node groups containing file outputs that are not part of any
@ -101,6 +241,17 @@ void do_versions_after_linking_520(FileData * /*fd*/, Main *bmain)
}
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 502, 16)) {
for (Object &object : bmain->objects) {
for (ModifierData &md : object.modifiers) {
if (md.type == eModifierType_Nodes) {
version_geometry_nodes_properties(
*bmain, object, reinterpret_cast<NodesModifierData &>(md));
}
}
}
}
/**
* Always bump subversion in BKE_blender_version.h when adding versioning
* code here, and wrap it inside a MAIN_VERSION_FILE_ATLEAST check.

View file

@ -283,7 +283,8 @@ RNANodeIdentifier RNANodeQuery::construct_node_identifier(const PointerRNA *ptr,
RNA_struct_is_a(ptr->type, RNA_MeshUVLoop) ||
RNA_struct_is_a(ptr->type, RNA_MeshLoopColor) ||
RNA_struct_is_a(ptr->type, RNA_VertexGroupElement) ||
RNA_struct_is_a(ptr->type, RNA_ShaderFx))
RNA_struct_is_a(ptr->type, RNA_ShaderFx) ||
(RNA_property_flag(const_cast<PropertyRNA *>(prop)) & PROP_FORCE_GEOMETRY_EVAL) != 0)
{
/* When modifier is used as FROM operation this is likely referencing to
* the property (for example, modifier's influence).

View file

@ -30,7 +30,9 @@
#include "BKE_customdata.hh"
#include "BKE_editmesh.hh"
#include "BKE_geometry_set.hh"
#include "BKE_global.hh"
#include "BKE_grease_pencil.hh"
#include "BKE_idprop_hash.hh"
#include "BKE_layer.hh"
#include "BKE_lib_id.hh"
#include "BKE_lib_query.hh"
@ -57,6 +59,8 @@
#include "RNA_access.hh"
#include "RNA_define.hh"
#include "RNA_enum_types.hh"
#include "RNA_prototypes.hh"
#include "UI_interface_layout.hh"
#include "UI_resources.hh"
@ -73,6 +77,7 @@
#include "NOD_geometry_nodes_caller_ui.hh"
#include "NOD_geometry_nodes_execute.hh"
#include "NOD_geometry_nodes_lazy_function.hh"
#include "NOD_geometry_nodes_srna.hh"
#include "AS_asset_catalog.hh"
#include "AS_asset_catalog_path.hh"
@ -120,6 +125,9 @@ struct OperatorTypeData : public wmOperatorType::TypeData {
std::string description;
GeometryNodeAssetTraitFlag flag;
std::unique_ptr<IDProperty, bke::idprop::IDPropertyDeleter> asset_meta_data_properties;
Vector<StructRNA *> generated_structs;
struct LocalRef {
uint32_t session_uid;
};
@ -167,6 +175,7 @@ void OperatorTypeData::ensure_hash()
}
},
this->group_ref);
bke::idprop::hash(*this->asset_meta_data_properties, hash_state);
static_assert(sizeof(this->hash) == sizeof(XXH128_hash_t));
const XXH128_hash_t xxh3_hash = XXH3_128bits_digest(hash_state);
this->hash[0] = xxh3_hash.low64;
@ -237,6 +246,14 @@ std::optional<OperatorTypeData> OperatorTypeData::from_asset(
}
type_data.flag = GeometryNodeAssetTraitFlag(IDP_int_get(traits_flag));
type_data.group_ref = asset.make_weak_reference();
const IDProperty *inputs = BKE_asset_metadata_idprop_find(&metadata, "inputs");
if (!inputs || inputs->type != IDP_GROUP) {
return std::nullopt;
}
type_data.asset_meta_data_properties =
std::unique_ptr<IDProperty, bke::idprop::IDPropertyDeleter>(IDP_CopyProperty(inputs));
type_data.ensure_hash();
return type_data;
}
@ -277,6 +294,11 @@ std::optional<OperatorTypeData> OperatorTypeData::from_group(const bNodeTree &gr
type_data.description = group.description ? group.description : "";
type_data.flag = GeometryNodeAssetTraitFlag(group.geometry_node_asset_traits->flag);
type_data.group_ref = OperatorTypeData::LocalRef{group.id.session_uid};
type_data.asset_meta_data_properties =
std::unique_ptr<IDProperty, bke::idprop::IDPropertyDeleter>(
bke::node_create_asset_meta_data_properties(group));
type_data.ensure_hash();
return type_data;
}
@ -990,8 +1012,14 @@ static wmOperatorStatus run_node_group_exec(bContext *C, wmOperator *op)
bke::GeometrySet geometry_orig = get_original_geometry_eval_copy(
*depsgraph_active, *object, operator_eval_data, orig_mesh_states);
IDProperty *properties_idprops = IDP_GetPropertyFromGroup(op->properties, "properties");
if (!properties_idprops) {
properties_idprops = bke::idprop::create_group("properties", IDP_FLAG_STATIC_TYPE).release();
IDP_AddToGroup(op->properties, properties_idprops);
}
bke::GeometrySet new_geometry = nodes::execute_geometry_nodes_on_geometry(
*node_tree, properties, compute_context, call_data, std::move(geometry_orig));
*node_tree, *op->ptr, compute_context, call_data, std::move(geometry_orig));
store_result_geometry(
*C, *op, *depsgraph_active, *bmain, *scene, *object, rv3d, std::move(new_geometry));
@ -1064,9 +1092,6 @@ static wmOperatorStatus run_node_group_invoke(bContext *C, wmOperator *op, const
store_input_node_values_rna_props(*C, *op, *event);
nodes::update_input_properties_from_node_tree(*node_tree, op->properties, *op->properties, true);
nodes::update_output_properties_from_node_tree(*node_tree, op->properties, *op->properties);
return run_node_group_exec(C, op);
}
@ -1121,6 +1146,280 @@ static bool run_node_group_poll(bContext *C, wmOperatorType *ot)
return true;
}
static const EnumPropertyItem *enum_input_items_fn(bContext * /*C*/,
PointerRNA *ptr,
PropertyRNA *prop,
bool *r_free)
{
const wmOperator *op = ptr->data_as<wmOperator>();
const OperatorTypeData &type_data = *static_cast<const OperatorTypeData *>(op->customdata);
const IDProperty &inputs_props = *IDP_GetPropertyFromGroup(
type_data.asset_meta_data_properties.get(), "inputs");
const IDProperty &input_idprop = *IDP_GetPropertyFromGroup(&inputs_props,
RNA_property_identifier(prop));
const IDProperty *items_idprop = IDP_GetPropertyFromGroup(&input_idprop, "items");
if (!items_idprop || items_idprop->type != IDP_GROUP) {
return rna_enum_dummy_NULL_items;
}
int totitem = 0;
EnumPropertyItem *items = nullptr;
for (IDProperty &item_idprop : items_idprop->data.group) {
EnumPropertyItem item;
item.identifier = item_idprop.name;
item.name = IDP_group_lookup_string(item_idprop, "name").value_or("").c_str();
item.description = IDP_group_lookup_string(item_idprop, "description").value_or("").c_str();
item.value = std::stoi(item_idprop.name);
RNA_enum_item_add(&items, &totitem, &item);
}
*r_free = true;
return items;
}
static void make_common_type_prop(StructRNA &srna,
const EnumPropertyItem *items,
const nodes::GeometryNodesInputType default_type)
{
PropertyRNA *prop = RNA_def_enum(&srna, "type", items, int(default_type), "Input Type", "");
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
}
static void make_common_attribute_name_prop(StructRNA &srna,
const StringRefNull name,
const StringRefNull description,
const IDProperty &input_idprop)
{
const std::optional<StringRefNull> default_name = IDP_group_lookup_string(
input_idprop, "default_attribute_name");
RNA_def_string(&srna,
"attribute_name",
default_name.has_value() ? default_name->c_str() : nullptr,
0,
name.c_str(),
description.c_str());
}
static void make_common_value_and_attribute_props(StructRNA &srna,
const StringRefNull name,
const StringRefNull description,
const IDProperty &input_idprop)
{
make_common_type_prop(srna,
nodes::geometry_nodes_input_type_items_value_or_attribute,
nodes::GeometryNodesInputType::Value);
make_common_attribute_name_prop(srna, name, description, input_idprop);
}
static void make_common_value_props(StructRNA &srna)
{
make_common_type_prop(
srna, nodes::geometry_nodes_input_type_items_value, nodes::GeometryNodesInputType::Value);
}
static StructRNA *get_input_socket_struct_rna(IDProperty &input_idprop,
Vector<StructRNA *> &r_generated)
{
const StringRefNull identifier = input_idprop.name;
const std::optional<int> type = IDP_group_lookup_int(input_idprop, "type");
if (!type) {
return nullptr;
}
StructRNA *srna = RNA_def_struct_ptr(
&RNA_blender_rna_get(), identifier.c_str(), RNA_PropertyGroup);
BLI_assert(!RNA_struct_in_public_namespace(srna));
r_generated.append(srna);
// RNA_def_struct_path_func_runtime(srna, rna_NodesModifierPropertyInput_path);
const StringRefNull name = IDP_group_lookup_string(input_idprop, "name").value_or(identifier);
const StringRefNull description =
IDP_group_lookup_string(input_idprop, "description").value_or("");
RNA_def_struct_ui_text(srna, name.c_str(), description.c_str());
switch (eNodeSocketDatatype(*type)) {
case SOCK_FLOAT: {
PropertyRNA *prop = RNA_def_float(
srna,
"value",
IDP_group_lookup_float(input_idprop, "default_value").value_or(0.0f),
-FLT_MAX,
FLT_MAX,
name.c_str(),
description.c_str(),
IDP_group_lookup_float(input_idprop, "min").value_or(-FLT_MAX),
IDP_group_lookup_float(input_idprop, "max").value_or(FLT_MAX));
RNA_def_property_subtype(
prop,
PropertySubType(IDP_group_lookup_int(input_idprop, "subtype").value_or(PROP_NONE)));
make_common_value_and_attribute_props(*srna, name, description, input_idprop);
break;
}
case SOCK_VECTOR: {
const int dimensions = IDP_group_lookup_int(input_idprop, "dimensions").value_or(3);
std::optional<Span<float>> defaults = IDP_group_lookup_float_array(
input_idprop, "default_value", dimensions);
PropertyRNA *prop = RNA_def_float_array(
srna,
"value",
dimensions,
defaults ? defaults->data() : nullptr,
-FLT_MAX,
FLT_MAX,
name.c_str(),
description.c_str(),
IDP_group_lookup_float(input_idprop, "min").value_or(-FLT_MAX),
IDP_group_lookup_float(input_idprop, "max").value_or(FLT_MAX));
RNA_def_property_subtype(
prop,
PropertySubType(IDP_group_lookup_int(input_idprop, "subtype").value_or(PROP_NONE)));
make_common_value_and_attribute_props(*srna, name, description, input_idprop);
break;
}
case SOCK_RGBA: {
std::optional<Span<float>> defaults = IDP_group_lookup_float_array(
input_idprop, "default_value", 4);
PropertyRNA *prop = RNA_def_float_color(srna,
"value",
4,
defaults ? defaults->data() : nullptr,
-FLT_MAX,
FLT_MAX,
name.c_str(),
description.c_str(),
0.0f,
1.0f);
RNA_def_property_subtype(prop, PROP_COLOR);
make_common_value_and_attribute_props(*srna, name, description, input_idprop);
break;
}
case SOCK_BOOLEAN: {
RNA_def_boolean(srna,
"value",
IDP_group_lookup_bool(input_idprop, "default_value").value_or(false),
name.c_str(),
description.c_str());
make_common_type_prop(*srna,
nodes::geometry_nodes_input_type_items_value_or_attribute_or_layer,
nodes::GeometryNodesInputType::Value);
make_common_attribute_name_prop(*srna, name, description, input_idprop);
break;
}
case SOCK_INT: {
PropertyRNA *prop = RNA_def_int(
srna,
"value",
IDP_group_lookup_int(input_idprop, "default_value").value_or(0),
INT_MIN,
INT_MAX,
name.c_str(),
description.c_str(),
IDP_group_lookup_int(input_idprop, "min").value_or(INT_MIN),
IDP_group_lookup_int(input_idprop, "max").value_or(INT_MIN));
RNA_def_property_subtype(
prop,
PropertySubType(IDP_group_lookup_int(input_idprop, "subtype").value_or(PROP_NONE)));
make_common_value_and_attribute_props(*srna, name, description, input_idprop);
break;
}
case SOCK_STRING: {
PropertyRNA *prop = RNA_def_string(
srna,
"value",
IDP_group_lookup_string(input_idprop, "default_value").value_or("").c_str(),
0,
name.c_str(),
description.c_str());
RNA_def_property_subtype(
prop,
PropertySubType(IDP_group_lookup_int(input_idprop, "subtype").value_or(PROP_NONE)));
make_common_value_props(*srna);
break;
}
case SOCK_IMAGE:
case SOCK_COLLECTION:
case SOCK_MATERIAL:
case SOCK_OBJECT: {
RNA_def_string(srna, "value", nullptr, 0, name.c_str(), description.c_str());
make_common_value_props(*srna);
break;
}
case SOCK_ROTATION: {
std::optional<Span<float>> defaults = IDP_group_lookup_float_array(
input_idprop, "default_value", 3);
RNA_def_float_rotation(srna,
"value",
3,
defaults ? defaults->data() : nullptr,
-FLT_MAX,
FLT_MAX,
name.c_str(),
description.c_str(),
-FLT_MAX,
FLT_MAX);
make_common_value_and_attribute_props(*srna, name, description, input_idprop);
break;
}
case SOCK_MENU: {
PropertyRNA *prop = RNA_def_enum(
srna, "value", rna_enum_dummy_NULL_items, 0, name.c_str(), description.c_str());
RNA_def_enum_funcs(prop, enum_input_items_fn);
make_common_value_props(*srna);
break;
}
default:
break;
}
return srna;
}
static StructRNA *create_inputs_srna(const IDProperty &properties,
Vector<StructRNA *> &r_generated)
{
StructRNA *srna = RNA_def_struct_ptr(
&RNA_blender_rna_get(), "GeometryNodesInterfaceInputs", RNA_PropertyGroup);
BLI_assert(!RNA_struct_in_public_namespace(srna));
r_generated.append(srna);
const IDProperty &inputs_props = *IDP_GetPropertyFromGroup(&properties, "inputs");
for (IDProperty &input_idprop : inputs_props.data.group) {
if (input_idprop.type != IDP_GROUP) {
continue;
}
StructRNA *input_srna = get_input_socket_struct_rna(input_idprop, r_generated);
if (!input_srna) {
continue;
}
BLI_assert(!RNA_struct_in_public_namespace(srna));
RNA_def_pointer_runtime(srna,
input_idprop.name,
input_srna,
RNA_struct_ui_name(input_srna),
RNA_struct_ui_description(input_srna));
}
return srna;
}
static StructRNA *create_panels_srna(const IDProperty &properties,
Vector<StructRNA *> &r_generated)
{
StructRNA *srna = RNA_def_struct_ptr(
&RNA_blender_rna_get(), "GeometryNodesInterfacePanels", RNA_PropertyGroup);
BLI_assert(!RNA_struct_in_public_namespace(srna));
r_generated.append(srna);
const IDProperty &panels_props = *IDP_GetPropertyFromGroup(&properties, "panels");
for (IDProperty &panel_prop : panels_props.data.group) {
printf("panel prop: %s\n", panel_prop.name);
RNA_def_boolean(srna, panel_prop.name, IDP_bool_get(&panel_prop), "Is Open", "");
}
return srna;
}
static void register_node_tool(wmOperatorType *ot,
std::unique_ptr<OperatorTypeData> &type_data_ptr)
{
@ -1143,6 +1442,13 @@ static void register_node_tool(wmOperatorType *ot,
ot->flag |= OPTYPE_DEPENDS_ON_CURSOR;
}
StructRNA *inputs_srna = create_inputs_srna(*type_data.asset_meta_data_properties,
type_data.generated_structs);
StructRNA *panels_srna = create_panels_srna(*type_data.asset_meta_data_properties,
type_data.generated_structs);
RNA_def_pointer_runtime(ot->srna, "inputs", inputs_srna, "Inputs", "Settings for input sockets");
RNA_def_pointer_runtime(ot->srna, "panels", panels_srna, "Panels", "Settings for panels");
/* See comment for #store_input_node_values_rna_props. */
prop = RNA_def_int_array(ot->srna,
"mouse_position",
@ -1336,6 +1642,14 @@ void register_node_group_operators(const bContext &C)
WM_operator_stack_clear(&wm, types_to_remove);
WM_operator_handlers_clear(&wm, types_to_remove);
for (wmOperatorType *ot : types_to_remove) {
OperatorTypeData &type_data = static_cast<OperatorTypeData &>(*ot->custom_data);
for (StructRNA *srna : type_data.generated_structs) {
/* Avoids warning when freeing the #StructRNA. */
RNA_struct_py_type_set(srna, nullptr);
RNA_struct_free(&RNA_blender_rna_get(), srna);
}
WM_operatortype_remove_ptr(ot);
}
}

View file

@ -105,8 +105,8 @@ std::optional<nodes::FoundNestedNodeID> find_nested_node_id_in_root(
const bNodeTree &root_tree, const ComputeContext *compute_context, const int node_id);
struct ObjectAndModifier {
const Object *object;
const NodesModifierData *nmd;
const Object *object = nullptr;
const NodesModifierData *nmd = nullptr;
};
/**
* Finds the context-modifier for the node editor.

View file

@ -1973,14 +1973,11 @@ static wmOperatorStatus shade_auto_smooth_exec(bContext *C, wmOperator *op)
BKE_modifier_unique_name(&object->modifiers, &smooth_by_angle_nmd->modifier);
}
IDProperty *angle_prop = IDP_GetPropertyFromGroup(smooth_by_angle_nmd->settings.properties,
angle_identifier.c_str());
if (angle_prop->type == IDP_FLOAT) {
IDP_float_set(angle_prop, angle);
}
else if (angle_prop->type == IDP_DOUBLE) {
IDP_double_set(angle_prop, angle);
}
PointerRNA nmd_ptr = RNA_pointer_create_discrete(
&object->id, RNA_NodesModifier, smooth_by_angle_nmd);
PointerRNA properties_ptr = RNA_pointer_get(&nmd_ptr, "properties");
RNA_float_set(&properties_ptr, angle_identifier.c_str(), angle);
DEG_id_tag_update(&object->id, ID_RECALC_GEOMETRY);
WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, object);

View file

@ -81,6 +81,7 @@
#include "DEG_depsgraph_build.hh"
#include "DEG_depsgraph_query.hh"
#include "NOD_geometry_nodes_srna.hh"
#include "RNA_access.hh"
#include "RNA_define.hh"
#include "RNA_enum_types.hh"
@ -3488,21 +3489,19 @@ static wmOperatorStatus geometry_nodes_input_attribute_toggle_exec(bContext *C,
char input_name[MAX_NAME];
RNA_string_get(op->ptr, "input_name", input_name);
IDProperty *use_attribute = IDP_GetPropertyFromGroup(
nmd->settings.properties, std::string(input_name + std::string("_use_attribute")).c_str());
if (!use_attribute) {
return OPERATOR_CANCELLED;
}
PointerRNA modifier_ptr = RNA_pointer_create_discrete(&ob->id, RNA_NodesModifier, nmd);
PointerRNA properties_ptr = RNA_pointer_get(&modifier_ptr, "properties");
PointerRNA inputs_ptr = RNA_pointer_get(&properties_ptr, "inputs");
PointerRNA input_ptr = RNA_pointer_get(&inputs_ptr, input_name);
if (use_attribute->type == IDP_INT) {
IDP_int_set(use_attribute, !IDP_int_get(use_attribute));
}
else if (use_attribute->type == IDP_BOOLEAN) {
IDP_bool_set(use_attribute, !IDP_bool_get(use_attribute));
int type = RNA_enum_get(&input_ptr, "type");
if (type == int(nodes::GeometryNodesInputType::Attribute)) {
type = int(nodes::GeometryNodesInputType::Value);
}
else {
return OPERATOR_CANCELLED;
type = int(nodes::GeometryNodesInputType::Attribute);
}
RNA_enum_set(&input_ptr, "type", type);
DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob);

View file

@ -201,6 +201,8 @@ struct ModifierData {
char *error = nullptr;
struct IDProperty *system_properties = nullptr;
/** Runtime field which contains runtime data which is specific to a modifier type. */
void *runtime = nullptr;
};

View file

@ -115,6 +115,11 @@ const char *RNA_struct_ui_description_raw(const StructRNA *type);
const char *RNA_struct_translation_context(const StructRNA *type);
int RNA_struct_ui_icon(const StructRNA *type);
/**
* Debug utility to print a #StructRNA.
*/
std::string RNA_struct_to_string(const StructRNA &type);
PropertyRNA *RNA_struct_name_property(const StructRNA *type);
const EnumPropertyItem *RNA_struct_property_tag_defines(const StructRNA *type);
PropertyRNA *RNA_struct_iterator_property(StructRNA *type);
@ -167,6 +172,12 @@ bool RNA_struct_idprops_contains_datablock(const StructRNA *type);
*/
bool RNA_struct_system_idprops_unset(PointerRNA *ptr, const char *identifier);
/**
* If true, the name of the struct is unique among all names in the public namespace and can be
* looked up with #RNA_struct_find.
*/
bool RNA_struct_in_public_namespace(const StructRNA *type);
PropertyRNA *RNA_struct_find_property(PointerRNA *ptr, const char *identifier);
/**
@ -666,6 +677,12 @@ int RNA_property_collection_raw_set(ReportList *reports,
size_t RNA_raw_type_sizeof(RawPropertyType type);
RawPropertyType RNA_property_raw_type(PropertyRNA *prop);
/**
* Update the system properties (IDProperties) for a specific RNA type, converting so that
* properties match the current RNA definition.
*/
void RNA_sync_system_properties(PointerRNA &ptr, IDProperty &idprops);
/* to create ID property groups */
void RNA_property_pointer_add(PointerRNA *ptr, PropertyRNA *prop);
void RNA_property_pointer_remove(PointerRNA *ptr, PropertyRNA *prop);

View file

@ -33,6 +33,11 @@ namespace blender {
struct Scene;
BlenderRNA *RNA_create();
/**
* Create a container for RNA types that are defined at runtime (in contrast to the main global
* #BlenderRNA which contains RNA types defined at startup from static data).
*/
BlenderRNA *RNA_create_runtime();
void RNA_define_free(BlenderRNA *brna);
void RNA_free(BlenderRNA *brna);
@ -101,6 +106,7 @@ void RNA_def_struct_register_funcs(StructRNA *srna,
* Paths must be compatible with #RNA_path_resolve & related functions.
*/
void RNA_def_struct_path_func(StructRNA *srna, const char *path);
void RNA_def_struct_path_func_runtime(StructRNA *srna, StructPathFunc path_fn);
/**
* Only used in one case when we name the struct for the purpose of useful error messages.
*/
@ -654,6 +660,10 @@ void RNA_def_property_string_funcs_runtime(PropertyRNA *prop,
void RNA_def_property_string_search_func_runtime(PropertyRNA *prop,
StringPropertySearchFunc search_fn,
eStringPropertySearchFlag search_flag);
void RNA_def_property_pointer_funcs_runtime(PropertyRNA *prop,
PointerPropertyGetFunc getfunc,
PointerPropertySetFunc setfunc,
PointerPropertyTypeFunc typefunc);
void RNA_def_property_translation_context(PropertyRNA *prop, const char *context);

View file

@ -486,6 +486,9 @@ enum PropertyFlag {
/** Do not write in presets (#PROP_HIDDEN and #PROP_SKIP_SAVE won't either). */
PROP_SKIP_PRESET = (1 << 11),
/** Use full geometry depsgraph evaluation when this property changes. */
PROP_FORCE_GEOMETRY_EVAL = (1 << 3),
};
ENUM_OPERATORS(PropertyFlag)
@ -780,6 +783,11 @@ using StringPropertySetTransformFunc = std::string (*)(PointerRNA *ptr,
const std::string &new_value,
const std::string &curr_value,
bool is_set);
using PointerPropertyGetFunc = PointerRNA (*)(PointerRNA *ptr);
using PointerPropertySetFunc = void (*)(PointerRNA *ptr, PointerRNA value, ReportList *reports);
using PointerPropertyTypeFunc = StructRNA *(*)(PointerRNA * ptr);
using StructPathFunc = std::optional<std::string> (*)(const PointerRNA *ptr);
struct StringPropertySearchVisitParams {
/** Text being searched for. */

View file

@ -746,6 +746,19 @@ PropertyRNA *RNA_struct_iterator_property(StructRNA *type)
return type->iteratorproperty;
}
std::string RNA_struct_to_string(const StructRNA &type)
{
fmt::memory_buffer buf;
fmt::appender dst{buf};
fmt::format_to(dst, "Type: {}\n", type.identifier);
for (const PropertyRNA &prop : type.cont.properties) {
fmt::format_to(dst, " {}\n", RNA_property_identifier(&prop));
}
return fmt::to_string(buf);
}
StructRNA *RNA_struct_base(StructRNA *type)
{
return type->base;
@ -772,6 +785,11 @@ bool RNA_struct_undo_check(const StructRNA *type)
return (type->flag & STRUCT_UNDO) != 0;
}
bool RNA_struct_in_public_namespace(const StructRNA *type)
{
return (type->flag & STRUCT_PUBLIC_NAMESPACE) != 0;
}
bool RNA_struct_idprops_datablock_allowed(const StructRNA *type)
{
return (type->flag & (STRUCT_NO_DATABLOCK_IDPROPERTIES | STRUCT_NO_IDPROPERTIES)) == 0;
@ -4494,16 +4512,15 @@ static PointerRNA property_pointer_get(PointerRNA *ptr, PropertyRNA *prop, const
if ((idprop = rna_idproperty_check(&prop, ptr))) {
pprop = reinterpret_cast<PointerPropertyRNA *>(prop);
if (RNA_struct_is_ID(pprop->pointer_type)) {
StructRNA *type = pprop->type_fn ? pprop->type_fn(ptr) : pprop->pointer_type;
if (RNA_struct_is_ID(type)) {
/* ID PointerRNA should not have ancestors currently. */
return RNA_id_pointer_create(idprop->type == IDP_GROUP ? nullptr : IDP_ID_get(idprop));
}
/* for groups, data is idprop itself */
if (pprop->type_fn) {
return RNA_pointer_create_with_parent(*ptr, pprop->type_fn(ptr), idprop);
}
return RNA_pointer_create_with_parent(*ptr, pprop->pointer_type, idprop);
return RNA_pointer_create_with_parent(*ptr, type, idprop);
}
if (pprop->get) {
return pprop->get(ptr);
@ -5864,6 +5881,494 @@ int RNA_property_collection_raw_set(ReportList *reports,
return rna_raw_access(reports, ptr, prop, propname, array, type, len, 1);
}
static void update_idprop_bool(PointerRNA &rna_ptr, PropertyRNA &rna_prop, IDProperty &idprop)
{
BLI_assert(RNA_property_type(&rna_prop) == PROP_BOOLEAN);
const int rna_array_size = RNA_property_array_length(&rna_ptr, &rna_prop);
const auto fill_new = [&]() {
IDP_ClearProperty(&idprop);
if (rna_array_size > 1) {
idprop.type = IDP_ARRAY;
idprop.subtype = IDP_BOOLEAN;
idprop.data.pointer = MEM_new_array_uninitialized<bool>(rna_array_size,
"update_idprop_bool");
idprop.len = rna_array_size;
idprop.totallen = rna_array_size;
RNA_property_boolean_get_default_array(
&rna_ptr, &rna_prop, static_cast<bool *>(idprop.data.pointer));
}
else {
idprop.type = IDP_BOOLEAN;
IDP_bool_set(&idprop, RNA_property_boolean_get_default(&rna_ptr, &rna_prop));
}
};
switch (eIDPropertyType(idprop.type)) {
case IDP_STRING:
case IDP_IDPARRAY:
case IDP_ID:
case IDP_GROUP: {
fill_new();
break;
}
case IDP_ARRAY: {
const int idprop_array_size = idprop.len;
switch (eIDPropertyType(idprop.subtype)) {
case IDP_IDPARRAY:
case IDP_ID:
case IDP_STRING:
case IDP_ARRAY:
case IDP_GROUP: {
BLI_assert_unreachable();
fill_new();
break;
}
case IDP_INT: {
const int *old_values = IDP_array_int_get(&idprop);
bool *new_values = MEM_new_array_zeroed<bool>(rna_array_size, __func__);
for (const int i : IndexRange(std::min(rna_array_size, idprop_array_size))) {
new_values[i] = bool(old_values[i]);
}
IDP_ClearProperty(&idprop);
idprop.subtype = IDP_BOOLEAN;
idprop.len = rna_array_size;
idprop.totallen = rna_array_size;
idprop.data.pointer = new_values;
break;
}
case IDP_FLOAT: {
const float *old_values = IDP_array_float_get(&idprop);
bool *new_values = MEM_new_array_zeroed<bool>(rna_array_size, __func__);
for (const int i : IndexRange(std::min(rna_array_size, idprop_array_size))) {
new_values[i] = bool(old_values[i]);
}
IDP_ClearProperty(&idprop);
idprop.subtype = IDP_BOOLEAN;
idprop.len = rna_array_size;
idprop.totallen = rna_array_size;
idprop.data.pointer = new_values;
break;
}
case IDP_DOUBLE: {
const double *old_values = IDP_array_double_get(&idprop);
bool *new_values = MEM_new_array_zeroed<bool>(rna_array_size, __func__);
for (const int i : IndexRange(std::min(rna_array_size, idprop_array_size))) {
new_values[i] = bool(old_values[i]);
}
IDP_ClearProperty(&idprop);
idprop.subtype = IDP_BOOLEAN;
idprop.len = rna_array_size;
idprop.totallen = rna_array_size;
idprop.data.pointer = new_values;
break;
}
case IDP_BOOLEAN: {
if (idprop_array_size != rna_array_size) {
if (rna_array_size == 0) {
fill_new();
}
else {
const int8_t *old_values = IDP_array_bool_get(&idprop);
auto *new_values = MEM_new_array_zeroed<int8_t>(rna_array_size, __func__);
std::copy_n(old_values, std::min(rna_array_size, idprop_array_size), new_values);
IDP_ClearProperty(&idprop);
idprop.subtype = IDP_BOOLEAN;
idprop.len = rna_array_size;
idprop.totallen = rna_array_size;
idprop.data.pointer = new_values;
}
}
break;
}
}
break;
}
case IDP_INT: {
const int value = IDP_int_get(&idprop);
IDP_ClearProperty(&idprop);
idprop.type = IDP_BOOLEAN;
IDP_bool_set(&idprop, bool(value));
break;
}
case IDP_FLOAT: {
const float value = IDP_float_get(&idprop);
IDP_ClearProperty(&idprop);
idprop.type = IDP_BOOLEAN;
IDP_bool_set(&idprop, bool(value));
break;
}
case IDP_DOUBLE: {
const double value = IDP_double_get(&idprop);
IDP_ClearProperty(&idprop);
idprop.type = IDP_BOOLEAN;
IDP_bool_set(&idprop, bool(value));
break;
}
case IDP_BOOLEAN: {
if (rna_array_size != 0) {
fill_new();
}
break;
}
}
}
static void update_idprop_int(PointerRNA &rna_ptr, PropertyRNA &rna_prop, IDProperty &idprop)
{
BLI_assert(RNA_property_type(&rna_prop) == PROP_INT);
const int rna_array_size = RNA_property_array_length(&rna_ptr, &rna_prop);
const auto fill_new = [&]() {
IDP_ClearProperty(&idprop);
if (rna_array_size > 1) {
idprop.type = IDP_ARRAY;
idprop.subtype = PROP_INT;
idprop.data.pointer = MEM_new_array_uninitialized<int>(rna_array_size, "update_idprop_int");
idprop.len = rna_array_size;
idprop.totallen = rna_array_size;
RNA_property_int_get_default_array(
&rna_ptr, &rna_prop, static_cast<int *>(idprop.data.pointer));
}
else {
idprop.type = PROP_INT;
IDP_int_set(&idprop, RNA_property_int_get_default(&rna_ptr, &rna_prop));
}
};
switch (eIDPropertyType(idprop.type)) {
case IDP_STRING:
case IDP_IDPARRAY:
case IDP_ID:
case IDP_GROUP: {
fill_new();
break;
}
case IDP_ARRAY: {
const int idprop_array_size = idprop.len;
switch (eIDPropertyType(idprop.subtype)) {
case IDP_IDPARRAY:
case IDP_ID:
case IDP_STRING:
case IDP_ARRAY:
case IDP_GROUP: {
BLI_assert_unreachable();
fill_new();
break;
}
case IDP_INT: {
if (idprop_array_size != rna_array_size) {
if (rna_array_size == 0) {
fill_new();
}
else {
const int *old_values = IDP_array_int_get(&idprop);
auto *new_values = MEM_new_array_zeroed<int>(rna_array_size, __func__);
std::copy_n(old_values, std::min(rna_array_size, idprop_array_size), new_values);
IDP_ClearProperty(&idprop);
idprop.subtype = PROP_INT;
idprop.len = rna_array_size;
idprop.totallen = rna_array_size;
idprop.data.pointer = new_values;
}
}
break;
}
case IDP_FLOAT: {
const float *old_values = IDP_array_float_get(&idprop);
auto *new_values = MEM_new_array_zeroed<int>(rna_array_size, __func__);
for (const int i : IndexRange(std::min(rna_array_size, idprop_array_size))) {
new_values[i] = int(old_values[i]);
}
IDP_ClearProperty(&idprop);
idprop.subtype = PROP_INT;
idprop.len = rna_array_size;
idprop.totallen = rna_array_size;
idprop.data.pointer = new_values;
break;
}
case IDP_DOUBLE: {
const double *old_values = IDP_array_double_get(&idprop);
auto *new_values = MEM_new_array_zeroed<int>(rna_array_size, __func__);
for (const int i : IndexRange(std::min(rna_array_size, idprop_array_size))) {
new_values[i] = int(old_values[i]);
}
IDP_ClearProperty(&idprop);
idprop.subtype = PROP_INT;
idprop.len = rna_array_size;
idprop.totallen = rna_array_size;
idprop.data.pointer = new_values;
break;
}
case IDP_BOOLEAN: {
const int8_t *old_values = IDP_array_bool_get(&idprop);
auto *new_values = MEM_new_array_zeroed<int>(rna_array_size, __func__);
for (const int i : IndexRange(std::min(rna_array_size, idprop_array_size))) {
new_values[i] = int(old_values[i]);
}
IDP_ClearProperty(&idprop);
idprop.subtype = PROP_INT;
idprop.len = rna_array_size;
idprop.totallen = rna_array_size;
idprop.data.pointer = new_values;
break;
}
}
break;
}
case IDP_INT: {
if (rna_array_size != 0) {
fill_new();
}
break;
}
case IDP_FLOAT: {
const float value = IDP_float_get(&idprop);
IDP_ClearProperty(&idprop);
idprop.type = PROP_INT;
IDP_int_set(&idprop, int(value));
break;
}
case IDP_DOUBLE: {
const double value = IDP_double_get(&idprop);
IDP_ClearProperty(&idprop);
idprop.type = PROP_INT;
IDP_int_set(&idprop, int(value));
break;
}
case IDP_BOOLEAN: {
const bool value = IDP_bool_get(&idprop);
IDP_ClearProperty(&idprop);
idprop.type = PROP_INT;
IDP_int_set(&idprop, int(value));
break;
}
}
}
static void update_idprop_float(PointerRNA &rna_ptr, PropertyRNA &rna_prop, IDProperty &idprop)
{
BLI_assert(RNA_property_type(&rna_prop) == PROP_FLOAT);
const int rna_array_size = RNA_property_array_length(&rna_ptr, &rna_prop);
const auto fill_new = [&]() {
IDP_ClearProperty(&idprop);
if (rna_array_size > 1) {
idprop.type = IDP_ARRAY;
idprop.subtype = IDP_FLOAT;
idprop.data.pointer = MEM_new_array_uninitialized<float>(rna_array_size,
"update_idprop_float");
idprop.len = rna_array_size;
idprop.totallen = rna_array_size;
RNA_property_float_get_default_array(
&rna_ptr, &rna_prop, static_cast<float *>(idprop.data.pointer));
}
else {
idprop.type = IDP_FLOAT;
IDP_float_set(&idprop, RNA_property_float_get_default(&rna_ptr, &rna_prop));
}
};
switch (eIDPropertyType(idprop.type)) {
case IDP_STRING:
case IDP_IDPARRAY:
case IDP_ID:
case IDP_GROUP: {
fill_new();
break;
}
case IDP_ARRAY: {
const int idprop_array_size = idprop.len;
switch (eIDPropertyType(idprop.subtype)) {
case IDP_IDPARRAY:
case IDP_ID:
case IDP_STRING:
case IDP_ARRAY:
case IDP_GROUP: {
BLI_assert_unreachable();
fill_new();
break;
}
case IDP_INT: {
const int *old_values = IDP_array_int_get(&idprop);
auto *new_values = MEM_new_array_zeroed<float>(rna_array_size, __func__);
for (const int i : IndexRange(std::min(rna_array_size, idprop_array_size))) {
new_values[i] = float(old_values[i]);
}
IDP_ClearProperty(&idprop);
idprop.subtype = IDP_FLOAT;
idprop.len = rna_array_size;
idprop.totallen = rna_array_size;
idprop.data.pointer = new_values;
break;
}
case IDP_FLOAT: {
if (idprop_array_size != rna_array_size) {
if (rna_array_size == 0) {
fill_new();
}
else {
const float *old_values = IDP_array_float_get(&idprop);
auto *new_values = MEM_new_array_zeroed<float>(rna_array_size, __func__);
std::copy_n(old_values, std::min(rna_array_size, idprop_array_size), new_values);
IDP_ClearProperty(&idprop);
idprop.subtype = IDP_FLOAT;
idprop.len = rna_array_size;
idprop.totallen = rna_array_size;
idprop.data.pointer = new_values;
}
}
break;
}
case IDP_DOUBLE: {
if (idprop_array_size != rna_array_size) {
if (rna_array_size == 0) {
fill_new();
}
else {
const double *old_values = IDP_array_double_get(&idprop);
auto *new_values = MEM_new_array_zeroed<double>(rna_array_size, __func__);
std::copy_n(old_values, std::min(rna_array_size, idprop_array_size), new_values);
IDP_ClearProperty(&idprop);
idprop.subtype = IDP_DOUBLE;
idprop.len = rna_array_size;
idprop.totallen = rna_array_size;
idprop.data.pointer = new_values;
}
}
break;
}
case IDP_BOOLEAN: {
const int8_t *old_values = IDP_array_bool_get(&idprop);
auto *new_values = MEM_new_array_zeroed<float>(rna_array_size, __func__);
for (const int i : IndexRange(std::min(rna_array_size, idprop_array_size))) {
new_values[i] = float(old_values[i]);
}
IDP_ClearProperty(&idprop);
idprop.subtype = IDP_FLOAT;
idprop.len = rna_array_size;
idprop.totallen = rna_array_size;
idprop.data.pointer = new_values;
break;
}
}
break;
}
case IDP_INT: {
const int value = IDP_int_get(&idprop);
IDP_ClearProperty(&idprop);
idprop.type = IDP_FLOAT;
IDP_float_set(&idprop, float(value));
break;
}
case IDP_FLOAT: {
if (rna_array_size != 0) {
fill_new();
}
break;
}
case IDP_DOUBLE: {
if (rna_array_size != 0) {
fill_new();
}
break;
}
case IDP_BOOLEAN: {
const bool value = IDP_bool_get(&idprop);
IDP_ClearProperty(&idprop);
idprop.type = IDP_FLOAT;
IDP_float_set(&idprop, float(value));
break;
}
}
}
void RNA_sync_system_properties(PointerRNA &ptr, IDProperty &idprops)
{
BLI_assert(idprops.type == IDP_GROUP);
Set<IDProperty *> used_props;
StructRNA &srna = *ptr.type;
for (PropertyRNA &rna_prop : *RNA_struct_type_properties(&srna)) {
if (RNA_property_builtin(&rna_prop)) {
continue;
}
if (STREQ(rna_prop.identifier, "rna_type")) {
/* Avoid infinite loop trying to create property group for this property that's defined
* automatically for every type, including the base type "RNA_Struct". */
continue;
}
const StringRefNull identifier = RNA_property_identifier(&rna_prop);
IDProperty *idprop = IDP_GetPropertyFromGroup(&idprops, identifier);
if (!idprop) {
continue;
}
used_props.add_new(idprop);
switch (RNA_property_type(&rna_prop)) {
case PROP_BOOLEAN: {
update_idprop_bool(ptr, rna_prop, *idprop);
break;
}
case PROP_INT: {
update_idprop_int(ptr, rna_prop, *idprop);
break;
}
case PROP_FLOAT: {
update_idprop_float(ptr, rna_prop, *idprop);
break;
}
case PROP_STRING: {
if (idprop->type != IDP_STRING) {
IDP_ClearProperty(idprop);
idprop->type = IDP_STRING;
idprop->subtype = IDP_STRING_SUB_UTF8;
int len;
char *string = RNA_property_string_get_default_alloc(&ptr, &rna_prop, nullptr, 0, &len);
idprop->data.pointer = string;
idprop->len = len + 1;
idprop->totallen = len;
}
break;
}
case PROP_ENUM: {
if (idprop->type != IDP_INT) {
IDP_ClearProperty(idprop);
idprop->type = IDP_INT;
idprop->data.val = RNA_property_enum_get_default(&ptr, &rna_prop);
}
break;
}
case PROP_POINTER: {
StructRNA *prop_srna = RNA_property_pointer_type(&ptr, &rna_prop);
if (RNA_struct_is_ID(prop_srna)) {
if (idprop->type != IDP_ID) {
IDP_ClearProperty(idprop);
idprop->type = IDP_ID;
}
}
else {
if (idprop->type != IDP_GROUP) {
IDP_ClearProperty(idprop);
idprop->type = IDP_GROUP;
continue;
}
PointerRNA prop_ptr = RNA_property_pointer_get(&ptr, &rna_prop);
RNA_sync_system_properties(prop_ptr, *idprop);
}
break;
}
case PROP_COLLECTION: {
/* Not yet supported. */
BLI_assert_unreachable();
break;
}
}
}
/* Remove old properties no longer used by the RNA type. */
for (IDProperty &prop : idprops.data.group.items_mutable()) {
if (!used_props.contains(&prop)) {
IDP_FreeFromGroup(&idprops, &prop);
}
}
}
/* Standard iterator functions */
void rna_iterator_listbase_begin(CollectionPropertyIterator *iter,

View file

@ -134,6 +134,17 @@ bool RNA_property_overridable_get(const PointerRNA *ptr, PropertyRNA *prop)
return true;
}
}
else if (!RNA_struct_in_public_namespace(ptr->type)) {
if (const std::optional<AncestorPointerRNA> ancestor =
RNA_struct_search_closest_ancestor_by_type(const_cast<PointerRNA *>(ptr),
RNA_Modifier))
{
ModifierData *mod = static_cast<ModifierData *>(ancestor->data);
if (mod->flag & eModifierFlag_OverrideLibrary_Local) {
return true;
}
}
}
else if (RNA_struct_is_a(ptr->type, RNA_Modifier)) {
ModifierData *mod = static_cast<ModifierData *>(ptr->data);
if (mod->flag & eModifierFlag_OverrideLibrary_Local) {

View file

@ -700,6 +700,13 @@ static bool rna_range_from_int_type(const char *dnatype, int r_range[2])
/* Blender Data Definition */
BlenderRNA *RNA_create_runtime()
{
BlenderRNA *brna = MEM_new<BlenderRNA>(__func__);
brna->runtime = true;
return brna;
}
BlenderRNA *RNA_create()
{
BlenderRNA *brna = MEM_new<BlenderRNA>(__func__);
@ -878,8 +885,19 @@ void RNA_free(BlenderRNA *brna)
/* Reverse iteration to make removing from vector faster. */
for (auto srna = brna->structs.rbegin(); srna != brna->structs.rend(); srna++) {
if (brna->runtime) {
#ifdef RNA_RUNTIME
# ifdef WITH_PYTHON
BPY_free_srna_pytype(srna->get());
# endif
#endif
}
RNA_struct_free(brna, srna->get());
}
if (brna->runtime) {
MEM_delete(brna);
}
}
#ifndef RNA_RUNTIME
@ -1285,6 +1303,11 @@ void RNA_def_struct_path_func(StructRNA *srna, const char *path)
}
}
void RNA_def_struct_path_func_runtime(StructRNA *srna, StructPathFunc path_fn)
{
srna->path = path_fn;
}
void RNA_def_struct_identifier(BlenderRNA *brna, StructRNA *srna, const char *identifier)
{
if (DefRNA.preprocess) {
@ -3837,6 +3860,23 @@ void RNA_def_property_string_search_func_runtime(PropertyRNA *prop,
}
}
void RNA_def_property_pointer_funcs_runtime(PropertyRNA *prop,
PointerPropertyGetFunc getfunc,
PointerPropertySetFunc setfunc,
PointerPropertyTypeFunc typefunc)
{
PointerPropertyRNA *pprop = reinterpret_cast<PointerPropertyRNA *>(prop);
if (getfunc) {
pprop->get = getfunc;
}
if (setfunc) {
pprop->set = setfunc;
}
if (typefunc) {
pprop->type_fn = typefunc;
}
}
void RNA_def_property_pointer_funcs(
PropertyRNA *prop, const char *get, const char *set, const char *type_fn, const char *poll)
{

View file

@ -58,7 +58,6 @@ using EditableFunc = int (*)(const PointerRNA *ptr, const char **r_info);
using ItemEditableFunc = int (*)(const PointerRNA *ptr, int index);
using IDPropertiesFunc = IDProperty **(*)(PointerRNA * ptr);
using StructRefineFunc = StructRNA *(*)(PointerRNA * ptr);
using StructPathFunc = std::optional<std::string> (*)(const PointerRNA *ptr);
using PropUINameFunc = const char *(*)(const PointerRNA *ptr,
const PropertyRNA *prop,
bool do_translate);
@ -746,6 +745,14 @@ struct BlenderRNA {
* These are ensured to have unique names (with #STRUCT_PUBLIC_NAMESPACE enabled).
*/
Map<StringRef, StructRNA *> structs_map;
/**
* This RNA container is created at runtime and is not the main static RNA. This is currently
* needed because we the main RNA static RNA container is cleared via #RNA_exit() rather than
* relying on static initialization order (and therefore the destructor), and we need some way to
* signal this.
*/
bool runtime;
};
#define CONTAINER_RNA_ID(cont) (*(const char **)(((ContainerRNA *)(cont)) + 1))

View file

@ -877,6 +877,8 @@ static const EnumPropertyItem grease_pencil_build_time_mode_items[] = {
# include "MOD_nodes.hh"
# include "NOD_geometry_nodes_srna.hh"
# include "ED_object.hh"
# ifdef WITH_ALEMBIC
@ -1929,6 +1931,36 @@ static void rna_NodesModifier_node_group_update(Main *bmain, Scene *scene, Point
MOD_nodes_update_interface(object, nmd);
}
static StructRNA *rna_NodesModifierProperties_refine(PointerRNA *ptr)
{
auto *nmd = ptr->data_as<NodesModifierData>();
if (!nmd->node_group || ID_MISSING(nmd->node_group)) {
return RNA_NodesModifierPropertiesEmpty;
}
return nmd->node_group->runtime->geometry_nodes_srna_data->properties_struct;
}
static std::optional<std::string> rna_NodesModifierProperties_path(const PointerRNA *ptr)
{
const auto *nmd = ptr->data_as<NodesModifierData>();
return fmt::format("modifiers[\"{}\"].properties", BLI_str_escape(nmd->modifier.name));
}
static IDProperty **rna_Modifier_idprops(PointerRNA *ptr)
{
auto *md = ptr->data_as<ModifierData>();
return &md->system_properties;
}
static PointerRNA rna_NodesModifierProperties_get(PointerRNA *ptr)
{
auto *nmd = ptr->data_as<NodesModifierData>();
if (!nmd->node_group) {
return PointerRNA_NULL;
}
return RNA_pointer_create_with_parent(*ptr, RNA_NodesModifierProperties, nmd);
}
static nodes::geo_eval_log::GeoTreeLog *get_nodes_modifier_log(const Object &object,
NodesModifierData &nmd)
{
@ -2001,17 +2033,18 @@ static int rna_NodesModifierWarning_type_get(PointerRNA *ptr)
return int(warning->type);
}
static bool rna_NodesModifier_is_input_visible(NodesModifierData *nmd,
static bool rna_NodesModifier_is_input_visible(PointerRNA nmd_ptr,
ReportList *reports,
const char *identifier)
{
const Object &object = *id_cast<Object *>(nmd_ptr.owner_id);
const NodesModifierData *nmd = nmd_ptr.data_as<NodesModifierData>();
bNodeTree *ntree = nmd->node_group;
if (ntree == nullptr) {
return false;
}
nmd->runtime->usage_cache.ensure(*nmd);
nmd->runtime->usage_cache.ensure(object, *nmd);
const auto &input_usages = nmd->runtime->usage_cache.inputs;
const int index = ntree->interface_input_index_by_identifier(identifier);
@ -2023,17 +2056,18 @@ static bool rna_NodesModifier_is_input_visible(NodesModifierData *nmd,
return false;
}
static bool rna_NodesModifier_is_input_used(NodesModifierData *nmd,
static bool rna_NodesModifier_is_input_used(PointerRNA nmd_ptr,
ReportList *reports,
const char *identifier)
{
const Object &object = *id_cast<Object *>(nmd_ptr.owner_id);
const NodesModifierData *nmd = nmd_ptr.data_as<NodesModifierData>();
bNodeTree *ntree = nmd->node_group;
if (ntree == nullptr) {
return false;
}
nmd->runtime->usage_cache.ensure(*nmd);
nmd->runtime->usage_cache.ensure(object, *nmd);
const auto &input_usages = nmd->runtime->usage_cache.inputs;
const int index = ntree->interface_input_index_by_identifier(identifier);
@ -2045,7 +2079,7 @@ static bool rna_NodesModifier_is_input_used(NodesModifierData *nmd,
return false;
}
static IDProperty **rna_NodesModifier_properties(PointerRNA *ptr)
static IDProperty **rna_NodesModifier_settings_properties(PointerRNA *ptr)
{
NodesModifierData *nmd = static_cast<NodesModifierData *>(ptr->data);
NodesModifierSettings *settings = &nmd->settings;
@ -8093,6 +8127,22 @@ static void rna_def_modifier_nodes_warning(BlenderRNA *brna)
RNA_def_property_enum_funcs(prop, "rna_NodesModifierWarning_type_get", nullptr, nullptr);
}
static void rna_def_modifier_nodes_properties(BlenderRNA *brna)
{
StructRNA *srna;
srna = RNA_def_struct(brna, "NodesModifierProperties", nullptr);
RNA_def_struct_ui_text(srna, "Geometry Nodes Modifier Properties", "");
RNA_def_struct_refine_func(srna, "rna_NodesModifierProperties_refine");
RNA_def_struct_system_idprops_func(srna, "rna_Modifier_idprops");
RNA_def_struct_path_func(srna, "rna_NodesModifierProperties_path");
srna = RNA_def_struct(brna, "NodesModifierPropertiesEmpty", nullptr);
RNA_def_struct_ui_text(srna, "Geometry Nodes Modifier Empty Properties", "");
RNA_def_struct_system_idprops_func(srna, "rna_Modifier_idprops");
RNA_def_struct_path_func(srna, "rna_NodesModifierProperties_path");
}
static void rna_def_modifier_nodes(BlenderRNA *brna)
{
StructRNA *srna;
@ -8110,13 +8160,12 @@ static void rna_def_modifier_nodes(BlenderRNA *brna)
rna_def_modifier_nodes_warning(brna);
rna_def_modifier_nodes_properties(brna);
srna = RNA_def_struct(brna, "NodesModifier", "Modifier");
RNA_def_struct_ui_text(srna, "Nodes Modifier", "");
RNA_def_struct_sdna(srna, "NodesModifierData");
/* NOTE: `RNA_def_struct_idprops_func` should be removed once #132129 is implemented.
* Similar to the issue with Operator (for node tools), see #rna_def_operator. */
RNA_def_struct_idprops_func(srna, "rna_NodesModifier_properties");
RNA_def_struct_system_idprops_func(srna, "rna_NodesModifier_properties");
RNA_def_struct_idprops_func(srna, "rna_NodesModifier_settings_properties");
RNA_def_struct_ui_icon(srna, ICON_GEOMETRY_NODES);
RNA_define_lib_overridable(true);
@ -8178,7 +8227,7 @@ static void rna_def_modifier_nodes(BlenderRNA *brna)
RNA_def_property_override_clear_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
func = RNA_def_function(srna, "is_input_visible", "rna_NodesModifier_is_input_visible");
RNA_def_function_flag(func, FUNC_USE_REPORTS);
RNA_def_function_flag(func, FUNC_SELF_AS_RNA | FUNC_USE_REPORTS);
RNA_def_function_ui_description(
func, "Check whether an input is currently visible based on modifier settings.");
parm = RNA_def_string(func, "identifier", "Identifier", 0, "", "The identifier of the input");
@ -8187,7 +8236,7 @@ static void rna_def_modifier_nodes(BlenderRNA *brna)
RNA_def_function_return(func, parm);
func = RNA_def_function(srna, "is_input_used", "rna_NodesModifier_is_input_used");
RNA_def_function_flag(func, FUNC_USE_REPORTS);
RNA_def_function_flag(func, FUNC_SELF_AS_RNA | FUNC_USE_REPORTS);
RNA_def_function_ui_description(
func, "Check whether an input is currently used based on modifier settings.");
parm = RNA_def_string(func, "identifier", "Identifier", 0, "", "The identifier of the input");
@ -8205,6 +8254,12 @@ static void rna_def_modifier_nodes(BlenderRNA *brna)
srna, "open_bake_data_blocks_panel", NODES_MODIFIER_PANEL_BAKE_DATA_BLOCKS);
rna_def_modifier_panel_open_prop(srna, "open_warnings_panel", NODES_MODIFIER_PANEL_WARNINGS);
prop = RNA_def_property(srna, "properties", PROP_POINTER, PROP_NONE);
RNA_def_property_struct_type(prop, "NodesModifierProperties");
RNA_def_property_ui_text(prop, "Properties", "");
RNA_def_property_pointer_funcs(
prop, "rna_NodesModifierProperties_get", nullptr, nullptr, nullptr);
RNA_define_lib_overridable(false);
}

View file

@ -39,7 +39,7 @@ class NodesModifierUsageInferenceCache {
Array<nodes::socket_usage_inference::SocketUsage> inputs;
Array<nodes::socket_usage_inference::SocketUsage> outputs;
void ensure(const NodesModifierData &nmd);
void ensure(const Object &object, const NodesModifierData &nmd);
void reset();
};

View file

@ -69,6 +69,7 @@
#include "RNA_access.hh"
#include "RNA_enum_types.hh"
#include "RNA_path.hh"
#include "RNA_prototypes.hh"
#include "DEG_depsgraph_build.hh"
@ -89,6 +90,7 @@
#include "NOD_geometry_nodes_execute.hh"
#include "NOD_geometry_nodes_gizmos.hh"
#include "NOD_geometry_nodes_lazy_function.hh"
#include "NOD_geometry_nodes_srna.hh"
#include "NOD_node_declaration.hh"
#include "NOD_socket_usage_inference.hh"
@ -108,14 +110,15 @@ static void init_data(ModifierData *md)
nmd->runtime->cache = std::make_shared<bake::ModifierCache>();
}
static void find_dependencies_from_settings(const NodesModifierSettings &settings,
static void find_dependencies_from_settings(const NodesModifierData &nmd,
nodes::EvalDependencies &deps)
{
IDP_foreach_property(settings.properties, IDP_TYPE_FILTER_ID, [&](IDProperty *property) {
if (ID *id = IDP_ID_get(property)) {
deps.add_generic_id_full(id);
}
});
IDP_foreach_property(
nmd.modifier.system_properties, IDP_TYPE_FILTER_ID, [&](IDProperty *property) {
if (ID *id = IDP_ID_get(property)) {
deps.add_generic_id_full(id);
}
});
}
/* We don't know exactly what attributes from the other object we will need. */
@ -178,7 +181,7 @@ static void update_depsgraph(ModifierData *md, const ModifierUpdateDepsgraphCont
nodes::EvalDependencies eval_deps = nodes::gather_eval_dependencies_recursive(*nmd->node_group);
/* Create dependencies to data-blocks referenced by the settings in the modifier. */
find_dependencies_from_settings(nmd->settings, eval_deps);
find_dependencies_from_settings(*nmd, eval_deps);
if (ctx->object->type == OB_CURVES) {
Curves *curves_id = id_cast<Curves *>(ctx->object->data);
@ -265,9 +268,18 @@ static void foreach_ID_link(ModifierData *md, Object *ob, IDWalkFunc walk, void
NodesModifierData *nmd = reinterpret_cast<NodesModifierData *>(md);
walk(user_data, ob, reinterpret_cast<ID **>(&nmd->node_group), IDWALK_CB_USER);
IDP_foreach_property(nmd->settings.properties, IDP_TYPE_FILTER_ID, [&](IDProperty *id_prop) {
walk(user_data, ob, reinterpret_cast<ID **>(&id_prop->data.pointer), IDWALK_CB_USER);
});
if (nmd->settings.properties) {
/* Also walk legacy properties because they are still used at runtime before post linking
* versioning. */
IDP_foreach_property(nmd->settings.properties, IDP_TYPE_FILTER_ID, [&](IDProperty *id_prop) {
walk(user_data, ob, reinterpret_cast<ID **>(&id_prop->data.pointer), IDWALK_CB_USER);
});
}
IDP_foreach_property(
nmd->modifier.system_properties, IDP_TYPE_FILTER_ID, [&](IDProperty *id_prop) {
walk(user_data, ob, reinterpret_cast<ID **>(&id_prop->data.pointer), IDWALK_CB_USER);
});
for (NodesModifierBake &bake : MutableSpan(nmd->bakes, nmd->bakes_num)) {
for (NodesModifierDataBlock &data_block : MutableSpan(bake.data_blocks, bake.data_blocks_num))
@ -306,29 +318,6 @@ static bool logging_enabled(const ModifierEvalContext *ctx)
return true;
}
static void update_id_properties_from_node_group(NodesModifierData *nmd)
{
if (nmd->node_group == nullptr || ID_MISSING(nmd->node_group)) {
if (nmd->settings.properties) {
IDP_FreeProperty(nmd->settings.properties);
nmd->settings.properties = nullptr;
}
return;
}
IDProperty *old_properties = nmd->settings.properties;
nmd->settings.properties = bke::idprop::create_group("Nodes Modifier Settings").release();
IDProperty *new_properties = nmd->settings.properties;
nodes::update_input_properties_from_node_tree(*nmd->node_group, old_properties, *new_properties);
nodes::update_output_properties_from_node_tree(
*nmd->node_group, old_properties, *new_properties);
if (old_properties != nullptr) {
IDP_FreeProperty(old_properties);
}
}
static void remove_outdated_bake_caches(NodesModifierData &nmd)
{
if (!nmd.runtime->cache) {
@ -451,9 +440,23 @@ static void update_panels_from_node_group(NodesModifierData &nmd)
nmd.panels_num = interface_panels.size();
}
static void update_system_properties(Object &object, NodesModifierData &nmd)
{
if (!nmd.modifier.system_properties) {
nmd.modifier.system_properties =
bke::idprop::create_group("NodesModifierProperties").release();
}
if (!nmd.node_group || ID_MISSING(nmd.node_group)) {
return;
}
PointerRNA properties_ptr = RNA_pointer_create_discrete(
&object.id, RNA_NodesModifierProperties, &nmd);
RNA_sync_system_properties(properties_ptr, *nmd.modifier.system_properties);
}
void MOD_nodes_update_interface(Object *object, NodesModifierData *nmd)
{
update_id_properties_from_node_group(nmd);
update_system_properties(*object, *nmd);
update_bakes_from_node_group(*nmd);
update_panels_from_node_group(*nmd);
nmd->runtime->usage_cache.reset();
@ -906,64 +909,6 @@ static void find_verbose_log_contexts(const NodesModifierData &nmd,
}
}
/**
* \note This could be done in #initialize_group_input, though that would require adding the
* the object as a parameter, so it's likely better to this check as a separate step.
*/
static void check_property_socket_sync(const Object *ob,
const IDProperty *properties,
ModifierData *md)
{
NodesModifierData *nmd = reinterpret_cast<NodesModifierData *>(md);
int geometry_socket_count = 0;
nmd->node_group->ensure_interface_cache();
const Span<nodes::StructureType> input_structure_types =
nmd->node_group->runtime->structure_type_interface->inputs;
for (const int i : nmd->node_group->interface_inputs().index_range()) {
const bNodeTreeInterfaceSocket *socket = nmd->node_group->interface_inputs()[i];
const bke::bNodeSocketType *typeinfo = socket->socket_typeinfo();
const eNodeSocketDatatype type = typeinfo ? typeinfo->type : SOCK_CUSTOM;
if (type == SOCK_GEOMETRY) {
geometry_socket_count++;
}
/* The first socket is the special geometry socket for the modifier object. */
if (i == 0 && type == SOCK_GEOMETRY) {
continue;
}
if (ELEM(input_structure_types[i], nodes::StructureType::Grid, nodes::StructureType::List)) {
continue;
}
IDProperty *property = IDP_GetPropertyFromGroup_null(properties, socket->identifier);
if (property == nullptr) {
if (!ELEM(type, SOCK_GEOMETRY, SOCK_MATRIX, SOCK_BUNDLE, SOCK_CLOSURE)) {
BKE_modifier_set_error(
ob, md, "Missing property for input socket \"%s\"", socket->name ? socket->name : "");
}
continue;
}
if (!nodes::id_property_type_matches_socket(*socket, *property)) {
BKE_modifier_set_error(ob,
md,
"Property type does not match input socket \"(%s)\"",
socket->name ? socket->name : "");
continue;
}
}
if (geometry_socket_count == 1) {
const bNodeTreeInterfaceSocket *first_socket = nmd->node_group->interface_inputs()[0];
const bke::bNodeSocketType *typeinfo = first_socket->socket_typeinfo();
const eNodeSocketDatatype type = typeinfo ? typeinfo->type : SOCK_CUSTOM;
if (type != SOCK_GEOMETRY) {
BKE_modifier_set_error(ob, md, "Node group's geometry input must be the first");
}
}
}
class NodesModifierBakeDataBlockMap : public bake::BakeDataBlockMap {
/** Protects access to `new_mappings` which may be added to from multiple threads. */
Mutex mutex_;
@ -1835,7 +1780,6 @@ static void modifyGeometry(ModifierData *md,
}
const bNodeTree &tree = *nmd->node_group;
check_property_socket_sync(ctx->object, nmd->settings.properties, md);
tree.ensure_topology_cache();
const bNode *output_node = tree.group_output_node();
@ -1904,11 +1848,11 @@ static void modifyGeometry(ModifierData *md,
bke::DataBlockComputeContext data_block_compute_context{nullptr, ctx->object->id};
bke::ModifierComputeContext modifier_compute_context{&data_block_compute_context, *nmd};
geometry_set = nodes::execute_geometry_nodes_on_geometry(tree,
nmd->settings.properties,
modifier_compute_context,
call_data,
std::move(geometry_set));
PointerRNA md_ptr = RNA_pointer_create_discrete(&ctx->object->id, RNA_NodesModifier, md);
PointerRNA properties_ptr = RNA_pointer_get(&md_ptr, "properties");
geometry_set = nodes::execute_geometry_nodes_on_geometry(
tree, properties_ptr, modifier_compute_context, call_data, std::move(geometry_set));
if (logging_enabled(ctx)) {
nmd_orig->runtime->eval_log = std::move(eval_log);
@ -1964,7 +1908,7 @@ static void modify_geometry_set(ModifierData *md,
modifyGeometry(md, ctx, *geometry_set);
}
void NodesModifierUsageInferenceCache::ensure(const NodesModifierData &nmd)
void NodesModifierUsageInferenceCache::ensure(const Object &object, const NodesModifierData &nmd)
{
if (!nmd.node_group || ID_MISSING(nmd.node_group)) {
this->reset();
@ -1973,9 +1917,14 @@ void NodesModifierUsageInferenceCache::ensure(const NodesModifierData &nmd)
const bNodeTree &tree = *nmd.node_group;
tree.ensure_interface_cache();
tree.ensure_topology_cache();
PointerRNA nmd_ptr = RNA_pointer_create_discrete(
const_cast<ID *>(&object.id), RNA_NodesModifier, const_cast<NodesModifierData *>(&nmd));
PointerRNA properties_ptr = RNA_pointer_get(&nmd_ptr, "properties");
ResourceScope scope;
const Vector<nodes::InferenceValue> group_input_values =
nodes::get_geometry_nodes_input_inference_values(tree, nmd.settings.properties, scope);
nodes::get_geometry_nodes_input_inference_values(tree, properties_ptr, scope);
/* Compute the hash of the input values. This has to be done every time currently, because there
* is no reliable callback yet that is called any of the modifier properties changes. */
@ -2037,24 +1986,9 @@ static void blend_write(BlendWriter *writer, const ID * /*id_owner*/, const Modi
writer->write_string(nmd->bake_directory);
Map<IDProperty *, IDPropertyUIDataBool *> boolean_props;
if (nmd->settings.properties != nullptr) {
if (!BLO_write_is_undo(writer)) {
/* Boolean properties are added automatically for boolean node group inputs. Integer
* properties are automatically converted to boolean sockets where applicable as well.
* However, boolean properties will crash old versions of Blender, so convert them to integer
* properties for writing. The actual value is stored in the same variable for both types */
for (IDProperty &prop : nmd->settings.properties->data.group) {
if (prop.type == IDP_BOOLEAN) {
boolean_props.add_new(&prop, reinterpret_cast<IDPropertyUIDataBool *>(prop.ui_data));
prop.type = IDP_INT;
prop.ui_data = nullptr;
}
}
}
/* Note that the property settings are based on the socket type info
* and don't necessarily need to be written, but we can't just free them. */
/* Write legacy settings for forward compatibility. Created by
* #create_legacy_geometry_nodes_properties. */
IDP_BlendWrite(writer, nmd->settings.properties);
}
@ -2090,28 +2024,14 @@ static void blend_write(BlendWriter *writer, const ID * /*id_owner*/, const Modi
}
}
writer->write_struct_array(nmd->panels_num, nmd->panels);
if (nmd->settings.properties) {
if (!BLO_write_is_undo(writer)) {
for (IDProperty &prop : nmd->settings.properties->data.group) {
if (prop.type == IDP_INT) {
if (IDPropertyUIDataBool **ui_data = boolean_props.lookup_ptr(&prop)) {
prop.type = IDP_BOOLEAN;
if (ui_data) {
prop.ui_data = reinterpret_cast<IDPropertyUIData *>(*ui_data);
}
}
}
}
}
}
}
static void blend_read(BlendDataReader *reader, ModifierData *md)
{
NodesModifierData *nmd = reinterpret_cast<NodesModifierData *>(md);
BLO_read_string(reader, &nmd->bake_directory);
if (nmd->node_group == nullptr) {
if (nmd->node_group == nullptr || nmd->modifier.system_properties) {
/* Don't bother reading old settings when modifier system properties are available. */
nmd->settings.properties = nullptr;
}
else {
@ -2234,10 +2154,6 @@ static void copy_data(const ModifierData *md, ModifierData *target, const int fl
/* Clear the bake path when duplicating. */
tnmd->bake_directory = nullptr;
}
if (nmd->settings.properties != nullptr) {
tnmd->settings.properties = IDP_CopyProperty_ex(nmd->settings.properties, flag);
}
}
void nodes_modifier_packed_bake_free(NodesModifierPackedBake *packed_bake)
@ -2274,11 +2190,6 @@ void nodes_modifier_bake_destruct(NodesModifierBake *bake, const bool do_id_user
static void free_data(ModifierData *md)
{
NodesModifierData *nmd = reinterpret_cast<NodesModifierData *>(md);
if (nmd->settings.properties != nullptr) {
IDP_FreeProperty_ex(nmd->settings.properties, false);
nmd->settings.properties = nullptr;
}
for (NodesModifierBake &bake : MutableSpan(nmd->bakes, nmd->bakes_num)) {
nodes_modifier_bake_destruct(&bake, false);
}

View file

@ -83,6 +83,7 @@ set(SRC
intern/geometry_nodes_list.cc
intern/geometry_nodes_log.cc
intern/geometry_nodes_repeat_zone.cc
intern/geometry_nodes_srna.cc
intern/geometry_nodes_warning.cc
intern/inverse_eval.cc
intern/list_function_eval.cc
@ -130,6 +131,7 @@ set(SRC
NOD_geometry_nodes_list.hh
NOD_geometry_nodes_list_fwd.hh
NOD_geometry_nodes_log.hh
NOD_geometry_nodes_srna.hh
NOD_geometry_nodes_values.hh
NOD_geometry_nodes_warning.hh
NOD_inverse_eval_params.hh

View file

@ -19,6 +19,7 @@ namespace blender {
struct bNodeTree;
struct bNodeTreeInterfaceSocket;
struct PointerRNA;
namespace bke {
struct GeometrySet;
}
@ -42,9 +43,6 @@ struct IDPropNameGetter {
}
};
std::optional<StringRef> input_attribute_name_get(const IDProperty *properties,
const bNodeTreeInterfaceSocket &io_input);
/**
* \return Whether using an attribute to input values of this type is supported.
*/
@ -56,37 +54,19 @@ bool socket_type_has_attribute_toggle(eNodeSocketDatatype type);
*/
bool input_has_attribute_toggle(const bNodeTree &node_tree, const int socket_index);
bool id_property_type_matches_socket(const bNodeTreeInterfaceSocket &socket,
const IDProperty &property,
bool use_name_for_ids = false);
std::unique_ptr<IDProperty, bke::idprop::IDPropertyDeleter> id_property_create_from_socket(
const bNodeTreeInterfaceSocket &socket,
nodes::StructureType structure_type,
bool use_name_for_ids);
bke::GeometrySet execute_geometry_nodes_on_geometry(const bNodeTree &btree,
const IDProperty *properties,
const PointerRNA &properties_ptr,
const ComputeContext &base_compute_context,
GeoNodesCallData &call_data,
bke::GeometrySet input_geometry);
void update_input_properties_from_node_tree(const bNodeTree &tree,
const IDProperty *old_properties,
IDProperty &properties,
bool use_name_for_ids = false);
void update_output_properties_from_node_tree(const bNodeTree &tree,
const IDProperty *old_properties,
IDProperty &properties);
/**
* Get input values for the node tree for static value/usage inferencing. Inferencing does not
* fully evaluate the node tree (would be way to slow), and does not support all socket types. So
* this function may return #InferenceValue::Unknown for some sockets.
*/
Vector<InferenceValue> get_geometry_nodes_input_inference_values(const bNodeTree &btree,
const IDProperty *properties,
const PointerRNA &properties_ptr,
ResourceScope &scope);
} // namespace nodes

View file

@ -0,0 +1,50 @@
/* SPDX-FileCopyrightText: 2025 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
#include "BLI_resource_scope.hh"
#include "RNA_types.hh"
namespace blender {
struct StructRNA;
struct bNodeTree;
namespace nodes {
/**
* Shared across all socket types, though some entries don't make sense for some types.
*/
enum class GeometryNodesInputType {
Fallback = 0,
Value = 1,
Attribute = 2,
Layer = 3,
};
extern const EnumPropertyItem geometry_nodes_input_type_items_fallback[];
extern const EnumPropertyItem geometry_nodes_input_type_items_value[];
extern const EnumPropertyItem geometry_nodes_input_type_items_value_or_attribute[];
extern const EnumPropertyItem geometry_nodes_input_type_items_value_or_attribute_or_layer[];
/**
* Contains a runtime registraction of RNA types generated based on the node group's interface for
* a particular context. These RNA types are not registered in the global list, but owned by the
* node group so that RNA access to interface properties works as long as the node group exists.
*/
struct GeneratedTreeSrnaData {
ResourceScope scope;
StructRNA *properties_struct;
BlenderRNA *generated_rna;
GeneratedTreeSrnaData();
~GeneratedTreeSrnaData();
};
std::shared_ptr<GeneratedTreeSrnaData> create_geometry_nodes_rna_for_modifier(
const bNodeTree &tree);
} // namespace nodes
} // namespace blender

View file

@ -123,9 +123,9 @@ void infer_group_interface_inputs_usage(const bNodeTree &group,
* Same as above, but automatically retrieves the input values from the given properties.
* This is used with the geometry nodes modifier and node tools.
*/
void infer_group_interface_usage(
void infer_group_interface_inputs_usage(
const bNodeTree &group,
const IDProperty *properties,
const PointerRNA &properties_ptr,
MutableSpan<SocketUsage> r_input_usages,
std::optional<MutableSpan<SocketUsage>> r_output_usages = std::nullopt);

View file

@ -32,6 +32,7 @@
#include "NOD_geometry.hh"
#include "NOD_geometry_nodes_caller_ui.hh"
#include "NOD_geometry_nodes_log.hh"
#include "NOD_geometry_nodes_srna.hh"
#include "NOD_socket_usage_inference.hh"
#include "RNA_access.hh"
@ -48,15 +49,10 @@ namespace blender::nodes {
namespace geo_log = geo_eval_log;
namespace {
struct PanelOpenProperty {
PointerRNA ptr;
StringRefNull name;
};
struct SearchInfo {
geo_log::GeoTreeLog *tree_log = nullptr;
bNodeTree *tree = nullptr;
IDProperty *properties = nullptr;
std::optional<PointerRNA> socket_props_ptr;
};
struct ModifierSearchData {
@ -66,7 +62,9 @@ struct ModifierSearchData {
struct OperatorSearchData {
/** Can store this data directly, because it's more persistent than for the modifier. */
SearchInfo info;
geo_log::GeoTreeLog *tree_log = nullptr;
bNodeTree *tree = nullptr;
wmOperator *op = nullptr;
};
struct SocketSearchData {
@ -84,13 +82,11 @@ struct DrawGroupInputsContext {
const bContext &C;
bNodeTree *tree;
geo_log::GeoTreeLog *tree_log;
IDProperty *properties;
PointerRNA *properties_ptr;
PointerRNA *bmain_ptr;
Array<nodes::socket_usage_inference::SocketUsage> input_usages;
Array<nodes::socket_usage_inference::SocketUsage> output_usages;
bool use_name_for_ids = false;
std::function<PanelOpenProperty(const bNodeTreeInterfacePanel &)> panel_open_property_fn;
std::function<SocketSearchData(const bNodeTreeInterfaceSocket &)> socket_search_data_fn;
std::function<void(ui::Layout &, int icon, const bNodeTreeInterfaceSocket &)>
draw_attribute_toggle_fn;
@ -153,10 +149,25 @@ SearchInfo SocketSearchData::info(const bContext &C) const
return {};
}
geo_log::GeoTreeLog *tree_log = get_root_tree_log(*object_and_modifier->object, *nmd);
return {tree_log, nmd->node_group, nmd->settings.properties};
PointerRNA nmd_ptr = RNA_pointer_create_discrete(
&const_cast<Object *>(object_and_modifier->object)->id,
RNA_NodesModifier,
const_cast<NodesModifierData *>(nmd));
PointerRNA properties_ptr = RNA_pointer_get(&nmd_ptr, "properties");
const bNodeTree &node_group = *nmd->node_group;
const bool is_output = bke::node_find_interface_output_by_identifier(node_group,
this->socket_identifier);
PointerRNA inputs_ptr = RNA_pointer_get(&properties_ptr, is_output ? "outputs" : "inputs");
PointerRNA socket_props_ptr = RNA_pointer_get(&inputs_ptr, this->socket_identifier);
return {tree_log, nmd->node_group, socket_props_ptr};
}
if (const auto *operator_search_data = std::get_if<OperatorSearchData>(&this->search_data)) {
return operator_search_data->info;
PointerRNA op_ptr = RNA_pointer_create_discrete(
&CTX_wm_manager(&C)->id, RNA_Operator, operator_search_data->op);
PointerRNA properties_ptr = RNA_pointer_get(&op_ptr, "properties");
PointerRNA inputs_ptr = RNA_pointer_get(&properties_ptr, "inputs");
PointerRNA socket_props_ptr = RNA_pointer_get(&inputs_ptr, this->socket_identifier);
return {operator_search_data->tree_log, operator_search_data->tree, socket_props_ptr};
}
return {};
}
@ -211,24 +222,22 @@ static void layer_name_search_exec_fn(bContext *C, void *data_v, void *item_v)
if (!item) {
return;
}
const SearchInfo info = data.info(*C);
if (!info.properties) {
SearchInfo info = data.info(*C);
if (!info.socket_props_ptr) {
return;
}
IDProperty &name_property = *IDP_GetPropertyFromGroup(info.properties, data.socket_identifier);
IDP_AssignString(&name_property, item->c_str());
RNA_string_set(&*info.socket_props_ptr, "layer_name", item->c_str());
ED_undo_push(C, "Assign Layer Name");
}
static void add_layer_name_search_button(DrawGroupInputsContext &ctx,
ui::Layout &layout,
const bNodeTreeInterfaceSocket &socket)
const bNodeTreeInterfaceSocket &socket,
PointerRNA *socket_props_ptr)
{
const std::string rna_path = fmt::format("[\"{}\"]", BLI_str_escape(socket.identifier));
if (!ctx.tree_log) {
layout.prop(ctx.properties_ptr, rna_path, UI_ITEM_NONE, "", ICON_NONE);
layout.prop(socket_props_ptr, "layer_name", UI_ITEM_NONE, "", ICON_NONE);
return;
}
@ -250,8 +259,8 @@ static void add_layer_name_search_button(DrawGroupInputsContext &ctx,
0,
10 * UI_UNIT_X, /* Dummy value, replaced by layout system. */
UI_UNIT_Y,
ctx.properties_ptr,
rna_path,
socket_props_ptr,
"layer_name",
0,
StringRef(socket.description));
button_placeholder_set(but, IFACE_("Layer"));
@ -335,26 +344,22 @@ static void attribute_search_exec_fn(bContext *C, void *data_v, void *item_v)
}
SocketSearchData &data = *static_cast<SocketSearchData *>(data_v);
const auto &item = *static_cast<const geo_log::GeometryAttributeInfo *>(item_v);
const SearchInfo info = data.info(*C);
if (!info.properties) {
SearchInfo info = data.info(*C);
if (!info.socket_props_ptr) {
return;
}
const std::string attribute_prop_name = data.socket_identifier +
nodes::input_attribute_name_suffix;
IDProperty &name_property = *IDP_GetPropertyFromGroup(info.properties, attribute_prop_name);
IDP_AssignString(&name_property, item.name.c_str());
RNA_string_set(&*info.socket_props_ptr, "attribute_name", item.name.c_str());
ED_undo_push(C, "Assign Attribute Name");
}
static void add_attribute_search_button(DrawGroupInputsContext &ctx,
ui::Layout &layout,
const StringRefNull rna_path_attribute_name,
PointerRNA *socket_props_ptr,
const bNodeTreeInterfaceSocket &socket)
{
if (!ctx.tree_log) {
layout.prop(ctx.properties_ptr, rna_path_attribute_name, UI_ITEM_NONE, "", ICON_NONE);
layout.prop(socket_props_ptr, "attribute_name", UI_ITEM_NONE, "", ICON_NONE);
return;
}
@ -367,8 +372,8 @@ static void add_attribute_search_button(DrawGroupInputsContext &ctx,
0,
10 * UI_UNIT_X, /* Dummy value, replaced by layout system. */
UI_UNIT_Y,
ctx.properties_ptr,
rna_path_attribute_name,
socket_props_ptr,
"attribute_name",
0,
StringRef(socket.description));
@ -394,7 +399,7 @@ static void add_attribute_search_button(DrawGroupInputsContext &ctx,
attribute_search_exec_fn,
nullptr);
std::string attribute_name = RNA_string_get(ctx.properties_ptr, rna_path_attribute_name.c_str());
std::string attribute_name = RNA_string_get(socket_props_ptr, "attribute_name");
const bool access_allowed = bke::allow_procedural_attribute_access(attribute_name);
if (!access_allowed) {
button_flag_enable(but, ui::BUT_REDALERT);
@ -404,14 +409,15 @@ static void add_attribute_search_button(DrawGroupInputsContext &ctx,
static void add_attribute_search_or_value_buttons(
DrawGroupInputsContext &ctx,
ui::Layout &layout,
const StringRefNull rna_path,
const bNodeTreeInterfaceSocket &socket,
PointerRNA *socket_props_ptr,
const std::optional<StringRefNull> use_name = std::nullopt)
{
const bke::bNodeSocketType *typeinfo = socket.socket_typeinfo();
const eNodeSocketDatatype type = typeinfo ? typeinfo->type : SOCK_CUSTOM;
const std::string rna_path_attribute_name = fmt::format(
"[\"{}{}\"]", BLI_str_escape(socket.identifier), nodes::input_attribute_name_suffix);
const bool show_attribute_input = RNA_enum_get(socket_props_ptr, "type") ==
int(nodes::GeometryNodesInputType::Attribute);
/* We're handling this manually in this case. */
layout.use_property_decorate_set(false);
@ -421,13 +427,11 @@ static void add_attribute_search_or_value_buttons(
name_row.alignment_set(ui::LayoutAlign::Right);
ui::Layout *prop_row = nullptr;
const std::optional<StringRef> attribute_name = nodes::input_attribute_name_get(ctx.properties,
socket);
const StringRefNull socket_name = use_name.has_value() ?
(*use_name) :
(socket.name ? IFACE_(socket.name) : "");
if (type == SOCK_BOOLEAN && !attribute_name) {
if (type == SOCK_BOOLEAN && !show_attribute_input) {
name_row.label("", ICON_NONE);
prop_row = &split.row(true);
}
@ -440,51 +444,30 @@ static void add_attribute_search_or_value_buttons(
prop_row->alignment_set(ui::LayoutAlign::Expand);
}
if (attribute_name) {
if (show_attribute_input) {
name_row.label(IFACE_(socket_name), ICON_NONE);
prop_row = &split.row(true);
add_attribute_search_button(ctx, *prop_row, rna_path_attribute_name, socket);
add_attribute_search_button(ctx, *prop_row, socket_props_ptr, socket);
layout.label("", ICON_BLANK1);
}
else {
const char *name = IFACE_(socket_name.c_str());
prop_row->prop(ctx.properties_ptr, rna_path, UI_ITEM_NONE, name, ICON_NONE);
layout.decorator(ctx.properties_ptr, rna_path.c_str(), -1);
prop_row->prop(socket_props_ptr, "value", UI_ITEM_NONE, name, ICON_NONE);
layout.decorator(socket_props_ptr, "value", -1);
}
ctx.draw_attribute_toggle_fn(*prop_row, ICON_SPREADSHEET, socket);
}
static NodesModifierPanel *find_panel_by_id(NodesModifierData &nmd, const int id)
{
for (const int i : IndexRange(nmd.panels_num)) {
if (nmd.panels[i].id == id) {
return &nmd.panels[i];
}
}
return nullptr;
}
/* Drawing the properties manually with #ui::Layout::prop instead of #uiDefAutoButsRNA allows using
* the node socket identifier for the property names, since they are unique, but also having
* the correct label displayed in the UI. */
static void draw_property_for_socket(DrawGroupInputsContext &ctx,
ui::Layout &layout,
const bNodeTreeInterfaceSocket &socket,
PointerRNA *socket_props_ptr,
const std::optional<StringRef> parent_name = std::nullopt)
{
const StringRefNull identifier = socket.identifier;
/* The property should be created in #MOD_nodes_update_interface with the correct type. */
IDProperty *property = IDP_GetPropertyFromGroup_null(ctx.properties, identifier);
/* IDProperties can be removed with python, so there could be a situation where
* there isn't a property for a socket or it doesn't have the correct type. */
if (property == nullptr ||
!nodes::id_property_type_matches_socket(socket, *property, ctx.use_name_for_ids))
{
return;
}
const int input_index = ctx.tree->interface_input_index(socket);
if (!ctx.input_is_visible(socket)) {
/* The input is not used currently, but it would be used if any menu input is changed.
@ -496,13 +479,16 @@ static void draw_property_for_socket(DrawGroupInputsContext &ctx,
row.use_property_decorate_set(true);
row.active_set(ctx.input_is_active(socket));
const std::string rna_path = fmt::format("[\"{}\"]", BLI_str_escape(identifier.c_str()));
/* Use #ui::Layout::prop_search to draw pointer properties because #ui::Layout::prop would not
* have enough information about what type of ID to select for editing the values. This is
* because pointer IDProperties contain no information about their type. */
const bke::bNodeSocketType *typeinfo = socket.socket_typeinfo();
const eNodeSocketDatatype type = typeinfo ? typeinfo->type : SOCK_CUSTOM;
if (ELEM(type, SOCK_GEOMETRY, SOCK_MATRIX, SOCK_BUNDLE, SOCK_CLOSURE)) {
return;
}
std::string name = socket.name ? IFACE_(socket.name) : "";
/* If the property has a prefix that's the same string as the name of the panel it's in, remove
@ -518,35 +504,29 @@ static void draw_property_for_socket(DrawGroupInputsContext &ctx,
switch (type) {
case SOCK_OBJECT: {
row.prop_search(
ctx.properties_ptr, rna_path, ctx.bmain_ptr, "objects", name, ICON_OBJECT_DATA);
row.prop_search(socket_props_ptr, "value", ctx.bmain_ptr, "objects", name, ICON_OBJECT_DATA);
break;
}
case SOCK_COLLECTION: {
row.prop_search(ctx.properties_ptr,
rna_path,
ctx.bmain_ptr,
"collections",
name,
ICON_OUTLINER_COLLECTION);
row.prop_search(
socket_props_ptr, "value", ctx.bmain_ptr, "collections", name, ICON_OUTLINER_COLLECTION);
break;
}
case SOCK_MATERIAL: {
row.prop_search(
ctx.properties_ptr, rna_path, ctx.bmain_ptr, "materials", name, ICON_MATERIAL);
row.prop_search(socket_props_ptr, "value", ctx.bmain_ptr, "materials", name, ICON_MATERIAL);
break;
}
case SOCK_TEXTURE: {
row.prop_search(ctx.properties_ptr, rna_path, ctx.bmain_ptr, "textures", name, ICON_TEXTURE);
row.prop_search(socket_props_ptr, "value", ctx.bmain_ptr, "textures", name, ICON_TEXTURE);
break;
}
case SOCK_FONT: {
PropertyRNA *prop = RNA_struct_find_property(ctx.properties_ptr, rna_path.c_str());
PropertyRNA *prop = RNA_struct_find_property(ctx.properties_ptr, "value");
if (prop && RNA_property_type(prop) == PROP_POINTER) {
template_id(&row,
&ctx.C,
ctx.properties_ptr,
rna_path,
"value",
nullptr,
"FONT_OT_open",
"FONT_OT_unlink",
@ -557,66 +537,57 @@ static void draw_property_for_socket(DrawGroupInputsContext &ctx,
else {
/* #template_id only supports pointer properties currently. Node tools store
* data-block pointers in strings currently. */
row.prop_search(
ctx.properties_ptr, rna_path, ctx.bmain_ptr, "fonts", name, ICON_FONT_DATA);
row.prop_search(ctx.properties_ptr, "value", ctx.bmain_ptr, "fonts", name, ICON_FONT_DATA);
}
break;
}
case SOCK_SCENE: {
row.prop_search(ctx.properties_ptr, rna_path, ctx.bmain_ptr, "scenes", name, ICON_SCENE);
row.prop_search(ctx.properties_ptr, "value", ctx.bmain_ptr, "scenes", name, ICON_SCENE);
break;
}
case SOCK_TEXT_ID: {
row.prop_search(ctx.properties_ptr, rna_path, ctx.bmain_ptr, "texts", name, ICON_TEXT);
row.prop_search(ctx.properties_ptr, "value", ctx.bmain_ptr, "texts", name, ICON_TEXT);
break;
}
case SOCK_MASK: {
row.prop_search(ctx.properties_ptr, rna_path, ctx.bmain_ptr, "masks", name, ICON_NONE);
row.prop_search(ctx.properties_ptr, "value", ctx.bmain_ptr, "masks", name, ICON_NONE);
break;
}
case SOCK_SOUND: {
row.prop_search(ctx.properties_ptr, rna_path, ctx.bmain_ptr, "sounds", name, ICON_SOUND);
row.prop_search(ctx.properties_ptr, "value", ctx.bmain_ptr, "sounds", name, ICON_SOUND);
break;
}
case SOCK_IMAGE: {
PropertyRNA *prop = RNA_struct_find_property(ctx.properties_ptr, rna_path.c_str());
if (prop && RNA_property_type(prop) == PROP_POINTER) {
template_id(&row,
&ctx.C,
ctx.properties_ptr,
rna_path,
"image.new",
"image.open",
nullptr,
ui::TEMPLATE_ID_FILTER_ALL,
false,
name);
}
else {
/* #template_id only supports pointer properties currently. Node tools store data-block
* pointers in strings currently. */
row.prop_search(ctx.properties_ptr, rna_path, ctx.bmain_ptr, "images", name, ICON_IMAGE);
}
template_id(&row,
&ctx.C,
socket_props_ptr,
"value",
"image.new",
"image.open",
nullptr,
ui::TEMPLATE_ID_FILTER_ALL,
false,
name);
break;
}
case SOCK_MENU: {
if (socket.flag & NODE_INTERFACE_SOCKET_MENU_EXPANDED) {
/* Use a single space when the name is empty to work around a bug with expanded enums. Also
* see #ui_item_enum_expand_exec. */
row.prop(ctx.properties_ptr,
rna_path,
row.prop(socket_props_ptr,
"value",
ui::ITEM_R_EXPAND,
StringRef(name).is_empty() ? " " : name,
ICON_NONE);
}
else {
row.prop(ctx.properties_ptr, rna_path, UI_ITEM_NONE, name, ICON_NONE);
row.prop(socket_props_ptr, "value", UI_ITEM_NONE, name, ICON_NONE);
}
break;
}
case SOCK_BOOLEAN: {
if (is_layer_selection_field(socket)) {
add_layer_name_search_button(ctx, row, socket);
add_layer_name_search_button(ctx, row, socket, socket_props_ptr);
/* Adds a spacing at the end of the row. */
row.label("", ICON_BLANK1);
break;
@ -625,11 +596,12 @@ static void draw_property_for_socket(DrawGroupInputsContext &ctx,
}
default: {
if (nodes::input_has_attribute_toggle(*ctx.tree, input_index)) {
add_attribute_search_or_value_buttons(ctx, row, rna_path, socket, name);
add_attribute_search_or_value_buttons(ctx, row, socket, socket_props_ptr, name);
}
else {
row.prop(ctx.properties_ptr, rna_path, UI_ITEM_NONE, name, ICON_NONE);
row.prop(socket_props_ptr, "value", UI_ITEM_NONE, name, ICON_NONE);
}
break;
}
}
if (!nodes::input_has_attribute_toggle(*ctx.tree, input_index)) {
@ -704,33 +676,22 @@ static void draw_interface_panel_as_panel(DrawGroupInputsContext &ctx,
if (!interface_panel_has_socket(ctx, interface_panel)) {
return;
}
PanelOpenProperty open_property = ctx.panel_open_property_fn(interface_panel);
PointerRNA panels_ptr = RNA_pointer_get(ctx.properties_ptr, "panels");
const std::string panel_open_name = fmt::format("open_{}", interface_panel.identifier);
ui::PanelLayout panel_layout;
bool skip_first = false;
/* Check if the panel should have a toggle in the header. */
const bNodeTreeInterfaceSocket *toggle_socket = interface_panel.header_toggle_socket();
const StringRef panel_name = interface_panel.name;
if (toggle_socket && !(toggle_socket->flag & NODE_INTERFACE_SOCKET_HIDE_IN_MODIFIER)) {
const StringRefNull identifier = toggle_socket->identifier;
IDProperty *property = IDP_GetPropertyFromGroup_null(ctx.properties, identifier);
/* IDProperties can be removed with python, so there could be a situation where
* there isn't a property for a socket or it doesn't have the correct type. */
if (property == nullptr ||
!nodes::id_property_type_matches_socket(*toggle_socket, *property, ctx.use_name_for_ids))
{
return;
}
const std::string rna_path = fmt::format("[\"{}\"]", BLI_str_escape(identifier.c_str()));
panel_layout = layout.panel_prop_with_bool_header(&ctx.C,
&open_property.ptr,
open_property.name,
ctx.properties_ptr,
rna_path,
IFACE_(panel_name));
PointerRNA inputs_ptr = RNA_pointer_get(ctx.properties_ptr, "inputs");
PointerRNA toggle_ptr = RNA_pointer_get(&inputs_ptr, toggle_socket->identifier);
panel_layout = layout.panel_prop_with_bool_header(
&ctx.C, &panels_ptr, panel_open_name, &toggle_ptr, "value", IFACE_(panel_name));
skip_first = true;
}
else {
panel_layout = layout.panel_prop(&ctx.C, &open_property.ptr, open_property.name);
panel_layout = layout.panel_prop(&ctx.C, &panels_ptr, panel_open_name);
panel_layout.header->label(IFACE_(panel_name), ICON_NONE);
}
if (!interface_panel_affects_output(ctx, interface_panel)) {
@ -768,7 +729,11 @@ static void draw_interface_panel_content(DrawGroupInputsContext &ctx,
const auto &interface_socket = *reinterpret_cast<const bNodeTreeInterfaceSocket *>(item);
if (interface_socket.flag & NODE_INTERFACE_SOCKET_INPUT) {
if (!(interface_socket.flag & NODE_INTERFACE_SOCKET_HIDE_IN_MODIFIER)) {
draw_property_for_socket(ctx, layout, interface_socket, parent_name);
PointerRNA inputs_ptr = RNA_pointer_get(ctx.properties_ptr, "inputs");
PointerRNA socket_props_ptr = RNA_pointer_get(&inputs_ptr,
interface_socket.identifier);
draw_property_for_socket(
ctx, layout, interface_socket, &socket_props_ptr, parent_name);
}
}
break;
@ -888,21 +853,25 @@ static void draw_property_for_output_socket(DrawGroupInputsContext &ctx,
ui::Layout &layout,
const bNodeTreeInterfaceSocket &socket)
{
const std::string rna_path_attribute_name = fmt::format(
"[\"{}{}\"]", BLI_str_escape(socket.identifier), nodes::input_attribute_name_suffix);
ui::Layout &split = layout.split(0.4f, false);
ui::Layout &name_row = split.row(false);
name_row.alignment_set(ui::LayoutAlign::Right);
name_row.label(socket.name ? socket.name : "", ICON_NONE);
PropertyRNA *prop = RNA_struct_find_property(ctx.properties_ptr, "outputs");
if (!prop) {
return;
}
PointerRNA outputs_ptr = RNA_property_pointer_get(ctx.properties_ptr, prop);
PointerRNA output_socket_ptr = RNA_pointer_get(&outputs_ptr, socket.identifier);
ui::Layout &row = split.row(true);
add_attribute_search_button(ctx, row, rna_path_attribute_name, socket);
add_attribute_search_button(ctx, row, &output_socket_ptr, socket);
}
static void draw_output_attributes_panel(DrawGroupInputsContext &ctx, ui::Layout &layout)
{
if (!ctx.tree || !ctx.properties) {
if (!ctx.tree) {
return;
}
const Span<const bNodeTreeInterfaceSocket *> interface_outputs = ctx.tree->interface_outputs();
@ -1021,20 +990,11 @@ void draw_geometry_nodes_modifier_ui(const bContext &C,
PointerRNA bmain_ptr = RNA_main_pointer_create(bmain);
NodesModifierData &nmd = *modifier_ptr->data_as<NodesModifierData>();
Object &object = *reinterpret_cast<Object *>(modifier_ptr->owner_id);
PointerRNA properties_ptr = RNA_pointer_get(modifier_ptr, "properties");
DrawGroupInputsContext ctx{C,
nmd.node_group,
get_root_tree_log(object, nmd),
nmd.settings.properties,
modifier_ptr,
&bmain_ptr};
DrawGroupInputsContext ctx{
C, nmd.node_group, get_root_tree_log(object, nmd), &properties_ptr, &bmain_ptr};
ctx.panel_open_property_fn = [&](const bNodeTreeInterfacePanel &io_panel) -> PanelOpenProperty {
NodesModifierPanel *panel = find_panel_by_id(nmd, io_panel.identifier);
PointerRNA panel_ptr = RNA_pointer_create_discrete(
modifier_ptr->owner_id, RNA_NodesModifierPanel, panel);
return {panel_ptr, "is_open"};
};
ctx.socket_search_data_fn = [&](const bNodeTreeInterfaceSocket &io_socket) -> SocketSearchData {
SocketSearchData data{};
ModifierSearchData &modifier_search_data = data.search_data.emplace<ModifierSearchData>();
@ -1066,10 +1026,8 @@ void draw_geometry_nodes_modifier_ui(const bContext &C,
template_id(&layout, &C, modifier_ptr, "node_group", newop, nullptr, nullptr);
}
if (nmd.node_group != nullptr && !ID_MISSING(nmd.node_group) &&
nmd.settings.properties != nullptr)
{
nmd.runtime->usage_cache.ensure(nmd);
if (nmd.node_group != nullptr && !ID_MISSING(nmd.node_group)) {
nmd.runtime->usage_cache.ensure(object, nmd);
ctx.input_usages = nmd.runtime->usage_cache.inputs;
ctx.output_usages = nmd.runtime->usage_cache.outputs;
draw_interface_panel_content(ctx, layout, nmd.node_group->tree_interface.root_panel);
@ -1105,31 +1063,22 @@ void draw_geometry_nodes_operator_redo_ui(const bContext &C,
Main &bmain = *CTX_data_main(&C);
PointerRNA bmain_ptr = RNA_main_pointer_create(&bmain);
DrawGroupInputsContext ctx{C, &tree, tree_log, op.properties, op.ptr, &bmain_ptr};
ctx.panel_open_property_fn = [&](const bNodeTreeInterfacePanel &io_panel) -> PanelOpenProperty {
Panel *root_panel = layout.root_panel();
LayoutPanelState *state = BKE_panel_layout_panel_state_ensure(
root_panel,
"node_operator_panel_" + std::to_string(io_panel.identifier),
io_panel.flag & NODE_INTERFACE_PANEL_DEFAULT_CLOSED);
PointerRNA state_ptr = RNA_pointer_create_discrete(nullptr, RNA_LayoutPanelState, state);
return {state_ptr, "is_open"};
};
DrawGroupInputsContext ctx{C, &tree, tree_log, op.ptr, &bmain_ptr};
ctx.socket_search_data_fn = [&](const bNodeTreeInterfaceSocket &io_socket) -> SocketSearchData {
SocketSearchData data{};
OperatorSearchData &operator_search_data = data.search_data.emplace<OperatorSearchData>();
operator_search_data.info.tree = &tree;
operator_search_data.info.tree_log = tree_log;
operator_search_data.info.properties = op.properties;
operator_search_data.tree = &tree;
operator_search_data.tree_log = tree_log;
operator_search_data.op = &op;
STRNCPY_UTF8(data.socket_identifier, io_socket.identifier);
data.is_output = io_socket.flag & NODE_INTERFACE_SOCKET_OUTPUT;
return data;
};
ctx.draw_attribute_toggle_fn =
[&](ui::Layout &layout, const int icon, const bNodeTreeInterfaceSocket &io_socket) {
const std::string prop_name = fmt::format(
"[\"{}{}\"]", BLI_str_escape(io_socket.identifier), nodes::input_use_attribute_suffix);
layout.prop(op.ptr, prop_name, ui::ITEM_R_ICON_ONLY, "", icon);
PointerRNA inputs_ptr = RNA_pointer_get(op.ptr, "inputs");
PointerRNA socket_props_ptr = RNA_pointer_get(&inputs_ptr, io_socket.identifier);
layout.prop(&socket_props_ptr, "type", ui::ITEM_R_ICON_ONLY, "", icon);
};
ctx.use_name_for_ids = true;
@ -1141,8 +1090,8 @@ void draw_geometry_nodes_operator_redo_ui(const bContext &C,
tree.ensure_interface_cache();
ctx.input_usages.reinitialize(tree.interface_inputs().size());
ctx.output_usages.reinitialize(tree.interface_outputs().size());
nodes::socket_usage_inference::infer_group_interface_usage(
tree, ctx.properties, ctx.input_usages, ctx.output_usages);
nodes::socket_usage_inference::infer_group_interface_inputs_usage(
tree, *ctx.properties_ptr, ctx.input_usages, ctx.output_usages);
draw_interface_panel_content(ctx, layout, tree.tree_interface.root_panel);
}

File diff suppressed because it is too large Load diff

View file

@ -26,6 +26,9 @@
#include "ED_node.hh"
#include "RNA_access.hh"
#include "RNA_prototypes.hh"
namespace blender::nodes::gizmos {
bool is_builtin_gizmo_node(const bNode &node)
@ -383,10 +386,13 @@ static void foreach_active_gizmo_exposed_to_modifier(
}
tree.ensure_interface_cache();
PointerRNA nmd_ptr = RNA_pointer_create_discrete(
const_cast<ID *>(&object.id), RNA_NodesModifier, const_cast<NodesModifierData *>(&nmd));
PointerRNA properties_ptr = RNA_pointer_get(&nmd_ptr, "properties");
ResourceScope scope;
const Vector<InferenceValue> input_values = get_geometry_nodes_input_inference_values(
*nmd.node_group, nmd.settings.properties, scope);
*nmd.node_group, properties_ptr, scope);
const auto get_input_value = [&](const int group_input_i) {
return input_values[group_input_i];

View file

@ -0,0 +1,235 @@
/* SPDX-FileCopyrightText: 2025 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include <fmt/format.h>
#include "BLI_listbase.h"
#include "BLI_string.h"
#include "NOD_geometry_nodes_srna.hh"
#include "NOD_socket.hh"
#include "DNA_modifier_types.h"
#include "DNA_node_types.h"
#include "BKE_idprop.hh"
#include "BKE_node_runtime.hh"
#include "RNA_access.hh"
#include "RNA_define.hh"
#include "RNA_prototypes.hh"
namespace blender::nodes {
static constexpr EnumPropertyItem input_type_item_fallback = {
int(GeometryNodesInputType::Fallback), "FALLBACK", 0, "Fallback", "Fallback"};
static constexpr EnumPropertyItem input_type_item_value = {
int(GeometryNodesInputType::Value), "VALUE", 0, "Value", "Pass a single value"};
static constexpr EnumPropertyItem input_type_item_attribute = {
int(GeometryNodesInputType::Attribute), "ATTRIBUTE", 0, "Attribute", "Pass an attribute"};
static constexpr EnumPropertyItem input_type_item_layer = {
int(GeometryNodesInputType::Layer), "LAYER", 0, "Layer", "Pass a layer selection"};
const EnumPropertyItem geometry_nodes_input_type_items_fallback[] = {
input_type_item_fallback,
{0},
};
const EnumPropertyItem geometry_nodes_input_type_items_value[] = {
input_type_item_value,
{0},
};
const EnumPropertyItem geometry_nodes_input_type_items_value_or_attribute[] = {
input_type_item_value,
input_type_item_attribute,
{0},
};
const EnumPropertyItem geometry_nodes_input_type_items_value_or_attribute_or_layer[] = {
input_type_item_value,
input_type_item_attribute,
input_type_item_layer,
{0},
};
static const ModifierData *find_modifier_data_from_system_property(const PointerRNA *ptr)
{
for (const AncestorPointerRNA &ancestor : ptr->ancestors) {
if (RNA_struct_is_a(ancestor.type, RNA_Modifier)) {
return static_cast<const ModifierData *>(ancestor.data);
}
}
const Object *object = id_cast<const Object *>(ptr->owner_id);
for (const ModifierData &md : object->modifiers) {
bool found = false;
IDP_foreach_property(md.system_properties, 0, [&](IDProperty *id_prop) {
if (id_prop == ptr->data) {
found = true;
}
});
if (found) {
return &md;
}
}
return nullptr;
}
static std::optional<std::string> rna_NodesModifierPropertyInput_path(const PointerRNA *ptr)
{
StructRNA *srna = ptr->type;
const char *identifier = RNA_struct_identifier(srna);
const ModifierData *md = find_modifier_data_from_system_property(ptr);
std::string name_esc = BLI_str_escape(md->name);
return fmt::format("modifiers[\"{}\"].properties.inputs.{}", name_esc, identifier);
}
static StructRNA *get_input_socket_struct_rna(const bNodeTree &tree,
const bNodeTreeInterfaceSocket &socket,
GeneratedTreeSrnaData &r_generated)
{
const bke::bNodeSocketType *stype = socket.socket_typeinfo();
if (!stype) {
return nullptr;
}
// TODO: Does this actually need to copy the string?
const StringRefNull srna_identifier = r_generated.scope.allocator().copy_string(
socket.identifier);
StructRNA *srna = RNA_def_struct_ptr(
r_generated.generated_rna, srna_identifier.c_str(), RNA_PropertyGroup);
RNA_def_struct_path_func_runtime(srna, rna_NodesModifierPropertyInput_path);
if (stype->make_geometry_nodes_input_srna) {
stype->make_geometry_nodes_input_srna(tree, *srna, socket, r_generated);
}
return srna;
}
static StructRNA *create_inputs_srna(const bNodeTree &tree, GeneratedTreeSrnaData &r_generated)
{
StructRNA *srna = RNA_def_struct_ptr(
r_generated.generated_rna, "GeometryNodesInterfaceInputs", RNA_PropertyGroup);
for (const bNodeTreeInterfaceSocket *socket : tree.interface_inputs()) {
StructRNA *socket_srna = get_input_socket_struct_rna(tree, *socket, r_generated);
if (!socket_srna) {
continue;
}
const StringRefNull identifier = r_generated.scope.allocator().copy_string(socket->identifier);
PropertyRNA *prop = RNA_def_pointer_runtime(
srna, identifier.c_str(), socket_srna, socket->name, "");
RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
}
return srna;
}
static std::optional<std::string> rna_NodesModifierPropertyOutput_path(const PointerRNA *ptr)
{
StructRNA *srna = ptr->type;
const char *identifier = RNA_struct_identifier(srna);
const ModifierData *md = find_modifier_data_from_system_property(ptr);
std::string name_esc = BLI_str_escape(md->name);
return fmt::format("modifiers[\"{}\"].properties.outputs.{}", name_esc, identifier);
}
static StructRNA *create_outputs_srna(const bNodeTree &tree, GeneratedTreeSrnaData &r_generated)
{
StructRNA *srna = RNA_def_struct_ptr(
r_generated.generated_rna, "GeometryNodesInterfaceOutputs", RNA_PropertyGroup);
LinearAllocator<> &allocator = r_generated.scope.allocator();
for (const bNodeTreeInterfaceSocket *output : tree.interface_outputs()) {
const bke::bNodeSocketType *socket_type = bke::node_socket_type_find(output->socket_type);
if (!nodes::socket_type_supports_attributes(eNodeSocketDatatype(socket_type->type))) {
continue;
}
const StringRefNull identifier = allocator.copy_string(output->identifier);
const StringRefNull name = allocator.copy_string(output->name);
const StringRefNull description = allocator.copy_string(output->description);
const StringRefNull default_value = allocator.copy_string(output->default_attribute_name);
StructRNA *output_srna = RNA_def_struct_ptr(
r_generated.generated_rna, identifier.c_str(), RNA_PropertyGroup);
RNA_def_struct_path_func_runtime(output_srna, rna_NodesModifierPropertyOutput_path);
PropertyRNA *prop = RNA_def_string(output_srna,
"attribute_name",
default_value.is_empty() ? nullptr : default_value.c_str(),
0,
name.c_str(),
description.c_str());
RNA_def_property_flag(prop, PROP_FORCE_GEOMETRY_EVAL);
RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
prop = RNA_def_pointer_runtime(srna, identifier.c_str(), output_srna, name.c_str(), "");
RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
}
return srna;
}
static StructRNA *create_panels_srna(const bNodeTree &tree, GeneratedTreeSrnaData &r_generated)
{
StructRNA *srna = RNA_def_struct_ptr(
r_generated.generated_rna, "GeometryNodesInterfacePanels", RNA_PropertyGroup);
LinearAllocator<> &allocator = r_generated.scope.allocator();
tree.ensure_interface_cache();
for (const bNodeTreeInterfaceItem *item : tree.interface_items()) {
if (item->item_type != NODE_INTERFACE_PANEL) {
continue;
}
const auto &panel = *reinterpret_cast<const bNodeTreeInterfacePanel *>(item);
const StringRefNull identifier = allocator.copy_string(
fmt::format("open_{}", panel.identifier));
RNA_def_boolean(srna,
identifier.c_str(),
!(panel.flag & NODE_INTERFACE_PANEL_DEFAULT_CLOSED),
"Is Open",
"");
}
return srna;
}
GeneratedTreeSrnaData::GeneratedTreeSrnaData()
{
generated_rna = RNA_create_runtime();
}
GeneratedTreeSrnaData::~GeneratedTreeSrnaData()
{
RNA_free(generated_rna);
}
std::shared_ptr<GeneratedTreeSrnaData> create_geometry_nodes_rna_for_modifier(
const bNodeTree &tree)
{
auto generated = std::make_unique<GeneratedTreeSrnaData>();
tree.ensure_interface_cache();
StructRNA *srna = RNA_def_struct_ptr(
generated->generated_rna, "GeometryNodesModifierInterface", RNA_NodesModifierProperties);
generated->properties_struct = srna;
StructRNA *inputs_srna = create_inputs_srna(tree, *generated);
StructRNA *outputs_srna = create_outputs_srna(tree, *generated);
StructRNA *panels_srna = create_panels_srna(tree, *generated);
PropertyRNA *prop;
prop = RNA_def_pointer_runtime(
srna, "inputs", inputs_srna, "Inputs", "Settings for input sockets");
RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
prop = RNA_def_pointer_runtime(
srna, "outputs", outputs_srna, "Outputs", "Settings for output sockets");
RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
prop = RNA_def_pointer_runtime(srna, "panels", panels_srna, "Panels", "Settings for panels");
RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
return generated;
}
} // namespace blender::nodes

View file

@ -548,7 +548,9 @@ static bool set_modifier_value(bContext &C,
DEG_id_tag_update(&object.id, ID_RECALC_GEOMETRY);
const std::string main_prop_rna_path = fmt::format(
"modifiers[\"{}\"][\"{}\"]", BLI_str_escape(nmd.modifier.name), interface_socket.identifier);
"modifiers[\"{}\"].properties.inputs.{}.value",
BLI_str_escape(nmd.modifier.name),
interface_socket.identifier);
switch (interface_socket.socket_typeinfo()->type) {
case SOCK_FLOAT: {

View file

@ -8,6 +8,8 @@
#include <climits>
#include <fmt/format.h>
#include "DNA_node_types.h"
#include "BLI_color_types.hh"
@ -23,6 +25,7 @@
#include "BKE_geometry_set.hh"
#include "BKE_lib_id.hh"
#include "BKE_node.hh"
#include "BKE_node_enum.hh"
#include "BKE_node_legacy_types.hh"
#include "BKE_node_runtime.hh"
#include "BKE_node_socket_value.hh"
@ -36,11 +39,15 @@
#include "DNA_vfont_types.h"
#include "RNA_access.hh"
#include "RNA_define.hh"
#include "RNA_enum_types.hh"
#include "RNA_prototypes.hh"
#include "MEM_guardedalloc.h"
#include "NOD_geometry_nodes_bundle.hh"
#include "NOD_geometry_nodes_closure.hh"
#include "NOD_geometry_nodes_srna.hh"
#include "NOD_menu_value.hh"
#include "NOD_node_declaration.hh"
#include "NOD_socket.hh"
@ -1053,6 +1060,74 @@ static bke::bNodeSocketType *make_socket_type_virtual()
return stype;
}
static void make_common_type_prop(StructRNA &srna,
const bNodeTreeInterfaceSocket &socket,
const EnumPropertyItem *items,
const nodes::GeometryNodesInputType default_type,
nodes::GeneratedTreeSrnaData &r_generated)
{
PropertyRNA *prop = RNA_def_enum(
&srna,
"type",
items,
int(default_type),
r_generated.scope.add_value(fmt::format("{} {}", TIP_("Type for"), socket.name)).c_str(),
"");
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
RNA_def_property_flag(prop, PROP_FORCE_GEOMETRY_EVAL);
RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
}
static void make_common_attribute_name_prop(StructRNA &srna,
const bNodeTreeInterfaceSocket &socket,
nodes::GeneratedTreeSrnaData &r_generated)
{
PropertyRNA *prop = RNA_def_string(
&srna,
"attribute_name",
socket.default_attribute_name,
0,
r_generated.scope.add_value(fmt::format("{} {}", TIP_("Attribute for"), socket.name))
.c_str(),
socket.description);
RNA_def_property_flag(prop, PROP_FORCE_GEOMETRY_EVAL);
RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
}
static void make_common_value_and_attribute_props(StructRNA &srna,
const bNodeTreeInterfaceSocket &socket,
nodes::GeneratedTreeSrnaData &r_generated)
{
make_common_type_prop(srna,
socket,
nodes::geometry_nodes_input_type_items_value_or_attribute,
nodes::GeometryNodesInputType::Value,
r_generated);
make_common_attribute_name_prop(srna, socket, r_generated);
}
static void make_common_value_props(StructRNA &srna,
const bNodeTreeInterfaceSocket &socket,
nodes::GeneratedTreeSrnaData &r_generated)
{
make_common_type_prop(srna,
socket,
nodes::geometry_nodes_input_type_items_value,
nodes::GeometryNodesInputType::Value,
r_generated);
}
static void make_common_fallback_props(StructRNA &srna,
const bNodeTreeInterfaceSocket &socket,
nodes::GeneratedTreeSrnaData &r_generated)
{
make_common_type_prop(srna,
socket,
nodes::geometry_nodes_input_type_items_fallback,
nodes::GeometryNodesInputType::Fallback,
r_generated);
}
static bke::bNodeSocketType *make_socket_type_bool()
{
bke::bNodeSocketType *socktype = make_standard_socket_type(SOCK_BOOLEAN, PROP_NONE);
@ -1068,6 +1143,31 @@ static bke::bNodeSocketType *make_socket_type_bool()
};
static SocketValueVariant default_value{false};
socktype->geometry_nodes_default_value = &default_value;
socktype->make_geometry_nodes_input_srna = [](const bNodeTree & /*tree*/,
StructRNA &srna,
const bNodeTreeInterfaceSocket &socket,
nodes::GeneratedTreeSrnaData &r_generated) {
const auto *data = static_cast<const bNodeSocketValueBoolean *>(socket.socket_data);
PropertyRNA *prop = RNA_def_boolean(
&srna, "value", data->value, socket.name, socket.description);
RNA_def_property_flag(prop, PROP_FORCE_GEOMETRY_EVAL);
RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
make_common_type_prop(srna,
socket,
nodes::geometry_nodes_input_type_items_value_or_attribute_or_layer,
nodes::GeometryNodesInputType::Value,
r_generated);
make_common_attribute_name_prop(srna, socket, r_generated);
prop = RNA_def_string(
&srna,
"layer_name",
nullptr,
0,
r_generated.scope.add_value(fmt::format("{} {}", TIP_("Layer for"), socket.name)).c_str(),
socket.description);
RNA_def_property_flag(prop, PROP_FORCE_GEOMETRY_EVAL);
RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
};
return socktype;
}
@ -1090,6 +1190,25 @@ static bke::bNodeSocketType *make_socket_type_rotation()
};
static SocketValueVariant default_value{math::Quaternion::identity()};
socktype->geometry_nodes_default_value = &default_value;
socktype->make_geometry_nodes_input_srna = [](const bNodeTree & /*tree*/,
StructRNA &srna,
const bNodeTreeInterfaceSocket &socket,
nodes::GeneratedTreeSrnaData &r_generated) {
const auto *data = static_cast<const bNodeSocketValueRotation *>(socket.socket_data);
PropertyRNA *prop = RNA_def_float_rotation(&srna,
"value",
3,
data->value_euler,
-FLT_MAX,
FLT_MAX,
socket.name,
socket.description,
-FLT_MAX,
FLT_MAX);
RNA_def_property_flag(prop, PROP_FORCE_GEOMETRY_EVAL);
RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
make_common_value_and_attribute_props(srna, socket, r_generated);
};
return socktype;
}
@ -1105,6 +1224,12 @@ static bke::bNodeSocketType *make_socket_type_matrix()
};
static SocketValueVariant default_value{float4x4::identity()};
socktype->geometry_nodes_default_value = &default_value;
socktype->make_geometry_nodes_input_srna = [](const bNodeTree & /*tree*/,
StructRNA &srna,
const bNodeTreeInterfaceSocket &socket,
nodes::GeneratedTreeSrnaData &r_generated) {
make_common_fallback_props(srna, socket, r_generated);
};
return socktype;
}
@ -1120,6 +1245,12 @@ static bke::bNodeSocketType *make_socket_type_bundle()
};
static SocketValueVariant default_value = SocketValueVariant::From(nodes::BundlePtr());
socktype->geometry_nodes_default_value = &default_value;
socktype->make_geometry_nodes_input_srna = [](const bNodeTree & /*tree*/,
StructRNA &srna,
const bNodeTreeInterfaceSocket &socket,
nodes::GeneratedTreeSrnaData &r_generated) {
make_common_fallback_props(srna, socket, r_generated);
};
return socktype;
}
@ -1135,6 +1266,12 @@ static bke::bNodeSocketType *make_socket_type_closure()
};
static SocketValueVariant default_value = SocketValueVariant::From(nodes::ClosurePtr());
socktype->geometry_nodes_default_value = &default_value;
socktype->make_geometry_nodes_input_srna = [](const bNodeTree & /*tree*/,
StructRNA &srna,
const bNodeTreeInterfaceSocket &socket,
nodes::GeneratedTreeSrnaData &r_generated) {
make_common_fallback_props(srna, socket, r_generated);
};
return socktype;
}
@ -1153,6 +1290,26 @@ static bke::bNodeSocketType *make_socket_type_float(PropertySubType subtype)
};
static SocketValueVariant default_value{0.0f};
socktype->geometry_nodes_default_value = &default_value;
socktype->make_geometry_nodes_input_srna = [](const bNodeTree & /*tree*/,
StructRNA &srna,
const bNodeTreeInterfaceSocket &socket,
nodes::GeneratedTreeSrnaData &r_generated) {
PropertyRNA *prop;
const auto *data = static_cast<const bNodeSocketValueFloat *>(socket.socket_data);
prop = RNA_def_float(&srna,
"value",
data->value,
-FLT_MAX,
FLT_MAX,
socket.name,
socket.description,
data->min,
data->max);
RNA_def_property_flag(prop, PROP_FORCE_GEOMETRY_EVAL);
RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_subtype(prop, PropertySubType(data->subtype));
make_common_value_and_attribute_props(srna, socket, r_generated);
};
return socktype;
}
@ -1171,6 +1328,26 @@ static bke::bNodeSocketType *make_socket_type_int(PropertySubType subtype)
};
static SocketValueVariant default_value{0};
socktype->geometry_nodes_default_value = &default_value;
socktype->make_geometry_nodes_input_srna = [](const bNodeTree & /*tree*/,
StructRNA &srna,
const bNodeTreeInterfaceSocket &socket,
nodes::GeneratedTreeSrnaData &r_generated) {
PropertyRNA *prop;
const auto *data = static_cast<const bNodeSocketValueInt *>(socket.socket_data);
prop = RNA_def_int(&srna,
"value",
data->value,
INT32_MIN,
INT32_MAX,
socket.name,
socket.description,
data->min,
data->max);
RNA_def_property_flag(prop, PROP_FORCE_GEOMETRY_EVAL);
RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_subtype(prop, PropertySubType(data->subtype));
make_common_value_and_attribute_props(srna, socket, r_generated);
};
return socktype;
}
@ -1189,6 +1366,27 @@ static bke::bNodeSocketType *make_socket_type_vector(PropertySubType subtype, co
};
static SocketValueVariant default_value{float3(0, 0, 0)};
socktype->geometry_nodes_default_value = &default_value;
socktype->make_geometry_nodes_input_srna = [](const bNodeTree & /*tree*/,
StructRNA &srna,
const bNodeTreeInterfaceSocket &socket,
nodes::GeneratedTreeSrnaData &r_generated) {
PropertyRNA *prop;
const auto *data = static_cast<const bNodeSocketValueVector *>(socket.socket_data);
prop = RNA_def_float_vector(&srna,
"value",
data->dimensions,
data->value,
-FLT_MAX,
FLT_MAX,
socket.name,
socket.description,
data->min,
data->max);
RNA_def_property_flag(prop, PROP_FORCE_GEOMETRY_EVAL);
RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_subtype(prop, PropertySubType(data->subtype));
make_common_value_and_attribute_props(srna, socket, r_generated);
};
return socktype;
}
@ -1213,6 +1411,25 @@ static bke::bNodeSocketType *make_socket_type_rgba()
};
static SocketValueVariant default_value{ColorGeometry4f(0, 0, 0, 0)};
socktype->geometry_nodes_default_value = &default_value;
socktype->make_geometry_nodes_input_srna = [](const bNodeTree & /*tree*/,
StructRNA &srna,
const bNodeTreeInterfaceSocket &socket,
nodes::GeneratedTreeSrnaData &r_generated) {
const auto *data = static_cast<const bNodeSocketValueRGBA *>(socket.socket_data);
PropertyRNA *prop = RNA_def_float_color(&srna,
"value",
4,
data->value,
-FLT_MAX,
FLT_MAX,
socket.name,
socket.description,
-FLT_MAX,
FLT_MAX);
RNA_def_property_flag(prop, PROP_FORCE_GEOMETRY_EVAL);
RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
make_common_value_and_attribute_props(srna, socket, r_generated);
};
return socktype;
}
@ -1231,6 +1448,23 @@ static bke::bNodeSocketType *make_socket_type_string(PropertySubType subtype)
};
static SocketValueVariant default_value{std::string()};
socktype->geometry_nodes_default_value = &default_value;
socktype->make_geometry_nodes_input_srna = [](const bNodeTree & /*tree*/,
StructRNA &srna,
const bNodeTreeInterfaceSocket &socket,
nodes::GeneratedTreeSrnaData &r_generated) {
PropertyRNA *prop;
const auto *data = static_cast<const bNodeSocketValueString *>(socket.socket_data);
prop = RNA_def_string(&srna,
"value",
data->value[0] ? data->value : nullptr,
0,
socket.name,
socket.description);
RNA_def_property_flag(prop, PROP_FORCE_GEOMETRY_EVAL);
RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_subtype(prop, PropertySubType(data->subtype));
make_common_value_props(srna, socket, r_generated);
};
return socktype;
}
@ -1249,6 +1483,45 @@ static bke::bNodeSocketType *make_socket_type_menu()
};
static SocketValueVariant default_value = SocketValueVariant::From(nodes::MenuValue());
socktype->geometry_nodes_default_value = &default_value;
socktype->make_geometry_nodes_input_srna = [](const bNodeTree & /*tree*/,
StructRNA &srna,
const bNodeTreeInterfaceSocket &socket,
nodes::GeneratedTreeSrnaData &r_generated) {
const auto *data = static_cast<const bNodeSocketValueMenu *>(socket.socket_data);
const EnumPropertyItem *items;
bool default_value_found = false;
if (data->has_conflict() || !data->enum_items) {
items = rna_enum_dummy_NULL_items;
}
else {
MutableSpan<EnumPropertyItem> new_items =
r_generated.scope.allocator().allocate_array<EnumPropertyItem>(
data->enum_items->items.size() + 1);
for (const int i : data->enum_items->items.index_range()) {
const bke::RuntimeNodeEnumItem &item_data = data->enum_items->items[i];
EnumPropertyItem item{};
item.value = item_data.identifier;
if (item.value == data->value) {
default_value_found = true;
}
item.identifier = item_data.name.c_str();
item.name = item_data.name.c_str();
item.description = item_data.description.c_str();
new_items[i] = item;
}
new_items.last() = {};
items = new_items.data();
}
PropertyRNA *prop = RNA_def_enum(&srna,
"value",
items,
default_value_found ? data->value : 0,
socket.name,
socket.description);
RNA_def_property_flag(prop, PROP_FORCE_GEOMETRY_EVAL);
RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
make_common_value_props(srna, socket, r_generated);
};
return socktype;
}
@ -1267,6 +1540,16 @@ static bke::bNodeSocketType *make_socket_type_object()
static SocketValueVariant default_value = SocketValueVariant::From(
static_cast<Object *>(nullptr));
socktype->geometry_nodes_default_value = &default_value;
socktype->make_geometry_nodes_input_srna = [](const bNodeTree & /*tree*/,
StructRNA &srna,
const bNodeTreeInterfaceSocket &socket,
nodes::GeneratedTreeSrnaData &r_generated) {
PropertyRNA *prop = RNA_def_pointer_runtime(
&srna, "value", RNA_Object, socket.name, socket.description);
RNA_def_property_flag(prop, PROP_FORCE_GEOMETRY_EVAL);
RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
make_common_value_props(srna, socket, r_generated);
};
return socktype;
}
@ -1282,6 +1565,12 @@ static bke::bNodeSocketType *make_socket_type_geometry()
};
static SocketValueVariant default_value = SocketValueVariant::From(bke::GeometrySet());
socktype->geometry_nodes_default_value = &default_value;
socktype->make_geometry_nodes_input_srna = [](const bNodeTree & /*tree*/,
StructRNA &srna,
const bNodeTreeInterfaceSocket &socket,
nodes::GeneratedTreeSrnaData &r_generated) {
make_common_fallback_props(srna, socket, r_generated);
};
return socktype;
}
@ -1300,6 +1589,16 @@ static bke::bNodeSocketType *make_socket_type_collection()
static SocketValueVariant default_value = SocketValueVariant::From(
static_cast<Collection *>(nullptr));
socktype->geometry_nodes_default_value = &default_value;
socktype->make_geometry_nodes_input_srna = [](const bNodeTree & /*tree*/,
StructRNA &srna,
const bNodeTreeInterfaceSocket &socket,
nodes::GeneratedTreeSrnaData &r_generated) {
PropertyRNA *prop = RNA_def_pointer_runtime(
&srna, "value", RNA_Collection, socket.name, socket.description);
RNA_def_property_flag(prop, PROP_FORCE_GEOMETRY_EVAL);
RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
make_common_value_props(srna, socket, r_generated);
};
return socktype;
}
@ -1317,6 +1616,16 @@ static bke::bNodeSocketType *make_socket_type_texture()
};
static SocketValueVariant default_value = SocketValueVariant::From(static_cast<Tex *>(nullptr));
socktype->geometry_nodes_default_value = &default_value;
socktype->make_geometry_nodes_input_srna = [](const bNodeTree & /*tree*/,
StructRNA &srna,
const bNodeTreeInterfaceSocket &socket,
nodes::GeneratedTreeSrnaData &r_generated) {
PropertyRNA *prop = RNA_def_pointer_runtime(
&srna, "value", RNA_Texture, socket.name, socket.description);
RNA_def_property_flag(prop, PROP_FORCE_GEOMETRY_EVAL);
RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
make_common_value_props(srna, socket, r_generated);
};
return socktype;
}
@ -1335,6 +1644,17 @@ static bke::bNodeSocketType *make_socket_type_image()
static SocketValueVariant default_value = SocketValueVariant::From(
static_cast<Image *>(nullptr));
socktype->geometry_nodes_default_value = &default_value;
socktype->make_geometry_nodes_input_srna = [](const bNodeTree & /*tree*/,
StructRNA &srna,
const bNodeTreeInterfaceSocket &socket,
nodes::GeneratedTreeSrnaData &r_generated) {
PropertyRNA *prop = RNA_def_pointer_runtime(
&srna, "value", RNA_Image, socket.name, socket.description);
RNA_def_property_flag(prop, PROP_FORCE_GEOMETRY_EVAL);
RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
make_common_value_props(srna, socket, r_generated);
};
return socktype;
}
@ -1353,6 +1673,16 @@ static bke::bNodeSocketType *make_socket_type_material()
static SocketValueVariant default_value = SocketValueVariant::From(
static_cast<Material *>(nullptr));
socktype->geometry_nodes_default_value = &default_value;
socktype->make_geometry_nodes_input_srna = [](const bNodeTree & /*tree*/,
StructRNA &srna,
const bNodeTreeInterfaceSocket &socket,
nodes::GeneratedTreeSrnaData &r_generated) {
PropertyRNA *prop = RNA_def_pointer_runtime(
&srna, "value", RNA_Material, socket.name, socket.description);
RNA_def_property_flag(prop, PROP_FORCE_GEOMETRY_EVAL);
RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
make_common_value_props(srna, socket, r_generated);
};
return socktype;
}

View file

@ -1030,14 +1030,14 @@ void infer_group_interface_inputs_usage(const bNodeTree &group,
infer_group_interface_usage(group, input_values, r_input_usages, {}); // TODO
}
void infer_group_interface_usage(const bNodeTree &group,
const IDProperty *properties,
MutableSpan<SocketUsage> r_input_usages,
std::optional<MutableSpan<SocketUsage>> r_output_usages)
void infer_group_interface_inputs_usage(const bNodeTree &group,
const PointerRNA &properties_ptr,
MutableSpan<SocketUsage> r_input_usages,
std::optional<MutableSpan<SocketUsage>> r_output_usages)
{
ResourceScope scope;
const Vector<InferenceValue> group_input_values =
nodes::get_geometry_nodes_input_inference_values(group, properties, scope);
nodes::get_geometry_nodes_input_inference_values(group, properties_ptr, scope);
nodes::socket_usage_inference::infer_group_interface_usage(
group, group_input_values, r_input_usages, r_output_usages);
}

View file

@ -361,8 +361,8 @@ void WM_operator_stack_clear(wmWindowManager *wm, const Set<wmOperatorType *> &t
bool any_removed = false;
for (wmOperator &op : wm->runtime->operators.items_mutable()) {
if (types.contains(op.type)) {
WM_operator_free(&op);
BLI_remlink(&wm->runtime->operators, &op);
WM_operator_free(&op);
any_removed = true;
}
}