Geometry Nodes: Switch Node Fields Update

This update of the Switch node allows for field compatible types
to be switched through the node. This includes the following:

Float, Int, Bool, String, Vector, and Color

The remaining types are processed with the orginal code:

Geometry, Object, Collection, Texture, and Material

Because the old types require a diffent "switch" socket than the
field types, versioning for old files is included to move links
of those types to a new switch socket. Once fields of other types
are supported, this node can be updated to support them as well.

Differential Revision: https://developer.blender.org/D12642
This commit is contained in:
Johnny Matthews 2021-10-02 17:33:25 -05:00
parent 54927caf4f
commit 34cf33eb12
3 changed files with 170 additions and 54 deletions

View File

@ -39,7 +39,7 @@ extern "C" {
/* Blender file format version. */
#define BLENDER_FILE_VERSION BLENDER_VERSION
#define BLENDER_FILE_SUBVERSION 31
#define BLENDER_FILE_SUBVERSION 32
/* Minimum Blender version that supports reading file written with the current
* version. Older Blender versions will test this and show a warning if the file

View File

@ -617,6 +617,32 @@ void do_versions_after_linking_300(Main *bmain, ReportList *UNUSED(reports))
do_versions_idproperty_ui_data(bmain);
}
if (!MAIN_VERSION_ATLEAST(bmain, 300, 32)) {
/* Update Switch Node Non-Fields switch input to Switch_001. */
LISTBASE_FOREACH (bNodeTree *, ntree, &bmain->nodetrees) {
if (ntree->type != NTREE_GEOMETRY) {
continue;
}
LISTBASE_FOREACH (bNodeLink *, link, &ntree->links) {
if (link->tonode->type == GEO_NODE_SWITCH) {
if (STREQ(link->tosock->identifier, "Switch")) {
bNode *to_node = link->tonode;
uint8_t mode = ((NodeSwitch *)to_node->storage)->input_type;
if (ELEM(mode,
SOCK_GEOMETRY,
SOCK_OBJECT,
SOCK_COLLECTION,
SOCK_TEXTURE,
SOCK_MATERIAL)) {
link->tosock = link->tosock->next;
}
}
}
}
}
}
/**
* Versioning code until next subversion bump goes here.
*

View File

@ -19,24 +19,37 @@
#include "UI_interface.h"
#include "UI_resources.h"
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
#include "BKE_material.h"
#include "FN_multi_function_signature.hh"
namespace blender::nodes {
static void geo_node_switch_declare(NodeDeclarationBuilder &b)
{
b.add_input<decl::Bool>("Switch");
b.add_input<decl::Bool>("Switch").default_value(false).supports_field();
b.add_input<decl::Bool>("Switch", "Switch_001").default_value(false);
b.add_input<decl::Float>("False").supports_field();
b.add_input<decl::Float>("True").supports_field();
b.add_input<decl::Int>("False", "False_001").min(-100000).max(100000).supports_field();
b.add_input<decl::Int>("True", "True_001").min(-100000).max(100000).supports_field();
b.add_input<decl::Bool>("False", "False_002").default_value(false).hide_value().supports_field();
b.add_input<decl::Bool>("True", "True_002").default_value(true).hide_value().supports_field();
b.add_input<decl::Vector>("False", "False_003").supports_field();
b.add_input<decl::Vector>("True", "True_003").supports_field();
b.add_input<decl::Color>("False", "False_004")
.default_value({0.8f, 0.8f, 0.8f, 1.0f})
.supports_field();
b.add_input<decl::Color>("True", "True_004")
.default_value({0.8f, 0.8f, 0.8f, 1.0f})
.supports_field();
b.add_input<decl::String>("False", "False_005").supports_field();
b.add_input<decl::String>("True", "True_005").supports_field();
b.add_input<decl::Float>("False");
b.add_input<decl::Float>("True");
b.add_input<decl::Int>("False", "False_001").min(-100000).max(100000);
b.add_input<decl::Int>("True", "True_001").min(-100000).max(100000);
b.add_input<decl::Bool>("False", "False_002");
b.add_input<decl::Bool>("True", "True_002");
b.add_input<decl::Vector>("False", "False_003");
b.add_input<decl::Vector>("True", "True_003");
b.add_input<decl::Color>("False", "False_004").default_value({0.8f, 0.8f, 0.8f, 1.0f});
b.add_input<decl::Color>("True", "True_004").default_value({0.8f, 0.8f, 0.8f, 1.0f});
b.add_input<decl::String>("False", "False_005");
b.add_input<decl::String>("True", "True_005");
b.add_input<decl::Geometry>("False", "False_006");
b.add_input<decl::Geometry>("True", "True_006");
b.add_input<decl::Object>("False", "False_007");
@ -48,12 +61,12 @@ static void geo_node_switch_declare(NodeDeclarationBuilder &b)
b.add_input<decl::Material>("False", "False_010");
b.add_input<decl::Material>("True", "True_010");
b.add_output<decl::Float>("Output");
b.add_output<decl::Int>("Output", "Output_001");
b.add_output<decl::Bool>("Output", "Output_002");
b.add_output<decl::Vector>("Output", "Output_003");
b.add_output<decl::Color>("Output", "Output_004");
b.add_output<decl::String>("Output", "Output_005");
b.add_output<decl::Float>("Output").dependent_field();
b.add_output<decl::Int>("Output", "Output_001").dependent_field();
b.add_output<decl::Bool>("Output", "Output_002").dependent_field();
b.add_output<decl::Vector>("Output", "Output_003").dependent_field();
b.add_output<decl::Color>("Output", "Output_004").dependent_field();
b.add_output<decl::String>("Output", "Output_005").dependent_field();
b.add_output<decl::Geometry>("Output", "Output_006");
b.add_output<decl::Object>("Output", "Output_007");
b.add_output<decl::Collection>("Output", "Output_008");
@ -77,91 +90,168 @@ static void geo_node_switch_update(bNodeTree *UNUSED(ntree), bNode *node)
{
NodeSwitch *node_storage = (NodeSwitch *)node->storage;
int index = 0;
LISTBASE_FOREACH (bNodeSocket *, socket, &node->inputs) {
nodeSetSocketAvailability(
socket, index == 0 || socket->type == (eNodeSocketDatatype)node_storage->input_type);
index++;
bNodeSocket *field_switch = (bNodeSocket *)node->inputs.first;
bNodeSocket *non_field_switch = (bNodeSocket *)field_switch->next;
const bool fields_type = ELEM((eNodeSocketDatatype)node_storage->input_type,
SOCK_FLOAT,
SOCK_INT,
SOCK_BOOLEAN,
SOCK_VECTOR,
SOCK_RGBA,
SOCK_STRING);
nodeSetSocketAvailability(field_switch, fields_type);
nodeSetSocketAvailability(non_field_switch, !fields_type);
LISTBASE_FOREACH_INDEX (bNodeSocket *, socket, &node->inputs, index) {
if (index <= 1) {
continue;
}
nodeSetSocketAvailability(socket,
socket->type == (eNodeSocketDatatype)node_storage->input_type);
}
LISTBASE_FOREACH (bNodeSocket *, socket, &node->outputs) {
nodeSetSocketAvailability(socket,
socket->type == (eNodeSocketDatatype)node_storage->input_type);
}
}
template<typename T>
static void output_input(GeoNodeExecParams &params,
const bool input,
const StringRef input_suffix,
const StringRef output_identifier)
template<typename T> class SwitchFieldsFunction : public fn::MultiFunction {
public:
SwitchFieldsFunction()
{
static fn::MFSignature signature = create_signature();
this->set_signature(&signature);
}
static fn::MFSignature create_signature()
{
fn::MFSignatureBuilder signature{"Switch"};
signature.single_input<bool>("Switch");
signature.single_input<T>("False");
signature.single_input<T>("True");
signature.single_output<T>("Output");
return signature.build();
}
void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override
{
const VArray<bool> &switches = params.readonly_single_input<bool>(0, "Switch");
const VArray<T> &falses = params.readonly_single_input<T>(1, "False");
const VArray<T> &trues = params.readonly_single_input<T>(2, "True");
MutableSpan<T> values = params.uninitialized_single_output_if_required<T>(3, "Output");
for (int64_t i : mask) {
new (&values[i]) T(switches[i] ? trues[i] : falses[i]);
}
}
};
template<typename T> void switch_fields(GeoNodeExecParams &params, const StringRef suffix)
{
const std::string name_a = "False" + input_suffix;
const std::string name_b = "True" + input_suffix;
if (input) {
params.set_input_unused(name_a);
if (params.lazy_require_input(name_b)) {
if (params.lazy_require_input("Switch")) {
return;
}
const std::string name_false = "False" + suffix;
const std::string name_true = "True" + suffix;
const std::string name_output = "Output" + suffix;
/* TODO: Allow for Laziness when the switch field is constant. */
const bool require_false = params.lazy_require_input(name_false);
const bool require_true = params.lazy_require_input(name_true);
if (require_false | require_true) {
return;
}
Field<bool> switches_field = params.extract_input<Field<bool>>("Switch");
Field<T> falses_field = params.extract_input<Field<T>>(name_false);
Field<T> trues_field = params.extract_input<Field<T>>(name_true);
auto switch_fn = std::make_unique<SwitchFieldsFunction<T>>();
auto switch_op = std::make_shared<FieldOperation>(FieldOperation(
std::move(switch_fn),
{std::move(switches_field), std::move(falses_field), std::move(trues_field)}));
params.set_output(name_output, Field<T>(switch_op, 0));
}
template<typename T> void switch_no_fields(GeoNodeExecParams &params, const StringRef suffix)
{
if (params.lazy_require_input("Switch_001")) {
return;
}
bool switch_value = params.get_input<bool>("Switch_001");
const std::string name_false = "False" + suffix;
const std::string name_true = "True" + suffix;
const std::string name_output = "Output" + suffix;
if (switch_value) {
params.set_input_unused(name_false);
if (params.lazy_require_input(name_true)) {
return;
}
params.set_output(output_identifier, params.extract_input<T>(name_b));
params.set_output(name_output, params.extract_input<T>(name_true));
}
else {
params.set_input_unused(name_b);
if (params.lazy_require_input(name_a)) {
params.set_input_unused(name_true);
if (params.lazy_require_input(name_false)) {
return;
}
params.set_output(output_identifier, params.extract_input<T>(name_a));
params.set_output(name_output, params.extract_input<T>(name_false));
}
}
static void geo_node_switch_exec(GeoNodeExecParams params)
{
if (params.lazy_require_input("Switch")) {
return;
}
const NodeSwitch &storage = *(const NodeSwitch *)params.node().storage;
const bool input = params.get_input<bool>("Switch");
switch ((eNodeSocketDatatype)storage.input_type) {
const eNodeSocketDatatype data_type = static_cast<eNodeSocketDatatype>(storage.input_type);
switch (data_type) {
case SOCK_FLOAT: {
output_input<float>(params, input, "", "Output");
switch_fields<float>(params, "");
break;
}
case SOCK_INT: {
output_input<int>(params, input, "_001", "Output_001");
switch_fields<int>(params, "_001");
break;
}
case SOCK_BOOLEAN: {
output_input<bool>(params, input, "_002", "Output_002");
switch_fields<bool>(params, "_002");
break;
}
case SOCK_VECTOR: {
output_input<float3>(params, input, "_003", "Output_003");
switch_fields<float3>(params, "_003");
break;
}
case SOCK_RGBA: {
output_input<ColorGeometry4f>(params, input, "_004", "Output_004");
switch_fields<ColorGeometry4f>(params, "_004");
break;
}
case SOCK_STRING: {
output_input<std::string>(params, input, "_005", "Output_005");
switch_fields<std::string>(params, "_005");
break;
}
case SOCK_GEOMETRY: {
output_input<GeometrySet>(params, input, "_006", "Output_006");
switch_no_fields<GeometrySet>(params, "_006");
break;
}
case SOCK_OBJECT: {
output_input<Object *>(params, input, "_007", "Output_007");
switch_no_fields<Object *>(params, "_007");
break;
}
case SOCK_COLLECTION: {
output_input<Collection *>(params, input, "_008", "Output_008");
switch_no_fields<Collection *>(params, "_008");
break;
}
case SOCK_TEXTURE: {
output_input<Tex *>(params, input, "_009", "Output_009");
switch_no_fields<Tex *>(params, "_009");
break;
}
case SOCK_MATERIAL: {
output_input<Material *>(params, input, "_010", "Output_010");
switch_no_fields<Material *>(params, "_010");
break;
}
default: