diff options
Diffstat (limited to 'source/blender/nodes/geometry')
22 files changed, 1334 insertions, 268 deletions
diff --git a/source/blender/nodes/geometry/node_geometry_tree.cc b/source/blender/nodes/geometry/node_geometry_tree.cc index afb86ff57b8..f4cd00b88ed 100644 --- a/source/blender/nodes/geometry/node_geometry_tree.cc +++ b/source/blender/nodes/geometry/node_geometry_tree.cc @@ -84,6 +84,17 @@ static void foreach_nodeclass(Scene *UNUSED(scene), void *calldata, bNodeClassCa func(calldata, NODE_CLASS_LAYOUT, N_("Layout")); } +static bool geometry_node_tree_validate_link(bNodeTree *UNUSED(ntree), bNodeLink *link) +{ + /* Geometry, string, object, material, texture and collection sockets can only be connected to + * themselves. The other types can be converted between each other. */ + if (ELEM(link->fromsock->type, SOCK_FLOAT, SOCK_VECTOR, SOCK_RGBA, SOCK_BOOLEAN, SOCK_INT) && + ELEM(link->tosock->type, SOCK_FLOAT, SOCK_VECTOR, SOCK_RGBA, SOCK_BOOLEAN, SOCK_INT)) { + return true; + } + return (link->tosock->type == link->fromsock->type); +} + static bool geometry_node_tree_socket_type_valid(eNodeSocketDatatype socket_type, bNodeTreeType *UNUSED(ntreetype)) { @@ -96,7 +107,9 @@ static bool geometry_node_tree_socket_type_valid(eNodeSocketDatatype socket_type SOCK_STRING, SOCK_OBJECT, SOCK_GEOMETRY, - SOCK_COLLECTION); + SOCK_COLLECTION, + SOCK_TEXTURE, + SOCK_MATERIAL); } void register_node_tree_type_geo(void) @@ -113,6 +126,7 @@ void register_node_tree_type_geo(void) tt->get_from_context = geometry_node_tree_get_from_context; tt->foreach_nodeclass = foreach_nodeclass; tt->valid_socket_type = geometry_node_tree_socket_type_valid; + tt->validate_link = geometry_node_tree_validate_link; ntreeTypeAdd(tt); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_align_rotation_to_vector.cc b/source/blender/nodes/geometry/nodes/node_geo_align_rotation_to_vector.cc index 720ca9731a8..d1b71d6f2ba 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_align_rotation_to_vector.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_align_rotation_to_vector.cc @@ -51,6 +51,28 @@ static void geo_node_align_rotation_to_vector_layout(uiLayout *layout, namespace blender::nodes { +static void geo_node_align_rotation_to_vector_init(bNodeTree *UNUSED(ntree), bNode *node) +{ + NodeGeometryAlignRotationToVector *node_storage = (NodeGeometryAlignRotationToVector *) + MEM_callocN(sizeof(NodeGeometryAlignRotationToVector), __func__); + + node_storage->axis = GEO_NODE_ALIGN_ROTATION_TO_VECTOR_AXIS_X; + node_storage->input_type_factor = GEO_NODE_ATTRIBUTE_INPUT_FLOAT; + node_storage->input_type_vector = GEO_NODE_ATTRIBUTE_INPUT_VECTOR; + + node->storage = node_storage; +} + +static void geo_node_align_rotation_to_vector_update(bNodeTree *UNUSED(ntree), bNode *node) +{ + NodeGeometryAlignRotationToVector *node_storage = (NodeGeometryAlignRotationToVector *) + node->storage; + update_attribute_input_socket_availabilities( + *node, "Factor", (GeometryNodeAttributeInputMode)node_storage->input_type_factor); + update_attribute_input_socket_availabilities( + *node, "Vector", (GeometryNodeAttributeInputMode)node_storage->input_type_vector); +} + static void align_rotations_auto_pivot(const VArray<float3> &vectors, const VArray<float> &factors, const float3 local_main_axis, @@ -195,28 +217,6 @@ static void geo_node_align_rotation_to_vector_exec(GeoNodeExecParams params) params.set_output("Geometry", geometry_set); } -static void geo_node_align_rotation_to_vector_init(bNodeTree *UNUSED(ntree), bNode *node) -{ - NodeGeometryAlignRotationToVector *node_storage = (NodeGeometryAlignRotationToVector *) - MEM_callocN(sizeof(NodeGeometryAlignRotationToVector), __func__); - - node_storage->axis = GEO_NODE_ALIGN_ROTATION_TO_VECTOR_AXIS_X; - node_storage->input_type_factor = GEO_NODE_ATTRIBUTE_INPUT_FLOAT; - node_storage->input_type_vector = GEO_NODE_ATTRIBUTE_INPUT_VECTOR; - - node->storage = node_storage; -} - -static void geo_node_align_rotation_to_vector_update(bNodeTree *UNUSED(ntree), bNode *node) -{ - NodeGeometryAlignRotationToVector *node_storage = (NodeGeometryAlignRotationToVector *) - node->storage; - update_attribute_input_socket_availabilities( - *node, "Factor", (GeometryNodeAttributeInputMode)node_storage->input_type_factor); - update_attribute_input_socket_availabilities( - *node, "Vector", (GeometryNodeAttributeInputMode)node_storage->input_type_vector); -} - } // namespace blender::nodes void register_node_type_geo_align_rotation_to_vector() diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_color_ramp.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_color_ramp.cc index 26ddb0da515..b13e82e676d 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_color_ramp.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_color_ramp.cc @@ -44,6 +44,14 @@ static void geo_node_attribute_color_ramp_layout(uiLayout *layout, namespace blender::nodes { +static void geo_node_attribute_color_ramp_init(bNodeTree *UNUSED(ntree), bNode *node) +{ + NodeAttributeColorRamp *node_storage = (NodeAttributeColorRamp *)MEM_callocN( + sizeof(NodeAttributeColorRamp), __func__); + BKE_colorband_init(&node_storage->color_ramp, true); + node->storage = node_storage; +} + static AttributeDomain get_result_domain(const GeometryComponent &component, StringRef input_name, StringRef result_name) @@ -115,14 +123,6 @@ static void geo_node_attribute_color_ramp_exec(GeoNodeExecParams params) params.set_output("Geometry", std::move(geometry_set)); } -static void geo_node_attribute_color_ramp_init(bNodeTree *UNUSED(ntree), bNode *node) -{ - NodeAttributeColorRamp *node_storage = (NodeAttributeColorRamp *)MEM_callocN( - sizeof(NodeAttributeColorRamp), __func__); - BKE_colorband_init(&node_storage->color_ramp, true); - node->storage = node_storage; -} - } // namespace blender::nodes void register_node_type_geo_attribute_color_ramp() diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_curve_map.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_curve_map.cc new file mode 100644 index 00000000000..2fc86269797 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_curve_map.cc @@ -0,0 +1,231 @@ +/* + * 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 "BLI_blenlib.h" +#include "BLI_task.hh" + +#include "BKE_colortools.h" + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "node_geometry_util.hh" + +static bNodeSocketTemplate geo_node_attribute_curve_map_in[] = { + {SOCK_GEOMETRY, N_("Geometry")}, + {SOCK_STRING, N_("Attribute")}, + {SOCK_STRING, N_("Result")}, + {-1, ""}, +}; + +static bNodeSocketTemplate geo_node_attribute_curve_map_out[] = { + {SOCK_GEOMETRY, N_("Geometry")}, + {-1, ""}, +}; + +static void geo_node_attribute_curve_map_layout(uiLayout *layout, + bContext *UNUSED(C), + PointerRNA *ptr) +{ + uiItemR(layout, ptr, "data_type", 0, "", ICON_NONE); + bNode *node = (bNode *)ptr->data; + NodeAttributeCurveMap *data = (NodeAttributeCurveMap *)node->storage; + switch (data->data_type) { + case CD_PROP_FLOAT: + uiTemplateCurveMapping(layout, ptr, "curve_vec", 0, false, false, false, false); + break; + case CD_PROP_FLOAT3: + uiTemplateCurveMapping(layout, ptr, "curve_vec", 'v', false, false, false, false); + break; + case CD_PROP_COLOR: + uiTemplateCurveMapping(layout, ptr, "curve_rgb", 'c', false, false, false, false); + break; + } +} + +static void geo_node_attribute_curve_map_free_storage(bNode *node) +{ + if (node->storage) { + NodeAttributeCurveMap *data = (NodeAttributeCurveMap *)node->storage; + BKE_curvemapping_free(data->curve_vec); + BKE_curvemapping_free(data->curve_rgb); + MEM_freeN(node->storage); + } +} + +static void geo_node_attribute_curve_map_copy_storage(bNodeTree *UNUSED(dest_ntree), + bNode *dest_node, + const bNode *src_node) +{ + dest_node->storage = MEM_dupallocN(src_node->storage); + NodeAttributeCurveMap *src_data = (NodeAttributeCurveMap *)src_node->storage; + NodeAttributeCurveMap *dest_data = (NodeAttributeCurveMap *)dest_node->storage; + dest_data->curve_vec = BKE_curvemapping_copy(src_data->curve_vec); + dest_data->curve_rgb = BKE_curvemapping_copy(src_data->curve_rgb); +} + +static void geo_node_attribute_curve_map_init(bNodeTree *UNUSED(ntree), bNode *node) +{ + NodeAttributeCurveMap *data = (NodeAttributeCurveMap *)MEM_callocN(sizeof(NodeAttributeCurveMap), + __func__); + + data->data_type = CD_PROP_FLOAT; + data->curve_vec = BKE_curvemapping_add(4, -1.0f, -1.0f, 1.0f, 1.0f); + data->curve_vec->cur = 3; + data->curve_rgb = BKE_curvemapping_add(4, 0.0f, 0.0f, 1.0f, 1.0f); + node->storage = data; +} + +static void geo_node_attribute_curve_map_update(bNodeTree *UNUSED(ntree), bNode *node) +{ + /* Set the active curve when data type is changed. */ + NodeAttributeCurveMap *data = (NodeAttributeCurveMap *)node->storage; + if (data->data_type == CD_PROP_FLOAT) { + data->curve_vec->cur = 3; + } + else if (data->data_type == CD_PROP_FLOAT3) { + data->curve_vec->cur = 0; + } +} + +namespace blender::nodes { + +static AttributeDomain get_result_domain(const GeometryComponent &component, + StringRef input_name, + StringRef result_name) +{ + /* Use the domain of the result attribute if it already exists. */ + ReadAttributeLookup result_attribute = component.attribute_try_get_for_read(result_name); + if (result_attribute) { + return result_attribute.domain; + } + + /* Otherwise use the input attribute's domain if it exists. */ + ReadAttributeLookup input_attribute = component.attribute_try_get_for_read(input_name); + if (input_attribute) { + return input_attribute.domain; + } + + return ATTR_DOMAIN_POINT; +} + +static void execute_on_component(const GeoNodeExecParams ¶ms, GeometryComponent &component) +{ + const bNode &bnode = params.node(); + NodeAttributeCurveMap &node_storage = *(NodeAttributeCurveMap *)bnode.storage; + const std::string result_name = params.get_input<std::string>("Result"); + const std::string input_name = params.get_input<std::string>("Attribute"); + + const CustomDataType result_type = (CustomDataType)node_storage.data_type; + const AttributeDomain result_domain = get_result_domain(component, input_name, result_name); + + OutputAttribute attribute_result = component.attribute_try_get_for_output_only( + result_name, result_domain, result_type); + if (!attribute_result) { + return; + } + + switch (result_type) { + case CD_PROP_FLOAT: { + const CurveMapping *cumap = (CurveMapping *)node_storage.curve_vec; + GVArray_Typed<float> attribute_in = component.attribute_get_for_read<float>( + input_name, result_domain, float(0.0f)); + MutableSpan<float> results = attribute_result.as_span<float>(); + parallel_for(IndexRange(attribute_in.size()), 512, [&](IndexRange range) { + for (const int i : range) { + results[i] = BKE_curvemapping_evaluateF(cumap, 3, attribute_in[i]); + } + }); + break; + } + case CD_PROP_FLOAT3: { + const CurveMapping *cumap = (CurveMapping *)node_storage.curve_vec; + GVArray_Typed<float3> attribute_in = component.attribute_get_for_read<float3>( + input_name, result_domain, float3(0.0f)); + MutableSpan<float3> results = attribute_result.as_span<float3>(); + parallel_for(IndexRange(attribute_in.size()), 512, [&](IndexRange range) { + for (const int i : range) { + BKE_curvemapping_evaluate3F(cumap, results[i], attribute_in[i]); + } + }); + break; + } + case CD_PROP_COLOR: { + const CurveMapping *cumap = (CurveMapping *)node_storage.curve_rgb; + GVArray_Typed<Color4f> attribute_in = component.attribute_get_for_read<Color4f>( + input_name, result_domain, Color4f(0.0f, 0.0f, 0.0f, 1.0f)); + MutableSpan<Color4f> results = attribute_result.as_span<Color4f>(); + parallel_for(IndexRange(attribute_in.size()), 512, [&](IndexRange range) { + for (const int i : range) { + BKE_curvemapping_evaluateRGBF(cumap, results[i], attribute_in[i]); + } + }); + break; + } + default: { + BLI_assert_unreachable(); + break; + } + } + + attribute_result.save(); +} + +static void geo_node_attribute_curve_map_exec(GeoNodeExecParams params) +{ + const bNode &bnode = params.node(); + NodeAttributeCurveMap *data = (NodeAttributeCurveMap *)bnode.storage; + BKE_curvemapping_init(data->curve_vec); + BKE_curvemapping_init(data->curve_rgb); + + 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_curve_map() +{ + static bNodeType ntype; + + geo_node_type_base( + &ntype, GEO_NODE_ATTRIBUTE_CURVE_MAP, "Attribute Curve Map", NODE_CLASS_ATTRIBUTE, 0); + node_type_socket_templates( + &ntype, geo_node_attribute_curve_map_in, geo_node_attribute_curve_map_out); + node_type_update(&ntype, geo_node_attribute_curve_map_update); + node_type_init(&ntype, geo_node_attribute_curve_map_init); + node_type_size_preset(&ntype, NODE_SIZE_LARGE); + node_type_storage(&ntype, + "NodeAttributeCurveMap", + geo_node_attribute_curve_map_free_storage, + geo_node_attribute_curve_map_copy_storage); + ntype.geometry_node_execute = blender::nodes::geo_node_attribute_curve_map_exec; + ntype.draw_buttons = geo_node_attribute_curve_map_layout; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_mix.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_mix.cc index 3e5326edbf6..e502a183ef5 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_mix.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_mix.cc @@ -59,6 +59,28 @@ static void geo_node_attribute_mix_layout(uiLayout *layout, bContext *UNUSED(C), namespace blender::nodes { +static void geo_node_attribute_mix_init(bNodeTree *UNUSED(ntree), bNode *node) +{ + NodeAttributeMix *data = (NodeAttributeMix *)MEM_callocN(sizeof(NodeAttributeMix), + "attribute mix node"); + data->blend_type = MA_RAMP_BLEND; + data->input_type_factor = GEO_NODE_ATTRIBUTE_INPUT_FLOAT; + data->input_type_a = GEO_NODE_ATTRIBUTE_INPUT_ATTRIBUTE; + data->input_type_b = GEO_NODE_ATTRIBUTE_INPUT_ATTRIBUTE; + node->storage = data; +} + +static void geo_node_attribute_mix_update(bNodeTree *UNUSED(ntree), bNode *node) +{ + NodeAttributeMix *node_storage = (NodeAttributeMix *)node->storage; + update_attribute_input_socket_availabilities( + *node, "Factor", (GeometryNodeAttributeInputMode)node_storage->input_type_factor); + 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); +} + static void do_mix_operation_float(const int blend_mode, const VArray<float> &factors, const VArray<float> &inputs_a, @@ -216,28 +238,6 @@ static void geo_node_attribute_mix_exec(GeoNodeExecParams params) params.set_output("Geometry", geometry_set); } -static void geo_node_attribute_mix_init(bNodeTree *UNUSED(ntree), bNode *node) -{ - NodeAttributeMix *data = (NodeAttributeMix *)MEM_callocN(sizeof(NodeAttributeMix), - "attribute mix node"); - data->blend_type = MA_RAMP_BLEND; - data->input_type_factor = GEO_NODE_ATTRIBUTE_INPUT_FLOAT; - data->input_type_a = GEO_NODE_ATTRIBUTE_INPUT_ATTRIBUTE; - data->input_type_b = GEO_NODE_ATTRIBUTE_INPUT_ATTRIBUTE; - node->storage = data; -} - -static void geo_node_attribute_mix_update(bNodeTree *UNUSED(ntree), bNode *node) -{ - NodeAttributeMix *node_storage = (NodeAttributeMix *)node->storage; - update_attribute_input_socket_availabilities( - *node, "Factor", (GeometryNodeAttributeInputMode)node_storage->input_type_factor); - 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); -} - } // namespace blender::nodes void register_node_type_geo_attribute_mix() diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_sample_texture.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_sample_texture.cc index 59790e5e46c..aa558314b9e 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_sample_texture.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_sample_texture.cc @@ -30,6 +30,7 @@ static bNodeSocketTemplate geo_node_attribute_sample_texture_in[] = { {SOCK_GEOMETRY, N_("Geometry")}, + {SOCK_TEXTURE, N_("Texture")}, {SOCK_STRING, N_("Mapping")}, {SOCK_STRING, N_("Result")}, {-1, ""}, @@ -40,13 +41,6 @@ static bNodeSocketTemplate geo_node_attribute_sample_texture_out[] = { {-1, ""}, }; -static void geo_node_attribute_sample_texture_layout(uiLayout *layout, - bContext *C, - PointerRNA *ptr) -{ - uiTemplateID(layout, C, ptr, "texture", "texture.new", nullptr, nullptr, 0, ICON_NONE, nullptr); -} - namespace blender::nodes { static AttributeDomain get_result_domain(const GeometryComponent &component, @@ -71,8 +65,7 @@ static AttributeDomain get_result_domain(const GeometryComponent &component, static void execute_on_component(GeometryComponent &component, const GeoNodeExecParams ¶ms) { - const bNode &node = params.node(); - Tex *texture = reinterpret_cast<Tex *>(node.id); + Tex *texture = params.get_input<Tex *>("Texture"); if (texture == nullptr) { return; } @@ -144,6 +137,5 @@ void register_node_type_geo_sample_texture() node_type_socket_templates( &ntype, geo_node_attribute_sample_texture_in, geo_node_attribute_sample_texture_out); ntype.geometry_node_execute = blender::nodes::geo_node_attribute_sample_texture_exec; - ntype.draw_buttons = geo_node_attribute_sample_texture_layout; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_vector_rotate.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_vector_rotate.cc new file mode 100644 index 00000000000..4d568ab5c3a --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_vector_rotate.cc @@ -0,0 +1,352 @@ +/* + * 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> ¢er, + const VArray<float3> &axis, + const VArray<float> &angle, + MutableSpan<float3> results, + const bool invert) +{ + 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> ¢er, + const float3 axis, + const VArray<float> &angle, + MutableSpan<float3> results, + const bool invert) +{ + 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> ¢er, + const VArray<float3> &rotation, + MutableSpan<float3> results, + const bool invert) +{ + 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 ¶ms, + 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 ¶ms, 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); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_collection_info.cc b/source/blender/nodes/geometry/nodes/node_geo_collection_info.cc index 5800d46b70d..9bc8a4bb4be 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_collection_info.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_collection_info.cc @@ -40,11 +40,17 @@ static void geo_node_collection_info_layout(uiLayout *layout, bContext *UNUSED(C namespace blender::nodes { +static void geo_node_collection_info_node_init(bNodeTree *UNUSED(tree), bNode *node) +{ + NodeGeometryCollectionInfo *data = (NodeGeometryCollectionInfo *)MEM_callocN( + sizeof(NodeGeometryCollectionInfo), __func__); + data->transform_space = GEO_NODE_TRANSFORM_SPACE_ORIGINAL; + node->storage = data; +} + static void geo_node_collection_info_exec(GeoNodeExecParams params) { - bke::PersistentCollectionHandle collection_handle = - params.extract_input<bke::PersistentCollectionHandle>("Collection"); - Collection *collection = params.handle_map().lookup(collection_handle); + Collection *collection = params.get_input<Collection *>("Collection"); GeometrySet geometry_set_out; @@ -76,14 +82,6 @@ static void geo_node_collection_info_exec(GeoNodeExecParams params) params.set_output("Geometry", geometry_set_out); } -static void geo_node_collection_info_node_init(bNodeTree *UNUSED(tree), bNode *node) -{ - NodeGeometryCollectionInfo *data = (NodeGeometryCollectionInfo *)MEM_callocN( - sizeof(NodeGeometryCollectionInfo), __func__); - data->transform_space = GEO_NODE_TRANSFORM_SPACE_ORIGINAL; - node->storage = data; -} - } // namespace blender::nodes void register_node_type_geo_collection_info() diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc new file mode 100644 index 00000000000..1c42b9341a0 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc @@ -0,0 +1,238 @@ +/* + * 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 "BLI_array.hh" +#include "BLI_task.hh" +#include "BLI_timeit.hh" + +#include "BKE_attribute_math.hh" +#include "BKE_spline.hh" + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "node_geometry_util.hh" + +using blender::fn::GVArray_For_GSpan; +using blender::fn::GVArray_For_Span; +using blender::fn::GVArray_Typed; + +static bNodeSocketTemplate geo_node_curve_resample_in[] = { + {SOCK_GEOMETRY, N_("Geometry")}, + {SOCK_INT, N_("Count"), 10, 0, 0, 0, 1, 100000}, + {SOCK_FLOAT, N_("Length"), 0.1f, 0.0f, 0.0f, 0.0f, 0.001f, FLT_MAX, PROP_DISTANCE}, + {-1, ""}, +}; + +static bNodeSocketTemplate geo_node_curve_resample_out[] = { + {SOCK_GEOMETRY, N_("Geometry")}, + {-1, ""}, +}; + +static void geo_node_curve_resample_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "mode", UI_ITEM_R_EXPAND, nullptr, ICON_NONE); +} + +static void geo_node_curve_resample_init(bNodeTree *UNUSED(tree), bNode *node) +{ + NodeGeometryCurveResample *data = (NodeGeometryCurveResample *)MEM_callocN( + sizeof(NodeGeometryCurveResample), __func__); + + data->mode = GEO_NODE_CURVE_SAMPLE_COUNT; + node->storage = data; +} + +static void geo_node_curve_resample_update(bNodeTree *UNUSED(ntree), bNode *node) +{ + NodeGeometryCurveResample &node_storage = *(NodeGeometryCurveResample *)node->storage; + const GeometryNodeCurveSampleMode mode = (GeometryNodeCurveSampleMode)node_storage.mode; + + bNodeSocket *count_socket = ((bNodeSocket *)node->inputs.first)->next; + bNodeSocket *length_socket = count_socket->next; + + nodeSetSocketAvailability(count_socket, mode == GEO_NODE_CURVE_SAMPLE_COUNT); + nodeSetSocketAvailability(length_socket, mode == GEO_NODE_CURVE_SAMPLE_LENGTH); +} + +namespace blender::nodes { + +struct SampleModeParam { + GeometryNodeCurveSampleMode mode; + std::optional<float> length; + std::optional<int> count; +}; + +template<typename T> +static void sample_span_to_output_spline(const Spline &input_spline, + Span<float> index_factors, + const VArray<T> &input_data, + MutableSpan<T> output_data) +{ + BLI_assert(input_data.size() == input_spline.evaluated_points_size()); + + parallel_for(output_data.index_range(), 1024, [&](IndexRange range) { + for (const int i : range) { + const Spline::LookupResult interp = input_spline.lookup_data_from_index_factor( + index_factors[i]); + output_data[i] = blender::attribute_math::mix2(interp.factor, + input_data[interp.evaluated_index], + input_data[interp.next_evaluated_index]); + } + }); +} + +static SplinePtr resample_spline(const Spline &input_spline, const int count) +{ + std::unique_ptr<PolySpline> output_spline = std::make_unique<PolySpline>(); + output_spline->set_cyclic(input_spline.is_cyclic()); + output_spline->normal_mode = input_spline.normal_mode; + + if (input_spline.evaluated_edges_size() < 1) { + output_spline->resize(1); + output_spline->positions().first() = input_spline.positions().first(); + return output_spline; + } + + output_spline->resize(count); + + Array<float> uniform_samples = input_spline.sample_uniform_index_factors(count); + + { + GVArray_For_Span positions(input_spline.evaluated_positions()); + GVArray_Typed<float3> positions_typed(positions); + sample_span_to_output_spline<float3>( + input_spline, uniform_samples, positions_typed, output_spline->positions()); + } + { + GVArrayPtr interpolated_data = input_spline.interpolate_to_evaluated_points( + GVArray_For_Span(input_spline.radii())); + GVArray_Typed<float> interpolated_data_typed{*interpolated_data}; + sample_span_to_output_spline<float>( + input_spline, uniform_samples, interpolated_data_typed, output_spline->radii()); + } + { + GVArrayPtr interpolated_data = input_spline.interpolate_to_evaluated_points( + GVArray_For_Span(input_spline.tilts())); + GVArray_Typed<float> interpolated_data_typed{*interpolated_data}; + sample_span_to_output_spline<float>( + input_spline, uniform_samples, interpolated_data_typed, output_spline->tilts()); + } + + output_spline->attributes.reallocate(count); + input_spline.attributes.foreach_attribute( + [&](StringRefNull name, const AttributeMetaData &meta_data) { + std::optional<GSpan> input_attribute = input_spline.attributes.get_for_read(name); + BLI_assert(input_attribute); + if (!output_spline->attributes.create(name, meta_data.data_type)) { + BLI_assert_unreachable(); + return false; + } + std::optional<GMutableSpan> output_attribute = output_spline->attributes.get_for_write( + name); + if (!output_attribute) { + BLI_assert_unreachable(); + return false; + } + GVArrayPtr interpolated_attribute = input_spline.interpolate_to_evaluated_points( + GVArray_For_GSpan(*input_attribute)); + attribute_math::convert_to_static_type(meta_data.data_type, [&](auto dummy) { + using T = decltype(dummy); + GVArray_Typed<T> interpolated_attribute_typed{*interpolated_attribute}; + sample_span_to_output_spline<T>(input_spline, + uniform_samples, + interpolated_attribute_typed, + (*output_attribute).typed<T>()); + }); + return true; + }, + ATTR_DOMAIN_POINT); + + return output_spline; +} + +static std::unique_ptr<CurveEval> resample_curve(const CurveEval &input_curve, + const SampleModeParam &mode_param) +{ + std::unique_ptr<CurveEval> output_curve = std::make_unique<CurveEval>(); + + for (const SplinePtr &spline : input_curve.splines()) { + if (mode_param.mode == GEO_NODE_CURVE_SAMPLE_COUNT) { + BLI_assert(mode_param.count); + output_curve->add_spline(resample_spline(*spline, *mode_param.count)); + } + else if (mode_param.mode == GEO_NODE_CURVE_SAMPLE_LENGTH) { + BLI_assert(mode_param.length); + const float length = spline->length(); + const int count = length / *mode_param.length; + output_curve->add_spline(resample_spline(*spline, count)); + } + } + + return output_curve; +} + +static void geo_node_resample_exec(GeoNodeExecParams params) +{ + GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); + + geometry_set = bke::geometry_set_realize_instances(geometry_set); + + if (!geometry_set.has_curve()) { + params.set_output("Geometry", GeometrySet()); + return; + } + + const CurveEval &input_curve = *geometry_set.get_curve_for_read(); + NodeGeometryCurveResample &node_storage = *(NodeGeometryCurveResample *)params.node().storage; + const GeometryNodeCurveSampleMode mode = (GeometryNodeCurveSampleMode)node_storage.mode; + SampleModeParam mode_param; + mode_param.mode = mode; + if (mode == GEO_NODE_CURVE_SAMPLE_COUNT) { + const int count = params.extract_input<int>("Count"); + if (count < 1) { + params.set_output("Geometry", GeometrySet()); + return; + } + mode_param.count.emplace(count); + } + else if (mode == GEO_NODE_CURVE_SAMPLE_LENGTH) { + /* Don't allow asymptotic count increase for low resolution values. */ + const float resolution = std::max(params.extract_input<float>("Length"), 0.0001f); + mode_param.length.emplace(resolution); + } + + std::unique_ptr<CurveEval> output_curve = resample_curve(input_curve, mode_param); + + params.set_output("Geometry", GeometrySet::create_with_curve(output_curve.release())); +} + +} // namespace blender::nodes + +void register_node_type_geo_curve_resample() +{ + static bNodeType ntype; + + geo_node_type_base(&ntype, GEO_NODE_CURVE_RESAMPLE, "Resample Curve", NODE_CLASS_GEOMETRY, 0); + node_type_socket_templates(&ntype, geo_node_curve_resample_in, geo_node_curve_resample_out); + ntype.draw_buttons = geo_node_curve_resample_layout; + node_type_storage( + &ntype, "NodeGeometryCurveResample", node_free_standard_storage, node_copy_standard_storage); + node_type_init(&ntype, geo_node_curve_resample_init); + node_type_update(&ntype, geo_node_curve_resample_update); + ntype.geometry_node_execute = blender::nodes::geo_node_resample_exec; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc index 071504ad8df..f0effdc71f6 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc @@ -58,7 +58,7 @@ static void vert_extrude_to_mesh_data(const Spline &spline, edge.flag = ME_LOOSEEDGE; } - if (spline.is_cyclic()) { + if (spline.is_cyclic() && spline.evaluated_edges_size() > 1) { MEdge &edge = r_edges[edge_offset++]; edge.v1 = vert_offset; edge.v2 = vert_offset + positions.size() - 1; @@ -198,7 +198,7 @@ static void spline_extrude_to_mesh_data(const Spline &spline, if (profile_spline.type() == Spline::Type::Bezier) { const BezierSpline &bezier_spline = static_cast<const BezierSpline &>(profile_spline); Span<int> control_point_offsets = bezier_spline.control_point_offsets(); - for (const int i : control_point_offsets.index_range()) { + for (const int i : IndexRange(bezier_spline.size())) { if (bezier_spline.point_is_sharp(i)) { mark_edges_sharp(r_edges.slice( spline_edges_start + spline_edge_len * control_point_offsets[i], spline_edge_len)); @@ -211,7 +211,7 @@ static Mesh *curve_to_mesh_calculate(const CurveEval &curve, const CurveEval &pr { int profile_vert_total = 0; int profile_edge_total = 0; - for (const SplinePtr &profile_spline : profile_curve.splines) { + for (const SplinePtr &profile_spline : profile_curve.splines()) { profile_vert_total += profile_spline->evaluated_points_size(); profile_edge_total += profile_spline->evaluated_edges_size(); } @@ -219,7 +219,7 @@ static Mesh *curve_to_mesh_calculate(const CurveEval &curve, const CurveEval &pr int vert_total = 0; int edge_total = 0; int poly_total = 0; - for (const SplinePtr &spline : curve.splines) { + for (const SplinePtr &spline : curve.splines()) { const int spline_vert_len = spline->evaluated_points_size(); const int spline_edge_len = spline->evaluated_edges_size(); vert_total += spline_vert_len * profile_vert_total; @@ -247,8 +247,8 @@ static Mesh *curve_to_mesh_calculate(const CurveEval &curve, const CurveEval &pr int edge_offset = 0; int loop_offset = 0; int poly_offset = 0; - for (const SplinePtr &spline : curve.splines) { - for (const SplinePtr &profile_spline : profile_curve.splines) { + for (const SplinePtr &spline : curve.splines()) { + for (const SplinePtr &profile_spline : profile_curve.splines()) { spline_extrude_to_mesh_data(*spline, *profile_spline, verts, @@ -272,7 +272,7 @@ static CurveEval get_curve_single_vert() CurveEval curve; std::unique_ptr<PolySpline> spline = std::make_unique<PolySpline>(); spline->add_point(float3(0), 0, 0.0f); - curve.splines.append(std::move(spline)); + curve.add_spline(std::move(spline)); return curve; } diff --git a/source/blender/nodes/geometry/nodes/node_geo_edge_split.cc b/source/blender/nodes/geometry/nodes/node_geo_edge_split.cc index 534b7d754ec..740b828d503 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_edge_split.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_edge_split.cc @@ -44,6 +44,7 @@ static bNodeSocketTemplate geo_node_edge_split_out[] = { }; namespace blender::nodes { + static void geo_node_edge_split_exec(GeoNodeExecParams params) { GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); @@ -82,6 +83,7 @@ static void geo_node_edge_split_exec(GeoNodeExecParams params) params.set_output("Geometry", std::move(geometry_set)); } + } // namespace blender::nodes void register_node_type_geo_edge_split() diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_material.cc b/source/blender/nodes/geometry/nodes/node_geo_input_material.cc new file mode 100644 index 00000000000..6bad71a62a2 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_input_material.cc @@ -0,0 +1,51 @@ +/* + * 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 "UI_interface.h" +#include "UI_resources.h" + +#include "node_geometry_util.hh" + +static bNodeSocketTemplate geo_node_input_material_out[] = { + {SOCK_MATERIAL, N_("Material")}, + {-1, ""}, +}; + +static void geo_node_input_material_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "material", 0, "", ICON_NONE); +} + +namespace blender::nodes { + +static void geo_node_input_material_exec(GeoNodeExecParams params) +{ + Material *material = (Material *)params.node().id; + params.set_output("Material", material); +} + +} // namespace blender::nodes + +void register_node_type_geo_input_material() +{ + static bNodeType ntype; + + geo_node_type_base(&ntype, GEO_NODE_INPUT_MATERIAL, "Material", NODE_CLASS_INPUT, 0); + node_type_socket_templates(&ntype, nullptr, geo_node_input_material_out); + ntype.draw_buttons = geo_node_input_material_layout; + ntype.geometry_node_execute = blender::nodes::geo_node_input_material_exec; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc b/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc index 68bb3614751..adfd924f185 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc @@ -14,6 +14,7 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +#include "BKE_material.h" #include "BKE_mesh.h" #include "BKE_mesh_runtime.h" #include "BKE_pointcloud.h" @@ -57,6 +58,8 @@ static Mesh *join_mesh_topology_and_builtin_attributes(Span<const MeshComponent int64_t cd_dirty_edge = 0; int64_t cd_dirty_loop = 0; + VectorSet<Material *> materials; + for (const MeshComponent *mesh_component : src_components) { const Mesh *mesh = mesh_component->get_for_read(); totverts += mesh->totvert; @@ -67,12 +70,22 @@ static Mesh *join_mesh_topology_and_builtin_attributes(Span<const MeshComponent cd_dirty_poly |= mesh->runtime.cd_dirty_poly; cd_dirty_edge |= mesh->runtime.cd_dirty_edge; cd_dirty_loop |= mesh->runtime.cd_dirty_loop; + + for (const int slot_index : IndexRange(mesh->totcol)) { + Material *material = mesh->mat[slot_index]; + materials.add(material); + } } const Mesh *first_input_mesh = src_components[0]->get_for_read(); Mesh *new_mesh = BKE_mesh_new_nomain(totverts, totedges, 0, totloops, totpolys); BKE_mesh_copy_settings(new_mesh, first_input_mesh); + for (const int i : IndexRange(materials.size())) { + Material *material = materials[i]; + BKE_id_material_eval_assign(&new_mesh->id, i + 1, material); + } + new_mesh->runtime.cd_dirty_vert = cd_dirty_vert; new_mesh->runtime.cd_dirty_poly = cd_dirty_poly; new_mesh->runtime.cd_dirty_edge = cd_dirty_edge; @@ -88,6 +101,13 @@ static Mesh *join_mesh_topology_and_builtin_attributes(Span<const MeshComponent continue; } + Array<int> material_index_map(mesh->totcol); + for (const int i : IndexRange(mesh->totcol)) { + Material *material = mesh->mat[i]; + const int new_material_index = materials.index_of(material); + material_index_map[i] = new_material_index; + } + for (const int i : IndexRange(mesh->totvert)) { const MVert &old_vert = mesh->mvert[i]; MVert &new_vert = new_mesh->mvert[vert_offset + i]; @@ -113,6 +133,13 @@ static Mesh *join_mesh_topology_and_builtin_attributes(Span<const MeshComponent MPoly &new_poly = new_mesh->mpoly[poly_offset + i]; new_poly = old_poly; new_poly.loopstart += loop_offset; + if (old_poly.mat_nr >= 0 && old_poly.mat_nr < mesh->totcol) { + new_poly.mat_nr = material_index_map[new_poly.mat_nr]; + } + else { + /* The material index was invalid before. */ + new_poly.mat_nr = 0; + } } vert_offset += mesh->totvert; @@ -305,11 +332,19 @@ static void join_curve_components(MutableSpan<GeometrySet> src_geometry_sets, Ge CurveEval *dst_curve = new CurveEval(); for (CurveComponent *component : src_components) { CurveEval *src_curve = component->get_for_write(); - for (SplinePtr &spline : src_curve->splines) { - dst_curve->splines.append(std::move(spline)); + for (SplinePtr &spline : src_curve->splines()) { + dst_curve->add_spline(std::move(spline)); } } + /* For now, remove all custom attributes, since they might have different types, + * or an attribute might not exist on all splines. */ + dst_curve->attributes.reallocate(dst_curve->splines().size()); + CustomData_reset(&dst_curve->attributes.data); + for (SplinePtr &spline : dst_curve->splines()) { + CustomData_reset(&spline->attributes.data); + } + dst_component.replace(dst_curve); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_material_assign.cc b/source/blender/nodes/geometry/nodes/node_geo_material_assign.cc new file mode 100644 index 00000000000..8c245afd3d2 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_material_assign.cc @@ -0,0 +1,98 @@ +/* + * 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 "UI_interface.h" +#include "UI_resources.h" + +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" + +#include "BKE_material.h" + +static bNodeSocketTemplate geo_node_material_assign_in[] = { + {SOCK_GEOMETRY, N_("Geometry")}, + {SOCK_MATERIAL, N_("Material")}, + {SOCK_STRING, N_("Selection")}, + {-1, ""}, +}; + +static bNodeSocketTemplate geo_node_material_assign_out[] = { + {SOCK_GEOMETRY, N_("Geometry")}, + {-1, ""}, +}; + +namespace blender::nodes { + +static void assign_material_to_faces(Mesh &mesh, const VArray<bool> &face_mask, Material *material) +{ + int new_material_index = -1; + for (const int i : IndexRange(mesh.totcol)) { + Material *other_material = mesh.mat[i]; + if (other_material == material) { + new_material_index = i; + break; + } + } + if (new_material_index == -1) { + /* Append a new material index. */ + new_material_index = mesh.totcol; + BKE_id_material_eval_assign(&mesh.id, new_material_index + 1, material); + } + + mesh.mpoly = (MPoly *)CustomData_duplicate_referenced_layer(&mesh.pdata, CD_MPOLY, mesh.totpoly); + for (const int i : IndexRange(mesh.totpoly)) { + if (face_mask[i]) { + MPoly &poly = mesh.mpoly[i]; + poly.mat_nr = new_material_index; + } + } +} + +static void geo_node_material_assign_exec(GeoNodeExecParams params) +{ + Material *material = params.extract_input<Material *>("Material"); + const std::string mask_name = params.extract_input<std::string>("Selection"); + + GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); + + geometry_set = geometry_set_realize_instances(geometry_set); + + if (geometry_set.has<MeshComponent>()) { + MeshComponent &mesh_component = geometry_set.get_component_for_write<MeshComponent>(); + Mesh *mesh = mesh_component.get_for_write(); + if (mesh != nullptr) { + GVArray_Typed<bool> face_mask = mesh_component.attribute_get_for_read<bool>( + mask_name, ATTR_DOMAIN_FACE, true); + assign_material_to_faces(*mesh, face_mask, material); + } + } + + params.set_output("Geometry", std::move(geometry_set)); +} + +} // namespace blender::nodes + +void register_node_type_geo_material_assign() +{ + static bNodeType ntype; + + geo_node_type_base(&ntype, GEO_NODE_MATERIAL_ASSIGN, "Material Assign", NODE_CLASS_GEOMETRY, 0); + node_type_socket_templates(&ntype, geo_node_material_assign_in, geo_node_material_assign_out); + ntype.geometry_node_execute = blender::nodes::geo_node_material_assign_exec; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_object_info.cc b/source/blender/nodes/geometry/nodes/node_geo_object_info.cc index de099b8062f..16c943b310c 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_object_info.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_object_info.cc @@ -47,9 +47,7 @@ static void geo_node_object_info_exec(GeoNodeExecParams params) const bool transform_space_relative = (node_storage->transform_space == GEO_NODE_TRANSFORM_SPACE_RELATIVE); - bke::PersistentObjectHandle object_handle = params.extract_input<bke::PersistentObjectHandle>( - "Object"); - Object *object = params.handle_map().lookup(object_handle); + Object *object = params.get_input<Object *>("Object"); float3 location = {0, 0, 0}; float3 rotation = {0, 0, 0}; diff --git a/source/blender/nodes/geometry/nodes/node_geo_point_instance.cc b/source/blender/nodes/geometry/nodes/node_geo_point_instance.cc index 7929a5a1546..44b8b14f4e7 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_point_instance.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_point_instance.cc @@ -14,11 +14,10 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -#include "BKE_persistent_data_handle.hh" - #include "DNA_collection_types.h" #include "BLI_hash.h" +#include "BLI_task.hh" #include "UI_interface.h" #include "UI_resources.h" @@ -48,6 +47,15 @@ static void geo_node_point_instance_layout(uiLayout *layout, bContext *UNUSED(C) namespace blender::nodes { +static void geo_node_point_instance_init(bNodeTree *UNUSED(tree), bNode *node) +{ + NodeGeometryPointInstance *data = (NodeGeometryPointInstance *)MEM_callocN( + sizeof(NodeGeometryPointInstance), __func__); + data->instance_type = GEO_NODE_POINT_INSTANCE_TYPE_OBJECT; + data->flag |= GEO_NODE_POINT_INSTANCE_WHOLE_COLLECTION; + node->storage = data; +} + static void geo_node_point_instance_update(bNodeTree *UNUSED(tree), bNode *node) { bNodeSocket *object_socket = (bNodeSocket *)BLI_findlink(&node->inputs, 1); @@ -65,98 +73,93 @@ static void geo_node_point_instance_update(bNodeTree *UNUSED(tree), bNode *node) seed_socket, type == GEO_NODE_POINT_INSTANCE_TYPE_COLLECTION && !use_whole_collection); } -static void get_instance_references__object(const GeoNodeExecParams ¶ms, - MutableSpan<InstanceReference> r_references) +static Vector<InstanceReference> get_instance_references__object(GeoNodeExecParams ¶ms) { - bke::PersistentObjectHandle object_handle = params.get_input<bke::PersistentObjectHandle>( - "Object"); - Object *object = params.handle_map().lookup(object_handle); + Object *object = params.extract_input<Object *>("Object"); if (object == params.self_object()) { - object = nullptr; + return {}; } if (object != nullptr) { - r_references.fill(*object); + return {*object}; } + return {}; } -static void get_instance_references__collection(const GeoNodeExecParams ¶ms, - const GeometryComponent &component, - MutableSpan<InstanceReference> r_references) +static Vector<InstanceReference> get_instance_references__collection(GeoNodeExecParams ¶ms) { const bNode &node = params.node(); NodeGeometryPointInstance *node_storage = (NodeGeometryPointInstance *)node.storage; - bke::PersistentCollectionHandle collection_handle = - params.get_input<bke::PersistentCollectionHandle>("Collection"); - Collection *collection = params.handle_map().lookup(collection_handle); + Collection *collection = params.get_input<Collection *>("Collection"); if (collection == nullptr) { - return; + return {}; } if (BLI_listbase_is_empty(&collection->children) && BLI_listbase_is_empty(&collection->gobject)) { params.error_message_add(NodeWarningType::Info, TIP_("Collection is empty")); - return; + return {}; } - const bool use_whole_collection = (node_storage->flag & - GEO_NODE_POINT_INSTANCE_WHOLE_COLLECTION) != 0; - if (use_whole_collection) { - r_references.fill(*collection); + if (node_storage->flag & GEO_NODE_POINT_INSTANCE_WHOLE_COLLECTION) { + return {*collection}; } - else { - Vector<InstanceReference> possible_references; - /* Direct child objects are instanced as objects. */ - LISTBASE_FOREACH (CollectionObject *, cob, &collection->gobject) { - possible_references.append(*cob->ob); - } - /* Direct child collections are instanced as collections. */ - LISTBASE_FOREACH (CollectionChild *, child, &collection->children) { - possible_references.append(*child->collection); - } - if (!possible_references.is_empty()) { - const int seed = params.get_input<int>("Seed"); - Array<uint32_t> ids = get_geometry_element_ids_as_uints(component, ATTR_DOMAIN_POINT); - for (const int i : r_references.index_range()) { - const int index = BLI_hash_int_2d(ids[i], seed) % possible_references.size(); - r_references[i] = possible_references[index]; - } - } + Vector<InstanceReference> references; + /* Direct child objects are instanced as objects. */ + LISTBASE_FOREACH (CollectionObject *, cob, &collection->gobject) { + references.append(*cob->ob); + } + /* Direct child collections are instanced as collections. */ + LISTBASE_FOREACH (CollectionChild *, child, &collection->children) { + references.append(*child->collection); } + + return references; } -static Array<InstanceReference> get_instance_references(const GeoNodeExecParams ¶ms, - const GeometryComponent &component, - const int amount) +static Vector<InstanceReference> get_instance_references(GeoNodeExecParams ¶ms) { const bNode &node = params.node(); NodeGeometryPointInstance *node_storage = (NodeGeometryPointInstance *)node.storage; const GeometryNodePointInstanceType type = (GeometryNodePointInstanceType) node_storage->instance_type; - Array<InstanceReference> references(amount); switch (type) { case GEO_NODE_POINT_INSTANCE_TYPE_OBJECT: { - get_instance_references__object(params, references); - break; + return get_instance_references__object(params); } case GEO_NODE_POINT_INSTANCE_TYPE_COLLECTION: { - get_instance_references__collection(params, component, references); - break; + return get_instance_references__collection(params); } } - return references; + return {}; } -static void add_instances_from_geometry_component(InstancesComponent &instances, - const GeometryComponent &src_geometry, - const GeoNodeExecParams ¶ms) +/** + * Add the instance references to the component as a separate step from actually creating the + * instances in order to avoid a map lookup for every transform. While this might add some + * unnecessary references if they are not chosen while adding transforms, in the common cases + * there are many more transforms than there are references, so that isn't likely. + */ +static Array<int> add_instance_references(InstancesComponent &instance_component, + Span<InstanceReference> possible_references) +{ + Array<int> possible_handles(possible_references.size()); + for (const int i : possible_references.index_range()) { + possible_handles[i] = instance_component.add_reference(possible_references[i]); + } + return possible_handles; +} + +static void add_instances_from_component(InstancesComponent &instances, + const GeometryComponent &src_geometry, + Span<int> possible_handles, + const GeoNodeExecParams ¶ms) { const AttributeDomain domain = ATTR_DOMAIN_POINT; const int domain_size = src_geometry.attribute_domain_size(domain); - Array<InstanceReference> references = get_instance_references(params, src_geometry, domain_size); GVArray_Typed<float3> positions = src_geometry.attribute_get_for_read<float3>( "position", domain, {0, 0, 0}); @@ -164,15 +167,39 @@ static void add_instances_from_geometry_component(InstancesComponent &instances, "rotation", domain, {0, 0, 0}); GVArray_Typed<float3> scales = src_geometry.attribute_get_for_read<float3>( "scale", domain, {1, 1, 1}); - GVArray_Typed<int> ids = src_geometry.attribute_get_for_read<int>("id", domain, -1); - - for (const int i : IndexRange(domain_size)) { - const InstanceReference &reference = references[i]; - if (reference.type() != InstanceReference::Type::None) { - const float4x4 matrix = float4x4::from_loc_eul_scale(positions[i], rotations[i], scales[i]); - const int handle = instances.add_reference(reference); - instances.add_instance(handle, matrix, ids[i]); - } + GVArray_Typed<int> id_attribute = src_geometry.attribute_get_for_read<int>("id", domain, -1); + + /* The initial size of the component might be non-zero if there are two component types. */ + const int start_len = instances.instances_amount(); + instances.resize(start_len + domain_size); + MutableSpan<int> handles = instances.instance_reference_handles().slice(start_len, domain_size); + MutableSpan<float4x4> transforms = instances.instance_transforms().slice(start_len, domain_size); + MutableSpan<int> instance_ids = instances.instance_ids().slice(start_len, domain_size); + + /* Skip all of the randomness handling if there is only a single possible instance + * (anything except for collection mode with "Whole Collection" turned off). */ + if (possible_handles.size() == 1) { + const int handle = possible_handles.first(); + parallel_for(IndexRange(domain_size), 1024, [&](IndexRange range) { + for (const int i : range) { + handles[i] = handle; + transforms[i] = float4x4::from_loc_eul_scale(positions[i], rotations[i], scales[i]); + instance_ids[i] = id_attribute[i]; + } + }); + } + else { + const int seed = params.get_input<int>("Seed"); + Array<uint32_t> ids = get_geometry_element_ids_as_uints(src_geometry, ATTR_DOMAIN_POINT); + parallel_for(IndexRange(domain_size), 1024, [&](IndexRange range) { + for (const int i : range) { + const int index = BLI_hash_int_2d(ids[i], seed) % possible_handles.size(); + const int handle = possible_handles[index]; + handles[i] = handle; + transforms[i] = float4x4::from_loc_eul_scale(positions[i], rotations[i], scales[i]); + instance_ids[i] = id_attribute[i]; + } + }); } } @@ -185,33 +212,37 @@ static void geo_node_point_instance_exec(GeoNodeExecParams params) * rather than making the entire input geometry set real. */ geometry_set = geometry_set_realize_instances(geometry_set); + const Vector<InstanceReference> possible_references = get_instance_references(params); + if (possible_references.is_empty()) { + params.set_output("Geometry", std::move(geometry_set_out)); + return; + } + InstancesComponent &instances = geometry_set_out.get_component_for_write<InstancesComponent>(); + Array<int> possible_handles = add_instance_references(instances, possible_references); + if (geometry_set.has<MeshComponent>()) { - add_instances_from_geometry_component( - instances, *geometry_set.get_component_for_read<MeshComponent>(), params); + add_instances_from_component(instances, + *geometry_set.get_component_for_read<MeshComponent>(), + possible_handles, + params); } if (geometry_set.has<PointCloudComponent>()) { - add_instances_from_geometry_component( - instances, *geometry_set.get_component_for_read<PointCloudComponent>(), params); + add_instances_from_component(instances, + *geometry_set.get_component_for_read<PointCloudComponent>(), + possible_handles, + params); } - if (geometry_set.has<CurveComponent>()) { - add_instances_from_geometry_component( - instances, *geometry_set.get_component_for_read<CurveComponent>(), params); + add_instances_from_component(instances, + *geometry_set.get_component_for_read<CurveComponent>(), + possible_handles, + params); } params.set_output("Geometry", std::move(geometry_set_out)); } -static void geo_node_point_instance_init(bNodeTree *UNUSED(tree), bNode *node) -{ - NodeGeometryPointInstance *data = (NodeGeometryPointInstance *)MEM_callocN( - sizeof(NodeGeometryPointInstance), __func__); - data->instance_type = GEO_NODE_POINT_INSTANCE_TYPE_OBJECT; - data->flag |= GEO_NODE_POINT_INSTANCE_WHOLE_COLLECTION; - node->storage = data; -} - } // namespace blender::nodes void register_node_type_geo_point_instance() diff --git a/source/blender/nodes/geometry/nodes/node_geo_point_rotate.cc b/source/blender/nodes/geometry/nodes/node_geo_point_rotate.cc index 73d489949ad..828d3f50551 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_point_rotate.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_point_rotate.cc @@ -59,6 +59,40 @@ static void geo_node_point_rotate_layout(uiLayout *layout, bContext *UNUSED(C), namespace blender::nodes { +static void geo_node_point_rotate_init(bNodeTree *UNUSED(ntree), bNode *node) +{ + NodeGeometryRotatePoints *node_storage = (NodeGeometryRotatePoints *)MEM_callocN( + sizeof(NodeGeometryRotatePoints), __func__); + + node_storage->type = GEO_NODE_POINT_ROTATE_TYPE_EULER; + node_storage->space = GEO_NODE_POINT_ROTATE_SPACE_OBJECT; + 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 void geo_node_point_rotate_update(bNodeTree *UNUSED(ntree), bNode *node) +{ + NodeGeometryRotatePoints *node_storage = (NodeGeometryRotatePoints *)node->storage; + update_attribute_input_socket_availabilities( + *node, + "Axis", + (GeometryNodeAttributeInputMode)node_storage->input_type_axis, + node_storage->type == GEO_NODE_POINT_ROTATE_TYPE_AXIS_ANGLE); + update_attribute_input_socket_availabilities( + *node, + "Angle", + (GeometryNodeAttributeInputMode)node_storage->input_type_angle, + node_storage->type == GEO_NODE_POINT_ROTATE_TYPE_AXIS_ANGLE); + update_attribute_input_socket_availabilities( + *node, + "Rotation", + (GeometryNodeAttributeInputMode)node_storage->input_type_rotation, + node_storage->type == GEO_NODE_POINT_ROTATE_TYPE_EULER); +} + static void point_rotate__axis_angle__object_space(const int domain_size, const VArray<float3> &axis, const VArray<float> &angles, @@ -176,44 +210,13 @@ static void geo_node_point_rotate_exec(GeoNodeExecParams params) if (geometry_set.has<PointCloudComponent>()) { point_rotate_on_component(geometry_set.get_component_for_write<PointCloudComponent>(), params); } + if (geometry_set.has<CurveComponent>()) { + point_rotate_on_component(geometry_set.get_component_for_write<CurveComponent>(), params); + } params.set_output("Geometry", geometry_set); } -static void geo_node_point_rotate_init(bNodeTree *UNUSED(ntree), bNode *node) -{ - NodeGeometryRotatePoints *node_storage = (NodeGeometryRotatePoints *)MEM_callocN( - sizeof(NodeGeometryRotatePoints), __func__); - - node_storage->type = GEO_NODE_POINT_ROTATE_TYPE_EULER; - node_storage->space = GEO_NODE_POINT_ROTATE_SPACE_OBJECT; - 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 void geo_node_point_rotate_update(bNodeTree *UNUSED(ntree), bNode *node) -{ - NodeGeometryRotatePoints *node_storage = (NodeGeometryRotatePoints *)node->storage; - update_attribute_input_socket_availabilities( - *node, - "Axis", - (GeometryNodeAttributeInputMode)node_storage->input_type_axis, - node_storage->type == GEO_NODE_POINT_ROTATE_TYPE_AXIS_ANGLE); - update_attribute_input_socket_availabilities( - *node, - "Angle", - (GeometryNodeAttributeInputMode)node_storage->input_type_angle, - node_storage->type == GEO_NODE_POINT_ROTATE_TYPE_AXIS_ANGLE); - update_attribute_input_socket_availabilities( - *node, - "Rotation", - (GeometryNodeAttributeInputMode)node_storage->input_type_rotation, - node_storage->type == GEO_NODE_POINT_ROTATE_TYPE_EULER); -} - } // namespace blender::nodes void register_node_type_geo_point_rotate() diff --git a/source/blender/nodes/geometry/nodes/node_geo_point_scale.cc b/source/blender/nodes/geometry/nodes/node_geo_point_scale.cc index 2ef21fb085b..655f5475856 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_point_scale.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_point_scale.cc @@ -43,6 +43,23 @@ static void geo_node_point_scale_layout(uiLayout *layout, bContext *UNUSED(C), P namespace blender::nodes { +static void geo_node_point_scale_init(bNodeTree *UNUSED(tree), bNode *node) +{ + NodeGeometryPointScale *data = (NodeGeometryPointScale *)MEM_callocN( + sizeof(NodeGeometryPointScale), __func__); + + data->input_type = GEO_NODE_ATTRIBUTE_INPUT_VECTOR; + node->storage = data; +} + +static void geo_node_point_scale_update(bNodeTree *UNUSED(ntree), bNode *node) +{ + NodeGeometryPointScale &node_storage = *(NodeGeometryPointScale *)node->storage; + + update_attribute_input_socket_availabilities( + *node, "Factor", (GeometryNodeAttributeInputMode)node_storage.input_type); +} + static void execute_on_component(GeoNodeExecParams params, GeometryComponent &component) { /* Note that scale doesn't necessarily need to be created with a vector type-- it could also use @@ -102,23 +119,6 @@ static void geo_node_point_scale_exec(GeoNodeExecParams params) params.set_output("Geometry", std::move(geometry_set)); } -static void geo_node_point_scale_init(bNodeTree *UNUSED(tree), bNode *node) -{ - NodeGeometryPointScale *data = (NodeGeometryPointScale *)MEM_callocN( - sizeof(NodeGeometryPointScale), __func__); - - data->input_type = GEO_NODE_ATTRIBUTE_INPUT_VECTOR; - node->storage = data; -} - -static void geo_node_point_scale_update(bNodeTree *UNUSED(ntree), bNode *node) -{ - NodeGeometryPointScale &node_storage = *(NodeGeometryPointScale *)node->storage; - - update_attribute_input_socket_availabilities( - *node, "Factor", (GeometryNodeAttributeInputMode)node_storage.input_type); -} - } // namespace blender::nodes void register_node_type_geo_point_scale() diff --git a/source/blender/nodes/geometry/nodes/node_geo_point_separate.cc b/source/blender/nodes/geometry/nodes/node_geo_point_separate.cc index 6541d982629..d2b024b208c 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_point_separate.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_point_separate.cc @@ -135,15 +135,27 @@ static GeometrySet separate_geometry_set(const GeometrySet &set_in, static void geo_node_point_separate_exec(GeoNodeExecParams params) { - const std::string mask_attribute_name = params.extract_input<std::string>("Mask"); - GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); + bool wait_for_inputs = false; + wait_for_inputs |= params.lazy_require_input("Geometry"); + wait_for_inputs |= params.lazy_require_input("Mask"); + if (wait_for_inputs) { + return; + } + const std::string mask_attribute_name = params.get_input<std::string>("Mask"); + GeometrySet geometry_set = params.get_input<GeometrySet>("Geometry"); /* TODO: This is not necessary-- the input geometry set can be read only, * but it must be rewritten to handle instance groups. */ geometry_set = geometry_set_realize_instances(geometry_set); - params.set_output("Geometry 1", separate_geometry_set(geometry_set, mask_attribute_name, true)); - params.set_output("Geometry 2", separate_geometry_set(geometry_set, mask_attribute_name, false)); + if (params.lazy_output_is_required("Geometry 1")) { + params.set_output("Geometry 1", + separate_geometry_set(geometry_set, mask_attribute_name, true)); + } + if (params.lazy_output_is_required("Geometry 2")) { + params.set_output("Geometry 2", + separate_geometry_set(geometry_set, mask_attribute_name, false)); + } } } // namespace blender::nodes @@ -155,5 +167,6 @@ void register_node_type_geo_point_separate() geo_node_type_base(&ntype, GEO_NODE_POINT_SEPARATE, "Point Separate", NODE_CLASS_GEOMETRY, 0); node_type_socket_templates(&ntype, geo_node_point_instance_in, geo_node_point_instance_out); ntype.geometry_node_execute = blender::nodes::geo_node_point_separate_exec; + ntype.geometry_node_execute_supports_lazyness = true; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_points_to_volume.cc b/source/blender/nodes/geometry/nodes/node_geo_points_to_volume.cc index e85f6aaee54..65306b1c452 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_points_to_volume.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_points_to_volume.cc @@ -55,6 +55,35 @@ static void geo_node_points_to_volume_layout(uiLayout *layout, namespace blender::nodes { +static void geo_node_points_to_volume_init(bNodeTree *UNUSED(ntree), bNode *node) +{ + NodeGeometryPointsToVolume *data = (NodeGeometryPointsToVolume *)MEM_callocN( + sizeof(NodeGeometryPointsToVolume), __func__); + data->resolution_mode = GEO_NODE_POINTS_TO_VOLUME_RESOLUTION_MODE_AMOUNT; + data->input_type_radius = GEO_NODE_ATTRIBUTE_INPUT_FLOAT; + node->storage = data; + + bNodeSocket *radius_attribute_socket = nodeFindSocket(node, SOCK_IN, "Radius"); + bNodeSocketValueString *radius_attribute_socket_value = + (bNodeSocketValueString *)radius_attribute_socket->default_value; + STRNCPY(radius_attribute_socket_value->value, "radius"); +} + +static void geo_node_points_to_volume_update(bNodeTree *UNUSED(ntree), bNode *node) +{ + NodeGeometryPointsToVolume *data = (NodeGeometryPointsToVolume *)node->storage; + bNodeSocket *voxel_size_socket = nodeFindSocket(node, SOCK_IN, "Voxel Size"); + bNodeSocket *voxel_amount_socket = nodeFindSocket(node, SOCK_IN, "Voxel Amount"); + nodeSetSocketAvailability(voxel_amount_socket, + data->resolution_mode == + GEO_NODE_POINTS_TO_VOLUME_RESOLUTION_MODE_AMOUNT); + nodeSetSocketAvailability( + voxel_size_socket, data->resolution_mode == GEO_NODE_POINTS_TO_VOLUME_RESOLUTION_MODE_SIZE); + + update_attribute_input_socket_availabilities( + *node, "Radius", (GeometryNodeAttributeInputMode)data->input_type_radius); +} + #ifdef WITH_OPENVDB namespace { /* Implements the interface required by #openvdb::tools::ParticlesToLevelSet. */ @@ -231,35 +260,6 @@ static void geo_node_points_to_volume_exec(GeoNodeExecParams params) params.set_output("Geometry", std::move(geometry_set_out)); } -static void geo_node_points_to_volume_init(bNodeTree *UNUSED(ntree), bNode *node) -{ - NodeGeometryPointsToVolume *data = (NodeGeometryPointsToVolume *)MEM_callocN( - sizeof(NodeGeometryPointsToVolume), __func__); - data->resolution_mode = GEO_NODE_POINTS_TO_VOLUME_RESOLUTION_MODE_AMOUNT; - data->input_type_radius = GEO_NODE_ATTRIBUTE_INPUT_FLOAT; - node->storage = data; - - bNodeSocket *radius_attribute_socket = nodeFindSocket(node, SOCK_IN, "Radius"); - bNodeSocketValueString *radius_attribute_socket_value = - (bNodeSocketValueString *)radius_attribute_socket->default_value; - STRNCPY(radius_attribute_socket_value->value, "radius"); -} - -static void geo_node_points_to_volume_update(bNodeTree *UNUSED(ntree), bNode *node) -{ - NodeGeometryPointsToVolume *data = (NodeGeometryPointsToVolume *)node->storage; - bNodeSocket *voxel_size_socket = nodeFindSocket(node, SOCK_IN, "Voxel Size"); - bNodeSocket *voxel_amount_socket = nodeFindSocket(node, SOCK_IN, "Voxel Amount"); - nodeSetSocketAvailability(voxel_amount_socket, - data->resolution_mode == - GEO_NODE_POINTS_TO_VOLUME_RESOLUTION_MODE_AMOUNT); - nodeSetSocketAvailability( - voxel_size_socket, data->resolution_mode == GEO_NODE_POINTS_TO_VOLUME_RESOLUTION_MODE_SIZE); - - update_attribute_input_socket_availabilities( - *node, "Radius", (GeometryNodeAttributeInputMode)data->input_type_radius); -} - } // namespace blender::nodes void register_node_type_geo_points_to_volume() diff --git a/source/blender/nodes/geometry/nodes/node_geo_switch.cc b/source/blender/nodes/geometry/nodes/node_geo_switch.cc index 6736e963184..bb0a20f4561 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_switch.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_switch.cc @@ -70,20 +70,6 @@ static void geo_node_switch_init(bNodeTree *UNUSED(tree), bNode *node) namespace blender::nodes { -template<typename T> -void output_input(GeoNodeExecParams ¶ms, - const bool input, - const StringRef input_suffix, - const StringRef output_identifier) -{ - if (input) { - params.set_output(output_identifier, params.extract_input<T>("B" + input_suffix)); - } - else { - params.set_output(output_identifier, params.extract_input<T>("A" + input_suffix)); - } -} - static void geo_node_switch_update(bNodeTree *UNUSED(ntree), bNode *node) { NodeSwitch *node_storage = (NodeSwitch *)node->storage; @@ -99,10 +85,37 @@ static void geo_node_switch_update(bNodeTree *UNUSED(ntree), bNode *node) } } +template<typename T> +static void output_input(GeoNodeExecParams ¶ms, + const bool input, + const StringRef input_suffix, + const StringRef output_identifier) +{ + const std::string name_a = "A" + input_suffix; + const std::string name_b = "B" + input_suffix; + if (input) { + params.set_input_unused(name_a); + if (params.lazy_require_input(name_b)) { + return; + } + params.set_output(output_identifier, params.extract_input<T>(name_b)); + } + else { + params.set_input_unused(name_b); + if (params.lazy_require_input(name_a)) { + return; + } + params.set_output(output_identifier, params.extract_input<T>(name_a)); + } +} + 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.extract_input<bool>("Switch"); + const bool input = params.get_input<bool>("Switch"); switch ((eNodeSocketDatatype)storage.input_type) { case SOCK_FLOAT: { output_input<float>(params, input, "", "Output"); @@ -133,11 +146,11 @@ static void geo_node_switch_exec(GeoNodeExecParams params) break; } case SOCK_OBJECT: { - output_input<bke::PersistentObjectHandle>(params, input, "_007", "Output_007"); + output_input<Object *>(params, input, "_007", "Output_007"); break; } case SOCK_COLLECTION: { - output_input<bke::PersistentCollectionHandle>(params, input, "_008", "Output_008"); + output_input<Collection *>(params, input, "_008", "Output_008"); break; } default: @@ -158,6 +171,7 @@ void register_node_type_geo_switch() node_type_update(&ntype, blender::nodes::geo_node_switch_update); node_type_storage(&ntype, "NodeSwitch", node_free_standard_storage, node_copy_standard_storage); ntype.geometry_node_execute = blender::nodes::geo_node_switch_exec; + ntype.geometry_node_execute_supports_lazyness = true; ntype.draw_buttons = geo_node_switch_layout; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_transform.cc b/source/blender/nodes/geometry/nodes/node_geo_transform.cc index ce52dc90668..9714a4f8a80 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_transform.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_transform.cc @@ -179,22 +179,18 @@ static void geo_node_transform_exec(GeoNodeExecParams params) Mesh *mesh = geometry_set.get_mesh_for_write(); transform_mesh(mesh, translation, rotation, scale); } - if (geometry_set.has_pointcloud()) { PointCloud *pointcloud = geometry_set.get_pointcloud_for_write(); transform_pointcloud(pointcloud, translation, rotation, scale); } - if (geometry_set.has_instances()) { InstancesComponent &instances = geometry_set.get_component_for_write<InstancesComponent>(); transform_instances(instances, translation, rotation, scale); } - if (geometry_set.has_volume()) { Volume *volume = geometry_set.get_volume_for_write(); transform_volume(volume, translation, rotation, scale, params); } - if (geometry_set.has_curve()) { CurveEval *curve = geometry_set.get_curve_for_write(); transform_curve(*curve, translation, rotation, scale); |