Geometry Nodes: Use checkbox for exposed boolean sockets

This uses the changes from ef68a37e5d to create IDProperties
for exposed boolean sockets with a boolean type instead of an integer
with a [0,1] range. Existing properties and values are converted
automatically.

For forward compatibility, the properties are switched to the integer
type for saving. Otherwise older versions crash immediately when opening
a newer file. The "Use Attribute" IDProperties aren't changed here,
since that wouldn't have a visible benefit.

Differential Revision: https://developer.blender.org/D12816
This commit is contained in:
Hans Goudey 2023-01-20 17:36:07 -06:00
parent 6a22230db4
commit 2ea47e0def
Notes: blender-bot 2023-02-18 23:32:00 +01:00
Referenced by issue #85306, Use boolean in the modifier for exposed boolean node group inputs
Referenced by issue #104902, Addon BoolProperty saved in 3.5 does not preserve value when opened in earlier Blender versions
3 changed files with 67 additions and 18 deletions

View File

@ -32,6 +32,9 @@ class IDPropertyDeleter {
}
};
/** \brief Allocate a new IDProperty of type IDP_BOOLEAN, set its name and value. */
std::unique_ptr<IDProperty, IDPropertyDeleter> create_bool(StringRefNull prop_name, bool value);
/** \brief Allocate a new IDProperty of type IDP_INT, set its name and value. */
std::unique_ptr<IDProperty, IDPropertyDeleter> create(StringRefNull prop_name, int32_t value);

View File

@ -21,6 +21,15 @@ std::unique_ptr<IDProperty, IDPropertyDeleter> create(const StringRefNull prop_n
return std::unique_ptr<IDProperty, IDPropertyDeleter>(property);
}
std::unique_ptr<IDProperty, IDPropertyDeleter> create_bool(const StringRefNull prop_name,
bool value)
{
IDPropertyTemplate prop_template{0};
prop_template.i = value;
IDProperty *property = IDP_New(IDP_BOOLEAN, &prop_template, prop_name.c_str());
return std::unique_ptr<IDProperty, IDPropertyDeleter>(property);
}
std::unique_ptr<IDProperty, IDPropertyDeleter> create(const StringRefNull prop_name, float value)
{
IDPropertyTemplate prop_template{0};

View File

@ -496,10 +496,8 @@ id_property_create_from_socket(const bNodeSocket &socket)
case SOCK_BOOLEAN: {
const bNodeSocketValueBoolean *value = static_cast<const bNodeSocketValueBoolean *>(
socket.default_value);
auto property = bke::idprop::create(socket.identifier, int(value->value));
IDPropertyUIDataInt *ui_data = (IDPropertyUIDataInt *)IDP_ui_data_ensure(property.get());
ui_data->min = ui_data->soft_min = 0;
ui_data->max = ui_data->soft_max = 1;
auto property = bke::idprop::create_bool(socket.identifier, value->value);
IDPropertyUIDataBool *ui_data = (IDPropertyUIDataBool *)IDP_ui_data_ensure(property.get());
ui_data->default_value = value->value != 0;
return property;
}
@ -553,7 +551,7 @@ static bool id_property_type_matches_socket(const bNodeSocket &socket, const IDP
case SOCK_RGBA:
return property.type == IDP_ARRAY && property.subtype == IDP_FLOAT && property.len == 4;
case SOCK_BOOLEAN:
return property.type == IDP_INT;
return property.type == IDP_BOOLEAN;
case SOCK_STRING:
return property.type == IDP_STRING;
case SOCK_OBJECT:
@ -601,7 +599,7 @@ static void init_socket_cpp_value_from_property(const IDProperty &property,
break;
}
case SOCK_BOOLEAN: {
bool value = IDP_Int(&property) != 0;
const bool value = IDP_Bool(&property);
new (r_value) ValueOrField<bool>(value);
break;
}
@ -682,16 +680,23 @@ void MOD_nodes_update_interface(Object *object, NodesModifierData *nmd)
if (old_properties != nullptr) {
IDProperty *old_prop = IDP_GetPropertyFromGroup(old_properties, socket->identifier);
if (old_prop != nullptr && id_property_type_matches_socket(*socket, *old_prop)) {
/* #IDP_CopyPropertyContent replaces the UI data as well, which we don't (we only
* want to replace the values). So release it temporarily and replace it after. */
IDPropertyUIData *ui_data = new_prop->ui_data;
new_prop->ui_data = nullptr;
IDP_CopyPropertyContent(new_prop, old_prop);
if (new_prop->ui_data != nullptr) {
IDP_ui_data_free(new_prop);
if (old_prop != nullptr) {
if (id_property_type_matches_socket(*socket, *old_prop)) {
/* #IDP_CopyPropertyContent replaces the UI data as well, which we don't (we only
* want to replace the values). So release it temporarily and replace it after. */
IDPropertyUIData *ui_data = new_prop->ui_data;
new_prop->ui_data = nullptr;
IDP_CopyPropertyContent(new_prop, old_prop);
if (new_prop->ui_data != nullptr) {
IDP_ui_data_free(new_prop);
}
new_prop->ui_data = ui_data;
}
else if (old_prop->type == IDP_INT && new_prop->type == IDP_BOOLEAN) {
/* Support versioning from integer to boolean property values. The actual value is stored
* in the same variable for both types. */
new_prop->data.val = old_prop->data.val != 0;
}
new_prop->ui_data = ui_data;
}
}
@ -1575,9 +1580,17 @@ static void add_attribute_search_or_value_buttons(const bContext &C,
uiLayout *split = uiLayoutSplit(layout, 0.4f, false);
uiLayout *name_row = uiLayoutRow(split, false);
uiLayoutSetAlignment(name_row, UI_LAYOUT_ALIGN_RIGHT);
uiItemL(name_row, socket.name, ICON_NONE);
if (socket.type == SOCK_BOOLEAN) {
uiItemL(name_row, "", ICON_NONE);
}
else {
uiItemL(name_row, socket.name, ICON_NONE);
}
uiLayout *prop_row = uiLayoutRow(split, true);
if (socket.type == SOCK_BOOLEAN) {
uiLayoutSetAlignment(prop_row, UI_LAYOUT_ALIGN_LEFT);
}
PointerRNA props;
uiItemFullO(prop_row,
@ -1594,10 +1607,10 @@ static void add_attribute_search_or_value_buttons(const bContext &C,
const int use_attribute = RNA_int_get(md_ptr, rna_path_use_attribute.c_str()) != 0;
if (use_attribute) {
add_attribute_search_button(C, prop_row, nmd, md_ptr, rna_path_attribute_name, socket, false);
uiItemL(layout, "", ICON_BLANK1);
}
else {
uiItemR(prop_row, md_ptr, rna_path.c_str(), 0, "", ICON_NONE);
const char *name = socket.type == SOCK_BOOLEAN ? socket.name : "";
uiItemR(prop_row, md_ptr, rna_path.c_str(), 0, name, ICON_NONE);
uiItemDecoratorR(layout, md_ptr, rna_path.c_str(), -1);
}
}
@ -1854,9 +1867,33 @@ static void blendWrite(BlendWriter *writer, const ID * /*id_owner*/, const Modif
BLO_write_struct(writer, NodesModifierData, nmd);
if (nmd->settings.properties != nullptr) {
/* 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 */
Map<IDProperty *, IDPropertyUIDataBool *> boolean_props;
LISTBASE_FOREACH (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. */
IDP_BlendWrite(writer, nmd->settings.properties);
LISTBASE_FOREACH (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);
}
}
}
}
}
}