Geometry Nodes: Attribute Vector Math Node

This patch implements the same operations and interface as the regular
vector math node, but it runs for every element of the attribute. This
should expand what's possible with geometry nodes quite a bit.

Differential Revision: https://developer.blender.org/D9914
This commit is contained in:
Hans Goudey 2021-01-11 12:06:52 -06:00
parent 5bd822dc46
commit ecdbd83a8d
Notes: blender-bot 2023-02-14 04:46:12 +01:00
Referenced by issue #82618, Attribute Vector Math Node for tree leaves and tree moss
12 changed files with 934 additions and 55 deletions

View File

@ -488,6 +488,7 @@ geometry_node_categories = [
NodeItem("GeometryNodeAttributeFill"),
NodeItem("GeometryNodeAttributeMix"),
NodeItem("GeometryNodeAttributeColorRamp"),
NodeItem("GeometryNodeAttributeVectorMath"),
]),
GeometryNodeCategory("GEO_COLOR", "Color", items=[
NodeItem("ShaderNodeValToRGB"),

View File

@ -1357,6 +1357,7 @@ int ntreeTexExecTree(struct bNodeTree *ntree,
#define GEO_NODE_POINT_SEPARATE 1014
#define GEO_NODE_ATTRIBUTE_COMPARE 1015
#define GEO_NODE_ROTATE_POINTS 1016
#define GEO_NODE_ATTRIBUTE_VECTOR_MATH 1017
/** \} */

View File

@ -4730,6 +4730,7 @@ static void registerGeometryNodes(void)
register_node_type_geo_attribute_compare();
register_node_type_geo_attribute_fill();
register_node_type_geo_attribute_vector_math();
register_node_type_geo_triangulate();
register_node_type_geo_edge_split();
register_node_type_geo_transform();

View File

@ -3228,6 +3228,35 @@ static void node_geometry_buts_attribute_math(uiLayout *layout,
uiItemR(layout, ptr, "input_type_b", DEFAULT_FLAGS, IFACE_("Type B"), ICON_NONE);
}
static void node_geometry_buts_attribute_vector_math(uiLayout *layout,
bContext *UNUSED(C),
PointerRNA *ptr)
{
bNode *node = (bNode *)ptr->data;
NodeAttributeVectorMath *node_storage = (NodeAttributeVectorMath *)node->storage;
uiItemR(layout, ptr, "operation", DEFAULT_FLAGS, "", ICON_NONE);
uiItemR(layout, ptr, "input_type_a", DEFAULT_FLAGS, IFACE_("Type A"), ICON_NONE);
/* These "use input b / c" checks are copied from the node's code. They could be deduplicated if
* the drawing code was moved to the node's file. */
if (!ELEM(node_storage->operation,
NODE_VECTOR_MATH_NORMALIZE,
NODE_VECTOR_MATH_FLOOR,
NODE_VECTOR_MATH_CEIL,
NODE_VECTOR_MATH_FRACTION,
NODE_VECTOR_MATH_ABSOLUTE,
NODE_VECTOR_MATH_SINE,
NODE_VECTOR_MATH_COSINE,
NODE_VECTOR_MATH_TANGENT,
NODE_VECTOR_MATH_LENGTH)) {
uiItemR(layout, ptr, "input_type_b", DEFAULT_FLAGS, IFACE_("Type B"), ICON_NONE);
}
if (ELEM(node_storage->operation, NODE_VECTOR_MATH_WRAP)) {
uiItemR(layout, ptr, "input_type_c", DEFAULT_FLAGS, IFACE_("Type C"), ICON_NONE);
}
}
static void node_geometry_buts_point_instance(uiLayout *layout,
bContext *UNUSED(C),
PointerRNA *ptr)
@ -3320,6 +3349,9 @@ static void node_geometry_set_butfunc(bNodeType *ntype)
case GEO_NODE_ATTRIBUTE_MIX:
ntype->draw_buttons = node_geometry_buts_attribute_mix;
break;
case GEO_NODE_ATTRIBUTE_VECTOR_MATH:
ntype->draw_buttons = node_geometry_buts_attribute_vector_math;
break;
case GEO_NODE_POINT_DISTRIBUTE:
ntype->draw_buttons = node_geometry_buts_attribute_point_distribute;
break;

View File

@ -1108,6 +1108,18 @@ typedef struct NodeAttributeMix {
uint8_t input_type_b;
} NodeAttributeMix;
typedef struct NodeAttributeVectorMath {
/* NodeVectorMathOperation */
uint8_t operation;
/* GeometryNodeAttributeInputMode */
uint8_t input_type_a;
uint8_t input_type_b;
uint8_t input_type_c;
char _pad[4];
} NodeAttributeVectorMath;
typedef struct NodeAttributeColorRamp {
ColorBand color_ramp;
} NodeAttributeColorRamp;
@ -1368,7 +1380,7 @@ enum {
};
/* Vector Math node operations. */
enum {
typedef enum NodeVectorMathOperation {
NODE_VECTOR_MATH_ADD = 0,
NODE_VECTOR_MATH_SUBTRACT = 1,
NODE_VECTOR_MATH_MULTIPLY = 2,
@ -1396,7 +1408,7 @@ enum {
NODE_VECTOR_MATH_SINE = 21,
NODE_VECTOR_MATH_COSINE = 22,
NODE_VECTOR_MATH_TANGENT = 23,
};
} NodeVectorMathOperation;
/* Boolean math node operations. */
enum {

View File

@ -426,59 +426,6 @@ static const EnumPropertyItem rna_node_geometry_triangulate_ngon_method_items[]
{0, NULL, 0, NULL, NULL},
};
# define ITEM_ATTRIBUTE \
{ \
GEO_NODE_ATTRIBUTE_INPUT_ATTRIBUTE, "ATTRIBUTE", 0, "Attribute", "" \
}
# define ITEM_FLOAT \
{ \
GEO_NODE_ATTRIBUTE_INPUT_FLOAT, "FLOAT", 0, "Float", "" \
}
# define ITEM_VECTOR \
{ \
GEO_NODE_ATTRIBUTE_INPUT_VECTOR, "VECTOR", 0, "Vector", "" \
}
# define ITEM_COLOR \
{ \
GEO_NODE_ATTRIBUTE_INPUT_COLOR, "COLOR", 0, "Color", "" \
}
# define ITEM_BOOLEAN \
{ \
GEO_NODE_ATTRIBUTE_INPUT_BOOLEAN, "BOOLEAN", 0, "Boolean", "" \
}
static const EnumPropertyItem rna_node_geometry_attribute_input_type_items_float[] = {
ITEM_ATTRIBUTE,
ITEM_FLOAT,
{0, NULL, 0, NULL, NULL},
};
static const EnumPropertyItem rna_node_geometry_attribute_input_type_items_vector[] = {
ITEM_ATTRIBUTE,
ITEM_VECTOR,
{0, NULL, 0, NULL, NULL},
};
static const EnumPropertyItem rna_node_geometry_attribute_input_type_items_no_boolean[] = {
ITEM_ATTRIBUTE,
ITEM_FLOAT,
ITEM_VECTOR,
ITEM_COLOR,
{0, NULL, 0, NULL, NULL},
};
static const EnumPropertyItem rna_node_geometry_attribute_input_type_items_any[] = {
ITEM_ATTRIBUTE,
ITEM_FLOAT,
ITEM_VECTOR,
ITEM_COLOR,
ITEM_BOOLEAN,
{0, NULL, 0, NULL, NULL},
};
# undef ITEM_ATTRIBUTE
# undef ITEM_FLOAT
# undef ITEM_VECTOR
# undef ITEM_COLOR
# undef ITEM_BOOLEAN
static const EnumPropertyItem rna_node_geometry_point_distribute_method_items[] = {
{GEO_NODE_POINT_DISTRIBUTE_RANDOM,
"RANDOM",
@ -495,6 +442,65 @@ static const EnumPropertyItem rna_node_geometry_point_distribute_method_items[]
#endif
#define ITEM_ATTRIBUTE \
{ \
GEO_NODE_ATTRIBUTE_INPUT_ATTRIBUTE, "ATTRIBUTE", 0, "Attribute", "" \
}
#define ITEM_FLOAT \
{ \
GEO_NODE_ATTRIBUTE_INPUT_FLOAT, "FLOAT", 0, "Float", "" \
}
#define ITEM_VECTOR \
{ \
GEO_NODE_ATTRIBUTE_INPUT_VECTOR, "VECTOR", 0, "Vector", "" \
}
#define ITEM_COLOR \
{ \
GEO_NODE_ATTRIBUTE_INPUT_COLOR, "COLOR", 0, "Color", "" \
}
#define ITEM_BOOLEAN \
{ \
GEO_NODE_ATTRIBUTE_INPUT_BOOLEAN, "BOOLEAN", 0, "Boolean", "" \
}
/* Used in both runtime and static code. */
static const EnumPropertyItem rna_node_geometry_attribute_input_type_items_any[] = {
ITEM_ATTRIBUTE,
ITEM_FLOAT,
ITEM_VECTOR,
ITEM_COLOR,
ITEM_BOOLEAN,
{0, NULL, 0, NULL, NULL},
};
#ifndef RNA_RUNTIME
static const EnumPropertyItem rna_node_geometry_attribute_input_type_items_vector[] = {
ITEM_ATTRIBUTE,
ITEM_VECTOR,
{0, NULL, 0, NULL, NULL},
};
static const EnumPropertyItem rna_node_geometry_attribute_input_type_items_float[] = {
ITEM_ATTRIBUTE,
ITEM_FLOAT,
{0, NULL, 0, NULL, NULL},
};
static const EnumPropertyItem rna_node_geometry_attribute_input_type_items_no_boolean[] = {
ITEM_ATTRIBUTE,
ITEM_FLOAT,
ITEM_VECTOR,
ITEM_COLOR,
{0, NULL, 0, NULL, NULL},
};
#endif
#undef ITEM_ATTRIBUTE
#undef ITEM_FLOAT
#undef ITEM_VECTOR
#undef ITEM_COLOR
#undef ITEM_BOOLEAN
#ifdef RNA_RUNTIME
# include "BLI_linklist.h"
@ -521,6 +527,8 @@ static const EnumPropertyItem rna_node_geometry_point_distribute_method_items[]
# include "DNA_scene_types.h"
# include "WM_api.h"
static void rna_Node_socket_update(Main *bmain, Scene *UNUSED(scene), PointerRNA *ptr);
int rna_node_tree_type_to_enum(bNodeTreeType *typeinfo)
{
int i = 0, result = -1;
@ -1971,6 +1979,69 @@ static const EnumPropertyItem *rna_GeometryNodeAttributeMath_operation_itemf(
return itemf_function_check(rna_enum_node_math_items, attribute_math_operation_supported);
}
/**
* This bit of ugly code makes sure the float / attribute option shows up instead of
* vector / attribute if the node uses an operation that uses a float for input B.
*/
static const EnumPropertyItem *rna_GeometryNodeAttributeVectorMath_input_type_b_itemf(
bContext *UNUSED(C), PointerRNA *ptr, PropertyRNA *UNUSED(prop), bool *r_free)
{
bNode *node = ptr->data;
NodeAttributeVectorMath *node_storage = (NodeAttributeVectorMath *)node->storage;
EnumPropertyItem *item_array = NULL;
int items_len = 0;
for (const EnumPropertyItem *item = rna_node_geometry_attribute_input_type_items_any;
item->identifier != NULL;
item++) {
if (item->value == GEO_NODE_ATTRIBUTE_INPUT_ATTRIBUTE) {
RNA_enum_item_add(&item_array, &items_len, item);
}
else if (item->value == GEO_NODE_ATTRIBUTE_INPUT_FLOAT) {
if (node_storage->operation == NODE_VECTOR_MATH_SCALE) {
RNA_enum_item_add(&item_array, &items_len, item);
}
}
else if (item->value == GEO_NODE_ATTRIBUTE_INPUT_VECTOR) {
if (node_storage->operation != NODE_VECTOR_MATH_SCALE) {
RNA_enum_item_add(&item_array, &items_len, item);
}
}
}
RNA_enum_item_end(&item_array, &items_len);
*r_free = true;
return item_array;
}
static void rna_GeometryNodeAttributeVectorMath_operation_update(Main *bmain,
Scene *scene,
PointerRNA *ptr)
{
bNode *node = ptr->data;
NodeAttributeVectorMath *node_storage = (NodeAttributeVectorMath *)node->storage;
const NodeVectorMathOperation operation = (NodeVectorMathOperation)node_storage->operation;
/* The scale operation can't use a vector input, so reset
* the input type enum in case it's set to vector. */
if (operation == NODE_VECTOR_MATH_SCALE) {
if (node_storage->input_type_b == GEO_NODE_ATTRIBUTE_INPUT_VECTOR) {
node_storage->input_type_b = GEO_NODE_ATTRIBUTE_INPUT_FLOAT;
}
}
/* Scale is also the only operation that uses the float input type, so a
* a check is also necessary for the other direction. */
if (operation != NODE_VECTOR_MATH_SCALE) {
if (node_storage->input_type_b == GEO_NODE_ATTRIBUTE_INPUT_FLOAT) {
node_storage->input_type_b = GEO_NODE_ATTRIBUTE_INPUT_VECTOR;
}
}
rna_Node_socket_update(bmain, scene, ptr);
}
static StructRNA *rna_ShaderNode_register(Main *bmain,
ReportList *reports,
void *data,
@ -8503,6 +8574,40 @@ static void def_geo_attribute_math(StructRNA *srna)
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
}
static void def_geo_attribute_vector_math(StructRNA *srna)
{
PropertyRNA *prop;
RNA_def_struct_sdna_from(srna, "NodeAttributeVectorMath", "storage");
prop = RNA_def_property(srna, "operation", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, NULL, "operation");
RNA_def_property_enum_items(prop, rna_enum_node_vec_math_items);
RNA_def_property_ui_text(prop, "Operation", "");
RNA_def_property_update(
prop, NC_NODE | NA_EDITED, "rna_GeometryNodeAttributeVectorMath_operation_update");
prop = RNA_def_property(srna, "input_type_a", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_bitflag_sdna(prop, NULL, "input_type_a");
RNA_def_property_enum_items(prop, rna_node_geometry_attribute_input_type_items_vector);
RNA_def_property_ui_text(prop, "Input Type A", "");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
prop = RNA_def_property(srna, "input_type_b", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_bitflag_sdna(prop, NULL, "input_type_b");
RNA_def_property_enum_items(prop, rna_node_geometry_attribute_input_type_items_any);
RNA_def_property_enum_funcs(
prop, NULL, NULL, "rna_GeometryNodeAttributeVectorMath_input_type_b_itemf");
RNA_def_property_ui_text(prop, "Input Type B", "");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
prop = RNA_def_property(srna, "input_type_c", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_bitflag_sdna(prop, NULL, "input_type_b");
RNA_def_property_enum_items(prop, rna_node_geometry_attribute_input_type_items_vector);
RNA_def_property_ui_text(prop, "Input Type B", "");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
}
static void def_geo_point_instance(StructRNA *srna)
{
static const EnumPropertyItem instance_type_items[] = {

View File

@ -146,6 +146,7 @@ set(SRC
geometry/nodes/node_geo_attribute_math.cc
geometry/nodes/node_geo_attribute_mix.cc
geometry/nodes/node_geo_attribute_randomize.cc
geometry/nodes/node_geo_attribute_vector_math.cc
geometry/nodes/node_geo_boolean.cc
geometry/nodes/node_geo_common.cc
geometry/nodes/node_geo_edge_split.cc

View File

@ -27,6 +27,7 @@ void register_node_tree_type_geo(void);
void register_node_type_geo_group(void);
void register_node_type_geo_attribute_fill(void);
void register_node_type_geo_attribute_vector_math(void);
void register_node_type_geo_boolean(void);
void register_node_type_geo_edge_split(void);
void register_node_type_geo_transform(void);

View File

@ -18,6 +18,7 @@
#include "DNA_node_types.h"
#include "BLI_float3.hh"
#include "BLI_math_base_safe.h"
#include "BLI_math_rotation.h"
#include "BLI_string_ref.hh"
@ -36,6 +37,7 @@ struct FloatMathOperationInfo {
};
const FloatMathOperationInfo *get_float_math_operation_info(const int operation);
const FloatMathOperationInfo *get_float3_math_operation_info(const int operation);
const FloatMathOperationInfo *get_float_compare_operation_info(const int operation);
/**
@ -231,4 +233,228 @@ inline bool try_dispatch_float_math_fl_fl_to_bool(const FloatCompareOperation op
return false;
}
/**
* This is similar to try_dispatch_float_math_fl_to_fl, just with a different callback signature.
*/
template<typename Callback>
inline bool try_dispatch_float_math_fl3_fl3_to_fl3(const NodeVectorMathOperation operation,
Callback &&callback)
{
const FloatMathOperationInfo *info = get_float3_math_operation_info(operation);
if (info == nullptr) {
return false;
}
/* This is just a utility function to keep the individual cases smaller. */
auto dispatch = [&](auto math_function) -> bool {
callback(math_function, *info);
return true;
};
switch (operation) {
case NODE_VECTOR_MATH_ADD:
return dispatch([](float3 a, float3 b) { return a + b; });
case NODE_VECTOR_MATH_SUBTRACT:
return dispatch([](float3 a, float3 b) { return a - b; });
case NODE_VECTOR_MATH_MULTIPLY:
return dispatch([](float3 a, float3 b) { return a * b; });
case NODE_VECTOR_MATH_DIVIDE:
return dispatch([](float3 a, float3 b) {
return float3(safe_divide(a.x, b.x), safe_divide(a.y, b.y), safe_divide(a.z, b.z));
});
case NODE_VECTOR_MATH_CROSS_PRODUCT:
return dispatch([](float3 a, float3 b) { return float3::cross_high_precision(a, b); });
case NODE_VECTOR_MATH_PROJECT:
return dispatch([](float3 a, float3 b) {
float length_squared = float3::dot(a, b);
return (length_squared != 0.0) ? (float3::dot(a, b) / length_squared) * b : float3(0.0f);
});
case NODE_VECTOR_MATH_REFLECT:
return dispatch([](float3 a, float3 b) {
b.normalize();
return a.reflected(b);
});
case NODE_VECTOR_MATH_SNAP:
return dispatch([](float3 a, float3 b) {
return float3(floor(safe_divide(a.x, b.x)),
floor(safe_divide(a.y, b.y)),
floor(safe_divide(a.z, b.z))) *
b;
});
case NODE_VECTOR_MATH_MODULO:
return dispatch([](float3 a, float3 b) {
return float3(safe_modf(a.x, b.x), safe_modf(a.y, b.y), safe_modf(a.z, b.z));
});
case NODE_VECTOR_MATH_MINIMUM:
return dispatch([](float3 a, float3 b) {
return float3(min_ff(a.x, b.x), min_ff(a.y, b.y), min_ff(a.z, b.z));
});
case NODE_VECTOR_MATH_MAXIMUM:
return dispatch([](float3 a, float3 b) {
return float3(max_ff(a.x, b.x), max_ff(a.y, b.y), max_ff(a.z, b.z));
});
default:
return false;
}
return false;
}
/**
* This is similar to try_dispatch_float_math_fl_to_fl, just with a different callback signature.
*/
template<typename Callback>
inline bool try_dispatch_float_math_fl3_fl3_to_fl(const NodeVectorMathOperation operation,
Callback &&callback)
{
const FloatMathOperationInfo *info = get_float3_math_operation_info(operation);
if (info == nullptr) {
return false;
}
/* This is just a utility function to keep the individual cases smaller. */
auto dispatch = [&](auto math_function) -> bool {
callback(math_function, *info);
return true;
};
switch (operation) {
case NODE_VECTOR_MATH_DOT_PRODUCT:
return dispatch([](float3 a, float3 b) { return float3::dot(a, b); });
case NODE_VECTOR_MATH_DISTANCE:
return dispatch([](float3 a, float3 b) { return float3::distance(a, b); });
default:
return false;
}
return false;
}
/**
* This is similar to try_dispatch_float_math_fl_to_fl, just with a different callback signature.
*/
template<typename Callback>
inline bool try_dispatch_float_math_fl3_fl3_fl3_to_fl3(const NodeVectorMathOperation operation,
Callback &&callback)
{
const FloatMathOperationInfo *info = get_float3_math_operation_info(operation);
if (info == nullptr) {
return false;
}
/* This is just a utility function to keep the individual cases smaller. */
auto dispatch = [&](auto math_function) -> bool {
callback(math_function, *info);
return true;
};
switch (operation) {
case NODE_VECTOR_MATH_WRAP:
return dispatch([](float3 a, float3 b, float3 c) {
return float3(wrapf(a.x, b.x, c.x), wrapf(a.y, b.y, c.y), wrapf(a.z, b.z, c.z));
});
default:
return false;
}
return false;
}
/**
* This is similar to try_dispatch_float_math_fl_to_fl, just with a different callback signature.
*/
template<typename Callback>
inline bool try_dispatch_float_math_fl3_to_fl(const NodeVectorMathOperation operation,
Callback &&callback)
{
const FloatMathOperationInfo *info = get_float3_math_operation_info(operation);
if (info == nullptr) {
return false;
}
/* This is just a utility function to keep the individual cases smaller. */
auto dispatch = [&](auto math_function) -> bool {
callback(math_function, *info);
return true;
};
switch (operation) {
case NODE_VECTOR_MATH_LENGTH:
return dispatch([](float3 in) { return in.length(); });
default:
return false;
}
return false;
}
/**
* This is similar to try_dispatch_float_math_fl_to_fl, just with a different callback signature.
*/
template<typename Callback>
inline bool try_dispatch_float_math_fl3_fl_to_fl3(const NodeVectorMathOperation operation,
Callback &&callback)
{
const FloatMathOperationInfo *info = get_float3_math_operation_info(operation);
if (info == nullptr) {
return false;
}
/* This is just a utility function to keep the individual cases smaller. */
auto dispatch = [&](auto math_function) -> bool {
callback(math_function, *info);
return true;
};
switch (operation) {
case NODE_VECTOR_MATH_SCALE:
return dispatch([](float3 a, float b) { return a * b; });
default:
return false;
}
return false;
}
/**
* This is similar to try_dispatch_float_math_fl_to_fl, just with a different callback signature.
*/
template<typename Callback>
inline bool try_dispatch_float_math_fl3_to_fl3(const NodeVectorMathOperation operation,
Callback &&callback)
{
const FloatMathOperationInfo *info = get_float3_math_operation_info(operation);
if (info == nullptr) {
return false;
}
/* This is just a utility function to keep the individual cases smaller. */
auto dispatch = [&](auto math_function) -> bool {
callback(math_function, *info);
return true;
};
switch (operation) {
case NODE_VECTOR_MATH_NORMALIZE:
return dispatch([](float3 in) {
float3 out = in;
out.normalize();
return out;
}); /* Should be safe. */
case NODE_VECTOR_MATH_FLOOR:
return dispatch([](float3 in) { return float3(floor(in.x), floor(in.y), floor(in.z)); });
case NODE_VECTOR_MATH_CEIL:
return dispatch([](float3 in) { return float3(ceil(in.x), ceil(in.y), ceil(in.z)); });
case NODE_VECTOR_MATH_FRACTION:
return dispatch(
[](float3 in) { return in - float3(floor(in.x), floor(in.y), floor(in.z)); });
case NODE_VECTOR_MATH_ABSOLUTE:
return dispatch([](float3 in) { return float3::abs(in); });
case NODE_VECTOR_MATH_SINE:
return dispatch([](float3 in) { return float3(sinf(in.x), sinf(in.y), sinf(in.z)); });
case NODE_VECTOR_MATH_COSINE:
return dispatch([](float3 in) { return float3(cosf(in.x), cosf(in.y), cosf(in.z)); });
case NODE_VECTOR_MATH_TANGENT:
return dispatch([](float3 in) { return float3(tanf(in.x), tanf(in.y), tanf(in.z)); });
default:
return false;
}
return false;
}
} // namespace blender::nodes

View File

@ -285,6 +285,7 @@ DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_COLOR_RAMP, def_geo_attribute_color_ram
DefNode(GeometryNode, GEO_NODE_POINT_SEPARATE, 0, "POINT_SEPARATE", PointSeparate, "Point Separate", "")
DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_COMPARE, def_geo_attribute_attribute_compare, "ATTRIBUTE_COMPARE", AttributeCompare, "Attribute Compare", "")
DefNode(GeometryNode, GEO_NODE_ROTATE_POINTS, def_geo_rotate_points, "ROTATE_POINTS", RotatePoints, "Rotate Points", "")
DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_VECTOR_MATH, def_geo_attribute_vector_math, "ATTRIBUTE_VECTOR_MATH", AttributeVectorMath, "Attribute Vector Math", "")
/* undefine macros */
#undef DefNode

View File

@ -0,0 +1,432 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "node_geometry_util.hh"
#include "BKE_attribute.h"
#include "BKE_attribute_access.hh"
#include "BLI_array.hh"
#include "BLI_math_base_safe.h"
#include "BLI_rand.hh"
#include "DNA_mesh_types.h"
#include "DNA_pointcloud_types.h"
#include "NOD_math_functions.hh"
static bNodeSocketTemplate geo_node_attribute_vector_math_in[] = {
{SOCK_GEOMETRY, N_("Geometry")},
{SOCK_STRING, N_("A")},
{SOCK_VECTOR, N_("A"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX},
{SOCK_STRING, N_("B")},
{SOCK_VECTOR, N_("B"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX},
{SOCK_FLOAT, N_("B"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX},
{SOCK_STRING, N_("C")},
{SOCK_VECTOR, N_("C"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX},
{SOCK_STRING, N_("Result")},
{-1, ""},
};
static bNodeSocketTemplate geo_node_attribute_vector_math_out[] = {
{SOCK_GEOMETRY, N_("Geometry")},
{-1, ""},
};
static void geo_node_attribute_vector_math_init(bNodeTree *UNUSED(tree), bNode *node)
{
NodeAttributeVectorMath *data = (NodeAttributeVectorMath *)MEM_callocN(
sizeof(NodeAttributeVectorMath), __func__);
data->operation = NODE_VECTOR_MATH_ADD;
data->input_type_a = GEO_NODE_ATTRIBUTE_INPUT_ATTRIBUTE;
data->input_type_b = GEO_NODE_ATTRIBUTE_INPUT_ATTRIBUTE;
node->storage = data;
}
static CustomDataType operation_get_read_type_b(const NodeVectorMathOperation operation)
{
if (operation == NODE_VECTOR_MATH_SCALE) {
return CD_PROP_FLOAT;
}
return CD_PROP_FLOAT3;
}
static bool operation_use_input_b(const NodeVectorMathOperation operation)
{
return !ELEM(operation,
NODE_VECTOR_MATH_NORMALIZE,
NODE_VECTOR_MATH_FLOOR,
NODE_VECTOR_MATH_CEIL,
NODE_VECTOR_MATH_FRACTION,
NODE_VECTOR_MATH_ABSOLUTE,
NODE_VECTOR_MATH_SINE,
NODE_VECTOR_MATH_COSINE,
NODE_VECTOR_MATH_TANGENT,
NODE_VECTOR_MATH_LENGTH);
}
static bool operation_use_input_c(const NodeVectorMathOperation operation)
{
return operation == NODE_VECTOR_MATH_WRAP;
}
static CustomDataType operation_get_result_type(const NodeVectorMathOperation operation)
{
switch (operation) {
case NODE_VECTOR_MATH_ADD:
case NODE_VECTOR_MATH_SUBTRACT:
case NODE_VECTOR_MATH_MULTIPLY:
case NODE_VECTOR_MATH_DIVIDE:
case NODE_VECTOR_MATH_CROSS_PRODUCT:
case NODE_VECTOR_MATH_PROJECT:
case NODE_VECTOR_MATH_REFLECT:
case NODE_VECTOR_MATH_SCALE:
case NODE_VECTOR_MATH_NORMALIZE:
case NODE_VECTOR_MATH_SNAP:
case NODE_VECTOR_MATH_FLOOR:
case NODE_VECTOR_MATH_CEIL:
case NODE_VECTOR_MATH_MODULO:
case NODE_VECTOR_MATH_FRACTION:
case NODE_VECTOR_MATH_ABSOLUTE:
case NODE_VECTOR_MATH_MINIMUM:
case NODE_VECTOR_MATH_MAXIMUM:
case NODE_VECTOR_MATH_WRAP:
case NODE_VECTOR_MATH_SINE:
case NODE_VECTOR_MATH_COSINE:
case NODE_VECTOR_MATH_TANGENT:
return CD_PROP_FLOAT3;
case NODE_VECTOR_MATH_DOT_PRODUCT:
case NODE_VECTOR_MATH_DISTANCE:
case NODE_VECTOR_MATH_LENGTH:
return CD_PROP_FLOAT;
}
BLI_assert(false);
return CD_PROP_FLOAT3;
}
namespace blender::nodes {
static void geo_node_attribute_vector_math_update(bNodeTree *UNUSED(ntree), bNode *node)
{
const NodeAttributeVectorMath *node_storage = (NodeAttributeVectorMath *)node->storage;
const NodeVectorMathOperation operation = (const NodeVectorMathOperation)node_storage->operation;
update_attribute_input_socket_availabilities(
*node, "A", (GeometryNodeAttributeInputMode)node_storage->input_type_a);
update_attribute_input_socket_availabilities(
*node,
"B",
(GeometryNodeAttributeInputMode)node_storage->input_type_b,
operation_use_input_b(operation));
update_attribute_input_socket_availabilities(
*node,
"C",
(GeometryNodeAttributeInputMode)node_storage->input_type_c,
operation_use_input_c(operation));
}
static void do_math_operation_fl3_fl3_to_fl3(const Float3ReadAttribute &input_a,
const Float3ReadAttribute &input_b,
Float3WriteAttribute result,
const NodeVectorMathOperation operation)
{
const int size = input_a.size();
Span<float3> span_a = input_a.get_span();
Span<float3> span_b = input_b.get_span();
MutableSpan<float3> span_result = result.get_span();
bool success = try_dispatch_float_math_fl3_fl3_to_fl3(
operation, [&](auto math_function, const FloatMathOperationInfo &UNUSED(info)) {
for (const int i : IndexRange(size)) {
const float3 a = span_a[i];
const float3 b = span_b[i];
const float3 out = math_function(a, b);
span_result[i] = out;
}
});
result.apply_span();
/* The operation is not supported by this node currently. */
BLI_assert(success);
UNUSED_VARS_NDEBUG(success);
}
static void do_math_operation_fl3_fl3_fl3_to_fl3(const Float3ReadAttribute &input_a,
const Float3ReadAttribute &input_b,
const Float3ReadAttribute &input_c,
Float3WriteAttribute result,
const NodeVectorMathOperation operation)
{
const int size = input_a.size();
Span<float3> span_a = input_a.get_span();
Span<float3> span_b = input_b.get_span();
Span<float3> span_c = input_c.get_span();
MutableSpan<float3> span_result = result.get_span();
bool success = try_dispatch_float_math_fl3_fl3_fl3_to_fl3(
operation, [&](auto math_function, const FloatMathOperationInfo &UNUSED(info)) {
for (const int i : IndexRange(size)) {
const float3 a = span_a[i];
const float3 b = span_b[i];
const float3 c = span_c[i];
const float3 out = math_function(a, b, c);
span_result[i] = out;
}
});
result.apply_span();
/* The operation is not supported by this node currently. */
BLI_assert(success);
UNUSED_VARS_NDEBUG(success);
}
static void do_math_operation_fl3_fl3_to_fl(const Float3ReadAttribute &input_a,
const Float3ReadAttribute &input_b,
FloatWriteAttribute result,
const NodeVectorMathOperation operation)
{
const int size = input_a.size();
Span<float3> span_a = input_a.get_span();
Span<float3> span_b = input_b.get_span();
MutableSpan<float> span_result = result.get_span();
bool success = try_dispatch_float_math_fl3_fl3_to_fl(
operation, [&](auto math_function, const FloatMathOperationInfo &UNUSED(info)) {
for (const int i : IndexRange(size)) {
const float3 a = span_a[i];
const float3 b = span_b[i];
const float out = math_function(a, b);
span_result[i] = out;
}
});
result.apply_span();
/* The operation is not supported by this node currently. */
BLI_assert(success);
UNUSED_VARS_NDEBUG(success);
}
static void do_math_operation_fl3_fl_to_fl3(const Float3ReadAttribute &input_a,
const FloatReadAttribute &input_b,
Float3WriteAttribute result,
const NodeVectorMathOperation operation)
{
const int size = input_a.size();
Span<float3> span_a = input_a.get_span();
Span<float> span_b = input_b.get_span();
MutableSpan<float3> span_result = result.get_span();
bool success = try_dispatch_float_math_fl3_fl_to_fl3(
operation, [&](auto math_function, const FloatMathOperationInfo &UNUSED(info)) {
for (const int i : IndexRange(size)) {
const float3 a = span_a[i];
const float b = span_b[i];
const float3 out = math_function(a, b);
span_result[i] = out;
}
});
result.apply_span();
/* The operation is not supported by this node currently. */
BLI_assert(success);
UNUSED_VARS_NDEBUG(success);
}
static void do_math_operation_fl3_to_fl3(const Float3ReadAttribute &input_a,
Float3WriteAttribute result,
const NodeVectorMathOperation operation)
{
const int size = input_a.size();
Span<float3> span_a = input_a.get_span();
MutableSpan<float3> span_result = result.get_span();
bool success = try_dispatch_float_math_fl3_to_fl3(
operation, [&](auto math_function, const FloatMathOperationInfo &UNUSED(info)) {
for (const int i : IndexRange(size)) {
const float3 in = span_a[i];
const float3 out = math_function(in);
span_result[i] = out;
}
});
result.apply_span();
/* The operation is not supported by this node currently. */
BLI_assert(success);
UNUSED_VARS_NDEBUG(success);
}
static void do_math_operation_fl3_to_fl(const Float3ReadAttribute &input_a,
FloatWriteAttribute result,
const NodeVectorMathOperation operation)
{
const int size = input_a.size();
Span<float3> span_a = input_a.get_span();
MutableSpan<float> span_result = result.get_span();
bool success = try_dispatch_float_math_fl3_to_fl(
operation, [&](auto math_function, const FloatMathOperationInfo &UNUSED(info)) {
for (const int i : IndexRange(size)) {
const float3 in = span_a[i];
const float out = math_function(in);
span_result[i] = out;
}
});
result.apply_span();
/* The operation is not supported by this node currently. */
BLI_assert(success);
UNUSED_VARS_NDEBUG(success);
}
static void attribute_vector_math_calc(GeometryComponent &component,
const GeoNodeExecParams &params)
{
const bNode &node = params.node();
const NodeAttributeVectorMath *node_storage = (const NodeAttributeVectorMath *)node.storage;
const NodeVectorMathOperation operation = (NodeVectorMathOperation)node_storage->operation;
/* The number and type of the input attribute depend on the operation. */
const CustomDataType read_type_a = CD_PROP_FLOAT3;
const bool use_input_b = operation_use_input_b(operation);
const CustomDataType read_type_b = operation_get_read_type_b(operation);
const bool use_input_c = operation_use_input_c(operation);
const CustomDataType read_type_c = CD_PROP_FLOAT3;
/* The result domain is always point for now. */
const CustomDataType result_type = operation_get_result_type(operation);
const AttributeDomain result_domain = ATTR_DOMAIN_POINT;
ReadAttributePtr attribute_a = params.get_input_attribute(
"A", component, result_domain, read_type_a, nullptr);
if (!attribute_a) {
return;
}
ReadAttributePtr attribute_b;
ReadAttributePtr attribute_c;
if (use_input_b) {
attribute_b = params.get_input_attribute("B", component, result_domain, read_type_b, nullptr);
if (!attribute_b) {
return;
}
}
if (use_input_c) {
attribute_c = params.get_input_attribute("C", component, result_domain, read_type_c, nullptr);
if (!attribute_c) {
return;
}
}
/* Get result attribute first, in case it has to overwrite one of the existing attributes. */
const std::string result_name = params.get_input<std::string>("Result");
WriteAttributePtr attribute_result = component.attribute_try_ensure_for_write(
result_name, result_domain, result_type);
if (!attribute_result) {
return;
}
switch (operation) {
case NODE_VECTOR_MATH_ADD:
case NODE_VECTOR_MATH_SUBTRACT:
case NODE_VECTOR_MATH_MULTIPLY:
case NODE_VECTOR_MATH_DIVIDE:
case NODE_VECTOR_MATH_CROSS_PRODUCT:
case NODE_VECTOR_MATH_PROJECT:
case NODE_VECTOR_MATH_REFLECT:
case NODE_VECTOR_MATH_SNAP:
case NODE_VECTOR_MATH_MODULO:
case NODE_VECTOR_MATH_MINIMUM:
case NODE_VECTOR_MATH_MAXIMUM:
do_math_operation_fl3_fl3_to_fl3(
std::move(attribute_a), std::move(attribute_b), std::move(attribute_result), operation);
break;
case NODE_VECTOR_MATH_DOT_PRODUCT:
case NODE_VECTOR_MATH_DISTANCE:
do_math_operation_fl3_fl3_to_fl(
std::move(attribute_a), std::move(attribute_b), std::move(attribute_result), operation);
break;
case NODE_VECTOR_MATH_LENGTH:
do_math_operation_fl3_to_fl(std::move(attribute_a), std::move(attribute_result), operation);
break;
case NODE_VECTOR_MATH_SCALE:
do_math_operation_fl3_fl_to_fl3(
std::move(attribute_a), std::move(attribute_b), std::move(attribute_result), operation);
break;
case NODE_VECTOR_MATH_NORMALIZE:
case NODE_VECTOR_MATH_FLOOR:
case NODE_VECTOR_MATH_CEIL:
case NODE_VECTOR_MATH_FRACTION:
case NODE_VECTOR_MATH_ABSOLUTE:
case NODE_VECTOR_MATH_SINE:
case NODE_VECTOR_MATH_COSINE:
case NODE_VECTOR_MATH_TANGENT:
do_math_operation_fl3_to_fl3(std::move(attribute_a), std::move(attribute_result), operation);
break;
case NODE_VECTOR_MATH_WRAP:
do_math_operation_fl3_fl3_fl3_to_fl3(std::move(attribute_a),
std::move(attribute_b),
std::move(attribute_c),
std::move(attribute_result),
operation);
break;
}
}
static void geo_node_attribute_vector_math_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
if (geometry_set.has<MeshComponent>()) {
attribute_vector_math_calc(geometry_set.get_component_for_write<MeshComponent>(), params);
}
if (geometry_set.has<PointCloudComponent>()) {
attribute_vector_math_calc(geometry_set.get_component_for_write<PointCloudComponent>(),
params);
}
params.set_output("Geometry", geometry_set);
}
} // namespace blender::nodes
void register_node_type_geo_attribute_vector_math()
{
static bNodeType ntype;
geo_node_type_base(
&ntype, GEO_NODE_ATTRIBUTE_VECTOR_MATH, "Attribute Vector Math", NODE_CLASS_ATTRIBUTE, 0);
node_type_socket_templates(
&ntype, geo_node_attribute_vector_math_in, geo_node_attribute_vector_math_out);
ntype.geometry_node_execute = blender::nodes::geo_node_attribute_vector_math_exec;
node_type_update(&ntype, blender::nodes::geo_node_attribute_vector_math_update);
node_type_init(&ntype, geo_node_attribute_vector_math_init);
node_type_storage(
&ntype, "NodeAttributeVectorMath", node_free_standard_storage, node_copy_standard_storage);
nodeRegisterType(&ntype);
}

View File

@ -146,4 +146,70 @@ const FloatMathOperationInfo *get_float_compare_operation_info(const int operati
return nullptr;
}
const FloatMathOperationInfo *get_float3_math_operation_info(const int operation)
{
#define RETURN_OPERATION_INFO(title_case_name, shader_name) \
{ \
static const FloatMathOperationInfo info{title_case_name, shader_name}; \
return &info; \
} \
((void)0)
switch (operation) {
case NODE_VECTOR_MATH_ADD:
RETURN_OPERATION_INFO("Add", "vector_math_add");
case NODE_VECTOR_MATH_SUBTRACT:
RETURN_OPERATION_INFO("Subtract", "vector_math_subtract");
case NODE_VECTOR_MATH_MULTIPLY:
RETURN_OPERATION_INFO("Multiply", "vector_math_multiply");
case NODE_VECTOR_MATH_DIVIDE:
RETURN_OPERATION_INFO("Divide", "vector_math_divide");
case NODE_VECTOR_MATH_CROSS_PRODUCT:
RETURN_OPERATION_INFO("Cross Product", "vector_math_cross");
case NODE_VECTOR_MATH_PROJECT:
RETURN_OPERATION_INFO("Project", "vector_math_project");
case NODE_VECTOR_MATH_REFLECT:
RETURN_OPERATION_INFO("Reflect", "vector_math_reflect");
case NODE_VECTOR_MATH_DOT_PRODUCT:
RETURN_OPERATION_INFO("Dot Product", "vector_math_dot");
case NODE_VECTOR_MATH_DISTANCE:
RETURN_OPERATION_INFO("Distance", "vector_math_distance");
case NODE_VECTOR_MATH_LENGTH:
RETURN_OPERATION_INFO("Length", "vector_math_length");
case NODE_VECTOR_MATH_SCALE:
RETURN_OPERATION_INFO("Scale", "vector_math_scale");
case NODE_VECTOR_MATH_NORMALIZE:
RETURN_OPERATION_INFO("Normalize", "vector_math_normalize");
case NODE_VECTOR_MATH_SNAP:
RETURN_OPERATION_INFO("Snap", "vector_math_snap");
case NODE_VECTOR_MATH_FLOOR:
RETURN_OPERATION_INFO("Floor", "vector_math_floor");
case NODE_VECTOR_MATH_CEIL:
RETURN_OPERATION_INFO("Ceiling", "vector_math_ceil");
case NODE_VECTOR_MATH_MODULO:
RETURN_OPERATION_INFO("Modulo", "vector_math_modulo");
case NODE_VECTOR_MATH_FRACTION:
RETURN_OPERATION_INFO("Fraction", "vector_math_fraction");
case NODE_VECTOR_MATH_ABSOLUTE:
RETURN_OPERATION_INFO("Absolute", "vector_math_absolute");
case NODE_VECTOR_MATH_MINIMUM:
RETURN_OPERATION_INFO("Minimum", "vector_math_minimum");
case NODE_VECTOR_MATH_MAXIMUM:
RETURN_OPERATION_INFO("Maximum", "vector_math_maximum");
case NODE_VECTOR_MATH_WRAP:
RETURN_OPERATION_INFO("Wrap", "vector_math_wrap");
case NODE_VECTOR_MATH_SINE:
RETURN_OPERATION_INFO("Sine", "vector_math_sine");
case NODE_VECTOR_MATH_COSINE:
RETURN_OPERATION_INFO("Cosine", "vector_math_cosine");
case NODE_VECTOR_MATH_TANGENT:
RETURN_OPERATION_INFO("Tangent", "vector_math_tangent");
}
#undef RETURN_OPERATION_INFO
return nullptr;
}
} // namespace blender::nodes