Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'source/blender/nodes/geometry')
-rw-r--r--source/blender/nodes/geometry/node_geometry_tree.cc16
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_align_rotation_to_vector.cc44
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_color_ramp.cc16
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_curve_map.cc231
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_mix.cc44
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_sample_texture.cc12
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_vector_rotate.cc352
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_collection_info.cc20
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc238
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc14
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_edge_split.cc2
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_input_material.cc51
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc39
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_material_assign.cc98
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_object_info.cc4
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_point_instance.cc185
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_point_rotate.cc71
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_point_scale.cc34
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_point_separate.cc21
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_points_to_volume.cc58
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_switch.cc48
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_transform.cc4
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 &params, 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 &params)
{
- 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> &center,
+ 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> &center,
+ 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> &center,
+ 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 &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);
+}
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 &params,
- MutableSpan<InstanceReference> r_references)
+static Vector<InstanceReference> get_instance_references__object(GeoNodeExecParams &params)
{
- 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 &params,
- const GeometryComponent &component,
- MutableSpan<InstanceReference> r_references)
+static Vector<InstanceReference> get_instance_references__collection(GeoNodeExecParams &params)
{
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 &params,
- const GeometryComponent &component,
- const int amount)
+static Vector<InstanceReference> get_instance_references(GeoNodeExecParams &params)
{
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 &params)
+/**
+ * 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 &params)
{
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 &params,
- 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 &params,
+ 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);