Geometry Nodes: Add Attribute Vector Rotate node

Port vector rotate node to geo attributes.

Request by @simonthommes

Reviewed By: simonthommes, HooglyBoogly
This commit is contained in:
Charlie Jolly 2021-05-11 10:53:13 +01:00 committed by Charlie Jolly
parent fc5bf09fd8
commit 93933ee8bb
9 changed files with 437 additions and 0 deletions

View File

@ -494,6 +494,7 @@ geometry_node_categories = [
NodeItem("GeometryNodeAttributeProximity"),
NodeItem("GeometryNodeAttributeColorRamp"),
NodeItem("GeometryNodeAttributeVectorMath"),
NodeItem("GeometryNodeAttributeVectorRotate"),
NodeItem("GeometryNodeAttributeSampleTexture"),
NodeItem("GeometryNodeAttributeCombineXYZ"),
NodeItem("GeometryNodeAttributeSeparateXYZ"),

View File

@ -1421,6 +1421,7 @@ int ntreeTexExecTree(struct bNodeTree *ntree,
#define GEO_NODE_CURVE_TO_MESH 1045
#define GEO_NODE_ATTRIBUTE_CURVE_MAP 1046
#define GEO_NODE_CURVE_RESAMPLE 1047
#define GEO_NODE_ATTRIBUTE_VECTOR_ROTATE 1048
/** \} */

View File

@ -4962,6 +4962,7 @@ static void registerGeometryNodes()
register_node_type_geo_attribute_separate_xyz();
register_node_type_geo_attribute_transfer();
register_node_type_geo_attribute_vector_math();
register_node_type_geo_attribute_vector_rotate();
register_node_type_geo_attribute_remove();
register_node_type_geo_boolean();
register_node_type_geo_bounding_box();

View File

@ -1184,6 +1184,19 @@ typedef struct NodeAttributeVectorMath {
uint8_t input_type_c;
} NodeAttributeVectorMath;
typedef struct NodeAttributeVectorRotate {
/* GeometryNodeAttributeVectorRotateMode */
uint8_t mode;
/* GeometryNodeAttributeInputMode */
uint8_t input_type_vector;
uint8_t input_type_center;
uint8_t input_type_axis;
uint8_t input_type_angle;
uint8_t input_type_rotation;
char _pad[2];
} NodeAttributeVectorRotate;
typedef struct NodeAttributeColorRamp {
ColorBand color_ramp;
} NodeAttributeColorRamp;
@ -1775,6 +1788,14 @@ typedef enum GeometryNodeRotatePointsType {
GEO_NODE_POINT_ROTATE_TYPE_AXIS_ANGLE = 1,
} GeometryNodeRotatePointsType;
typedef enum GeometryNodeAttributeVectorRotateMode {
GEO_NODE_VECTOR_ROTATE_TYPE_AXIS = 0,
GEO_NODE_VECTOR_ROTATE_TYPE_AXIS_X = 1,
GEO_NODE_VECTOR_ROTATE_TYPE_AXIS_Y = 2,
GEO_NODE_VECTOR_ROTATE_TYPE_AXIS_Z = 3,
GEO_NODE_VECTOR_ROTATE_TYPE_EULER_XYZ = 4,
} GeometryNodeAttributeVectorRotateMode;
typedef enum GeometryNodeAttributeRandomizeMode {
GEO_NODE_ATTRIBUTE_RANDOMIZE_REPLACE_CREATE = 0,
GEO_NODE_ATTRIBUTE_RANDOMIZE_ADD = 1,

View File

@ -9297,6 +9297,61 @@ static void def_geo_attribute_curve_map(StructRNA *srna)
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
}
static void def_geo_attribute_vector_rotate(StructRNA *srna)
{
static const EnumPropertyItem rotate_mode_items[] = {
{GEO_NODE_VECTOR_ROTATE_TYPE_AXIS,
"AXIS_ANGLE",
0,
"Axis Angle",
"Rotate a point using axis angle"},
{GEO_NODE_VECTOR_ROTATE_TYPE_AXIS_X, "X_AXIS", 0, "X Axis", "Rotate a point using X axis"},
{GEO_NODE_VECTOR_ROTATE_TYPE_AXIS_Y, "Y_AXIS", 0, "Y Axis", "Rotate a point using Y axis"},
{GEO_NODE_VECTOR_ROTATE_TYPE_AXIS_Z, "Z_AXIS", 0, "Z Axis", "Rotate a point using Z axis"},
{GEO_NODE_VECTOR_ROTATE_TYPE_EULER_XYZ,
"EULER_XYZ",
0,
"Euler",
"Rotate a point using XYZ order"},
{0, NULL, 0, NULL, NULL},
};
PropertyRNA *prop;
RNA_def_struct_sdna_from(srna, "NodeAttributeVectorRotate", "storage");
prop = RNA_def_property(srna, "rotation_mode", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, NULL, "mode");
RNA_def_property_enum_items(prop, rotate_mode_items);
RNA_def_property_ui_text(prop, "Mode", "Type of rotation");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_ShaderNode_socket_update");
prop = RNA_def_property(srna, "input_type_vector", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_items(prop, rna_node_geometry_attribute_input_type_items_vector);
RNA_def_property_ui_text(prop, "Input Type Vector", "");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
prop = RNA_def_property(srna, "input_type_center", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_items(prop, rna_node_geometry_attribute_input_type_items_vector);
RNA_def_property_ui_text(prop, "Input Type Center", "");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
prop = RNA_def_property(srna, "input_type_axis", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_items(prop, rna_node_geometry_attribute_input_type_items_vector);
RNA_def_property_ui_text(prop, "Input Type Axis", "");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
prop = RNA_def_property(srna, "input_type_angle", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_items(prop, rna_node_geometry_attribute_input_type_items_float);
RNA_def_property_ui_text(prop, "Input Type Angle", "");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
prop = RNA_def_property(srna, "input_type_rotation", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_items(prop, rna_node_geometry_attribute_input_type_items_vector);
RNA_def_property_ui_text(prop, "Input Type Rotation", "");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
}
static void def_geo_point_rotate(StructRNA *srna)
{
static const EnumPropertyItem type_items[] = {

View File

@ -157,6 +157,7 @@ set(SRC
geometry/nodes/node_geo_attribute_separate_xyz.cc
geometry/nodes/node_geo_attribute_transfer.cc
geometry/nodes/node_geo_attribute_vector_math.cc
geometry/nodes/node_geo_attribute_vector_rotate.cc
geometry/nodes/node_geo_boolean.cc
geometry/nodes/node_geo_bounding_box.cc
geometry/nodes/node_geo_collection_info.cc

View File

@ -45,6 +45,7 @@ void register_node_type_geo_attribute_randomize(void);
void register_node_type_geo_attribute_separate_xyz(void);
void register_node_type_geo_attribute_transfer(void);
void register_node_type_geo_attribute_vector_math(void);
void register_node_type_geo_attribute_vector_rotate(void);
void register_node_type_geo_attribute_remove(void);
void register_node_type_geo_boolean(void);
void register_node_type_geo_bounding_box(void);

View File

@ -285,6 +285,7 @@ DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_SAMPLE_TEXTURE, def_geo_attribute_sampl
DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_SEPARATE_XYZ, def_geo_attribute_separate_xyz, "ATTRIBUTE_SEPARATE_XYZ", AttributeSeparateXYZ, "Attribute Separate XYZ", "")
DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_TRANSFER, def_geo_attribute_transfer, "ATTRIBUTE_TRANSFER", AttributeTransfer, "Attribute Transfer", "")
DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_VECTOR_MATH, def_geo_attribute_vector_math, "ATTRIBUTE_VECTOR_MATH", AttributeVectorMath, "Attribute Vector Math", "")
DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_VECTOR_ROTATE, def_geo_attribute_vector_rotate, "ATTRIBUTE_VECTOR_ROTATE", AttributeVectorRotate, "Attribute Vector Rotate", "")
DefNode(GeometryNode, GEO_NODE_BOOLEAN, def_geo_boolean, "BOOLEAN", Boolean, "Boolean", "")
DefNode(GeometryNode, GEO_NODE_BOUNDING_BOX, 0, "BOUNDING_BOX", BoundBox, "Bounding Box", "")
DefNode(GeometryNode, GEO_NODE_COLLECTION_INFO, def_geo_collection_info, "COLLECTION_INFO", CollectionInfo, "Collection Info", "")

View File

@ -0,0 +1,355 @@
/*
* 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 "BLI_task.hh"
#include "UI_interface.h"
#include "UI_resources.h"
static bNodeSocketTemplate geo_node_attribute_vector_rotate_in[] = {
{SOCK_GEOMETRY, N_("Geometry")},
{SOCK_STRING, N_("Vector")},
{SOCK_VECTOR, N_("Vector"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_NONE, SOCK_HIDE_VALUE},
{SOCK_STRING, N_("Center")},
{SOCK_VECTOR, N_("Center"), 0.0f, 0.0f, 0.0f, 1.0f, -FLT_MAX, FLT_MAX, PROP_XYZ},
{SOCK_STRING, N_("Axis")},
{SOCK_VECTOR, N_("Axis"), 0.0f, 0.0f, 1.0f, 0.0f, -1.0f, 1.0f, PROP_XYZ, PROP_NONE},
{SOCK_STRING, N_("Angle")},
{SOCK_FLOAT, N_("Angle"), 0.0f, 0.0f, 0.0f, 1.0f, -FLT_MAX, FLT_MAX, PROP_ANGLE, PROP_NONE},
{SOCK_STRING, N_("Rotation")},
{SOCK_VECTOR, N_("Rotation"), 0.0f, 0.0f, 0.0f, 1.0f, -FLT_MAX, FLT_MAX, PROP_EULER},
{SOCK_BOOLEAN, N_("Invert")},
{SOCK_STRING, N_("Result")},
{-1, ""},
};
static bNodeSocketTemplate geo_node_attribute_vector_rotate_out[] = {
{SOCK_GEOMETRY, N_("Geometry")},
{-1, ""},
};
static void geo_node_attribute_vector_rotate_layout(uiLayout *layout,
bContext *UNUSED(C),
PointerRNA *ptr)
{
bNode *node = (bNode *)ptr->data;
const NodeAttributeVectorRotate &node_storage = *(NodeAttributeVectorRotate *)node->storage;
const GeometryNodeAttributeVectorRotateMode mode = (const GeometryNodeAttributeVectorRotateMode)
node_storage.mode;
uiLayoutSetPropSep(layout, true);
uiLayoutSetPropDecorate(layout, false);
uiLayout *column = uiLayoutColumn(layout, false);
uiItemR(column, ptr, "rotation_mode", 0, "", ICON_NONE);
uiItemR(column, ptr, "input_type_vector", 0, IFACE_("Vector"), ICON_NONE);
uiItemR(column, ptr, "input_type_center", 0, IFACE_("Center"), ICON_NONE);
if (mode == GEO_NODE_VECTOR_ROTATE_TYPE_AXIS) {
uiItemR(column, ptr, "input_type_axis", 0, IFACE_("Axis"), ICON_NONE);
}
if (mode != GEO_NODE_VECTOR_ROTATE_TYPE_EULER_XYZ) {
uiItemR(column, ptr, "input_type_angle", 0, IFACE_("Angle"), ICON_NONE);
}
if (mode == GEO_NODE_VECTOR_ROTATE_TYPE_EULER_XYZ) {
uiItemR(column, ptr, "input_type_rotation", 0, IFACE_("Rotation"), ICON_NONE);
}
}
namespace blender::nodes {
static void geo_node_attribute_vector_rotate_update(bNodeTree *UNUSED(ntree), bNode *node)
{
const NodeAttributeVectorRotate *node_storage = (NodeAttributeVectorRotate *)node->storage;
const GeometryNodeAttributeVectorRotateMode mode = (const GeometryNodeAttributeVectorRotateMode)
node_storage->mode;
update_attribute_input_socket_availabilities(
*node, "Vector", (GeometryNodeAttributeInputMode)node_storage->input_type_vector);
update_attribute_input_socket_availabilities(
*node, "Center", (GeometryNodeAttributeInputMode)node_storage->input_type_center);
update_attribute_input_socket_availabilities(
*node,
"Axis",
(GeometryNodeAttributeInputMode)node_storage->input_type_axis,
(mode == GEO_NODE_VECTOR_ROTATE_TYPE_AXIS));
update_attribute_input_socket_availabilities(
*node,
"Angle",
(GeometryNodeAttributeInputMode)node_storage->input_type_angle,
(mode != GEO_NODE_VECTOR_ROTATE_TYPE_EULER_XYZ));
update_attribute_input_socket_availabilities(
*node,
"Rotation",
(GeometryNodeAttributeInputMode)node_storage->input_type_rotation,
(mode == GEO_NODE_VECTOR_ROTATE_TYPE_EULER_XYZ));
}
static float3 vector_rotate_around_axis(const float3 vector,
const float3 center,
const float3 axis,
const float angle)
{
float3 result = vector - center;
float mat[3][3];
axis_angle_to_mat3(mat, axis, angle);
mul_m3_v3(mat, result);
return result + center;
}
static void geo_node_attribute_vector_rotate_init(bNodeTree *UNUSED(ntree), bNode *node)
{
NodeAttributeVectorRotate *node_storage = (NodeAttributeVectorRotate *)MEM_callocN(
sizeof(NodeAttributeVectorRotate), __func__);
node_storage->mode = GEO_NODE_VECTOR_ROTATE_TYPE_AXIS;
node_storage->input_type_vector = GEO_NODE_ATTRIBUTE_INPUT_ATTRIBUTE;
node_storage->input_type_center = GEO_NODE_ATTRIBUTE_INPUT_VECTOR;
node_storage->input_type_axis = GEO_NODE_ATTRIBUTE_INPUT_VECTOR;
node_storage->input_type_angle = GEO_NODE_ATTRIBUTE_INPUT_FLOAT;
node_storage->input_type_rotation = GEO_NODE_ATTRIBUTE_INPUT_VECTOR;
node->storage = node_storage;
}
static float3 vector_rotate_euler(const float3 vector,
const float3 center,
const float3 rotation,
const bool invert)
{
float mat[3][3];
float3 result = vector - center;
eul_to_mat3(mat, rotation);
if (invert) {
invert_m3(mat);
}
mul_m3_v3(mat, result);
return result + center;
}
static void do_vector_rotate_around_axis(const VArray<float3> &vector,
const VArray<float3> &center,
const VArray<float3> &axis,
const VArray<float> &angle,
MutableSpan<float3> results,
const bool invert)
{
const int size = vector.size();
VArray_Span<float3> span_vector{vector};
VArray_Span<float3> span_center{center};
VArray_Span<float3> span_axis{axis};
VArray_Span<float> span_angle{angle};
parallel_for(IndexRange(results.size()), 1024, [&](IndexRange range) {
for (const int i : range) {
float angle = (invert) ? -span_angle[i] : span_angle[i];
results[i] = vector_rotate_around_axis(span_vector[i], span_center[i], span_axis[i], angle);
}
});
}
static void do_vector_rotate_around_fixed_axis(const VArray<float3> &vector,
const VArray<float3> &center,
const float3 axis,
const VArray<float> &angle,
MutableSpan<float3> results,
const bool invert)
{
const int size = vector.size();
VArray_Span<float3> span_vector{vector};
VArray_Span<float3> span_center{center};
VArray_Span<float> span_angle{angle};
parallel_for(IndexRange(results.size()), 1024, [&](IndexRange range) {
for (const int i : range) {
float angle = (invert) ? -span_angle[i] : span_angle[i];
results[i] = vector_rotate_around_axis(span_vector[i], span_center[i], axis, angle);
}
});
}
static void do_vector_rotate_euler(const VArray<float3> &vector,
const VArray<float3> &center,
const VArray<float3> &rotation,
MutableSpan<float3> results,
const bool invert)
{
const int size = vector.size();
VArray_Span<float3> span_vector{vector};
VArray_Span<float3> span_center{center};
VArray_Span<float3> span_rotation{rotation};
parallel_for(IndexRange(results.size()), 1024, [&](IndexRange range) {
for (const int i : range) {
results[i] = vector_rotate_euler(span_vector[i], span_center[i], span_rotation[i], invert);
}
});
}
static AttributeDomain get_result_domain(const GeometryComponent &component,
const GeoNodeExecParams &params,
StringRef result_name)
{
/* Use the domain of the result attribute if it already exists. */
std::optional<AttributeMetaData> meta_data = component.attribute_get_meta_data(result_name);
if (meta_data) {
return meta_data->domain;
}
/* Otherwise use the highest priority domain from existing input attributes, or the default. */
const AttributeDomain default_domain = ATTR_DOMAIN_POINT;
return params.get_highest_priority_input_domain({"Vector", "Center"}, component, default_domain);
}
static void execute_on_component(const GeoNodeExecParams &params, GeometryComponent &component)
{
const bNode &node = params.node();
const NodeAttributeVectorRotate *node_storage = (const NodeAttributeVectorRotate *)node.storage;
const GeometryNodeAttributeVectorRotateMode mode = (GeometryNodeAttributeVectorRotateMode)
node_storage->mode;
const std::string result_name = params.get_input<std::string>("Result");
const AttributeDomain result_domain = get_result_domain(component, params, result_name);
const bool invert = params.get_input<bool>("Invert");
GVArrayPtr attribute_vector = params.get_input_attribute(
"Vector", component, result_domain, CD_PROP_FLOAT3, nullptr);
if (!attribute_vector) {
return;
}
GVArrayPtr attribute_center = params.get_input_attribute(
"Center", component, result_domain, CD_PROP_FLOAT3, nullptr);
if (!attribute_center) {
return;
}
OutputAttribute attribute_result = component.attribute_try_get_for_output_only(
result_name, result_domain, CD_PROP_FLOAT3);
if (!attribute_result) {
return;
}
if (mode == GEO_NODE_VECTOR_ROTATE_TYPE_EULER_XYZ) {
GVArrayPtr attribute_rotation = params.get_input_attribute(
"Rotation", component, result_domain, CD_PROP_FLOAT3, nullptr);
if (!attribute_rotation) {
return;
}
do_vector_rotate_euler(attribute_vector->typed<float3>(),
attribute_center->typed<float3>(),
attribute_rotation->typed<float3>(),
attribute_result.as_span<float3>(),
invert);
attribute_result.save();
return;
}
GVArrayPtr attribute_angle = params.get_input_attribute(
"Angle", component, result_domain, CD_PROP_FLOAT, nullptr);
if (!attribute_angle) {
return;
}
switch (mode) {
case GEO_NODE_VECTOR_ROTATE_TYPE_AXIS: {
GVArrayPtr attribute_axis = params.get_input_attribute(
"Axis", component, result_domain, CD_PROP_FLOAT3, nullptr);
if (!attribute_axis) {
return;
}
do_vector_rotate_around_axis(attribute_vector->typed<float3>(),
attribute_center->typed<float3>(),
attribute_axis->typed<float3>(),
attribute_angle->typed<float>(),
attribute_result.as_span<float3>(),
invert);
} break;
case GEO_NODE_VECTOR_ROTATE_TYPE_AXIS_X:
do_vector_rotate_around_fixed_axis(attribute_vector->typed<float3>(),
attribute_center->typed<float3>(),
float3(1.0f, 0.0f, 0.0f),
attribute_angle->typed<float>(),
attribute_result.as_span<float3>(),
invert);
break;
case GEO_NODE_VECTOR_ROTATE_TYPE_AXIS_Y:
do_vector_rotate_around_fixed_axis(attribute_vector->typed<float3>(),
attribute_center->typed<float3>(),
float3(0.0f, 1.0f, 0.0f),
attribute_angle->typed<float>(),
attribute_result.as_span<float3>(),
invert);
break;
case GEO_NODE_VECTOR_ROTATE_TYPE_AXIS_Z:
do_vector_rotate_around_fixed_axis(attribute_vector->typed<float3>(),
attribute_center->typed<float3>(),
float3(0.0f, 0.0f, 1.0f),
attribute_angle->typed<float>(),
attribute_result.as_span<float3>(),
invert);
break;
case GEO_NODE_VECTOR_ROTATE_TYPE_EULER_XYZ:
/* Euler is handled before other modes to avoid processing the unavailable angle socket. */
BLI_assert_unreachable();
break;
}
attribute_result.save();
}
static void geo_node_attribute_vector_rotate_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
geometry_set = geometry_set_realize_instances(geometry_set);
if (geometry_set.has<MeshComponent>()) {
execute_on_component(params, geometry_set.get_component_for_write<MeshComponent>());
}
if (geometry_set.has<PointCloudComponent>()) {
execute_on_component(params, geometry_set.get_component_for_write<PointCloudComponent>());
}
if (geometry_set.has<CurveComponent>()) {
execute_on_component(params, geometry_set.get_component_for_write<CurveComponent>());
}
params.set_output("Geometry", std::move(geometry_set));
}
} // namespace blender::nodes
void register_node_type_geo_attribute_vector_rotate()
{
static bNodeType ntype;
geo_node_type_base(&ntype,
GEO_NODE_ATTRIBUTE_VECTOR_ROTATE,
"Attribute Vector Rotate",
NODE_CLASS_ATTRIBUTE,
0);
node_type_socket_templates(
&ntype, geo_node_attribute_vector_rotate_in, geo_node_attribute_vector_rotate_out);
node_type_update(&ntype, blender::nodes::geo_node_attribute_vector_rotate_update);
node_type_init(&ntype, blender::nodes::geo_node_attribute_vector_rotate_init);
node_type_size(&ntype, 165, 100, 600);
node_type_storage(
&ntype, "NodeAttributeVectorRotate", node_free_standard_storage, node_copy_standard_storage);
ntype.geometry_node_execute = blender::nodes::geo_node_attribute_vector_rotate_exec;
ntype.draw_buttons = geo_node_attribute_vector_rotate_layout;
nodeRegisterType(&ntype);
}