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.cc30
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_align_rotation_to_vector.cc144
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_clamp.cc54
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_color_ramp.cc37
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_combine_xyz.cc27
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_compare.cc92
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_convert.cc71
-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_fill.cc32
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_map_range.cc140
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_math.cc94
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_mix.cc122
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_proximity.cc38
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_randomize.cc91
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_remove.cc4
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_sample_texture.cc61
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_separate_xyz.cc62
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_transfer.cc597
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_vector_math.cc247
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_vector_rotate.cc352
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_bounding_box.cc11
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_collection_info.cc12
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc208
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc312
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc89
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cone.cc8
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_grid.cc8
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_uv_sphere.cc8
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_object_info.cc9
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_point_distribute.cc175
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_point_instance.cc186
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_point_rotate.cc26
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_point_scale.cc12
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_point_separate.cc18
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_point_translate.cc19
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_points_to_volume.cc14
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_switch.cc163
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_transform.cc23
38 files changed, 2925 insertions, 902 deletions
diff --git a/source/blender/nodes/geometry/node_geometry_tree.cc b/source/blender/nodes/geometry/node_geometry_tree.cc
index 6d3b1d55005..f4cd00b88ed 100644
--- a/source/blender/nodes/geometry/node_geometry_tree.cc
+++ b/source/blender/nodes/geometry/node_geometry_tree.cc
@@ -84,6 +84,34 @@ 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))
+{
+ return ELEM(socket_type,
+ SOCK_FLOAT,
+ SOCK_VECTOR,
+ SOCK_RGBA,
+ SOCK_BOOLEAN,
+ SOCK_INT,
+ SOCK_STRING,
+ SOCK_OBJECT,
+ SOCK_GEOMETRY,
+ SOCK_COLLECTION,
+ SOCK_TEXTURE,
+ SOCK_MATERIAL);
+}
+
void register_node_tree_type_geo(void)
{
bNodeTreeType *tt = ntreeType_Geometry = static_cast<bNodeTreeType *>(
@@ -97,6 +125,8 @@ void register_node_tree_type_geo(void)
tt->update = geometry_node_tree_update;
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 f10b81a33b4..720ca9731a8 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
@@ -15,6 +15,7 @@
*/
#include "BLI_math_rotation.h"
+#include "BLI_task.hh"
#include "UI_interface.h"
#include "UI_resources.h"
@@ -50,91 +51,95 @@ static void geo_node_align_rotation_to_vector_layout(uiLayout *layout,
namespace blender::nodes {
-static void align_rotations_auto_pivot(const Float3ReadAttribute &vectors,
- const FloatReadAttribute &factors,
+static void align_rotations_auto_pivot(const VArray<float3> &vectors,
+ const VArray<float> &factors,
const float3 local_main_axis,
- MutableSpan<float3> rotations)
+ const MutableSpan<float3> rotations)
{
- for (const int i : IndexRange(vectors.size())) {
- const float3 vector = vectors[i];
- if (is_zero_v3(vector)) {
- continue;
- }
+ parallel_for(IndexRange(vectors.size()), 128, [&](IndexRange range) {
+ for (const int i : range) {
+ const float3 vector = vectors[i];
+ if (is_zero_v3(vector)) {
+ continue;
+ }
- float old_rotation[3][3];
- eul_to_mat3(old_rotation, rotations[i]);
- float3 old_axis;
- mul_v3_m3v3(old_axis, old_rotation, local_main_axis);
+ float old_rotation[3][3];
+ eul_to_mat3(old_rotation, rotations[i]);
+ float3 old_axis;
+ mul_v3_m3v3(old_axis, old_rotation, local_main_axis);
- const float3 new_axis = vector.normalized();
- float3 rotation_axis = float3::cross_high_precision(old_axis, new_axis);
- if (is_zero_v3(rotation_axis)) {
- /* The vectors are linearly dependent, so we fall back to another axis. */
- rotation_axis = float3::cross_high_precision(old_axis, float3(1, 0, 0));
+ const float3 new_axis = vector.normalized();
+ float3 rotation_axis = float3::cross_high_precision(old_axis, new_axis);
if (is_zero_v3(rotation_axis)) {
- /* This is now guaranteed to not be zero. */
- rotation_axis = float3::cross_high_precision(old_axis, float3(0, 1, 0));
+ /* The vectors are linearly dependent, so we fall back to another axis. */
+ rotation_axis = float3::cross_high_precision(old_axis, float3(1, 0, 0));
+ if (is_zero_v3(rotation_axis)) {
+ /* This is now guaranteed to not be zero. */
+ rotation_axis = float3::cross_high_precision(old_axis, float3(0, 1, 0));
+ }
}
- }
- const float full_angle = angle_normalized_v3v3(old_axis, new_axis);
- const float angle = factors[i] * full_angle;
+ const float full_angle = angle_normalized_v3v3(old_axis, new_axis);
+ const float angle = factors[i] * full_angle;
- float rotation[3][3];
- axis_angle_to_mat3(rotation, rotation_axis, angle);
+ float rotation[3][3];
+ axis_angle_to_mat3(rotation, rotation_axis, angle);
- float new_rotation_matrix[3][3];
- mul_m3_m3m3(new_rotation_matrix, rotation, old_rotation);
+ float new_rotation_matrix[3][3];
+ mul_m3_m3m3(new_rotation_matrix, rotation, old_rotation);
- float3 new_rotation;
- mat3_to_eul(new_rotation, new_rotation_matrix);
+ float3 new_rotation;
+ mat3_to_eul(new_rotation, new_rotation_matrix);
- rotations[i] = new_rotation;
- }
+ rotations[i] = new_rotation;
+ }
+ });
}
-static void align_rotations_fixed_pivot(const Float3ReadAttribute &vectors,
- const FloatReadAttribute &factors,
+static void align_rotations_fixed_pivot(const VArray<float3> &vectors,
+ const VArray<float> &factors,
const float3 local_main_axis,
const float3 local_pivot_axis,
- MutableSpan<float3> rotations)
+ const MutableSpan<float3> rotations)
{
if (local_main_axis == local_pivot_axis) {
/* Can't compute any meaningful rotation angle in this case. */
return;
}
- for (const int i : IndexRange(vectors.size())) {
- const float3 vector = vectors[i];
- if (is_zero_v3(vector)) {
- continue;
- }
+ parallel_for(IndexRange(vectors.size()), 128, [&](IndexRange range) {
+ for (const int i : range) {
+ const float3 vector = vectors[i];
+ if (is_zero_v3(vector)) {
+ continue;
+ }
- float old_rotation[3][3];
- eul_to_mat3(old_rotation, rotations[i]);
- float3 old_axis;
- mul_v3_m3v3(old_axis, old_rotation, local_main_axis);
- float3 pivot_axis;
- mul_v3_m3v3(pivot_axis, old_rotation, local_pivot_axis);
-
- float full_angle = angle_signed_on_axis_v3v3_v3(vector, old_axis, pivot_axis);
- if (full_angle > M_PI) {
- /* Make sure the point is rotated as little as possible. */
- full_angle -= 2.0f * M_PI;
- }
- const float angle = factors[i] * full_angle;
+ float old_rotation[3][3];
+ eul_to_mat3(old_rotation, rotations[i]);
+ float3 old_axis;
+ mul_v3_m3v3(old_axis, old_rotation, local_main_axis);
+ float3 pivot_axis;
+ mul_v3_m3v3(pivot_axis, old_rotation, local_pivot_axis);
+
+ float full_angle = angle_signed_on_axis_v3v3_v3(vector, old_axis, pivot_axis);
+ if (full_angle > M_PI) {
+ /* Make sure the point is rotated as little as possible. */
+ full_angle -= 2.0f * M_PI;
+ }
+ const float angle = factors[i] * full_angle;
- float rotation[3][3];
- axis_angle_to_mat3(rotation, pivot_axis, angle);
+ float rotation[3][3];
+ axis_angle_to_mat3(rotation, pivot_axis, angle);
- float new_rotation_matrix[3][3];
- mul_m3_m3m3(new_rotation_matrix, rotation, old_rotation);
+ float new_rotation_matrix[3][3];
+ mul_m3_m3m3(new_rotation_matrix, rotation, old_rotation);
- float3 new_rotation;
- mat3_to_eul(new_rotation, new_rotation_matrix);
+ float3 new_rotation;
+ mat3_to_eul(new_rotation, new_rotation_matrix);
- rotations[i] = new_rotation;
- }
+ rotations[i] = new_rotation;
+ }
+ });
}
static void align_rotations_on_component(GeometryComponent &component,
@@ -144,30 +149,30 @@ static void align_rotations_on_component(GeometryComponent &component,
const NodeGeometryAlignRotationToVector &storage = *(const NodeGeometryAlignRotationToVector *)
node.storage;
- OutputAttributePtr rotation_attribute = component.attribute_try_get_for_output(
- "rotation", ATTR_DOMAIN_POINT, CD_PROP_FLOAT3);
- if (!rotation_attribute) {
+ OutputAttribute_Typed<float3> rotations = component.attribute_try_get_for_output<float3>(
+ "rotation", ATTR_DOMAIN_POINT, {0, 0, 0});
+ if (!rotations) {
return;
}
- MutableSpan<float3> rotations = rotation_attribute->get_span<float3>();
- FloatReadAttribute factors = params.get_input_attribute<float>(
+ GVArray_Typed<float> factors = params.get_input_attribute<float>(
"Factor", component, ATTR_DOMAIN_POINT, 1.0f);
- Float3ReadAttribute vectors = params.get_input_attribute<float3>(
+ GVArray_Typed<float3> vectors = params.get_input_attribute<float3>(
"Vector", component, ATTR_DOMAIN_POINT, {0, 0, 1});
float3 local_main_axis{0, 0, 0};
local_main_axis[storage.axis] = 1;
if (storage.pivot_axis == GEO_NODE_ALIGN_ROTATION_TO_VECTOR_PIVOT_AXIS_AUTO) {
- align_rotations_auto_pivot(vectors, factors, local_main_axis, rotations);
+ align_rotations_auto_pivot(vectors, factors, local_main_axis, rotations.as_span());
}
else {
float3 local_pivot_axis{0, 0, 0};
local_pivot_axis[storage.pivot_axis - 1] = 1;
- align_rotations_fixed_pivot(vectors, factors, local_main_axis, local_pivot_axis, rotations);
+ align_rotations_fixed_pivot(
+ vectors, factors, local_main_axis, local_pivot_axis, rotations.as_span());
}
- rotation_attribute.apply_span_and_save();
+ rotations.save();
}
static void geo_node_align_rotation_to_vector_exec(GeoNodeExecParams params)
@@ -183,6 +188,9 @@ static void geo_node_align_rotation_to_vector_exec(GeoNodeExecParams params)
align_rotations_on_component(geometry_set.get_component_for_write<PointCloudComponent>(),
params);
}
+ if (geometry_set.has<CurveComponent>()) {
+ align_rotations_on_component(geometry_set.get_component_for_write<CurveComponent>(), params);
+ }
params.set_output("Geometry", geometry_set);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_clamp.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_clamp.cc
index 95fa24c8bac..21538db5455 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_attribute_clamp.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_clamp.cc
@@ -112,10 +112,13 @@ template<> inline Color4f clamp_value(const Color4f val, const Color4f min, cons
}
template<typename T>
-static void clamp_attribute(Span<T> read_span, MutableSpan<T> span, const T min, const T max)
+static void clamp_attribute(const VArray<T> &inputs,
+ const MutableSpan<T> outputs,
+ const T min,
+ const T max)
{
- for (const int i : span.index_range()) {
- span[i] = clamp_value<T>(read_span[i], min, max);
+ for (const int i : IndexRange(outputs.size())) {
+ outputs[i] = clamp_value<T>(inputs[i], min, max);
}
}
@@ -123,13 +126,13 @@ static AttributeDomain get_result_domain(const GeometryComponent &component,
StringRef source_name,
StringRef result_name)
{
- ReadAttributePtr result_attribute = component.attribute_try_get_for_read(result_name);
- if (result_attribute) {
- return result_attribute->domain();
+ std::optional<AttributeMetaData> result_info = component.attribute_get_meta_data(result_name);
+ if (result_info) {
+ return result_info->domain;
}
- ReadAttributePtr source_attribute = component.attribute_try_get_for_read(source_name);
- if (source_attribute) {
- return source_attribute->domain();
+ std::optional<AttributeMetaData> source_info = component.attribute_get_meta_data(source_name);
+ if (source_info) {
+ return source_info->domain;
}
return ATTR_DOMAIN_POINT;
}
@@ -154,10 +157,10 @@ static void clamp_attribute(GeometryComponent &component, const GeoNodeExecParam
const AttributeDomain domain = get_result_domain(component, attribute_name, result_name);
const int operation = static_cast<int>(storage.operation);
- ReadAttributePtr attribute_input = component.attribute_try_get_for_read(
+ GVArrayPtr attribute_input = component.attribute_try_get_for_read(
attribute_name, domain, data_type);
- OutputAttributePtr attribute_result = component.attribute_try_get_for_output(
+ OutputAttribute attribute_result = component.attribute_try_get_for_output_only(
result_name, domain, data_type);
if (!attribute_result) {
@@ -169,8 +172,6 @@ static void clamp_attribute(GeometryComponent &component, const GeoNodeExecParam
switch (data_type) {
case CD_PROP_FLOAT3: {
- Span<float3> read_span = attribute_input->get_span<float3>();
- MutableSpan<float3> span = attribute_result->get_span_for_write_only<float3>();
float3 min = params.get_input<float3>("Min");
float3 max = params.get_input<float3>("Max");
if (operation == NODE_CLAMP_RANGE) {
@@ -184,38 +185,35 @@ static void clamp_attribute(GeometryComponent &component, const GeoNodeExecParam
std::swap(min.z, max.z);
}
}
- clamp_attribute<float3>(read_span, span, min, max);
+ MutableSpan<float3> results = attribute_result.as_span<float3>();
+ clamp_attribute<float3>(attribute_input->typed<float3>(), results, min, max);
break;
}
case CD_PROP_FLOAT: {
- Span<float> read_span = attribute_input->get_span<float>();
- MutableSpan<float> span = attribute_result->get_span_for_write_only<float>();
const float min = params.get_input<float>("Min_001");
const float max = params.get_input<float>("Max_001");
+ MutableSpan<float> results = attribute_result.as_span<float>();
if (operation == NODE_CLAMP_RANGE && min > max) {
- clamp_attribute<float>(read_span, span, max, min);
+ clamp_attribute<float>(attribute_input->typed<float>(), results, max, min);
}
else {
- clamp_attribute<float>(read_span, span, min, max);
+ clamp_attribute<float>(attribute_input->typed<float>(), results, min, max);
}
break;
}
case CD_PROP_INT32: {
- Span<int> read_span = attribute_input->get_span<int>();
- MutableSpan<int> span = attribute_result->get_span_for_write_only<int>();
const int min = params.get_input<int>("Min_002");
const int max = params.get_input<int>("Max_002");
+ MutableSpan<int> results = attribute_result.as_span<int>();
if (operation == NODE_CLAMP_RANGE && min > max) {
- clamp_attribute<int>(read_span, span, max, min);
+ clamp_attribute<int>(attribute_input->typed<int>(), results, max, min);
}
else {
- clamp_attribute<int>(read_span, span, min, max);
+ clamp_attribute<int>(attribute_input->typed<int>(), results, min, max);
}
break;
}
case CD_PROP_COLOR: {
- Span<Color4f> read_span = attribute_input->get_span<Color4f>();
- MutableSpan<Color4f> span = attribute_result->get_span_for_write_only<Color4f>();
Color4f min = params.get_input<Color4f>("Min_003");
Color4f max = params.get_input<Color4f>("Max_003");
if (operation == NODE_CLAMP_RANGE) {
@@ -232,7 +230,8 @@ static void clamp_attribute(GeometryComponent &component, const GeoNodeExecParam
std::swap(min.a, max.a);
}
}
- clamp_attribute<Color4f>(read_span, span, min, max);
+ MutableSpan<Color4f> results = attribute_result.as_span<Color4f>();
+ clamp_attribute<Color4f>(attribute_input->typed<Color4f>(), results, min, max);
break;
}
default: {
@@ -241,7 +240,7 @@ static void clamp_attribute(GeometryComponent &component, const GeoNodeExecParam
}
}
- attribute_result.apply_span_and_save();
+ attribute_result.save();
}
static void geo_node_attribute_clamp_exec(GeoNodeExecParams params)
@@ -256,6 +255,9 @@ static void geo_node_attribute_clamp_exec(GeoNodeExecParams params)
if (geometry_set.has<PointCloudComponent>()) {
clamp_attribute(geometry_set.get_component_for_write<PointCloudComponent>(), params);
}
+ if (geometry_set.has<CurveComponent>()) {
+ clamp_attribute(geometry_set.get_component_for_write<CurveComponent>(), params);
+ }
params.set_output("Geometry", geometry_set);
}
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 2b913beb670..26ddb0da515 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
@@ -14,6 +14,8 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+#include "BLI_task.hh"
+
#include "BKE_colorband.h"
#include "UI_interface.h"
@@ -47,15 +49,15 @@ static AttributeDomain get_result_domain(const GeometryComponent &component,
StringRef result_name)
{
/* Use the domain of the result attribute if it already exists. */
- ReadAttributePtr result_attribute = component.attribute_try_get_for_read(result_name);
- if (result_attribute) {
- return result_attribute->domain();
+ std::optional<AttributeMetaData> result_info = component.attribute_get_meta_data(result_name);
+ if (result_info) {
+ return result_info->domain;
}
/* Otherwise use the input attribute's domain if it exists. */
- ReadAttributePtr input_attribute = component.attribute_try_get_for_read(input_name);
- if (input_attribute) {
- return input_attribute->domain();
+ std::optional<AttributeMetaData> source_info = component.attribute_get_meta_data(input_name);
+ if (source_info) {
+ return source_info->domain;
}
return ATTR_DOMAIN_POINT;
@@ -71,27 +73,27 @@ static void execute_on_component(const GeoNodeExecParams &params, GeometryCompon
/* Always output a color attribute for now. We might want to allow users to customize.
* Using the type of an existing attribute could work, but does not have a real benefit
* currently. */
- const CustomDataType result_type = CD_PROP_COLOR;
const AttributeDomain result_domain = get_result_domain(component, input_name, result_name);
- OutputAttributePtr attribute_result = component.attribute_try_get_for_output(
- result_name, result_domain, result_type);
+ OutputAttribute_Typed<Color4f> attribute_result =
+ component.attribute_try_get_for_output_only<Color4f>(result_name, result_domain);
if (!attribute_result) {
return;
}
- FloatReadAttribute attribute_in = component.attribute_get_for_read<float>(
+ GVArray_Typed<float> attribute_in = component.attribute_get_for_read<float>(
input_name, result_domain, 0.0f);
- Span<float> data_in = attribute_in.get_span();
- MutableSpan<Color4f> data_out = attribute_result->get_span_for_write_only<Color4f>();
+ MutableSpan<Color4f> results = attribute_result.as_span();
ColorBand *color_ramp = &node_storage->color_ramp;
- for (const int i : data_in.index_range()) {
- BKE_colorband_evaluate(color_ramp, data_in[i], data_out[i]);
- }
+ parallel_for(IndexRange(attribute_in.size()), 512, [&](IndexRange range) {
+ for (const int i : range) {
+ BKE_colorband_evaluate(color_ramp, attribute_in[i], results[i]);
+ }
+ });
- attribute_result.apply_span_and_save();
+ attribute_result.save();
}
static void geo_node_attribute_color_ramp_exec(GeoNodeExecParams params)
@@ -106,6 +108,9 @@ static void geo_node_attribute_color_ramp_exec(GeoNodeExecParams params)
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));
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_combine_xyz.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_combine_xyz.cc
index e9e07d34c17..d8c52d16f41 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_attribute_combine_xyz.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_combine_xyz.cc
@@ -77,9 +77,9 @@ static AttributeDomain get_result_domain(const GeometryComponent &component,
StringRef result_name)
{
/* Use the domain of the result attribute if it already exists. */
- ReadAttributePtr result_attribute = component.attribute_try_get_for_read(result_name);
- if (result_attribute) {
- return result_attribute->domain();
+ std::optional<AttributeMetaData> result_info = component.attribute_get_meta_data(result_name);
+ if (result_info) {
+ return result_info->domain;
}
/* Otherwise use the highest priority domain from existing input attributes, or the default. */
@@ -94,27 +94,25 @@ static void combine_attributes(GeometryComponent &component, const GeoNodeExecPa
}
const AttributeDomain result_domain = get_result_domain(component, params, result_name);
- OutputAttributePtr attribute_result = component.attribute_try_get_for_output(
- result_name, result_domain, CD_PROP_FLOAT3);
+ OutputAttribute_Typed<float3> attribute_result =
+ component.attribute_try_get_for_output_only<float3>(result_name, result_domain);
if (!attribute_result) {
return;
}
- FloatReadAttribute attribute_x = params.get_input_attribute<float>(
+ GVArray_Typed<float> attribute_x = params.get_input_attribute<float>(
"X", component, result_domain, 0.0f);
- FloatReadAttribute attribute_y = params.get_input_attribute<float>(
+ GVArray_Typed<float> attribute_y = params.get_input_attribute<float>(
"Y", component, result_domain, 0.0f);
- FloatReadAttribute attribute_z = params.get_input_attribute<float>(
+ GVArray_Typed<float> attribute_z = params.get_input_attribute<float>(
"Z", component, result_domain, 0.0f);
- MutableSpan<float3> results = attribute_result->get_span_for_write_only<float3>();
- for (const int i : results.index_range()) {
+ for (const int i : IndexRange(attribute_result->size())) {
const float x = attribute_x[i];
const float y = attribute_y[i];
const float z = attribute_z[i];
- const float3 result = float3(x, y, z);
- results[i] = result;
+ attribute_result->set(i, {x, y, z});
}
- attribute_result.apply_span_and_save();
+ attribute_result.save();
}
static void geo_node_attribute_combine_xyz_exec(GeoNodeExecParams params)
@@ -129,6 +127,9 @@ static void geo_node_attribute_combine_xyz_exec(GeoNodeExecParams params)
if (geometry_set.has<PointCloudComponent>()) {
combine_attributes(geometry_set.get_component_for_write<PointCloudComponent>(), params);
}
+ if (geometry_set.has<CurveComponent>()) {
+ combine_attributes(geometry_set.get_component_for_write<CurveComponent>(), params);
+ }
params.set_output("Geometry", geometry_set);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_compare.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_compare.cc
index fe4045c39a6..a2ff1668a06 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_attribute_compare.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_compare.cc
@@ -81,21 +81,18 @@ static void geo_node_attribute_compare_update(bNodeTree *UNUSED(ntree), bNode *n
nodeSetSocketAvailability(socket_threshold, operation_tests_equality(*node_storage));
}
-static void do_math_operation(const FloatReadAttribute &input_a,
- const FloatReadAttribute &input_b,
+static void do_math_operation(const VArray<float> &input_a,
+ const VArray<float> &input_b,
const FloatCompareOperation operation,
MutableSpan<bool> span_result)
{
const int size = input_a.size();
- Span<float> span_a = input_a.get_span();
- Span<float> span_b = input_b.get_span();
-
if (try_dispatch_float_math_fl_fl_to_bool(
operation, [&](auto math_function, const FloatMathOperationInfo &UNUSED(info)) {
for (const int i : IndexRange(size)) {
- const float a = span_a[i];
- const float b = span_b[i];
+ const float a = input_a[i];
+ const float b = input_b[i];
const bool out = math_function(a, b);
span_result[i] = out;
}
@@ -107,8 +104,8 @@ static void do_math_operation(const FloatReadAttribute &input_a,
BLI_assert(false);
}
-static void do_equal_operation_float(const FloatReadAttribute &input_a,
- const FloatReadAttribute &input_b,
+static void do_equal_operation_float(const VArray<float> &input_a,
+ const VArray<float> &input_b,
const float threshold,
MutableSpan<bool> span_result)
{
@@ -120,8 +117,8 @@ static void do_equal_operation_float(const FloatReadAttribute &input_a,
}
}
-static void do_equal_operation_float3(const Float3ReadAttribute &input_a,
- const Float3ReadAttribute &input_b,
+static void do_equal_operation_float3(const VArray<float3> &input_a,
+ const VArray<float3> &input_b,
const float threshold,
MutableSpan<bool> span_result)
{
@@ -134,8 +131,8 @@ static void do_equal_operation_float3(const Float3ReadAttribute &input_a,
}
}
-static void do_equal_operation_color4f(const Color4fReadAttribute &input_a,
- const Color4fReadAttribute &input_b,
+static void do_equal_operation_color4f(const VArray<Color4f> &input_a,
+ const VArray<Color4f> &input_b,
const float threshold,
MutableSpan<bool> span_result)
{
@@ -148,8 +145,8 @@ static void do_equal_operation_color4f(const Color4fReadAttribute &input_a,
}
}
-static void do_equal_operation_bool(const BooleanReadAttribute &input_a,
- const BooleanReadAttribute &input_b,
+static void do_equal_operation_bool(const VArray<bool> &input_a,
+ const VArray<bool> &input_b,
const float UNUSED(threshold),
MutableSpan<bool> span_result)
{
@@ -161,8 +158,8 @@ static void do_equal_operation_bool(const BooleanReadAttribute &input_a,
}
}
-static void do_not_equal_operation_float(const FloatReadAttribute &input_a,
- const FloatReadAttribute &input_b,
+static void do_not_equal_operation_float(const VArray<float> &input_a,
+ const VArray<float> &input_b,
const float threshold,
MutableSpan<bool> span_result)
{
@@ -174,8 +171,8 @@ static void do_not_equal_operation_float(const FloatReadAttribute &input_a,
}
}
-static void do_not_equal_operation_float3(const Float3ReadAttribute &input_a,
- const Float3ReadAttribute &input_b,
+static void do_not_equal_operation_float3(const VArray<float3> &input_a,
+ const VArray<float3> &input_b,
const float threshold,
MutableSpan<bool> span_result)
{
@@ -188,8 +185,8 @@ static void do_not_equal_operation_float3(const Float3ReadAttribute &input_a,
}
}
-static void do_not_equal_operation_color4f(const Color4fReadAttribute &input_a,
- const Color4fReadAttribute &input_b,
+static void do_not_equal_operation_color4f(const VArray<Color4f> &input_a,
+ const VArray<Color4f> &input_b,
const float threshold,
MutableSpan<bool> span_result)
{
@@ -202,8 +199,8 @@ static void do_not_equal_operation_color4f(const Color4fReadAttribute &input_a,
}
}
-static void do_not_equal_operation_bool(const BooleanReadAttribute &input_a,
- const BooleanReadAttribute &input_b,
+static void do_not_equal_operation_bool(const VArray<bool> &input_a,
+ const VArray<bool> &input_b,
const float UNUSED(threshold),
MutableSpan<bool> span_result)
{
@@ -237,9 +234,9 @@ static AttributeDomain get_result_domain(const GeometryComponent &component,
StringRef result_name)
{
/* Use the domain of the result attribute if it already exists. */
- ReadAttributePtr result_attribute = component.attribute_try_get_for_read(result_name);
- if (result_attribute) {
- return result_attribute->domain();
+ std::optional<AttributeMetaData> result_info = component.attribute_get_meta_data(result_name);
+ if (result_info) {
+ return result_info->domain;
}
/* Otherwise use the highest priority domain from existing input attributes, or the default. */
@@ -254,20 +251,19 @@ static void attribute_compare_calc(GeometryComponent &component, const GeoNodeEx
node_storage->operation);
const std::string result_name = params.get_input<std::string>("Result");
- const CustomDataType result_type = CD_PROP_BOOL;
const AttributeDomain result_domain = get_result_domain(component, params, result_name);
- OutputAttributePtr attribute_result = component.attribute_try_get_for_output(
- result_name, result_domain, result_type);
+ OutputAttribute_Typed<bool> attribute_result = component.attribute_try_get_for_output_only<bool>(
+ result_name, result_domain);
if (!attribute_result) {
return;
}
const CustomDataType input_data_type = get_data_type(component, params, *node_storage);
- ReadAttributePtr attribute_a = params.get_input_attribute(
+ GVArrayPtr attribute_a = params.get_input_attribute(
"A", component, result_domain, input_data_type, nullptr);
- ReadAttributePtr attribute_b = params.get_input_attribute(
+ GVArrayPtr attribute_b = params.get_input_attribute(
"B", component, result_domain, input_data_type, nullptr);
if (!attribute_a || !attribute_b) {
@@ -275,7 +271,7 @@ static void attribute_compare_calc(GeometryComponent &component, const GeoNodeEx
return;
}
- MutableSpan<bool> result_span = attribute_result->get_span_for_write_only<bool>();
+ MutableSpan<bool> result_span = attribute_result.as_span();
/* Use specific types for correct equality operations, but for other operations we use implicit
* conversions and float comparison. In other words, the comparison is not element-wise. */
@@ -283,38 +279,47 @@ static void attribute_compare_calc(GeometryComponent &component, const GeoNodeEx
const float threshold = params.get_input<float>("Threshold");
if (operation == NODE_FLOAT_COMPARE_EQUAL) {
if (input_data_type == CD_PROP_FLOAT) {
- do_equal_operation_float(*attribute_a, *attribute_b, threshold, result_span);
+ do_equal_operation_float(
+ attribute_a->typed<float>(), attribute_b->typed<float>(), threshold, result_span);
}
else if (input_data_type == CD_PROP_FLOAT3) {
- do_equal_operation_float3(*attribute_a, *attribute_b, threshold, result_span);
+ do_equal_operation_float3(
+ attribute_a->typed<float3>(), attribute_b->typed<float3>(), threshold, result_span);
}
else if (input_data_type == CD_PROP_COLOR) {
- do_equal_operation_color4f(*attribute_a, *attribute_b, threshold, result_span);
+ do_equal_operation_color4f(
+ attribute_a->typed<Color4f>(), attribute_b->typed<Color4f>(), threshold, result_span);
}
else if (input_data_type == CD_PROP_BOOL) {
- do_equal_operation_bool(*attribute_a, *attribute_b, threshold, result_span);
+ do_equal_operation_bool(
+ attribute_a->typed<bool>(), attribute_b->typed<bool>(), threshold, result_span);
}
}
else if (operation == NODE_FLOAT_COMPARE_NOT_EQUAL) {
if (input_data_type == CD_PROP_FLOAT) {
- do_not_equal_operation_float(*attribute_a, *attribute_b, threshold, result_span);
+ do_not_equal_operation_float(
+ attribute_a->typed<float>(), attribute_b->typed<float>(), threshold, result_span);
}
else if (input_data_type == CD_PROP_FLOAT3) {
- do_not_equal_operation_float3(*attribute_a, *attribute_b, threshold, result_span);
+ do_not_equal_operation_float3(
+ attribute_a->typed<float3>(), attribute_b->typed<float3>(), threshold, result_span);
}
else if (input_data_type == CD_PROP_COLOR) {
- do_not_equal_operation_color4f(*attribute_a, *attribute_b, threshold, result_span);
+ do_not_equal_operation_color4f(
+ attribute_a->typed<Color4f>(), attribute_b->typed<Color4f>(), threshold, result_span);
}
else if (input_data_type == CD_PROP_BOOL) {
- do_not_equal_operation_bool(*attribute_a, *attribute_b, threshold, result_span);
+ do_not_equal_operation_bool(
+ attribute_a->typed<bool>(), attribute_b->typed<bool>(), threshold, result_span);
}
}
}
else {
- do_math_operation(*attribute_a, *attribute_b, operation, result_span);
+ do_math_operation(
+ attribute_a->typed<float>(), attribute_b->typed<float>(), operation, result_span);
}
- attribute_result.apply_span_and_save();
+ attribute_result.save();
}
static void geo_node_attribute_compare_exec(GeoNodeExecParams params)
@@ -329,6 +334,9 @@ static void geo_node_attribute_compare_exec(GeoNodeExecParams params)
if (geometry_set.has<PointCloudComponent>()) {
attribute_compare_calc(geometry_set.get_component_for_write<PointCloudComponent>(), params);
}
+ if (geometry_set.has<CurveComponent>()) {
+ attribute_compare_calc(geometry_set.get_component_for_write<CurveComponent>(), params);
+ }
params.set_output("Geometry", geometry_set);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_convert.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_convert.cc
index 19c3aaa9c85..7b40456b180 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_attribute_convert.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_convert.cc
@@ -35,8 +35,10 @@ static void geo_node_attribute_convert_layout(uiLayout *layout,
bContext *UNUSED(C),
PointerRNA *ptr)
{
- uiItemR(layout, ptr, "domain", 0, "", ICON_NONE);
- uiItemR(layout, ptr, "data_type", 0, "", ICON_NONE);
+ uiLayoutSetPropSep(layout, true);
+ uiLayoutSetPropDecorate(layout, false);
+ uiItemR(layout, ptr, "domain", 0, nullptr, ICON_NONE);
+ uiItemR(layout, ptr, "data_type", 0, IFACE_("Type"), ICON_NONE);
}
static void geo_node_attribute_convert_init(bNodeTree *UNUSED(tree), bNode *node)
@@ -44,26 +46,27 @@ static void geo_node_attribute_convert_init(bNodeTree *UNUSED(tree), bNode *node
NodeAttributeConvert *data = (NodeAttributeConvert *)MEM_callocN(sizeof(NodeAttributeConvert),
__func__);
- data->data_type = CD_PROP_FLOAT;
+ data->data_type = CD_AUTO_FROM_NAME;
data->domain = ATTR_DOMAIN_AUTO;
node->storage = data;
}
namespace blender::nodes {
-static AttributeDomain get_result_domain(const GeometryComponent &component,
- StringRef source_name,
- StringRef result_name)
+static AttributeMetaData get_result_domain_and_type(const GeometryComponent &component,
+ const StringRef source_name,
+ const StringRef result_name)
{
- ReadAttributePtr result_attribute = component.attribute_try_get_for_read(result_name);
- if (result_attribute) {
- return result_attribute->domain();
+ std::optional<AttributeMetaData> result_info = component.attribute_get_meta_data(result_name);
+ if (result_info) {
+ return *result_info;
}
- ReadAttributePtr source_attribute = component.attribute_try_get_for_read(source_name);
- if (source_attribute) {
- return source_attribute->domain();
+ std::optional<AttributeMetaData> source_info = component.attribute_get_meta_data(source_name);
+ if (source_info) {
+ return *source_info;
}
- return ATTR_DOMAIN_POINT;
+ /* The node won't do anything in this case, but we still have to return a value. */
+ return AttributeMetaData{ATTR_DOMAIN_POINT, CD_PROP_BOOL};
}
static bool conversion_can_be_skipped(const GeometryComponent &component,
@@ -75,14 +78,14 @@ static bool conversion_can_be_skipped(const GeometryComponent &component,
if (source_name != result_name) {
return false;
}
- ReadAttributePtr read_attribute = component.attribute_try_get_for_read(source_name);
- if (!read_attribute) {
+ std::optional<AttributeMetaData> info = component.attribute_get_meta_data(result_name);
+ if (!info) {
return false;
}
- if (read_attribute->domain() != result_domain) {
+ if (info->domain != result_domain) {
return false;
}
- if (read_attribute->cpp_type() != *bke::custom_data_type_to_cpp_type(result_type)) {
+ if (info->data_type != result_type) {
return false;
}
return true;
@@ -92,19 +95,20 @@ static void attribute_convert_calc(GeometryComponent &component,
const GeoNodeExecParams &params,
const StringRef source_name,
const StringRef result_name,
- const CustomDataType result_type,
+ const CustomDataType data_type,
const AttributeDomain domain)
{
- const AttributeDomain result_domain = (domain == ATTR_DOMAIN_AUTO) ?
- get_result_domain(
- component, source_name, result_name) :
- domain;
+ const AttributeMetaData auto_info = get_result_domain_and_type(
+ component, source_name, result_name);
+ const AttributeDomain result_domain = (domain == ATTR_DOMAIN_AUTO) ? auto_info.domain : domain;
+ const CustomDataType result_type = (data_type == CD_AUTO_FROM_NAME) ? auto_info.data_type :
+ data_type;
if (conversion_can_be_skipped(component, source_name, result_name, result_domain, result_type)) {
return;
}
- ReadAttributePtr source_attribute = component.attribute_try_get_for_read(
+ GVArrayPtr source_attribute = component.attribute_try_get_for_read(
source_name, result_domain, result_type);
if (!source_attribute) {
params.error_message_add(NodeWarningType::Error,
@@ -112,25 +116,22 @@ static void attribute_convert_calc(GeometryComponent &component,
return;
}
- OutputAttributePtr result_attribute = component.attribute_try_get_for_output(
+ OutputAttribute result_attribute = component.attribute_try_get_for_output_only(
result_name, result_domain, result_type);
if (!result_attribute) {
return;
}
- fn::GSpan source_span = source_attribute->get_span();
- fn::GMutableSpan result_span = result_attribute->get_span_for_write_only();
- if (source_span.is_empty() || result_span.is_empty()) {
- return;
- }
+ GVArray_GSpan source_span{*source_attribute};
+ GMutableSpan result_span = result_attribute.as_span();
+
BLI_assert(source_span.size() == result_span.size());
const CPPType *cpp_type = bke::custom_data_type_to_cpp_type(result_type);
BLI_assert(cpp_type != nullptr);
cpp_type->copy_to_initialized_n(source_span.data(), result_span.data(), result_span.size());
-
- result_attribute.apply_span_and_save();
+ result_attribute.save();
}
static void geo_node_attribute_convert_exec(GeoNodeExecParams params)
@@ -166,6 +167,14 @@ static void geo_node_attribute_convert_exec(GeoNodeExecParams params)
data_type,
domain);
}
+ if (geometry_set.has<CurveComponent>()) {
+ attribute_convert_calc(geometry_set.get_component_for_write<CurveComponent>(),
+ params,
+ source_name,
+ result_name,
+ data_type,
+ domain);
+ }
params.set_output("Geometry", geometry_set);
}
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_fill.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_fill.cc
index 7b4483a31a1..60522fd0f72 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_attribute_fill.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_fill.cc
@@ -68,13 +68,12 @@ static void geo_node_attribute_fill_update(bNodeTree *UNUSED(ntree), bNode *node
namespace blender::nodes {
-static AttributeDomain get_result_domain(const GeometryComponent &component,
- StringRef attribute_name)
+static AttributeDomain get_result_domain(const GeometryComponent &component, const StringRef name)
{
/* Use the domain of the result attribute if it already exists. */
- ReadAttributePtr result_attribute = component.attribute_try_get_for_read(attribute_name);
- if (result_attribute) {
- return result_attribute->domain();
+ std::optional<AttributeMetaData> result_info = component.attribute_get_meta_data(name);
+ if (result_info) {
+ return result_info->domain;
}
return ATTR_DOMAIN_POINT;
}
@@ -93,7 +92,7 @@ static void fill_attribute(GeometryComponent &component, const GeoNodeExecParams
get_result_domain(component, attribute_name) :
domain;
- OutputAttributePtr attribute = component.attribute_try_get_for_output(
+ OutputAttribute attribute = component.attribute_try_get_for_output_only(
attribute_name, result_domain, data_type);
if (!attribute) {
return;
@@ -102,38 +101,34 @@ static void fill_attribute(GeometryComponent &component, const GeoNodeExecParams
switch (data_type) {
case CD_PROP_FLOAT: {
const float value = params.get_input<float>("Value_001");
- MutableSpan<float> attribute_span = attribute->get_span_for_write_only<float>();
- attribute_span.fill(value);
+ attribute->fill(&value);
break;
}
case CD_PROP_FLOAT3: {
const float3 value = params.get_input<float3>("Value");
- MutableSpan<float3> attribute_span = attribute->get_span_for_write_only<float3>();
- attribute_span.fill(value);
+ attribute->fill(&value);
break;
}
case CD_PROP_COLOR: {
const Color4f value = params.get_input<Color4f>("Value_002");
- MutableSpan<Color4f> attribute_span = attribute->get_span_for_write_only<Color4f>();
- attribute_span.fill(value);
+ attribute->fill(&value);
break;
}
case CD_PROP_BOOL: {
const bool value = params.get_input<bool>("Value_003");
- MutableSpan<bool> attribute_span = attribute->get_span_for_write_only<bool>();
- attribute_span.fill(value);
+ attribute->fill(&value);
break;
}
case CD_PROP_INT32: {
const int value = params.get_input<int>("Value_004");
- MutableSpan<int> attribute_span = attribute->get_span_for_write_only<int>();
- attribute_span.fill(value);
+ attribute->fill(&value);
+ break;
}
default:
break;
}
- attribute.apply_span_and_save();
+ attribute.save();
}
static void geo_node_attribute_fill_exec(GeoNodeExecParams params)
@@ -148,6 +143,9 @@ static void geo_node_attribute_fill_exec(GeoNodeExecParams params)
if (geometry_set.has<PointCloudComponent>()) {
fill_attribute(geometry_set.get_component_for_write<PointCloudComponent>(), params);
}
+ if (geometry_set.has<CurveComponent>()) {
+ fill_attribute(geometry_set.get_component_for_write<CurveComponent>(), params);
+ }
params.set_output("Geometry", geometry_set);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_map_range.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_map_range.cc
index fdbdadf90b6..40fe675bd6c 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_attribute_map_range.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_map_range.cc
@@ -15,6 +15,7 @@
*/
#include "BLI_math_base_safe.h"
+#include "BLI_task.hh"
#include "UI_interface.h"
#include "UI_resources.h"
@@ -192,8 +193,8 @@ static float map_smootherstep(const float value,
return min_to + factor_mapped * (max_to - min_to);
}
-static void map_range_float(FloatReadAttribute attribute_input,
- FloatWriteAttribute attribute_result,
+static void map_range_float(const VArray<float> &attribute_input,
+ MutableSpan<float> results,
const GeoNodeExecParams &params)
{
const bNode &node = params.node();
@@ -204,33 +205,40 @@ static void map_range_float(FloatReadAttribute attribute_input,
const float min_to = params.get_input<float>("To Min");
const float max_to = params.get_input<float>("To Max");
- Span<float> span = attribute_input.get_span();
- MutableSpan<float> result_span = attribute_result.get_span();
+ VArray_Span<float> span{attribute_input};
switch (interpolation_type) {
case NODE_MAP_RANGE_LINEAR: {
- for (int i : span.index_range()) {
- result_span[i] = map_linear(span[i], min_from, max_from, min_to, max_to);
- }
+ parallel_for(span.index_range(), 2048, [&](IndexRange range) {
+ for (const int i : range) {
+ results[i] = map_linear(span[i], min_from, max_from, min_to, max_to);
+ }
+ });
break;
}
case NODE_MAP_RANGE_STEPPED: {
const float steps = params.get_input<float>("Steps");
- for (int i : span.index_range()) {
- result_span[i] = map_stepped(span[i], min_from, max_from, min_to, max_to, steps);
- }
+ parallel_for(span.index_range(), 1024, [&](IndexRange range) {
+ for (const int i : range) {
+ results[i] = map_stepped(span[i], min_from, max_from, min_to, max_to, steps);
+ }
+ });
break;
}
case NODE_MAP_RANGE_SMOOTHSTEP: {
- for (int i : span.index_range()) {
- result_span[i] = map_smoothstep(span[i], min_from, max_from, min_to, max_to);
- }
+ parallel_for(span.index_range(), 1024, [&](IndexRange range) {
+ for (const int i : range) {
+ results[i] = map_smoothstep(span[i], min_from, max_from, min_to, max_to);
+ }
+ });
break;
}
case NODE_MAP_RANGE_SMOOTHERSTEP: {
- for (int i : span.index_range()) {
- result_span[i] = map_smootherstep(span[i], min_from, max_from, min_to, max_to);
- }
+ parallel_for(span.index_range(), 1024, [&](IndexRange range) {
+ for (const int i : range) {
+ results[i] = map_smootherstep(span[i], min_from, max_from, min_to, max_to);
+ }
+ });
break;
}
}
@@ -241,14 +249,16 @@ static void map_range_float(FloatReadAttribute attribute_input,
const float clamp_min = min_to < max_to ? min_to : max_to;
const float clamp_max = min_to < max_to ? max_to : min_to;
- for (int i : result_span.index_range()) {
- result_span[i] = std::clamp(result_span[i], clamp_min, clamp_max);
- }
+ parallel_for(results.index_range(), 2048, [&](IndexRange range) {
+ for (const int i : range) {
+ results[i] = std::clamp(results[i], clamp_min, clamp_max);
+ }
+ });
}
}
-static void map_range_float3(Float3ReadAttribute attribute_input,
- Float3WriteAttribute attribute_result,
+static void map_range_float3(const VArray<float3> &attribute_input,
+ const MutableSpan<float3> results,
const GeoNodeExecParams &params)
{
const bNode &node = params.node();
@@ -259,44 +269,51 @@ static void map_range_float3(Float3ReadAttribute attribute_input,
const float3 min_to = params.get_input<float3>("To Min_001");
const float3 max_to = params.get_input<float3>("To Max_001");
- Span<float3> span = attribute_input.get_span();
- MutableSpan<float3> result_span = attribute_result.get_span();
+ VArray_Span<float3> span{attribute_input};
switch (interpolation_type) {
case NODE_MAP_RANGE_LINEAR: {
- for (int i : span.index_range()) {
- result_span[i].x = map_linear(span[i].x, min_from.x, max_from.x, min_to.x, max_to.x);
- result_span[i].y = map_linear(span[i].y, min_from.y, max_from.y, min_to.y, max_to.y);
- result_span[i].z = map_linear(span[i].z, min_from.z, max_from.z, min_to.z, max_to.z);
- }
+ parallel_for(span.index_range(), 1024, [&](IndexRange range) {
+ for (const int i : range) {
+ results[i].x = map_linear(span[i].x, min_from.x, max_from.x, min_to.x, max_to.x);
+ results[i].y = map_linear(span[i].y, min_from.y, max_from.y, min_to.y, max_to.y);
+ results[i].z = map_linear(span[i].z, min_from.z, max_from.z, min_to.z, max_to.z);
+ }
+ });
break;
}
case NODE_MAP_RANGE_STEPPED: {
const float3 steps = params.get_input<float3>("Steps_001");
- for (int i : span.index_range()) {
- result_span[i].x = map_stepped(
- span[i].x, min_from.x, max_from.x, min_to.x, max_to.x, steps.x);
- result_span[i].y = map_stepped(
- span[i].y, min_from.y, max_from.y, min_to.y, max_to.y, steps.y);
- result_span[i].z = map_stepped(
- span[i].z, min_from.z, max_from.z, min_to.z, max_to.z, steps.z);
- }
+ parallel_for(span.index_range(), 1024, [&](IndexRange range) {
+ for (const int i : range) {
+ results[i].x = map_stepped(
+ span[i].x, min_from.x, max_from.x, min_to.x, max_to.x, steps.x);
+ results[i].y = map_stepped(
+ span[i].y, min_from.y, max_from.y, min_to.y, max_to.y, steps.y);
+ results[i].z = map_stepped(
+ span[i].z, min_from.z, max_from.z, min_to.z, max_to.z, steps.z);
+ }
+ });
break;
}
case NODE_MAP_RANGE_SMOOTHSTEP: {
- for (int i : span.index_range()) {
- result_span[i].x = map_smoothstep(span[i].x, min_from.x, max_from.x, min_to.x, max_to.x);
- result_span[i].y = map_smoothstep(span[i].y, min_from.y, max_from.y, min_to.y, max_to.y);
- result_span[i].z = map_smoothstep(span[i].z, min_from.z, max_from.z, min_to.z, max_to.z);
- }
+ parallel_for(span.index_range(), 1024, [&](IndexRange range) {
+ for (const int i : range) {
+ results[i].x = map_smoothstep(span[i].x, min_from.x, max_from.x, min_to.x, max_to.x);
+ results[i].y = map_smoothstep(span[i].y, min_from.y, max_from.y, min_to.y, max_to.y);
+ results[i].z = map_smoothstep(span[i].z, min_from.z, max_from.z, min_to.z, max_to.z);
+ }
+ });
break;
}
case NODE_MAP_RANGE_SMOOTHERSTEP: {
- for (int i : span.index_range()) {
- result_span[i].x = map_smootherstep(span[i].x, min_from.x, max_from.x, min_to.x, max_to.x);
- result_span[i].y = map_smootherstep(span[i].y, min_from.y, max_from.y, min_to.y, max_to.y);
- result_span[i].z = map_smootherstep(span[i].z, min_from.z, max_from.z, min_to.z, max_to.z);
- }
+ parallel_for(span.index_range(), 1024, [&](IndexRange range) {
+ for (const int i : range) {
+ results[i].x = map_smootherstep(span[i].x, min_from.x, max_from.x, min_to.x, max_to.x);
+ results[i].y = map_smootherstep(span[i].y, min_from.y, max_from.y, min_to.y, max_to.y);
+ results[i].z = map_smootherstep(span[i].z, min_from.z, max_from.z, min_to.z, max_to.z);
+ }
+ });
break;
}
}
@@ -313,8 +330,8 @@ static void map_range_float3(Float3ReadAttribute attribute_input,
clamp_min.z = min_to.z < max_to.z ? min_to.z : max_to.z;
clamp_max.z = min_to.z < max_to.z ? max_to.z : min_to.z;
- for (int i : result_span.index_range()) {
- clamp_v3_v3v3(result_span[i], clamp_min, clamp_max);
+ for (int i : results.index_range()) {
+ clamp_v3_v3v3(results[i], clamp_min, clamp_max);
}
}
}
@@ -323,13 +340,13 @@ static AttributeDomain get_result_domain(const GeometryComponent &component,
StringRef source_name,
StringRef result_name)
{
- ReadAttributePtr result_attribute = component.attribute_try_get_for_read(result_name);
- if (result_attribute) {
- return result_attribute->domain();
+ std::optional<AttributeMetaData> result_info = component.attribute_get_meta_data(result_name);
+ if (result_info) {
+ return result_info->domain;
}
- ReadAttributePtr source_attribute = component.attribute_try_get_for_read(source_name);
- if (source_attribute) {
- return source_attribute->domain();
+ std::optional<AttributeMetaData> source_info = component.attribute_get_meta_data(source_name);
+ if (source_info) {
+ return source_info->domain;
}
return ATTR_DOMAIN_POINT;
}
@@ -349,8 +366,7 @@ static void map_range_attribute(GeometryComponent &component, const GeoNodeExecP
const AttributeDomain domain = get_result_domain(component, input_name, result_name);
- ReadAttributePtr attribute_input = component.attribute_try_get_for_read(
- input_name, domain, data_type);
+ GVArrayPtr attribute_input = component.attribute_try_get_for_read(input_name, domain, data_type);
if (!attribute_input) {
params.error_message_add(NodeWarningType::Error,
@@ -358,7 +374,7 @@ static void map_range_attribute(GeometryComponent &component, const GeoNodeExecP
return;
}
- OutputAttributePtr attribute_result = component.attribute_try_get_for_output(
+ OutputAttribute attribute_result = component.attribute_try_get_for_output_only(
result_name, domain, data_type);
if (!attribute_result) {
params.error_message_add(NodeWarningType::Error,
@@ -369,18 +385,19 @@ static void map_range_attribute(GeometryComponent &component, const GeoNodeExecP
switch (data_type) {
case CD_PROP_FLOAT: {
- map_range_float(*attribute_input, *attribute_result, params);
+ map_range_float(attribute_input->typed<float>(), attribute_result.as_span<float>(), params);
break;
}
case CD_PROP_FLOAT3: {
- map_range_float3(*attribute_input, *attribute_result, params);
+ map_range_float3(
+ attribute_input->typed<float3>(), attribute_result.as_span<float3>(), params);
break;
}
default:
BLI_assert_unreachable();
}
- attribute_result.apply_span_and_save();
+ attribute_result.save();
}
static void geo_node_attribute_map_range_exec(GeoNodeExecParams params)
@@ -393,6 +410,9 @@ static void geo_node_attribute_map_range_exec(GeoNodeExecParams params)
if (geometry_set.has<PointCloudComponent>()) {
map_range_attribute(geometry_set.get_component_for_write<PointCloudComponent>(), params);
}
+ if (geometry_set.has<CurveComponent>()) {
+ map_range_attribute(geometry_set.get_component_for_write<CurveComponent>(), params);
+ }
params.set_output("Geometry", geometry_set);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_math.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_math.cc
index 5ee31e78be2..ce0ca31cc2b 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_attribute_math.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_math.cc
@@ -14,6 +14,8 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+#include "BLI_task.hh"
+
#include "UI_interface.h"
#include "UI_resources.h"
@@ -149,46 +151,52 @@ static void geo_node_attribute_math_update(bNodeTree *UNUSED(ntree), bNode *node
operation_use_input_c(operation));
}
-static void do_math_operation(Span<float> span_a,
- Span<float> span_b,
- Span<float> span_c,
+static void do_math_operation(const VArray<float> &span_a,
+ const VArray<float> &span_b,
+ const VArray<float> &span_c,
MutableSpan<float> span_result,
const NodeMathOperation operation)
{
bool success = try_dispatch_float_math_fl_fl_fl_to_fl(
operation, [&](auto math_function, const FloatMathOperationInfo &UNUSED(info)) {
- for (const int i : IndexRange(span_result.size())) {
- span_result[i] = math_function(span_a[i], span_b[i], span_c[i]);
- }
+ parallel_for(IndexRange(span_result.size()), 512, [&](IndexRange range) {
+ for (const int i : range) {
+ span_result[i] = math_function(span_a[i], span_b[i], span_c[i]);
+ }
+ });
});
BLI_assert(success);
UNUSED_VARS_NDEBUG(success);
}
-static void do_math_operation(Span<float> span_a,
- Span<float> span_b,
+static void do_math_operation(const VArray<float> &span_a,
+ const VArray<float> &span_b,
MutableSpan<float> span_result,
const NodeMathOperation operation)
{
bool success = try_dispatch_float_math_fl_fl_to_fl(
operation, [&](auto math_function, const FloatMathOperationInfo &UNUSED(info)) {
- for (const int i : IndexRange(span_result.size())) {
- span_result[i] = math_function(span_a[i], span_b[i]);
- }
+ parallel_for(IndexRange(span_result.size()), 1024, [&](IndexRange range) {
+ for (const int i : range) {
+ span_result[i] = math_function(span_a[i], span_b[i]);
+ }
+ });
});
BLI_assert(success);
UNUSED_VARS_NDEBUG(success);
}
-static void do_math_operation(Span<float> span_input,
+static void do_math_operation(const VArray<float> &span_input,
MutableSpan<float> span_result,
const NodeMathOperation operation)
{
bool success = try_dispatch_float_math_fl_to_fl(
operation, [&](auto math_function, const FloatMathOperationInfo &UNUSED(info)) {
- for (const int i : IndexRange(span_result.size())) {
- span_result[i] = math_function(span_input[i]);
- }
+ parallel_for(IndexRange(span_result.size()), 1024, [&](IndexRange range) {
+ for (const int i : range) {
+ span_result[i] = math_function(span_input[i]);
+ }
+ });
});
BLI_assert(success);
UNUSED_VARS_NDEBUG(success);
@@ -200,9 +208,9 @@ static AttributeDomain get_result_domain(const GeometryComponent &component,
StringRef result_name)
{
/* Use the domain of the result attribute if it already exists. */
- ReadAttributePtr result_attribute = component.attribute_try_get_for_read(result_name);
- if (result_attribute) {
- return result_attribute->domain();
+ std::optional<AttributeMetaData> result_info = component.attribute_get_meta_data(result_name);
+ if (result_info) {
+ return result_info->domain;
}
/* Otherwise use the highest priority domain from existing input attributes, or the default. */
@@ -224,56 +232,39 @@ static void attribute_math_calc(GeometryComponent &component, const GeoNodeExecP
const std::string result_name = params.get_input<std::string>("Result");
/* The result type of this node is always float. */
- const CustomDataType result_type = CD_PROP_FLOAT;
const AttributeDomain result_domain = get_result_domain(
component, params, operation, result_name);
- OutputAttributePtr attribute_result = component.attribute_try_get_for_output(
- result_name, result_domain, result_type);
+ OutputAttribute_Typed<float> attribute_result =
+ component.attribute_try_get_for_output_only<float>(result_name, result_domain);
if (!attribute_result) {
return;
}
- ReadAttributePtr attribute_a = params.get_input_attribute(
- "A", component, result_domain, result_type, nullptr);
- if (!attribute_a) {
- return;
- }
+ GVArray_Typed<float> attribute_a = params.get_input_attribute<float>(
+ "A", component, result_domain, 0.0f);
- /* Note that passing the data with `get_span<float>()` works
+ MutableSpan<float> result_span = attribute_result.as_span();
+
+ /* Note that passing the data with `get_internal_span<float>()` works
* because the attributes were accessed with #CD_PROP_FLOAT. */
if (operation_use_input_b(operation)) {
- ReadAttributePtr attribute_b = params.get_input_attribute(
- "B", component, result_domain, result_type, nullptr);
- if (!attribute_b) {
- return;
- }
+ GVArray_Typed<float> attribute_b = params.get_input_attribute<float>(
+ "B", component, result_domain, 0.0f);
if (operation_use_input_c(operation)) {
- ReadAttributePtr attribute_c = params.get_input_attribute(
- "C", component, result_domain, result_type, nullptr);
- if (!attribute_c) {
- return;
- }
- do_math_operation(attribute_a->get_span<float>(),
- attribute_b->get_span<float>(),
- attribute_c->get_span<float>(),
- attribute_result->get_span_for_write_only<float>(),
- operation);
+ GVArray_Typed<float> attribute_c = params.get_input_attribute<float>(
+ "C", component, result_domain, 0.0f);
+ do_math_operation(attribute_a, attribute_b, attribute_c, result_span, operation);
}
else {
- do_math_operation(attribute_a->get_span<float>(),
- attribute_b->get_span<float>(),
- attribute_result->get_span_for_write_only<float>(),
- operation);
+ do_math_operation(attribute_a, attribute_b, result_span, operation);
}
}
else {
- do_math_operation(attribute_a->get_span<float>(),
- attribute_result->get_span_for_write_only<float>(),
- operation);
+ do_math_operation(attribute_a, result_span, operation);
}
- attribute_result.apply_span_and_save();
+ attribute_result.save();
}
static void geo_node_attribute_math_exec(GeoNodeExecParams params)
@@ -288,6 +279,9 @@ static void geo_node_attribute_math_exec(GeoNodeExecParams params)
if (geometry_set.has<PointCloudComponent>()) {
attribute_math_calc(geometry_set.get_component_for_write<PointCloudComponent>(), params);
}
+ if (geometry_set.has<CurveComponent>()) {
+ attribute_math_calc(geometry_set.get_component_for_write<CurveComponent>(), params);
+ }
params.set_output("Geometry", geometry_set);
}
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 9d8cd3dfa82..3e5326edbf6 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_attribute_mix.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_mix.cc
@@ -14,6 +14,8 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+#include "BLI_task.hh"
+
#include "BKE_material.h"
#include "DNA_material_types.h"
@@ -58,72 +60,87 @@ static void geo_node_attribute_mix_layout(uiLayout *layout, bContext *UNUSED(C),
namespace blender::nodes {
static void do_mix_operation_float(const int blend_mode,
- const FloatReadAttribute &factors,
- const FloatReadAttribute &inputs_a,
- const FloatReadAttribute &inputs_b,
- FloatWriteAttribute results)
+ const VArray<float> &factors,
+ const VArray<float> &inputs_a,
+ const VArray<float> &inputs_b,
+ VMutableArray<float> &results)
{
const int size = results.size();
- for (const int i : IndexRange(size)) {
- const float factor = factors[i];
- float3 a{inputs_a[i]};
- const float3 b{inputs_b[i]};
- ramp_blend(blend_mode, a, factor, b);
- const float result = a.x;
- results.set(i, result);
- }
+ parallel_for(IndexRange(size), 512, [&](IndexRange range) {
+ for (const int i : range) {
+ const float factor = factors[i];
+ float3 a{inputs_a[i]};
+ const float3 b{inputs_b[i]};
+ ramp_blend(blend_mode, a, factor, b);
+ const float result = a.x;
+ results.set(i, result);
+ }
+ });
}
static void do_mix_operation_float3(const int blend_mode,
- const FloatReadAttribute &factors,
- const Float3ReadAttribute &inputs_a,
- const Float3ReadAttribute &inputs_b,
- Float3WriteAttribute results)
+ const VArray<float> &factors,
+ const VArray<float3> &inputs_a,
+ const VArray<float3> &inputs_b,
+ VMutableArray<float3> &results)
{
const int size = results.size();
- for (const int i : IndexRange(size)) {
- const float factor = factors[i];
- float3 a = inputs_a[i];
- const float3 b = inputs_b[i];
- ramp_blend(blend_mode, a, factor, b);
- results.set(i, a);
- }
+ parallel_for(IndexRange(size), 512, [&](IndexRange range) {
+ for (const int i : range) {
+ const float factor = factors[i];
+ float3 a = inputs_a[i];
+ const float3 b = inputs_b[i];
+ ramp_blend(blend_mode, a, factor, b);
+ results.set(i, a);
+ }
+ });
}
static void do_mix_operation_color4f(const int blend_mode,
- const FloatReadAttribute &factors,
- const Color4fReadAttribute &inputs_a,
- const Color4fReadAttribute &inputs_b,
- Color4fWriteAttribute results)
+ const VArray<float> &factors,
+ const VArray<Color4f> &inputs_a,
+ const VArray<Color4f> &inputs_b,
+ VMutableArray<Color4f> &results)
{
const int size = results.size();
- for (const int i : IndexRange(size)) {
- const float factor = factors[i];
- Color4f a = inputs_a[i];
- const Color4f b = inputs_b[i];
- ramp_blend(blend_mode, a, factor, b);
- results.set(i, a);
- }
+ parallel_for(IndexRange(size), 512, [&](IndexRange range) {
+ for (const int i : range) {
+ const float factor = factors[i];
+ Color4f a = inputs_a[i];
+ const Color4f b = inputs_b[i];
+ ramp_blend(blend_mode, a, factor, b);
+ results.set(i, a);
+ }
+ });
}
static void do_mix_operation(const CustomDataType result_type,
int blend_mode,
- const FloatReadAttribute &attribute_factor,
- const ReadAttribute &attribute_a,
- const ReadAttribute &attribute_b,
- WriteAttribute &attribute_result)
+ const VArray<float> &attribute_factor,
+ const GVArray &attribute_a,
+ const GVArray &attribute_b,
+ GVMutableArray &attribute_result)
{
if (result_type == CD_PROP_FLOAT) {
- do_mix_operation_float(
- blend_mode, attribute_factor, attribute_a, attribute_b, attribute_result);
+ do_mix_operation_float(blend_mode,
+ attribute_factor,
+ attribute_a.typed<float>(),
+ attribute_b.typed<float>(),
+ attribute_result.typed<float>());
}
else if (result_type == CD_PROP_FLOAT3) {
- do_mix_operation_float3(
- blend_mode, attribute_factor, attribute_a, attribute_b, attribute_result);
+ do_mix_operation_float3(blend_mode,
+ attribute_factor,
+ attribute_a.typed<float3>(),
+ attribute_b.typed<float3>(),
+ attribute_result.typed<float3>());
}
else if (result_type == CD_PROP_COLOR) {
- do_mix_operation_color4f(
- blend_mode, attribute_factor, attribute_a, attribute_b, attribute_result);
+ do_mix_operation_color4f(blend_mode,
+ attribute_factor,
+ attribute_a.typed<Color4f>(),
+ attribute_b.typed<Color4f>(),
+ attribute_result.typed<Color4f>());
}
}
@@ -132,9 +149,9 @@ static AttributeDomain get_result_domain(const GeometryComponent &component,
StringRef result_name)
{
/* Use the domain of the result attribute if it already exists. */
- ReadAttributePtr result_attribute = component.attribute_try_get_for_read(result_name);
- if (result_attribute) {
- return result_attribute->domain();
+ std::optional<AttributeMetaData> result_info = component.attribute_get_meta_data(result_name);
+ if (result_info) {
+ return result_info->domain;
}
/* Otherwise use the highest priority domain from existing input attributes, or the default. */
@@ -158,17 +175,17 @@ static void attribute_mix_calc(GeometryComponent &component, const GeoNodeExecPa
const AttributeDomain result_domain = get_result_domain(component, params, result_name);
- OutputAttributePtr attribute_result = component.attribute_try_get_for_output(
+ OutputAttribute attribute_result = component.attribute_try_get_for_output_only(
result_name, result_domain, result_type);
if (!attribute_result) {
return;
}
- FloatReadAttribute attribute_factor = params.get_input_attribute<float>(
+ GVArray_Typed<float> attribute_factor = params.get_input_attribute<float>(
"Factor", component, result_domain, 0.5f);
- ReadAttributePtr attribute_a = params.get_input_attribute(
+ GVArrayPtr attribute_a = params.get_input_attribute(
"A", component, result_domain, result_type, nullptr);
- ReadAttributePtr attribute_b = params.get_input_attribute(
+ GVArrayPtr attribute_b = params.get_input_attribute(
"B", component, result_domain, result_type, nullptr);
do_mix_operation(result_type,
@@ -192,6 +209,9 @@ static void geo_node_attribute_mix_exec(GeoNodeExecParams params)
if (geometry_set.has<PointCloudComponent>()) {
attribute_mix_calc(geometry_set.get_component_for_write<PointCloudComponent>(), params);
}
+ if (geometry_set.has<CurveComponent>()) {
+ attribute_mix_calc(geometry_set.get_component_for_write<CurveComponent>(), params);
+ }
params.set_output("Geometry", geometry_set);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_proximity.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_proximity.cc
index f09a9bf056e..9c22b7fa87f 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_attribute_proximity.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_proximity.cc
@@ -62,7 +62,7 @@ namespace blender::nodes {
static void proximity_calc(MutableSpan<float> distance_span,
MutableSpan<float3> location_span,
- Span<float3> positions,
+ const VArray<float3> &positions,
BVHTreeFromMesh &tree_data_mesh,
BVHTreeFromPointCloud &tree_data_pointcloud,
const bool bvh_mesh_success,
@@ -169,19 +169,18 @@ static void attribute_calc_proximity(GeometryComponent &component,
const AttributeDomain result_domain = ATTR_DOMAIN_POINT;
const std::string distance_attribute_name = params.get_input<std::string>("Distance");
- OutputAttributePtr distance_attribute = component.attribute_try_get_for_output(
- distance_attribute_name, result_domain, CD_PROP_FLOAT);
+ OutputAttribute_Typed<float> distance_attribute =
+ component.attribute_try_get_for_output_only<float>(distance_attribute_name, result_domain);
const std::string location_attribute_name = params.get_input<std::string>("Position");
- OutputAttributePtr location_attribute = component.attribute_try_get_for_output(
- location_attribute_name, result_domain, CD_PROP_FLOAT3);
-
- ReadAttributePtr position_attribute = component.attribute_try_get_for_read("position");
- BLI_assert(position_attribute->custom_data_type() == CD_PROP_FLOAT3);
+ OutputAttribute_Typed<float3> location_attribute =
+ component.attribute_try_get_for_output_only<float3>(location_attribute_name, result_domain);
+ ReadAttributeLookup position_attribute = component.attribute_try_get_for_read("position");
if (!position_attribute || (!distance_attribute && !location_attribute)) {
return;
}
+ BLI_assert(position_attribute.varray->type().is<float3>());
const bNode &node = params.node();
const NodeGeometryAttributeProximity &storage = *(const NodeGeometryAttributeProximity *)
@@ -204,18 +203,15 @@ static void attribute_calc_proximity(GeometryComponent &component,
tree_data_pointcloud);
}
- Span<float3> position_span = position_attribute->get_span<float3>();
-
- MutableSpan<float> distance_span = distance_attribute ?
- distance_attribute->get_span_for_write_only<float>() :
- MutableSpan<float>();
- MutableSpan<float3> location_span = location_attribute ?
- location_attribute->get_span_for_write_only<float3>() :
- MutableSpan<float3>();
+ GVArray_Typed<float3> positions{*position_attribute.varray};
+ MutableSpan<float> distance_span = distance_attribute ? distance_attribute.as_span() :
+ MutableSpan<float>();
+ MutableSpan<float3> location_span = location_attribute ? location_attribute.as_span() :
+ MutableSpan<float3>();
proximity_calc(distance_span,
location_span,
- position_span,
+ positions,
tree_data_mesh,
tree_data_pointcloud,
bvh_mesh_success,
@@ -231,10 +227,10 @@ static void attribute_calc_proximity(GeometryComponent &component,
}
if (distance_attribute) {
- distance_attribute.apply_span_and_save();
+ distance_attribute.save();
}
if (location_attribute) {
- location_attribute.apply_span_and_save();
+ location_attribute.save();
}
}
@@ -257,6 +253,10 @@ static void geo_node_attribute_proximity_exec(GeoNodeExecParams params)
attribute_calc_proximity(
geometry_set.get_component_for_write<PointCloudComponent>(), geometry_set_target, params);
}
+ if (geometry_set.has<CurveComponent>()) {
+ attribute_calc_proximity(
+ geometry_set.get_component_for_write<CurveComponent>(), geometry_set_target, params);
+ }
params.set_output("Geometry", geometry_set);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_randomize.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_randomize.cc
index 28263287a10..286411b7d28 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_attribute_randomize.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_randomize.cc
@@ -16,6 +16,7 @@
#include "BLI_hash.h"
#include "BLI_rand.hh"
+#include "BLI_task.hh"
#include "UI_interface.h"
#include "UI_resources.h"
@@ -125,28 +126,36 @@ static void randomize_attribute(MutableSpan<T> span,
/* The operations could be templated too, but it doesn't make the code much shorter. */
switch (operation) {
case GEO_NODE_ATTRIBUTE_RANDOMIZE_REPLACE_CREATE:
- for (const int i : span.index_range()) {
- const T random_value = random_value_in_range<T>(ids[i], seed, min, max);
- span[i] = random_value;
- }
+ parallel_for(span.index_range(), 512, [&](IndexRange range) {
+ for (const int i : range) {
+ const T random_value = random_value_in_range<T>(ids[i], seed, min, max);
+ span[i] = random_value;
+ }
+ });
break;
case GEO_NODE_ATTRIBUTE_RANDOMIZE_ADD:
- for (const int i : span.index_range()) {
- const T random_value = random_value_in_range<T>(ids[i], seed, min, max);
- span[i] = span[i] + random_value;
- }
+ parallel_for(span.index_range(), 512, [&](IndexRange range) {
+ for (const int i : range) {
+ const T random_value = random_value_in_range<T>(ids[i], seed, min, max);
+ span[i] = span[i] + random_value;
+ }
+ });
break;
case GEO_NODE_ATTRIBUTE_RANDOMIZE_SUBTRACT:
- for (const int i : span.index_range()) {
- const T random_value = random_value_in_range<T>(ids[i], seed, min, max);
- span[i] = span[i] - random_value;
- }
+ parallel_for(span.index_range(), 512, [&](IndexRange range) {
+ for (const int i : range) {
+ const T random_value = random_value_in_range<T>(ids[i], seed, min, max);
+ span[i] = span[i] - random_value;
+ }
+ });
break;
case GEO_NODE_ATTRIBUTE_RANDOMIZE_MULTIPLY:
- for (const int i : span.index_range()) {
- const T random_value = random_value_in_range<T>(ids[i], seed, min, max);
- span[i] = span[i] * random_value;
- }
+ parallel_for(span.index_range(), 512, [&](IndexRange range) {
+ for (const int i : range) {
+ const T random_value = random_value_in_range<T>(ids[i], seed, min, max);
+ span[i] = span[i] * random_value;
+ }
+ });
break;
default:
BLI_assert(false);
@@ -161,10 +170,12 @@ static void randomize_attribute_bool(MutableSpan<bool> span,
{
BLI_assert(operation == GEO_NODE_ATTRIBUTE_RANDOMIZE_REPLACE_CREATE);
UNUSED_VARS_NDEBUG(operation);
- for (const int i : span.index_range()) {
- const bool random_value = BLI_hash_int_2d_to_float(ids[i], seed) > 0.5f;
- span[i] = random_value;
- }
+ parallel_for(span.index_range(), 512, [&](IndexRange range) {
+ for (const int i : range) {
+ const bool random_value = BLI_hash_int_2d_to_float(ids[i], seed) > 0.5f;
+ span[i] = random_value;
+ }
+ });
}
Array<uint32_t> get_geometry_element_ids_as_uints(const GeometryComponent &component,
@@ -173,15 +184,17 @@ Array<uint32_t> get_geometry_element_ids_as_uints(const GeometryComponent &compo
const int domain_size = component.attribute_domain_size(domain);
/* Hash the reserved name attribute "id" as a (hopefully) stable seed for each point. */
- ReadAttributePtr hash_attribute = component.attribute_try_get_for_read("id", domain);
+ GVArrayPtr hash_attribute = component.attribute_try_get_for_read("id", domain);
Array<uint32_t> hashes(domain_size);
if (hash_attribute) {
BLI_assert(hashes.size() == hash_attribute->size());
- const CPPType &cpp_type = hash_attribute->cpp_type();
- fn::GSpan items = hash_attribute->get_span();
- for (const int i : hashes.index_range()) {
- hashes[i] = cpp_type.hash(items[i]);
- }
+ const CPPType &cpp_type = hash_attribute->type();
+ GVArray_GSpan items{*hash_attribute};
+ parallel_for(hashes.index_range(), 512, [&](IndexRange range) {
+ for (const int i : range) {
+ hashes[i] = cpp_type.hash(items[i]);
+ }
+ });
}
else {
/* If there is no "id" attribute for per-point variation, just create it here. */
@@ -196,12 +209,12 @@ Array<uint32_t> get_geometry_element_ids_as_uints(const GeometryComponent &compo
static AttributeDomain get_result_domain(const GeometryComponent &component,
const GeoNodeExecParams &params,
- StringRef attribute_name)
+ const StringRef name)
{
/* Use the domain of the result attribute if it already exists. */
- ReadAttributePtr result_attribute = component.attribute_try_get_for_read(attribute_name);
- if (result_attribute) {
- return result_attribute->domain();
+ std::optional<AttributeMetaData> result_info = component.attribute_get_meta_data(name);
+ if (result_info) {
+ return result_info->domain;
}
/* Otherwise use the input domain chosen in the interface. */
@@ -228,15 +241,13 @@ static void randomize_attribute_on_component(GeometryComponent &component,
const AttributeDomain domain = get_result_domain(component, params, attribute_name);
- OutputAttributePtr attribute = component.attribute_try_get_for_output(
+ OutputAttribute attribute = component.attribute_try_get_for_output(
attribute_name, domain, data_type);
if (!attribute) {
return;
}
- fn::GMutableSpan span = (operation == GEO_NODE_ATTRIBUTE_RANDOMIZE_REPLACE_CREATE) ?
- attribute->get_span_for_write_only() :
- attribute->get_span();
+ GMutableSpan span = attribute.as_span();
Array<uint32_t> hashes = get_geometry_element_ids_as_uints(component, domain);
@@ -269,8 +280,8 @@ static void randomize_attribute_on_component(GeometryComponent &component,
}
}
- attribute.apply_span_and_save();
-} // namespace blender::nodes
+ attribute.save();
+}
static void geo_node_random_attribute_exec(GeoNodeExecParams params)
{
@@ -304,6 +315,14 @@ static void geo_node_random_attribute_exec(GeoNodeExecParams params)
operation,
seed);
}
+ if (geometry_set.has<CurveComponent>()) {
+ randomize_attribute_on_component(geometry_set.get_component_for_write<CurveComponent>(),
+ params,
+ attribute_name,
+ data_type,
+ operation,
+ seed);
+ }
params.set_output("Geometry", geometry_set);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_remove.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_remove.cc
index 837f0c3629a..e4f3230ebb9 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_attribute_remove.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_remove.cc
@@ -70,6 +70,10 @@ static void geo_node_attribute_remove_exec(GeoNodeExecParams params)
remove_attribute(
geometry_set.get_component_for_write<PointCloudComponent>(), params, attribute_names);
}
+ if (geometry_set.has<CurveComponent>()) {
+ remove_attribute(
+ geometry_set.get_component_for_write<CurveComponent>(), params, attribute_names);
+ }
params.set_output("Geometry", geometry_set);
}
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 d0b2595b5b9..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
@@ -15,6 +15,7 @@
*/
#include "BLI_compiler_attrs.h"
+#include "BLI_task.hh"
#include "DNA_texture_types.h"
@@ -29,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, ""},
@@ -39,29 +41,22 @@ 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,
- StringRef result_attribute_name,
- StringRef map_attribute_name)
+ const StringRef result_name,
+ const StringRef map_name)
{
/* Use the domain of the result attribute if it already exists. */
- ReadAttributePtr result_attribute = component.attribute_try_get_for_read(result_attribute_name);
- if (result_attribute) {
- return result_attribute->domain();
+ std::optional<AttributeMetaData> result_info = component.attribute_get_meta_data(result_name);
+ if (result_info) {
+ return result_info->domain;
}
/* Otherwise use the name of the map attribute. */
- ReadAttributePtr map_attribute = component.attribute_try_get_for_read(map_attribute_name);
- if (map_attribute) {
- return map_attribute->domain();
+ std::optional<AttributeMetaData> map_info = component.attribute_get_meta_data(map_name);
+ if (map_info) {
+ return map_info->domain;
}
/* The node won't execute in this case, but we still have to return a value. */
@@ -70,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;
}
@@ -85,25 +79,28 @@ static void execute_on_component(GeometryComponent &component, const GeoNodeExec
const AttributeDomain result_domain = get_result_domain(
component, result_attribute_name, mapping_name);
- OutputAttributePtr attribute_out = component.attribute_try_get_for_output(
- result_attribute_name, result_domain, CD_PROP_COLOR);
+ OutputAttribute_Typed<Color4f> attribute_out =
+ component.attribute_try_get_for_output_only<Color4f>(result_attribute_name, result_domain);
if (!attribute_out) {
return;
}
- Float3ReadAttribute mapping_attribute = component.attribute_get_for_read<float3>(
+ GVArray_Typed<float3> mapping_attribute = component.attribute_get_for_read<float3>(
mapping_name, result_domain, {0, 0, 0});
- MutableSpan<Color4f> colors = attribute_out->get_span<Color4f>();
- for (const int i : IndexRange(mapping_attribute.size())) {
- TexResult texture_result = {0};
- const float3 position = mapping_attribute[i];
- /* For legacy reasons we have to map [0, 1] to [-1, 1] to support uv mappings. */
- const float3 remapped_position = position * 2.0f - float3(1.0f);
- BKE_texture_get_value(nullptr, texture, remapped_position, &texture_result, false);
- colors[i] = {texture_result.tr, texture_result.tg, texture_result.tb, texture_result.ta};
- }
- attribute_out.apply_span_and_save();
+ MutableSpan<Color4f> colors = attribute_out.as_span();
+ parallel_for(IndexRange(mapping_attribute.size()), 128, [&](IndexRange range) {
+ for (const int i : range) {
+ TexResult texture_result = {0};
+ const float3 position = mapping_attribute[i];
+ /* For legacy reasons we have to map [0, 1] to [-1, 1] to support uv mappings. */
+ const float3 remapped_position = position * 2.0f - float3(1.0f);
+ BKE_texture_get_value(nullptr, texture, remapped_position, &texture_result, false);
+ colors[i] = {texture_result.tr, texture_result.tg, texture_result.tb, texture_result.ta};
+ }
+ });
+
+ attribute_out.save();
}
static void geo_node_attribute_sample_texture_exec(GeoNodeExecParams params)
@@ -118,6 +115,9 @@ static void geo_node_attribute_sample_texture_exec(GeoNodeExecParams params)
if (geometry_set.has<PointCloudComponent>()) {
execute_on_component(geometry_set.get_component_for_write<PointCloudComponent>(), params);
}
+ if (geometry_set.has<CurveComponent>()) {
+ execute_on_component(geometry_set.get_component_for_write<CurveComponent>(), params);
+ }
params.set_output("Geometry", geometry_set);
}
@@ -137,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_separate_xyz.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_separate_xyz.cc
index 656dc51149e..137a72bb707 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_attribute_separate_xyz.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_separate_xyz.cc
@@ -71,23 +71,23 @@ static void extract_input(const int index, const Span<float3> &input, MutableSpa
static AttributeDomain get_result_domain(const GeometryComponent &component,
const GeoNodeExecParams &params,
- StringRef result_name_x,
- StringRef result_name_y,
- StringRef result_name_z)
+ const StringRef name_x,
+ const StringRef name_y,
+ const StringRef name_z)
{
/* Use the highest priority domain from any existing attribute outputs. */
Vector<AttributeDomain, 3> output_domains;
- ReadAttributePtr attribute_x = component.attribute_try_get_for_read(result_name_x);
- ReadAttributePtr attribute_y = component.attribute_try_get_for_read(result_name_y);
- ReadAttributePtr attribute_z = component.attribute_try_get_for_read(result_name_z);
- if (attribute_x) {
- output_domains.append(attribute_x->domain());
+ std::optional<AttributeMetaData> info_x = component.attribute_get_meta_data(name_x);
+ std::optional<AttributeMetaData> info_y = component.attribute_get_meta_data(name_y);
+ std::optional<AttributeMetaData> info_z = component.attribute_get_meta_data(name_z);
+ if (info_x) {
+ output_domains.append(info_x->domain);
}
- if (attribute_y) {
- output_domains.append(attribute_y->domain());
+ if (info_y) {
+ output_domains.append(info_y->domain);
}
- if (attribute_z) {
- output_domains.append(attribute_z->domain());
+ if (info_z) {
+ output_domains.append(info_z->domain);
}
if (output_domains.size() > 0) {
return bke::attribute_domain_highest_priority(output_domains);
@@ -107,37 +107,32 @@ static void separate_attribute(GeometryComponent &component, const GeoNodeExecPa
}
/* The node is only for float3 to float conversions. */
- const CustomDataType input_type = CD_PROP_FLOAT3;
- const CustomDataType result_type = CD_PROP_FLOAT;
const AttributeDomain result_domain = get_result_domain(
component, params, result_name_x, result_name_y, result_name_z);
- ReadAttributePtr attribute_input = params.get_input_attribute(
- "Vector", component, result_domain, input_type, nullptr);
- if (!attribute_input) {
- return;
- }
- const Span<float3> input_span = attribute_input->get_span<float3>();
+ GVArray_Typed<float3> attribute_input = params.get_input_attribute<float3>(
+ "Vector", component, result_domain, {0, 0, 0});
+ VArray_Span<float3> input_span{*attribute_input};
- OutputAttributePtr attribute_result_x = component.attribute_try_get_for_output(
- result_name_x, result_domain, result_type);
- OutputAttributePtr attribute_result_y = component.attribute_try_get_for_output(
- result_name_y, result_domain, result_type);
- OutputAttributePtr attribute_result_z = component.attribute_try_get_for_output(
- result_name_z, result_domain, result_type);
+ OutputAttribute_Typed<float> attribute_result_x =
+ component.attribute_try_get_for_output_only<float>(result_name_x, result_domain);
+ OutputAttribute_Typed<float> attribute_result_y =
+ component.attribute_try_get_for_output_only<float>(result_name_y, result_domain);
+ OutputAttribute_Typed<float> attribute_result_z =
+ component.attribute_try_get_for_output_only<float>(result_name_z, result_domain);
/* Only extract the components for the outputs with a given attribute. */
if (attribute_result_x) {
- extract_input(0, input_span, attribute_result_x->get_span_for_write_only<float>());
- attribute_result_x.apply_span_and_save();
+ extract_input(0, input_span, attribute_result_x.as_span());
+ attribute_result_x.save();
}
if (attribute_result_y) {
- extract_input(1, input_span, attribute_result_y->get_span_for_write_only<float>());
- attribute_result_y.apply_span_and_save();
+ extract_input(1, input_span, attribute_result_y.as_span());
+ attribute_result_y.save();
}
if (attribute_result_z) {
- extract_input(2, input_span, attribute_result_z->get_span_for_write_only<float>());
- attribute_result_z.apply_span_and_save();
+ extract_input(2, input_span, attribute_result_z.as_span());
+ attribute_result_z.save();
}
}
@@ -153,6 +148,9 @@ static void geo_node_attribute_separate_xyz_exec(GeoNodeExecParams params)
if (geometry_set.has<PointCloudComponent>()) {
separate_attribute(geometry_set.get_component_for_write<PointCloudComponent>(), params);
}
+ if (geometry_set.has<CurveComponent>()) {
+ separate_attribute(geometry_set.get_component_for_write<CurveComponent>(), params);
+ }
params.set_output("Geometry", geometry_set);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_transfer.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_transfer.cc
new file mode 100644
index 00000000000..4b677dc5c82
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_transfer.cc
@@ -0,0 +1,597 @@
+/*
+ * 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_kdopbvh.h"
+
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+#include "DNA_pointcloud_types.h"
+
+#include "BKE_bvhutils.h"
+#include "BKE_mesh_runtime.h"
+#include "BKE_mesh_sample.hh"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "node_geometry_util.hh"
+
+static bNodeSocketTemplate geo_node_attribute_transfer_in[] = {
+ {SOCK_GEOMETRY, N_("Geometry")},
+ {SOCK_GEOMETRY, N_("Source Geometry")},
+ {SOCK_STRING, N_("Source")},
+ {SOCK_STRING, N_("Destination")},
+ {-1, ""},
+};
+
+static bNodeSocketTemplate geo_node_attribute_transfer_out[] = {
+ {SOCK_GEOMETRY, N_("Geometry")},
+ {-1, ""},
+};
+
+static void geo_node_attribute_transfer_layout(uiLayout *layout,
+ bContext *UNUSED(C),
+ PointerRNA *ptr)
+{
+ uiLayoutSetPropSep(layout, true);
+ uiLayoutSetPropDecorate(layout, false);
+ uiItemR(layout, ptr, "domain", 0, IFACE_("Domain"), ICON_NONE);
+ uiItemR(layout, ptr, "mapping", 0, IFACE_("Mapping"), ICON_NONE);
+}
+
+namespace blender::nodes {
+
+static void geo_node_attribute_transfer_init(bNodeTree *UNUSED(tree), bNode *node)
+{
+ NodeGeometryAttributeTransfer *data = (NodeGeometryAttributeTransfer *)MEM_callocN(
+ sizeof(NodeGeometryAttributeTransfer), __func__);
+ data->domain = ATTR_DOMAIN_AUTO;
+ node->storage = data;
+}
+
+static void get_result_domain_and_data_type(const GeometrySet &src_geometry,
+ const GeometryComponent &dst_component,
+ const StringRef attribute_name,
+ CustomDataType *r_data_type,
+ AttributeDomain *r_domain)
+{
+ Vector<CustomDataType> data_types;
+ Vector<AttributeDomain> domains;
+
+ const PointCloudComponent *pointcloud_component =
+ src_geometry.get_component_for_read<PointCloudComponent>();
+ if (pointcloud_component != nullptr) {
+ std::optional<AttributeMetaData> meta_data = pointcloud_component->attribute_get_meta_data(
+ attribute_name);
+ if (meta_data.has_value()) {
+ data_types.append(meta_data->data_type);
+ domains.append(meta_data->domain);
+ }
+ }
+
+ const MeshComponent *mesh_component = src_geometry.get_component_for_read<MeshComponent>();
+ if (mesh_component != nullptr) {
+ std::optional<AttributeMetaData> meta_data = mesh_component->attribute_get_meta_data(
+ attribute_name);
+ if (meta_data.has_value()) {
+ data_types.append(meta_data->data_type);
+ domains.append(meta_data->domain);
+ }
+ }
+
+ *r_data_type = bke::attribute_data_type_highest_complexity(data_types);
+
+ if (dst_component.type() == GEO_COMPONENT_TYPE_POINT_CLOUD) {
+ *r_domain = ATTR_DOMAIN_POINT;
+ }
+ else {
+ *r_domain = bke::attribute_domain_highest_priority(domains);
+ }
+}
+
+static Span<MLoopTri> get_mesh_looptris(const Mesh &mesh)
+{
+ /* This only updates a cache and can be considered to be logically const. */
+ const MLoopTri *looptris = BKE_mesh_runtime_looptri_ensure(const_cast<Mesh *>(&mesh));
+ const int looptris_len = BKE_mesh_runtime_looptri_len(&mesh);
+ return {looptris, looptris_len};
+}
+
+static void get_closest_in_bvhtree(BVHTreeFromMesh &tree_data,
+ const VArray<float3> &positions,
+ const MutableSpan<int> r_indices,
+ const MutableSpan<float> r_distances_sq,
+ const MutableSpan<float3> r_positions)
+{
+ BLI_assert(positions.size() == r_indices.size() || r_indices.is_empty());
+ BLI_assert(positions.size() == r_distances_sq.size() || r_distances_sq.is_empty());
+ BLI_assert(positions.size() == r_positions.size() || r_positions.is_empty());
+
+ for (const int i : positions.index_range()) {
+ BVHTreeNearest nearest;
+ nearest.dist_sq = FLT_MAX;
+ const float3 position = positions[i];
+ BLI_bvhtree_find_nearest(
+ tree_data.tree, position, &nearest, tree_data.nearest_callback, &tree_data);
+ if (!r_indices.is_empty()) {
+ r_indices[i] = nearest.index;
+ }
+ if (!r_distances_sq.is_empty()) {
+ r_distances_sq[i] = nearest.dist_sq;
+ }
+ if (!r_positions.is_empty()) {
+ r_positions[i] = nearest.co;
+ }
+ }
+}
+
+static void get_closest_pointcloud_points(const PointCloud &pointcloud,
+ const VArray<float3> &positions,
+ const MutableSpan<int> r_indices,
+ const MutableSpan<float> r_distances_sq)
+{
+ BLI_assert(positions.size() == r_indices.size());
+ BLI_assert(pointcloud.totpoint > 0);
+
+ BVHTreeFromPointCloud tree_data;
+ BKE_bvhtree_from_pointcloud_get(&tree_data, &pointcloud, 2);
+
+ for (const int i : positions.index_range()) {
+ BVHTreeNearest nearest;
+ nearest.dist_sq = FLT_MAX;
+ const float3 position = positions[i];
+ BLI_bvhtree_find_nearest(
+ tree_data.tree, position, &nearest, tree_data.nearest_callback, &tree_data);
+ r_indices[i] = nearest.index;
+ r_distances_sq[i] = nearest.dist_sq;
+ }
+
+ free_bvhtree_from_pointcloud(&tree_data);
+}
+
+static void get_closest_mesh_points(const Mesh &mesh,
+ const VArray<float3> &positions,
+ const MutableSpan<int> r_point_indices,
+ const MutableSpan<float> r_distances_sq,
+ const MutableSpan<float3> r_positions)
+{
+ BLI_assert(mesh.totvert > 0);
+ BVHTreeFromMesh tree_data;
+ BKE_bvhtree_from_mesh_get(&tree_data, const_cast<Mesh *>(&mesh), BVHTREE_FROM_VERTS, 2);
+ get_closest_in_bvhtree(tree_data, positions, r_point_indices, r_distances_sq, r_positions);
+ free_bvhtree_from_mesh(&tree_data);
+}
+
+static void get_closest_mesh_edges(const Mesh &mesh,
+ const VArray<float3> &positions,
+ const MutableSpan<int> r_edge_indices,
+ const MutableSpan<float> r_distances_sq,
+ const MutableSpan<float3> r_positions)
+{
+ BLI_assert(mesh.totedge > 0);
+ BVHTreeFromMesh tree_data;
+ BKE_bvhtree_from_mesh_get(&tree_data, const_cast<Mesh *>(&mesh), BVHTREE_FROM_EDGES, 2);
+ get_closest_in_bvhtree(tree_data, positions, r_edge_indices, r_distances_sq, r_positions);
+ free_bvhtree_from_mesh(&tree_data);
+}
+
+static void get_closest_mesh_looptris(const Mesh &mesh,
+ const VArray<float3> &positions,
+ const MutableSpan<int> r_looptri_indices,
+ const MutableSpan<float> r_distances_sq,
+ const MutableSpan<float3> r_positions)
+{
+ BLI_assert(mesh.totpoly > 0);
+ BVHTreeFromMesh tree_data;
+ BKE_bvhtree_from_mesh_get(&tree_data, const_cast<Mesh *>(&mesh), BVHTREE_FROM_LOOPTRI, 2);
+ get_closest_in_bvhtree(tree_data, positions, r_looptri_indices, r_distances_sq, r_positions);
+ free_bvhtree_from_mesh(&tree_data);
+}
+
+static void get_closest_mesh_polygons(const Mesh &mesh,
+ const VArray<float3> &positions,
+ const MutableSpan<int> r_poly_indices,
+ const MutableSpan<float> r_distances_sq,
+ const MutableSpan<float3> r_positions)
+{
+ BLI_assert(mesh.totpoly > 0);
+
+ Array<int> looptri_indices(positions.size());
+ get_closest_mesh_looptris(mesh, positions, looptri_indices, r_distances_sq, r_positions);
+
+ Span<MLoopTri> looptris = get_mesh_looptris(mesh);
+ for (const int i : positions.index_range()) {
+ const MLoopTri &looptri = looptris[looptri_indices[i]];
+ r_poly_indices[i] = looptri.poly;
+ }
+}
+
+/* The closest corner is defined to be the closest corner on the closest face. */
+static void get_closest_mesh_corners(const Mesh &mesh,
+ const VArray<float3> &positions,
+ const MutableSpan<int> r_corner_indices,
+ const MutableSpan<float> r_distances_sq,
+ const MutableSpan<float3> r_positions)
+{
+ BLI_assert(mesh.totloop > 0);
+ Array<int> poly_indices(positions.size());
+ get_closest_mesh_polygons(mesh, positions, poly_indices, {}, {});
+
+ for (const int i : positions.index_range()) {
+ const float3 position = positions[i];
+ const int poly_index = poly_indices[i];
+ const MPoly &poly = mesh.mpoly[poly_index];
+
+ /* Find the closest vertex in the polygon. */
+ float min_distance_sq = FLT_MAX;
+ const MVert *closest_mvert;
+ int closest_loop_index = 0;
+ for (const int loop_index : IndexRange(poly.loopstart, poly.totloop)) {
+ const MLoop &loop = mesh.mloop[loop_index];
+ const int vertex_index = loop.v;
+ const MVert &mvert = mesh.mvert[vertex_index];
+ const float distance_sq = float3::distance_squared(position, mvert.co);
+ if (distance_sq < min_distance_sq) {
+ min_distance_sq = distance_sq;
+ closest_loop_index = loop_index;
+ closest_mvert = &mvert;
+ }
+ }
+ if (!r_corner_indices.is_empty()) {
+ r_corner_indices[i] = closest_loop_index;
+ }
+ if (!r_positions.is_empty()) {
+ r_positions[i] = closest_mvert->co;
+ }
+ if (!r_distances_sq.is_empty()) {
+ r_distances_sq[i] = min_distance_sq;
+ }
+ }
+}
+
+static void get_barycentric_coords(const Mesh &mesh,
+ const Span<int> looptri_indices,
+ const Span<float3> positions,
+ const MutableSpan<float3> r_bary_coords)
+{
+ BLI_assert(r_bary_coords.size() == positions.size());
+ BLI_assert(r_bary_coords.size() == looptri_indices.size());
+
+ Span<MLoopTri> looptris = get_mesh_looptris(mesh);
+
+ for (const int i : r_bary_coords.index_range()) {
+ const int looptri_index = looptri_indices[i];
+ const MLoopTri &looptri = looptris[looptri_index];
+
+ const int v0_index = mesh.mloop[looptri.tri[0]].v;
+ const int v1_index = mesh.mloop[looptri.tri[1]].v;
+ const int v2_index = mesh.mloop[looptri.tri[2]].v;
+
+ interp_weights_tri_v3(r_bary_coords[i],
+ mesh.mvert[v0_index].co,
+ mesh.mvert[v1_index].co,
+ mesh.mvert[v2_index].co,
+ positions[i]);
+ }
+}
+
+static void transfer_attribute_nearest_face_interpolated(const GeometrySet &src_geometry,
+ GeometryComponent &dst_component,
+ const VArray<float3> &dst_positions,
+ const AttributeDomain dst_domain,
+ const CustomDataType data_type,
+ const StringRef src_name,
+ const StringRef dst_name)
+{
+ const int tot_samples = dst_positions.size();
+ const MeshComponent *component = src_geometry.get_component_for_read<MeshComponent>();
+ if (component == nullptr) {
+ return;
+ }
+ const Mesh *mesh = component->get_for_read();
+ if (mesh == nullptr) {
+ return;
+ }
+ if (mesh->totpoly == 0) {
+ return;
+ }
+ ReadAttributeLookup src_attribute = component->attribute_try_get_for_read(src_name, data_type);
+ if (!src_attribute) {
+ return;
+ }
+
+ /* Find closest points on the mesh surface. */
+ Array<int> looptri_indices(tot_samples);
+ Array<float3> positions(tot_samples);
+ get_closest_mesh_looptris(*mesh, dst_positions, looptri_indices, {}, positions);
+
+ OutputAttribute dst_attribute = dst_component.attribute_try_get_for_output_only(
+ dst_name, dst_domain, data_type);
+ if (!dst_attribute) {
+ return;
+ }
+ GMutableSpan dst_span = dst_attribute.as_span();
+ Array<float3> bary_coords;
+
+ /* Compute barycentric coordinates only when they are needed. */
+ if (src_attribute.domain != ATTR_DOMAIN_FACE) {
+ bary_coords.reinitialize(tot_samples);
+ get_barycentric_coords(*mesh, looptri_indices, positions, bary_coords);
+ }
+ /* Interpolate the source attribute on the surface. */
+ switch (src_attribute.domain) {
+ case ATTR_DOMAIN_POINT: {
+ bke::mesh_surface_sample::sample_point_attribute(
+ *mesh, looptri_indices, bary_coords, *src_attribute.varray, dst_span);
+ break;
+ }
+ case ATTR_DOMAIN_FACE: {
+ bke::mesh_surface_sample::sample_face_attribute(
+ *mesh, looptri_indices, *src_attribute.varray, dst_span);
+ break;
+ }
+ case ATTR_DOMAIN_CORNER: {
+ bke::mesh_surface_sample::sample_corner_attribute(
+ *mesh, looptri_indices, bary_coords, *src_attribute.varray, dst_span);
+ break;
+ }
+ case ATTR_DOMAIN_EDGE: {
+ /* Not yet supported. */
+ break;
+ }
+ default: {
+ BLI_assert_unreachable();
+ break;
+ }
+ }
+ dst_attribute.save();
+}
+
+static void transfer_attribute_nearest(const GeometrySet &src_geometry,
+ GeometryComponent &dst_component,
+ const VArray<float3> &dst_positions,
+ const AttributeDomain dst_domain,
+ const CustomDataType data_type,
+ const StringRef src_name,
+ const StringRef dst_name)
+{
+ const CPPType &type = *bke::custom_data_type_to_cpp_type(data_type);
+
+ /* Get pointcloud data from geometry. */
+ const PointCloudComponent *pointcloud_component =
+ src_geometry.get_component_for_read<PointCloudComponent>();
+ const PointCloud *pointcloud = pointcloud_component ? pointcloud_component->get_for_read() :
+ nullptr;
+
+ /* Get mesh data from geometry. */
+ const MeshComponent *mesh_component = src_geometry.get_component_for_read<MeshComponent>();
+ const Mesh *mesh = mesh_component ? mesh_component->get_for_read() : nullptr;
+
+ const int tot_samples = dst_positions.size();
+
+ Array<int> pointcloud_indices;
+ Array<float> pointcloud_distances_sq;
+ bool use_pointcloud = false;
+
+ /* Depending on where what domain the source attribute lives, these indices are either vertex,
+ * corner, edge or polygon indices. */
+ Array<int> mesh_indices;
+ Array<float> mesh_distances_sq;
+ bool use_mesh = false;
+
+ /* If there is a pointcloud, find the closest points. */
+ if (pointcloud != nullptr && pointcloud->totpoint > 0) {
+ if (pointcloud_component->attribute_exists(src_name)) {
+ use_pointcloud = true;
+ pointcloud_indices.reinitialize(tot_samples);
+ pointcloud_distances_sq.reinitialize(tot_samples);
+ get_closest_pointcloud_points(
+ *pointcloud, dst_positions, pointcloud_indices, pointcloud_distances_sq);
+ }
+ }
+
+ /* If there is a mesh, find the closest mesh elements. */
+ if (mesh != nullptr) {
+ ReadAttributeLookup src_attribute = mesh_component->attribute_try_get_for_read(src_name);
+ if (src_attribute) {
+ switch (src_attribute.domain) {
+ case ATTR_DOMAIN_POINT: {
+ if (mesh->totvert > 0) {
+ use_mesh = true;
+ mesh_indices.reinitialize(tot_samples);
+ mesh_distances_sq.reinitialize(tot_samples);
+ get_closest_mesh_points(*mesh, dst_positions, mesh_indices, mesh_distances_sq, {});
+ }
+ break;
+ }
+ case ATTR_DOMAIN_EDGE: {
+ if (mesh->totedge > 0) {
+ use_mesh = true;
+ mesh_indices.reinitialize(tot_samples);
+ mesh_distances_sq.reinitialize(tot_samples);
+ get_closest_mesh_edges(*mesh, dst_positions, mesh_indices, mesh_distances_sq, {});
+ }
+ break;
+ }
+ case ATTR_DOMAIN_FACE: {
+ if (mesh->totpoly > 0) {
+ use_mesh = true;
+ mesh_indices.reinitialize(tot_samples);
+ mesh_distances_sq.reinitialize(tot_samples);
+ get_closest_mesh_polygons(*mesh, dst_positions, mesh_indices, mesh_distances_sq, {});
+ }
+ break;
+ }
+ case ATTR_DOMAIN_CORNER: {
+ use_mesh = true;
+ mesh_indices.reinitialize(tot_samples);
+ mesh_distances_sq.reinitialize(tot_samples);
+ get_closest_mesh_corners(*mesh, dst_positions, mesh_indices, mesh_distances_sq, {});
+ break;
+ }
+ default: {
+ break;
+ }
+ }
+ }
+ }
+
+ if (!use_pointcloud && !use_mesh) {
+ return;
+ }
+
+ OutputAttribute dst_attribute = dst_component.attribute_try_get_for_output_only(
+ dst_name, dst_domain, data_type);
+ if (!dst_attribute) {
+ return;
+ }
+
+ /* Create a buffer for intermediate values. */
+ BUFFER_FOR_CPP_TYPE_VALUE(type, buffer);
+
+ if (use_mesh && use_pointcloud) {
+ /* When there is a mesh and a pointcloud, we still have to check whether a pointcloud point or
+ * a mesh element is closer to every point. */
+ ReadAttributeLookup pointcloud_src_attribute =
+ pointcloud_component->attribute_try_get_for_read(src_name, data_type);
+ ReadAttributeLookup mesh_src_attribute = mesh_component->attribute_try_get_for_read(src_name,
+ data_type);
+ for (const int i : IndexRange(tot_samples)) {
+ if (pointcloud_distances_sq[i] < mesh_distances_sq[i]) {
+ /* Pointcloud point is closer. */
+ const int index = pointcloud_indices[i];
+ pointcloud_src_attribute.varray->get(index, buffer);
+ dst_attribute->set_by_relocate(i, buffer);
+ }
+ else {
+ /* Mesh element is closer. */
+ const int index = mesh_indices[i];
+ mesh_src_attribute.varray->get(index, buffer);
+ dst_attribute->set_by_relocate(i, buffer);
+ }
+ }
+ }
+ else if (use_pointcloud) {
+ /* The source geometry only has a pointcloud. */
+ ReadAttributeLookup src_attribute = pointcloud_component->attribute_try_get_for_read(
+ src_name, data_type);
+ for (const int i : IndexRange(tot_samples)) {
+ const int index = pointcloud_indices[i];
+ src_attribute.varray->get(index, buffer);
+ dst_attribute->set_by_relocate(i, buffer);
+ }
+ }
+ else if (use_mesh) {
+ /* The source geometry only has a mesh. */
+ ReadAttributeLookup src_attribute = mesh_component->attribute_try_get_for_read(src_name,
+ data_type);
+ for (const int i : IndexRange(tot_samples)) {
+ const int index = mesh_indices[i];
+ src_attribute.varray->get(index, buffer);
+ dst_attribute->set_by_relocate(i, buffer);
+ }
+ }
+
+ dst_attribute.save();
+}
+
+static void transfer_attribute(const GeoNodeExecParams &params,
+ const GeometrySet &src_geometry,
+ GeometryComponent &dst_component,
+ const StringRef src_name,
+ const StringRef dst_name)
+{
+ const NodeGeometryAttributeTransfer &storage =
+ *(const NodeGeometryAttributeTransfer *)params.node().storage;
+ const GeometryNodeAttributeTransferMapMode mapping = (GeometryNodeAttributeTransferMapMode)
+ storage.mapping;
+ const AttributeDomain input_domain = (AttributeDomain)storage.domain;
+
+ CustomDataType data_type;
+ AttributeDomain auto_domain;
+ get_result_domain_and_data_type(src_geometry, dst_component, src_name, &data_type, &auto_domain);
+ const AttributeDomain dst_domain = (input_domain == ATTR_DOMAIN_AUTO) ? auto_domain :
+ input_domain;
+
+ GVArray_Typed<float3> dst_positions = dst_component.attribute_get_for_read<float3>(
+ "position", dst_domain, {0, 0, 0});
+
+ switch (mapping) {
+ case GEO_NODE_ATTRIBUTE_TRANSFER_NEAREST_FACE_INTERPOLATED: {
+ transfer_attribute_nearest_face_interpolated(
+ src_geometry, dst_component, dst_positions, dst_domain, data_type, src_name, dst_name);
+ break;
+ }
+ case GEO_NODE_ATTRIBUTE_TRANSFER_NEAREST: {
+ transfer_attribute_nearest(
+ src_geometry, dst_component, dst_positions, dst_domain, data_type, src_name, dst_name);
+ break;
+ }
+ }
+}
+
+static void geo_node_attribute_transfer_exec(GeoNodeExecParams params)
+{
+ GeometrySet dst_geometry_set = params.extract_input<GeometrySet>("Geometry");
+ GeometrySet src_geometry_set = params.extract_input<GeometrySet>("Source Geometry");
+ const std::string src_attribute_name = params.extract_input<std::string>("Source");
+ const std::string dst_attribute_name = params.extract_input<std::string>("Destination");
+
+ if (src_attribute_name.empty() || dst_attribute_name.empty()) {
+ params.set_output("Geometry", dst_geometry_set);
+ return;
+ }
+
+ dst_geometry_set = bke::geometry_set_realize_instances(dst_geometry_set);
+ src_geometry_set = bke::geometry_set_realize_instances(src_geometry_set);
+
+ if (dst_geometry_set.has<MeshComponent>()) {
+ transfer_attribute(params,
+ src_geometry_set,
+ dst_geometry_set.get_component_for_write<MeshComponent>(),
+ src_attribute_name,
+ dst_attribute_name);
+ }
+ if (dst_geometry_set.has<PointCloudComponent>()) {
+ transfer_attribute(params,
+ src_geometry_set,
+ dst_geometry_set.get_component_for_write<PointCloudComponent>(),
+ src_attribute_name,
+ dst_attribute_name);
+ }
+
+ params.set_output("Geometry", dst_geometry_set);
+}
+
+} // namespace blender::nodes
+
+void register_node_type_geo_attribute_transfer()
+{
+ static bNodeType ntype;
+
+ geo_node_type_base(
+ &ntype, GEO_NODE_ATTRIBUTE_TRANSFER, "Attribute Transfer", NODE_CLASS_ATTRIBUTE, 0);
+ node_type_socket_templates(
+ &ntype, geo_node_attribute_transfer_in, geo_node_attribute_transfer_out);
+ node_type_init(&ntype, blender::nodes::geo_node_attribute_transfer_init);
+ node_type_storage(&ntype,
+ "NodeGeometryAttributeTransfer",
+ node_free_standard_storage,
+ node_copy_standard_storage);
+ ntype.geometry_node_execute = blender::nodes::geo_node_attribute_transfer_exec;
+ ntype.draw_buttons = geo_node_attribute_transfer_layout;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_vector_math.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_vector_math.cc
index 1ae095a27d2..8877af445f9 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_attribute_vector_math.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_vector_math.cc
@@ -15,6 +15,7 @@
*/
#include "BLI_math_base_safe.h"
+#include "BLI_task.hh"
#include "UI_interface.h"
#include "UI_resources.h"
@@ -168,196 +169,210 @@ static void geo_node_attribute_vector_math_update(bNodeTree *UNUSED(ntree), bNod
operation_use_input_c(operation));
}
-static void do_math_operation_fl3_fl3_to_fl3(const Float3ReadAttribute &input_a,
- const Float3ReadAttribute &input_b,
- Float3WriteAttribute result,
+static void do_math_operation_fl3_fl3_to_fl3(const VArray<float3> &input_a,
+ const VArray<float3> &input_b,
+ VMutableArray<float3> &result,
const NodeVectorMathOperation operation)
{
const int size = input_a.size();
- Span<float3> span_a = input_a.get_span();
- Span<float3> span_b = input_b.get_span();
- MutableSpan<float3> span_result = result.get_span_for_write_only();
+ VArray_Span<float3> span_a{input_a};
+ VArray_Span<float3> span_b{input_b};
+ VMutableArray_Span<float3> span_result{result, false};
bool success = try_dispatch_float_math_fl3_fl3_to_fl3(
operation, [&](auto math_function, const FloatMathOperationInfo &UNUSED(info)) {
- for (const int i : IndexRange(size)) {
- const float3 a = span_a[i];
- const float3 b = span_b[i];
- const float3 out = math_function(a, b);
- span_result[i] = out;
- }
+ parallel_for(IndexRange(size), 512, [&](IndexRange range) {
+ for (const int i : range) {
+ const float3 a = span_a[i];
+ const float3 b = span_b[i];
+ const float3 out = math_function(a, b);
+ span_result[i] = out;
+ }
+ });
});
- result.apply_span();
+ span_result.save();
/* The operation is not supported by this node currently. */
BLI_assert(success);
UNUSED_VARS_NDEBUG(success);
}
-static void do_math_operation_fl3_fl3_fl3_to_fl3(const Float3ReadAttribute &input_a,
- const Float3ReadAttribute &input_b,
- const Float3ReadAttribute &input_c,
- Float3WriteAttribute result,
+static void do_math_operation_fl3_fl3_fl3_to_fl3(const VArray<float3> &input_a,
+ const VArray<float3> &input_b,
+ const VArray<float3> &input_c,
+ VMutableArray<float3> &result,
const NodeVectorMathOperation operation)
{
const int size = input_a.size();
- Span<float3> span_a = input_a.get_span();
- Span<float3> span_b = input_b.get_span();
- Span<float3> span_c = input_c.get_span();
- MutableSpan<float3> span_result = result.get_span_for_write_only();
+ VArray_Span<float3> span_a{input_a};
+ VArray_Span<float3> span_b{input_b};
+ VArray_Span<float3> span_c{input_c};
+ VMutableArray_Span<float3> span_result{result};
bool success = try_dispatch_float_math_fl3_fl3_fl3_to_fl3(
operation, [&](auto math_function, const FloatMathOperationInfo &UNUSED(info)) {
- for (const int i : IndexRange(size)) {
- const float3 a = span_a[i];
- const float3 b = span_b[i];
- const float3 c = span_c[i];
- const float3 out = math_function(a, b, c);
- span_result[i] = out;
- }
+ parallel_for(IndexRange(size), 512, [&](IndexRange range) {
+ for (const int i : range) {
+ const float3 a = span_a[i];
+ const float3 b = span_b[i];
+ const float3 c = span_c[i];
+ const float3 out = math_function(a, b, c);
+ span_result[i] = out;
+ }
+ });
});
- result.apply_span();
+ span_result.save();
/* The operation is not supported by this node currently. */
BLI_assert(success);
UNUSED_VARS_NDEBUG(success);
}
-static void do_math_operation_fl3_fl3_fl_to_fl3(const Float3ReadAttribute &input_a,
- const Float3ReadAttribute &input_b,
- const FloatReadAttribute &input_c,
- Float3WriteAttribute result,
+static void do_math_operation_fl3_fl3_fl_to_fl3(const VArray<float3> &input_a,
+ const VArray<float3> &input_b,
+ const VArray<float> &input_c,
+ VMutableArray<float3> &result,
const NodeVectorMathOperation operation)
{
const int size = input_a.size();
- Span<float3> span_a = input_a.get_span();
- Span<float3> span_b = input_b.get_span();
- Span<float> span_c = input_c.get_span();
- MutableSpan<float3> span_result = result.get_span_for_write_only();
+ VArray_Span<float3> span_a{input_a};
+ VArray_Span<float3> span_b{input_b};
+ VArray_Span<float> span_c{input_c};
+ VMutableArray_Span<float3> span_result{result, false};
bool success = try_dispatch_float_math_fl3_fl3_fl_to_fl3(
operation, [&](auto math_function, const FloatMathOperationInfo &UNUSED(info)) {
- for (const int i : IndexRange(size)) {
- const float3 a = span_a[i];
- const float3 b = span_b[i];
- const float c = span_c[i];
- const float3 out = math_function(a, b, c);
- span_result[i] = out;
- }
+ parallel_for(IndexRange(size), 512, [&](IndexRange range) {
+ for (const int i : range) {
+ const float3 a = span_a[i];
+ const float3 b = span_b[i];
+ const float c = span_c[i];
+ const float3 out = math_function(a, b, c);
+ span_result[i] = out;
+ }
+ });
});
- result.apply_span();
+ span_result.save();
/* The operation is not supported by this node currently. */
BLI_assert(success);
UNUSED_VARS_NDEBUG(success);
}
-static void do_math_operation_fl3_fl3_to_fl(const Float3ReadAttribute &input_a,
- const Float3ReadAttribute &input_b,
- FloatWriteAttribute result,
+static void do_math_operation_fl3_fl3_to_fl(const VArray<float3> &input_a,
+ const VArray<float3> &input_b,
+ VMutableArray<float> &result,
const NodeVectorMathOperation operation)
{
const int size = input_a.size();
- Span<float3> span_a = input_a.get_span();
- Span<float3> span_b = input_b.get_span();
- MutableSpan<float> span_result = result.get_span_for_write_only();
+ VArray_Span<float3> span_a{input_a};
+ VArray_Span<float3> span_b{input_b};
+ VMutableArray_Span<float> span_result{result, false};
bool success = try_dispatch_float_math_fl3_fl3_to_fl(
operation, [&](auto math_function, const FloatMathOperationInfo &UNUSED(info)) {
- for (const int i : IndexRange(size)) {
- const float3 a = span_a[i];
- const float3 b = span_b[i];
- const float out = math_function(a, b);
- span_result[i] = out;
- }
+ parallel_for(IndexRange(size), 512, [&](IndexRange range) {
+ for (const int i : range) {
+ const float3 a = span_a[i];
+ const float3 b = span_b[i];
+ const float out = math_function(a, b);
+ span_result[i] = out;
+ }
+ });
});
- result.apply_span();
+ span_result.save();
/* The operation is not supported by this node currently. */
BLI_assert(success);
UNUSED_VARS_NDEBUG(success);
}
-static void do_math_operation_fl3_fl_to_fl3(const Float3ReadAttribute &input_a,
- const FloatReadAttribute &input_b,
- Float3WriteAttribute result,
+static void do_math_operation_fl3_fl_to_fl3(const VArray<float3> &input_a,
+ const VArray<float> &input_b,
+ VMutableArray<float3> &result,
const NodeVectorMathOperation operation)
{
const int size = input_a.size();
- Span<float3> span_a = input_a.get_span();
- Span<float> span_b = input_b.get_span();
- MutableSpan<float3> span_result = result.get_span_for_write_only();
+ VArray_Span<float3> span_a{input_a};
+ VArray_Span<float> span_b{input_b};
+ VMutableArray_Span<float3> span_result{result, false};
bool success = try_dispatch_float_math_fl3_fl_to_fl3(
operation, [&](auto math_function, const FloatMathOperationInfo &UNUSED(info)) {
- for (const int i : IndexRange(size)) {
- const float3 a = span_a[i];
- const float b = span_b[i];
- const float3 out = math_function(a, b);
- span_result[i] = out;
- }
+ parallel_for(IndexRange(size), 512, [&](IndexRange range) {
+ for (const int i : range) {
+ const float3 a = span_a[i];
+ const float b = span_b[i];
+ const float3 out = math_function(a, b);
+ span_result[i] = out;
+ }
+ });
});
- result.apply_span();
+ span_result.save();
/* The operation is not supported by this node currently. */
BLI_assert(success);
UNUSED_VARS_NDEBUG(success);
}
-static void do_math_operation_fl3_to_fl3(const Float3ReadAttribute &input_a,
- Float3WriteAttribute result,
+static void do_math_operation_fl3_to_fl3(const VArray<float3> &input_a,
+ VMutableArray<float3> &result,
const NodeVectorMathOperation operation)
{
const int size = input_a.size();
- Span<float3> span_a = input_a.get_span();
- MutableSpan<float3> span_result = result.get_span_for_write_only();
+ VArray_Span<float3> span_a{input_a};
+ VMutableArray_Span<float3> span_result{result, false};
bool success = try_dispatch_float_math_fl3_to_fl3(
operation, [&](auto math_function, const FloatMathOperationInfo &UNUSED(info)) {
- for (const int i : IndexRange(size)) {
- const float3 in = span_a[i];
- const float3 out = math_function(in);
- span_result[i] = out;
- }
+ parallel_for(IndexRange(size), 512, [&](IndexRange range) {
+ for (const int i : range) {
+ const float3 in = span_a[i];
+ const float3 out = math_function(in);
+ span_result[i] = out;
+ }
+ });
});
- result.apply_span();
+ span_result.save();
/* The operation is not supported by this node currently. */
BLI_assert(success);
UNUSED_VARS_NDEBUG(success);
}
-static void do_math_operation_fl3_to_fl(const Float3ReadAttribute &input_a,
- FloatWriteAttribute result,
+static void do_math_operation_fl3_to_fl(const VArray<float3> &input_a,
+ VMutableArray<float> &result,
const NodeVectorMathOperation operation)
{
const int size = input_a.size();
- Span<float3> span_a = input_a.get_span();
- MutableSpan<float> span_result = result.get_span_for_write_only();
+ VArray_Span<float3> span_a{input_a};
+ VMutableArray_Span<float> span_result{result, false};
bool success = try_dispatch_float_math_fl3_to_fl(
operation, [&](auto math_function, const FloatMathOperationInfo &UNUSED(info)) {
- for (const int i : IndexRange(size)) {
- const float3 in = span_a[i];
- const float out = math_function(in);
- span_result[i] = out;
- }
+ parallel_for(IndexRange(size), 512, [&](IndexRange range) {
+ for (const int i : range) {
+ const float3 in = span_a[i];
+ const float out = math_function(in);
+ span_result[i] = out;
+ }
+ });
});
- result.apply_span();
+ span_result.save();
/* The operation is not supported by this node currently. */
BLI_assert(success);
@@ -370,9 +385,9 @@ static AttributeDomain get_result_domain(const GeometryComponent &component,
StringRef result_name)
{
/* Use the domain of the result attribute if it already exists. */
- ReadAttributePtr result_attribute = component.attribute_try_get_for_read(result_name);
+ ReadAttributeLookup result_attribute = component.attribute_try_get_for_read(result_name);
if (result_attribute) {
- return result_attribute->domain();
+ return result_attribute.domain;
}
/* Otherwise use the highest priority domain from existing input attributes, or the default. */
@@ -406,13 +421,13 @@ static void attribute_vector_math_calc(GeometryComponent &component,
const AttributeDomain result_domain = get_result_domain(
component, params, operation, result_name);
- ReadAttributePtr attribute_a = params.get_input_attribute(
+ GVArrayPtr attribute_a = params.get_input_attribute(
"A", component, result_domain, read_type_a, nullptr);
if (!attribute_a) {
return;
}
- ReadAttributePtr attribute_b;
- ReadAttributePtr attribute_c;
+ GVArrayPtr attribute_b;
+ GVArrayPtr attribute_c;
if (use_input_b) {
attribute_b = params.get_input_attribute("B", component, result_domain, read_type_b, nullptr);
if (!attribute_b) {
@@ -427,7 +442,7 @@ static void attribute_vector_math_calc(GeometryComponent &component,
}
/* Get result attribute first, in case it has to overwrite one of the existing attributes. */
- OutputAttributePtr attribute_result = component.attribute_try_get_for_output(
+ OutputAttribute attribute_result = component.attribute_try_get_for_output_only(
result_name, result_domain, result_type);
if (!attribute_result) {
return;
@@ -445,17 +460,27 @@ static void attribute_vector_math_calc(GeometryComponent &component,
case NODE_VECTOR_MATH_MODULO:
case NODE_VECTOR_MATH_MINIMUM:
case NODE_VECTOR_MATH_MAXIMUM:
- do_math_operation_fl3_fl3_to_fl3(*attribute_a, *attribute_b, *attribute_result, operation);
+ do_math_operation_fl3_fl3_to_fl3(attribute_a->typed<float3>(),
+ attribute_b->typed<float3>(),
+ attribute_result->typed<float3>(),
+ operation);
break;
case NODE_VECTOR_MATH_DOT_PRODUCT:
case NODE_VECTOR_MATH_DISTANCE:
- do_math_operation_fl3_fl3_to_fl(*attribute_a, *attribute_b, *attribute_result, operation);
+ do_math_operation_fl3_fl3_to_fl(attribute_a->typed<float3>(),
+ attribute_b->typed<float3>(),
+ attribute_result->typed<float>(),
+ operation);
break;
case NODE_VECTOR_MATH_LENGTH:
- do_math_operation_fl3_to_fl(*attribute_a, *attribute_result, operation);
+ do_math_operation_fl3_to_fl(
+ attribute_a->typed<float3>(), attribute_result->typed<float>(), operation);
break;
case NODE_VECTOR_MATH_SCALE:
- do_math_operation_fl3_fl_to_fl3(*attribute_a, *attribute_b, *attribute_result, operation);
+ do_math_operation_fl3_fl_to_fl3(attribute_a->typed<float3>(),
+ attribute_b->typed<float>(),
+ attribute_result->typed<float3>(),
+ operation);
break;
case NODE_VECTOR_MATH_NORMALIZE:
case NODE_VECTOR_MATH_FLOOR:
@@ -465,16 +490,23 @@ static void attribute_vector_math_calc(GeometryComponent &component,
case NODE_VECTOR_MATH_SINE:
case NODE_VECTOR_MATH_COSINE:
case NODE_VECTOR_MATH_TANGENT:
- do_math_operation_fl3_to_fl3(*attribute_a, *attribute_result, operation);
+ do_math_operation_fl3_to_fl3(
+ attribute_a->typed<float3>(), attribute_result->typed<float3>(), operation);
break;
case NODE_VECTOR_MATH_WRAP:
case NODE_VECTOR_MATH_FACEFORWARD:
- do_math_operation_fl3_fl3_fl3_to_fl3(
- *attribute_a, *attribute_b, *attribute_c, *attribute_result, operation);
+ do_math_operation_fl3_fl3_fl3_to_fl3(attribute_a->typed<float3>(),
+ attribute_b->typed<float3>(),
+ attribute_c->typed<float3>(),
+ attribute_result->typed<float3>(),
+ operation);
break;
case NODE_VECTOR_MATH_REFRACT:
- do_math_operation_fl3_fl3_fl_to_fl3(
- *attribute_a, *attribute_b, *attribute_c, *attribute_result, operation);
+ do_math_operation_fl3_fl3_fl_to_fl3(attribute_a->typed<float3>(),
+ attribute_b->typed<float3>(),
+ attribute_c->typed<float>(),
+ attribute_result->typed<float3>(),
+ operation);
break;
}
attribute_result.save();
@@ -493,6 +525,9 @@ static void geo_node_attribute_vector_math_exec(GeoNodeExecParams params)
attribute_vector_math_calc(geometry_set.get_component_for_write<PointCloudComponent>(),
params);
}
+ if (geometry_set.has<CurveComponent>()) {
+ attribute_vector_math_calc(geometry_set.get_component_for_write<CurveComponent>(), params);
+ }
params.set_output("Geometry", geometry_set);
}
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_bounding_box.cc b/source/blender/nodes/geometry/nodes/node_geo_bounding_box.cc
index 96455f080e7..b6fa4c0d48f 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_bounding_box.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_bounding_box.cc
@@ -39,15 +39,12 @@ static void compute_min_max_from_position_and_transform(const GeometryComponent
float3 &r_min,
float3 &r_max)
{
- ReadAttributePtr position_attribute = component.attribute_try_get_for_read("position");
- if (!position_attribute) {
- BLI_assert(component.attribute_domain_size(ATTR_DOMAIN_POINT) == 0);
- return;
- }
- Span<float3> positions = position_attribute->get_span<float3>();
+ GVArray_Typed<float3> positions = component.attribute_get_for_read<float3>(
+ "position", ATTR_DOMAIN_POINT, {0, 0, 0});
for (const float4x4 &transform : transforms) {
- for (const float3 &position : positions) {
+ for (const int i : positions.index_range()) {
+ const float3 position = positions[i];
const float3 transformed_position = transform * position;
minmax_v3v3_v3(r_min, r_max, transformed_position);
}
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 0ad495aa4db..bd4de710dbb 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_collection_info.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_collection_info.cc
@@ -42,9 +42,7 @@ namespace blender::nodes {
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;
@@ -58,10 +56,6 @@ static void geo_node_collection_info_exec(GeoNodeExecParams params)
const bool transform_space_relative = (node_storage->transform_space ==
GEO_NODE_TRANSFORM_SPACE_RELATIVE);
- InstancedData instance;
- instance.type = INSTANCE_DATA_TYPE_COLLECTION;
- instance.data.collection = collection;
-
InstancesComponent &instances = geometry_set_out.get_component_for_write<InstancesComponent>();
float transform_mat[4][4];
@@ -73,7 +67,9 @@ static void geo_node_collection_info_exec(GeoNodeExecParams params)
mul_m4_m4_pre(transform_mat, self_object->imat);
}
- instances.add_instance(instance, transform_mat, -1);
+
+ const int handle = instances.add_reference(*collection);
+ instances.add_instance(handle, transform_mat, -1);
params.set_output("Geometry", geometry_set_out);
}
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..d7d31a4ef92
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc
@@ -0,0 +1,208 @@
+/*
+ * 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_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());
+ }
+
+ 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
new file mode 100644
index 00000000000..f0effdc71f6
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc
@@ -0,0 +1,312 @@
+/*
+ * 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_float4x4.hh"
+#include "BLI_timeit.hh"
+
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+
+#include "BKE_mesh.h"
+#include "BKE_spline.hh"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "node_geometry_util.hh"
+
+static bNodeSocketTemplate geo_node_curve_to_mesh_in[] = {
+ {SOCK_GEOMETRY, N_("Curve")},
+ {SOCK_GEOMETRY, N_("Profile Curve")},
+ {-1, ""},
+};
+
+static bNodeSocketTemplate geo_node_curve_to_mesh_out[] = {
+ {SOCK_GEOMETRY, N_("Mesh")},
+ {-1, ""},
+};
+
+namespace blender::nodes {
+
+static void vert_extrude_to_mesh_data(const Spline &spline,
+ const float3 profile_vert,
+ MutableSpan<MVert> r_verts,
+ MutableSpan<MEdge> r_edges,
+ int &vert_offset,
+ int &edge_offset)
+{
+ Span<float3> positions = spline.evaluated_positions();
+
+ for (const int i : IndexRange(positions.size() - 1)) {
+ MEdge &edge = r_edges[edge_offset++];
+ edge.v1 = vert_offset + i;
+ edge.v2 = vert_offset + i + 1;
+ edge.flag = ME_LOOSEEDGE;
+ }
+
+ 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;
+ edge.flag = ME_LOOSEEDGE;
+ }
+
+ for (const int i : positions.index_range()) {
+ MVert &vert = r_verts[vert_offset++];
+ copy_v3_v3(vert.co, positions[i] + profile_vert);
+ }
+}
+
+static void mark_edges_sharp(MutableSpan<MEdge> edges)
+{
+ for (MEdge &edge : edges) {
+ edge.flag |= ME_SHARP;
+ }
+}
+
+static void spline_extrude_to_mesh_data(const Spline &spline,
+ const Spline &profile_spline,
+ MutableSpan<MVert> r_verts,
+ MutableSpan<MEdge> r_edges,
+ MutableSpan<MLoop> r_loops,
+ MutableSpan<MPoly> r_polys,
+ int &vert_offset,
+ int &edge_offset,
+ int &loop_offset,
+ int &poly_offset)
+{
+ const int spline_vert_len = spline.evaluated_points_size();
+ const int spline_edge_len = spline.evaluated_edges_size();
+ const int profile_vert_len = profile_spline.evaluated_points_size();
+ const int profile_edge_len = profile_spline.evaluated_edges_size();
+ if (spline_vert_len == 0) {
+ return;
+ }
+
+ if (profile_vert_len == 1) {
+ vert_extrude_to_mesh_data(spline,
+ profile_spline.evaluated_positions()[0],
+ r_verts,
+ r_edges,
+ vert_offset,
+ edge_offset);
+ return;
+ }
+
+ /* Add the edges running along the length of the curve, starting at each profile vertex. */
+ const int spline_edges_start = edge_offset;
+ for (const int i_profile : IndexRange(profile_vert_len)) {
+ for (const int i_ring : IndexRange(spline_edge_len)) {
+ const int i_next_ring = (i_ring == spline_vert_len - 1) ? 0 : i_ring + 1;
+
+ const int ring_vert_offset = vert_offset + profile_vert_len * i_ring;
+ const int next_ring_vert_offset = vert_offset + profile_vert_len * i_next_ring;
+
+ MEdge &edge = r_edges[edge_offset++];
+ edge.v1 = ring_vert_offset + i_profile;
+ edge.v2 = next_ring_vert_offset + i_profile;
+ edge.flag = ME_EDGEDRAW | ME_EDGERENDER;
+ }
+ }
+
+ /* Add the edges running along each profile ring. */
+ const int profile_edges_start = edge_offset;
+ for (const int i_ring : IndexRange(spline_vert_len)) {
+ const int ring_vert_offset = vert_offset + profile_vert_len * i_ring;
+
+ for (const int i_profile : IndexRange(profile_edge_len)) {
+ const int i_next_profile = (i_profile == profile_vert_len - 1) ? 0 : i_profile + 1;
+
+ MEdge &edge = r_edges[edge_offset++];
+ edge.v1 = ring_vert_offset + i_profile;
+ edge.v2 = ring_vert_offset + i_next_profile;
+ edge.flag = ME_EDGEDRAW | ME_EDGERENDER;
+ }
+ }
+
+ /* Calculate poly and corner indices. */
+ for (const int i_ring : IndexRange(spline_edge_len)) {
+ const int i_next_ring = (i_ring == spline_vert_len - 1) ? 0 : i_ring + 1;
+
+ const int ring_vert_offset = vert_offset + profile_vert_len * i_ring;
+ const int next_ring_vert_offset = vert_offset + profile_vert_len * i_next_ring;
+
+ const int ring_edge_start = profile_edges_start + profile_edge_len * i_ring;
+ const int next_ring_edge_offset = profile_edges_start + profile_edge_len * i_next_ring;
+
+ for (const int i_profile : IndexRange(profile_edge_len)) {
+ const int i_next_profile = (i_profile == profile_vert_len - 1) ? 0 : i_profile + 1;
+
+ const int spline_edge_start = spline_edges_start + spline_edge_len * i_profile;
+ const int next_spline_edge_start = spline_edges_start + spline_edge_len * i_next_profile;
+
+ MPoly &poly = r_polys[poly_offset++];
+ poly.loopstart = loop_offset;
+ poly.totloop = 4;
+ poly.flag = ME_SMOOTH;
+
+ MLoop &loop_a = r_loops[loop_offset++];
+ loop_a.v = ring_vert_offset + i_profile;
+ loop_a.e = ring_edge_start + i_profile;
+ MLoop &loop_b = r_loops[loop_offset++];
+ loop_b.v = ring_vert_offset + i_next_profile;
+ loop_b.e = next_spline_edge_start + i_ring;
+ MLoop &loop_c = r_loops[loop_offset++];
+ loop_c.v = next_ring_vert_offset + i_next_profile;
+ loop_c.e = next_ring_edge_offset + i_profile;
+ MLoop &loop_d = r_loops[loop_offset++];
+ loop_d.v = next_ring_vert_offset + i_profile;
+ loop_d.e = spline_edge_start + i_ring;
+ }
+ }
+
+ /* Calculate the positions of each profile ring profile along the spline. */
+ Span<float3> positions = spline.evaluated_positions();
+ Span<float3> tangents = spline.evaluated_tangents();
+ Span<float3> normals = spline.evaluated_normals();
+ Span<float3> profile_positions = profile_spline.evaluated_positions();
+
+ GVArray_Typed<float> radii{
+ spline.interpolate_to_evaluated_points(blender::fn::GVArray_For_Span(spline.radii()))};
+ for (const int i_ring : IndexRange(spline_vert_len)) {
+ float4x4 point_matrix = float4x4::from_normalized_axis_data(
+ positions[i_ring], normals[i_ring], tangents[i_ring]);
+
+ point_matrix.apply_scale(radii[i_ring]);
+
+ for (const int i_profile : IndexRange(profile_vert_len)) {
+ MVert &vert = r_verts[vert_offset++];
+ copy_v3_v3(vert.co, point_matrix * profile_positions[i_profile]);
+ }
+ }
+
+ /* Mark edge loops from sharp vector control points sharp. */
+ 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 : 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));
+ }
+ }
+ }
+}
+
+static Mesh *curve_to_mesh_calculate(const CurveEval &curve, const CurveEval &profile_curve)
+{
+ int profile_vert_total = 0;
+ int profile_edge_total = 0;
+ for (const SplinePtr &profile_spline : profile_curve.splines()) {
+ profile_vert_total += profile_spline->evaluated_points_size();
+ profile_edge_total += profile_spline->evaluated_edges_size();
+ }
+
+ int vert_total = 0;
+ int edge_total = 0;
+ int poly_total = 0;
+ 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;
+ poly_total += spline_edge_len * profile_edge_total;
+
+ /* Add the ring edges, with one ring for every curve vertex, and the edge loops
+ * that run along the length of the curve, starting on the first profile. */
+ edge_total += profile_edge_total * spline_vert_len + profile_vert_total * spline_edge_len;
+ }
+ const int corner_total = poly_total * 4;
+
+ if (vert_total == 0) {
+ return nullptr;
+ }
+
+ Mesh *mesh = BKE_mesh_new_nomain(vert_total, edge_total, 0, corner_total, poly_total);
+ MutableSpan<MVert> verts{mesh->mvert, mesh->totvert};
+ MutableSpan<MEdge> edges{mesh->medge, mesh->totedge};
+ MutableSpan<MLoop> loops{mesh->mloop, mesh->totloop};
+ MutableSpan<MPoly> polys{mesh->mpoly, mesh->totpoly};
+ mesh->flag |= ME_AUTOSMOOTH;
+ mesh->smoothresh = DEG2RADF(180.0f);
+
+ int vert_offset = 0;
+ 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()) {
+ spline_extrude_to_mesh_data(*spline,
+ *profile_spline,
+ verts,
+ edges,
+ loops,
+ polys,
+ vert_offset,
+ edge_offset,
+ loop_offset,
+ poly_offset);
+ }
+ }
+
+ BKE_mesh_calc_normals(mesh);
+
+ return mesh;
+}
+
+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.add_spline(std::move(spline));
+
+ return curve;
+}
+
+static void geo_node_curve_to_mesh_exec(GeoNodeExecParams params)
+{
+ GeometrySet curve_set = params.extract_input<GeometrySet>("Curve");
+ GeometrySet profile_set = params.extract_input<GeometrySet>("Profile Curve");
+
+ curve_set = bke::geometry_set_realize_instances(curve_set);
+ profile_set = bke::geometry_set_realize_instances(profile_set);
+
+ if (!curve_set.has_curve()) {
+ params.set_output("Mesh", GeometrySet());
+ return;
+ }
+
+ const CurveEval *profile_curve = profile_set.get_curve_for_read();
+
+ static const CurveEval vert_curve = get_curve_single_vert();
+
+ Mesh *mesh = curve_to_mesh_calculate(*curve_set.get_curve_for_read(),
+ (profile_curve == nullptr) ? vert_curve : *profile_curve);
+ params.set_output("Mesh", GeometrySet::create_with_mesh(mesh));
+}
+
+} // namespace blender::nodes
+
+void register_node_type_geo_curve_to_mesh()
+{
+ static bNodeType ntype;
+
+ geo_node_type_base(&ntype, GEO_NODE_CURVE_TO_MESH, "Curve to Mesh", NODE_CLASS_GEOMETRY, 0);
+ node_type_socket_templates(&ntype, geo_node_curve_to_mesh_in, geo_node_curve_to_mesh_out);
+ ntype.geometry_node_execute = blender::nodes::geo_node_curve_to_mesh_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 77d2b0df2a9..a6e78048ea6 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc
@@ -17,6 +17,7 @@
#include "BKE_mesh.h"
#include "BKE_mesh_runtime.h"
#include "BKE_pointcloud.h"
+#include "BKE_spline.hh"
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
@@ -149,10 +150,10 @@ static void determine_final_data_type_and_domain(Span<const GeometryComponent *>
Vector<CustomDataType> data_types;
Vector<AttributeDomain> domains;
for (const GeometryComponent *component : components) {
- ReadAttributePtr attribute = component->attribute_try_get_for_read(attribute_name);
+ ReadAttributeLookup attribute = component->attribute_try_get_for_read(attribute_name);
if (attribute) {
- data_types.append(attribute->custom_data_type());
- domains.append(attribute->domain());
+ data_types.append(bke::cpp_type_to_custom_data_type(attribute.varray->type()));
+ domains.append(attribute.domain);
}
}
@@ -164,7 +165,7 @@ static void fill_new_attribute(Span<const GeometryComponent *> src_components,
StringRef attribute_name,
const CustomDataType data_type,
const AttributeDomain domain,
- fn::GMutableSpan dst_span)
+ GMutableSpan dst_span)
{
const CPPType *cpp_type = bke::custom_data_type_to_cpp_type(data_type);
BLI_assert(cpp_type != nullptr);
@@ -175,10 +176,10 @@ static void fill_new_attribute(Span<const GeometryComponent *> src_components,
if (domain_size == 0) {
continue;
}
- ReadAttributePtr read_attribute = component->attribute_get_for_read(
+ GVArrayPtr read_attribute = component->attribute_get_for_read(
attribute_name, domain, data_type, nullptr);
- fn::GSpan src_span = read_attribute->get_span();
+ GVArray_GSpan src_span{*read_attribute};
const void *src_buffer = src_span.data();
void *dst_buffer = dst_span[offset];
cpp_type->copy_to_initialized_n(src_buffer, dst_buffer, domain_size);
@@ -201,16 +202,14 @@ static void join_attributes(Span<const GeometryComponent *> src_components,
AttributeDomain domain;
determine_final_data_type_and_domain(src_components, attribute_name, &data_type, &domain);
- OutputAttributePtr write_attribute = result.attribute_try_get_for_output(
+ OutputAttribute write_attribute = result.attribute_try_get_for_output_only(
attribute_name, domain, data_type);
- if (!write_attribute ||
- &write_attribute->cpp_type() != bke::custom_data_type_to_cpp_type(data_type) ||
- write_attribute->domain() != domain) {
+ if (!write_attribute) {
continue;
}
- fn::GMutableSpan dst_span = write_attribute->get_span_for_write_only();
+ GMutableSpan dst_span = write_attribute.as_span();
fill_new_attribute(src_components, attribute_name, data_type, domain, dst_span);
- write_attribute.apply_span_and_save();
+ write_attribute.save();
}
}
@@ -244,13 +243,30 @@ static void join_components(Span<const PointCloudComponent *> src_components, Ge
static void join_components(Span<const InstancesComponent *> src_components, GeometrySet &result)
{
InstancesComponent &dst_component = result.get_component_for_write<InstancesComponent>();
- for (const InstancesComponent *component : src_components) {
- const int size = component->instances_amount();
- Span<InstancedData> instanced_data = component->instanced_data();
- Span<float4x4> transforms = component->transforms();
- Span<int> ids = component->ids();
- for (const int i : IndexRange(size)) {
- dst_component.add_instance(instanced_data[i], transforms[i], ids[i]);
+
+ int tot_instances = 0;
+ for (const InstancesComponent *src_component : src_components) {
+ tot_instances += src_component->instances_amount();
+ }
+ dst_component.reserve(tot_instances);
+
+ for (const InstancesComponent *src_component : src_components) {
+ Span<InstanceReference> src_references = src_component->references();
+ Array<int> handle_map(src_references.size());
+ for (const int src_handle : src_references.index_range()) {
+ handle_map[src_handle] = dst_component.add_reference(src_references[src_handle]);
+ }
+
+ Span<float4x4> src_transforms = src_component->instance_transforms();
+ Span<int> src_ids = src_component->instance_ids();
+ Span<int> src_reference_handles = src_component->instance_reference_handles();
+
+ for (const int i : src_transforms.index_range()) {
+ const int src_handle = src_reference_handles[i];
+ const int dst_handle = handle_map[src_handle];
+ const float4x4 &transform = src_transforms[i];
+ const int id = src_ids[i];
+ dst_component.add_instance(dst_handle, transform, id);
}
}
}
@@ -263,6 +279,40 @@ static void join_components(Span<const VolumeComponent *> src_components, Geomet
UNUSED_VARS(src_components, dst_component);
}
+static void join_curve_components(MutableSpan<GeometrySet> src_geometry_sets, GeometrySet &result)
+{
+ Vector<CurveComponent *> src_components;
+ for (GeometrySet &geometry_set : src_geometry_sets) {
+ if (geometry_set.has_curve()) {
+ /* Retrieving with write access seems counterintuitive, but it can allow avoiding a copy
+ * in the case where the input spline has no other users, because the splines can be
+ * moved from the source curve rather than copied from a read-only source. Retrieving
+ * the curve for write will make a copy only when it has a user elsewhere. */
+ CurveComponent &component = geometry_set.get_component_for_write<CurveComponent>();
+ src_components.append(&component);
+ }
+ }
+
+ if (src_components.size() == 0) {
+ return;
+ }
+ if (src_components.size() == 1) {
+ result.add(*src_components[0]);
+ return;
+ }
+
+ CurveComponent &dst_component = result.get_component_for_write<CurveComponent>();
+ 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->add_spline(std::move(spline));
+ }
+ }
+
+ dst_component.replace(dst_curve);
+}
+
template<typename Component>
static void join_component_type(Span<GeometrySet> src_geometry_sets, GeometrySet &result)
{
@@ -293,6 +343,7 @@ static void geo_node_join_geometry_exec(GeoNodeExecParams params)
join_component_type<PointCloudComponent>(geometry_sets, geometry_set_result);
join_component_type<InstancesComponent>(geometry_sets, geometry_set_result);
join_component_type<VolumeComponent>(geometry_sets, geometry_set_result);
+ join_curve_components(geometry_sets, geometry_set_result);
params.set_output("Geometry", std::move(geometry_set_result));
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cone.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cone.cc
index 761d5d6c388..2806472286e 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cone.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cone.cc
@@ -194,9 +194,9 @@ static void calculate_uvs(Mesh *mesh,
{
MeshComponent mesh_component;
mesh_component.replace(mesh, GeometryOwnershipType::Editable);
- OutputAttributePtr uv_attribute = mesh_component.attribute_try_get_for_output(
- "uv_map", ATTR_DOMAIN_CORNER, CD_PROP_FLOAT2, nullptr);
- MutableSpan<float2> uvs = uv_attribute->get_span_for_write_only<float2>();
+ OutputAttribute_Typed<float2> uv_attribute =
+ mesh_component.attribute_try_get_for_output_only<float2>("uv_map", ATTR_DOMAIN_CORNER);
+ MutableSpan<float2> uvs = uv_attribute.as_span();
Array<float2> circle(verts_num);
float angle = 0.0f;
@@ -271,7 +271,7 @@ static void calculate_uvs(Mesh *mesh,
}
}
- uv_attribute.apply_span_and_save();
+ uv_attribute.save();
}
Mesh *create_cylinder_or_cone_mesh(const float radius_top,
diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_grid.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_grid.cc
index 14c57bc7135..5a4bab86421 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_grid.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_grid.cc
@@ -44,9 +44,9 @@ static void calculate_uvs(
{
MeshComponent mesh_component;
mesh_component.replace(mesh, GeometryOwnershipType::Editable);
- OutputAttributePtr uv_attribute = mesh_component.attribute_try_get_for_output(
- "uv_map", ATTR_DOMAIN_CORNER, CD_PROP_FLOAT2, nullptr);
- MutableSpan<float2> uvs = uv_attribute->get_span_for_write_only<float2>();
+ OutputAttribute_Typed<float2> uv_attribute =
+ mesh_component.attribute_try_get_for_output_only<float2>("uv_map", ATTR_DOMAIN_CORNER);
+ MutableSpan<float2> uvs = uv_attribute.as_span();
const float dx = (size_x == 0.0f) ? 0.0f : 1.0f / size_x;
const float dy = (size_y == 0.0f) ? 0.0f : 1.0f / size_y;
@@ -56,7 +56,7 @@ static void calculate_uvs(
uvs[i].y = (co.y + size_y * 0.5f) * dy;
}
- uv_attribute.apply_span_and_save();
+ uv_attribute.save();
}
static Mesh *create_grid_mesh(const int verts_x,
diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_uv_sphere.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_uv_sphere.cc
index fd95cdc81f7..cc93e71a5dd 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_uv_sphere.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_uv_sphere.cc
@@ -224,9 +224,9 @@ static void calculate_sphere_uvs(Mesh *mesh, const float segments, const float r
{
MeshComponent mesh_component;
mesh_component.replace(mesh, GeometryOwnershipType::Editable);
- OutputAttributePtr uv_attribute = mesh_component.attribute_try_get_for_output(
- "uv_map", ATTR_DOMAIN_CORNER, CD_PROP_FLOAT2, nullptr);
- MutableSpan<float2> uvs = uv_attribute->get_span_for_write_only<float2>();
+ OutputAttribute_Typed<float2> uv_attribute =
+ mesh_component.attribute_try_get_for_output_only<float2>("uv_map", ATTR_DOMAIN_CORNER);
+ MutableSpan<float2> uvs = uv_attribute.as_span();
int loop_index = 0;
const float dy = 1.0f / rings;
@@ -256,7 +256,7 @@ static void calculate_sphere_uvs(Mesh *mesh, const float segments, const float r
uvs[loop_index++] = float2(segment / segments, 1.0f - dy);
}
- uv_attribute.apply_span_and_save();
+ uv_attribute.save();
}
static Mesh *create_uv_sphere_mesh(const float radius, const int segments, const int rings)
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 bd42b4c11d6..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};
@@ -73,14 +71,15 @@ static void geo_node_object_info_exec(GeoNodeExecParams params)
if (object != self_object) {
InstancesComponent &instances = geometry_set.get_component_for_write<InstancesComponent>();
+ const int handle = instances.add_reference(*object);
if (transform_space_relative) {
- instances.add_instance(object, transform);
+ instances.add_instance(handle, transform);
}
else {
float unit_transform[4][4];
unit_m4(unit_transform);
- instances.add_instance(object, unit_transform);
+ instances.add_instance(handle, unit_transform);
}
}
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_point_distribute.cc b/source/blender/nodes/geometry/nodes/node_geo_point_distribute.cc
index 74cca8a2f3c..772bd8a1080 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_point_distribute.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_point_distribute.cc
@@ -28,6 +28,7 @@
#include "BKE_geometry_set_instances.hh"
#include "BKE_mesh.h"
#include "BKE_mesh_runtime.h"
+#include "BKE_mesh_sample.hh"
#include "BKE_pointcloud.h"
#include "UI_interface.h"
@@ -91,7 +92,7 @@ static Span<MLoopTri> get_mesh_looptris(const Mesh &mesh)
static void sample_mesh_surface(const Mesh &mesh,
const float4x4 &transform,
const float base_density,
- const FloatReadAttribute *density_factors,
+ const VArray<float> *density_factors,
const int seed,
Vector<float3> &r_positions,
Vector<float3> &r_bary_coords,
@@ -113,9 +114,9 @@ static void sample_mesh_surface(const Mesh &mesh,
float looptri_density_factor = 1.0f;
if (density_factors != nullptr) {
- const float v0_density_factor = std::max(0.0f, (*density_factors)[v0_loop]);
- const float v1_density_factor = std::max(0.0f, (*density_factors)[v1_loop]);
- const float v2_density_factor = std::max(0.0f, (*density_factors)[v2_loop]);
+ const float v0_density_factor = std::max(0.0f, density_factors->get(v0_loop));
+ const float v1_density_factor = std::max(0.0f, density_factors->get(v1_loop));
+ const float v2_density_factor = std::max(0.0f, density_factors->get(v2_loop));
looptri_density_factor = (v0_density_factor + v1_density_factor + v2_density_factor) / 3.0f;
}
const float area = area_tri_v3(v0_pos, v1_pos, v2_pos);
@@ -203,7 +204,7 @@ BLI_NOINLINE static void update_elimination_mask_for_close_points(
BLI_NOINLINE static void update_elimination_mask_based_on_density_factors(
const Mesh &mesh,
- const FloatReadAttribute &density_factors,
+ const VArray<float> &density_factors,
Span<float3> bary_coords,
Span<int> looptri_indices,
MutableSpan<bool> elimination_mask)
@@ -249,99 +250,27 @@ BLI_NOINLINE static void eliminate_points_based_on_mask(Span<bool> elimination_m
}
}
-template<typename T>
-BLI_NOINLINE static void interpolate_attribute_point(const Mesh &mesh,
- const Span<float3> bary_coords,
- const Span<int> looptri_indices,
- const Span<T> data_in,
- MutableSpan<T> data_out)
-{
- BLI_assert(data_in.size() == mesh.totvert);
- Span<MLoopTri> looptris = get_mesh_looptris(mesh);
-
- for (const int i : bary_coords.index_range()) {
- const int looptri_index = looptri_indices[i];
- const MLoopTri &looptri = looptris[looptri_index];
- const float3 &bary_coord = bary_coords[i];
-
- const int v0_index = mesh.mloop[looptri.tri[0]].v;
- const int v1_index = mesh.mloop[looptri.tri[1]].v;
- const int v2_index = mesh.mloop[looptri.tri[2]].v;
-
- const T &v0 = data_in[v0_index];
- const T &v1 = data_in[v1_index];
- const T &v2 = data_in[v2_index];
-
- const T interpolated_value = attribute_math::mix3(bary_coord, v0, v1, v2);
- data_out[i] = interpolated_value;
- }
-}
-
-template<typename T>
-BLI_NOINLINE static void interpolate_attribute_corner(const Mesh &mesh,
- const Span<float3> bary_coords,
- const Span<int> looptri_indices,
- const Span<T> data_in,
- MutableSpan<T> data_out)
-{
- BLI_assert(data_in.size() == mesh.totloop);
- Span<MLoopTri> looptris = get_mesh_looptris(mesh);
-
- for (const int i : bary_coords.index_range()) {
- const int looptri_index = looptri_indices[i];
- const MLoopTri &looptri = looptris[looptri_index];
- const float3 &bary_coord = bary_coords[i];
-
- const int loop_index_0 = looptri.tri[0];
- const int loop_index_1 = looptri.tri[1];
- const int loop_index_2 = looptri.tri[2];
-
- const T &v0 = data_in[loop_index_0];
- const T &v1 = data_in[loop_index_1];
- const T &v2 = data_in[loop_index_2];
-
- const T interpolated_value = attribute_math::mix3(bary_coord, v0, v1, v2);
- data_out[i] = interpolated_value;
- }
-}
-
-template<typename T>
-BLI_NOINLINE static void interpolate_attribute_face(const Mesh &mesh,
- const Span<int> looptri_indices,
- const Span<T> data_in,
- MutableSpan<T> data_out)
-{
- BLI_assert(data_in.size() == mesh.totpoly);
- Span<MLoopTri> looptris = get_mesh_looptris(mesh);
-
- for (const int i : data_out.index_range()) {
- const int looptri_index = looptri_indices[i];
- const MLoopTri &looptri = looptris[looptri_index];
- const int poly_index = looptri.poly;
- data_out[i] = data_in[poly_index];
- }
-}
-
-template<typename T>
BLI_NOINLINE static void interpolate_attribute(const Mesh &mesh,
Span<float3> bary_coords,
Span<int> looptri_indices,
const AttributeDomain source_domain,
- Span<T> source_span,
- MutableSpan<T> output_span)
+ const GVArray &source_data,
+ GMutableSpan output_data)
{
switch (source_domain) {
case ATTR_DOMAIN_POINT: {
- interpolate_attribute_point<T>(mesh, bary_coords, looptri_indices, source_span, output_span);
+ bke::mesh_surface_sample::sample_point_attribute(
+ mesh, looptri_indices, bary_coords, source_data, output_data);
break;
}
case ATTR_DOMAIN_CORNER: {
- interpolate_attribute_corner<T>(
- mesh, bary_coords, looptri_indices, source_span, output_span);
+ bke::mesh_surface_sample::sample_corner_attribute(
+ mesh, looptri_indices, bary_coords, source_data, output_data);
break;
}
case ATTR_DOMAIN_FACE: {
- interpolate_attribute_face<T>(mesh, looptri_indices, source_span, output_span);
+ bke::mesh_surface_sample::sample_face_attribute(
+ mesh, looptri_indices, source_data, output_data);
break;
}
default: {
@@ -363,13 +292,13 @@ BLI_NOINLINE static void interpolate_existing_attributes(
StringRef attribute_name = entry.key;
const CustomDataType output_data_type = entry.value.data_type;
/* The output domain is always #ATTR_DOMAIN_POINT, since we are creating a point cloud. */
- OutputAttributePtr attribute_out = component.attribute_try_get_for_output(
+ OutputAttribute attribute_out = component.attribute_try_get_for_output_only(
attribute_name, ATTR_DOMAIN_POINT, output_data_type);
if (!attribute_out) {
continue;
}
- fn::GMutableSpan out_span = attribute_out->get_span_for_write_only();
+ GMutableSpan out_span = attribute_out.as_span();
int i_instance = 0;
for (const GeometryInstanceGroup &set_group : set_groups) {
@@ -377,47 +306,41 @@ BLI_NOINLINE static void interpolate_existing_attributes(
const MeshComponent &source_component = *set.get_component_for_read<MeshComponent>();
const Mesh &mesh = *source_component.get_for_read();
- /* Use a dummy read without specifying a domain or data type in order to
- * get the existing attribute's domain. Interpolation is done manually based
- * on the bary coords in #interpolate_attribute. */
- ReadAttributePtr dummy_attribute = source_component.attribute_try_get_for_read(
+ std::optional<AttributeMetaData> attribute_info = component.attribute_get_meta_data(
attribute_name);
- if (!dummy_attribute) {
+ if (!attribute_info) {
i_instance += set_group.transforms.size();
continue;
}
- const AttributeDomain source_domain = dummy_attribute->domain();
- ReadAttributePtr source_attribute = source_component.attribute_get_for_read(
+ const AttributeDomain source_domain = attribute_info->domain;
+ GVArrayPtr source_attribute = source_component.attribute_get_for_read(
attribute_name, source_domain, output_data_type, nullptr);
if (!source_attribute) {
i_instance += set_group.transforms.size();
continue;
}
- fn::GSpan source_span = source_attribute->get_span();
+
+ for (const int UNUSED(i_set_instance) : set_group.transforms.index_range()) {
+ const int offset = instance_start_offsets[i_instance];
+ Span<float3> bary_coords = bary_coords_array[i_instance];
+ Span<int> looptri_indices = looptri_indices_array[i_instance];
+
+ GMutableSpan instance_span = out_span.slice(offset, bary_coords.size());
+ interpolate_attribute(
+ mesh, bary_coords, looptri_indices, source_domain, *source_attribute, instance_span);
+
+ i_instance++;
+ }
attribute_math::convert_to_static_type(output_data_type, [&](auto dummy) {
using T = decltype(dummy);
- for (const int UNUSED(i_set_instance) : set_group.transforms.index_range()) {
- const int offset = instance_start_offsets[i_instance];
- Span<float3> bary_coords = bary_coords_array[i_instance];
- Span<int> looptri_indices = looptri_indices_array[i_instance];
-
- MutableSpan<T> instance_span = out_span.typed<T>().slice(offset, bary_coords.size());
- interpolate_attribute<T>(mesh,
- bary_coords,
- looptri_indices,
- source_domain,
- source_span.typed<T>(),
- instance_span);
-
- i_instance++;
- }
+ GVArray_Span<T> source_span{*source_attribute};
});
}
- attribute_out.apply_span_and_save();
+ attribute_out.save();
}
}
@@ -427,16 +350,16 @@ BLI_NOINLINE static void compute_special_attributes(Span<GeometryInstanceGroup>
Span<Vector<float3>> bary_coords_array,
Span<Vector<int>> looptri_indices_array)
{
- OutputAttributePtr id_attribute = component.attribute_try_get_for_output(
- "id", ATTR_DOMAIN_POINT, CD_PROP_INT32);
- OutputAttributePtr normal_attribute = component.attribute_try_get_for_output(
- "normal", ATTR_DOMAIN_POINT, CD_PROP_FLOAT3);
- OutputAttributePtr rotation_attribute = component.attribute_try_get_for_output(
- "rotation", ATTR_DOMAIN_POINT, CD_PROP_FLOAT3);
+ OutputAttribute_Typed<int> id_attribute = component.attribute_try_get_for_output_only<int>(
+ "id", ATTR_DOMAIN_POINT);
+ OutputAttribute_Typed<float3> normal_attribute =
+ component.attribute_try_get_for_output_only<float3>("normal", ATTR_DOMAIN_POINT);
+ OutputAttribute_Typed<float3> rotation_attribute =
+ component.attribute_try_get_for_output_only<float3>("rotation", ATTR_DOMAIN_POINT);
- MutableSpan<int> result_ids = id_attribute->get_span_for_write_only<int>();
- MutableSpan<float3> result_normals = normal_attribute->get_span_for_write_only<float3>();
- MutableSpan<float3> result_rotations = rotation_attribute->get_span_for_write_only<float3>();
+ MutableSpan<int> result_ids = id_attribute.as_span();
+ MutableSpan<float3> result_normals = normal_attribute.as_span();
+ MutableSpan<float3> result_rotations = rotation_attribute.as_span();
int i_instance = 0;
for (const GeometryInstanceGroup &set_group : sets) {
@@ -480,9 +403,9 @@ BLI_NOINLINE static void compute_special_attributes(Span<GeometryInstanceGroup>
}
}
- id_attribute.apply_span_and_save();
- normal_attribute.apply_span_and_save();
- rotation_attribute.apply_span_and_save();
+ id_attribute.save();
+ normal_attribute.save();
+ rotation_attribute.save();
}
BLI_NOINLINE static void add_remaining_point_attributes(
@@ -520,7 +443,7 @@ static void distribute_points_random(Span<GeometryInstanceGroup> set_groups,
for (const GeometryInstanceGroup &set_group : set_groups) {
const GeometrySet &set = set_group.geometry_set;
const MeshComponent &component = *set.get_component_for_read<MeshComponent>();
- const FloatReadAttribute density_factors = component.attribute_get_for_read<float>(
+ GVArray_Typed<float> density_factors = component.attribute_get_for_read<float>(
density_attribute_name, ATTR_DOMAIN_CORNER, use_one_default ? 1.0f : 0.0f);
const Mesh &mesh = *component.get_for_read();
for (const float4x4 &transform : set_group.transforms) {
@@ -530,7 +453,7 @@ static void distribute_points_random(Span<GeometryInstanceGroup> set_groups,
sample_mesh_surface(mesh,
transform,
density,
- &density_factors,
+ &*density_factors,
seed,
positions,
bary_coords,
@@ -589,7 +512,7 @@ static void distribute_points_poisson_disk(Span<GeometryInstanceGroup> set_group
const GeometrySet &set = set_group.geometry_set;
const MeshComponent &component = *set.get_component_for_read<MeshComponent>();
const Mesh &mesh = *component.get_for_read();
- const FloatReadAttribute density_factors = component.attribute_get_for_read<float>(
+ const GVArray_Typed<float> density_factors = component.attribute_get_for_read<float>(
density_attribute_name, ATTR_DOMAIN_CORNER, use_one_default ? 1.0f : 0.0f);
for (const int UNUSED(i_set_instance) : set_group.transforms.index_range()) {
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 20022e8d29d..6f66f2145fa 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"
@@ -65,128 +64,133 @@ 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_instanced_data__object(const GeoNodeExecParams &params,
- MutableSpan<std::optional<InstancedData>> r_instances_data)
+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) {
- InstancedData instance;
- instance.type = INSTANCE_DATA_TYPE_OBJECT;
- instance.data.object = object;
- r_instances_data.fill(instance);
+ return {*object};
}
+ return {};
}
-static void get_instanced_data__collection(
- const GeoNodeExecParams &params,
- const GeometryComponent &component,
- MutableSpan<std::optional<InstancedData>> r_instances_data)
+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) {
- InstancedData instance;
- instance.type = INSTANCE_DATA_TYPE_COLLECTION;
- instance.data.collection = collection;
- r_instances_data.fill(instance);
+ if (node_storage->flag & GEO_NODE_POINT_INSTANCE_WHOLE_COLLECTION) {
+ return {*collection};
}
- else {
- Vector<InstancedData> possible_instances;
- /* Direct child objects are instanced as objects. */
- LISTBASE_FOREACH (CollectionObject *, cob, &collection->gobject) {
- Object *object = cob->ob;
- InstancedData instance;
- instance.type = INSTANCE_DATA_TYPE_OBJECT;
- instance.data.object = object;
- possible_instances.append(instance);
- }
- /* Direct child collections are instanced as collections. */
- LISTBASE_FOREACH (CollectionChild *, child, &collection->children) {
- Collection *child_collection = child->collection;
- InstancedData instance;
- instance.type = INSTANCE_DATA_TYPE_COLLECTION;
- instance.data.collection = child_collection;
- possible_instances.append(instance);
- }
- if (!possible_instances.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_instances_data.index_range()) {
- const int index = BLI_hash_int_2d(ids[i], seed) % possible_instances.size();
- r_instances_data[i] = possible_instances[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<std::optional<InstancedData>> get_instanced_data(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<std::optional<InstancedData>> instances_data(amount);
switch (type) {
case GEO_NODE_POINT_INSTANCE_TYPE_OBJECT: {
- get_instanced_data__object(params, instances_data);
- break;
+ return get_instance_references__object(params);
}
case GEO_NODE_POINT_INSTANCE_TYPE_COLLECTION: {
- get_instanced_data__collection(params, component, instances_data);
- break;
+ return get_instance_references__collection(params);
}
}
- return instances_data;
+ return {};
+}
+
+/**
+ * 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_geometry_component(InstancesComponent &instances,
- const GeometryComponent &src_geometry,
- const GeoNodeExecParams &params)
+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<std::optional<InstancedData>> instances_data = get_instanced_data(
- params, src_geometry, domain_size);
- Float3ReadAttribute positions = src_geometry.attribute_get_for_read<float3>(
+ GVArray_Typed<float3> positions = src_geometry.attribute_get_for_read<float3>(
"position", domain, {0, 0, 0});
- Float3ReadAttribute rotations = src_geometry.attribute_get_for_read<float3>(
+ GVArray_Typed<float3> rotations = src_geometry.attribute_get_for_read<float3>(
"rotation", domain, {0, 0, 0});
- Float3ReadAttribute scales = src_geometry.attribute_get_for_read<float3>(
+ GVArray_Typed<float3> scales = src_geometry.attribute_get_for_read<float3>(
"scale", domain, {1, 1, 1});
- Int32ReadAttribute ids = src_geometry.attribute_get_for_read<int>("id", domain, -1);
-
- for (const int i : IndexRange(domain_size)) {
- if (instances_data[i].has_value()) {
- const float4x4 matrix = float4x4::from_loc_eul_scale(positions[i], rotations[i], scales[i]);
- instances.add_instance(*instances_data[i], 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];
+ }
+ });
}
}
@@ -199,14 +203,32 @@ 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_component(instances,
+ *geometry_set.get_component_for_read<CurveComponent>(),
+ possible_handles,
+ params);
}
params.set_output("Geometry", std::move(geometry_set_out));
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 2e7fce6ea30..73d489949ad 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_point_rotate.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_point_rotate.cc
@@ -60,8 +60,8 @@ static void geo_node_point_rotate_layout(uiLayout *layout, bContext *UNUSED(C),
namespace blender::nodes {
static void point_rotate__axis_angle__object_space(const int domain_size,
- const Float3ReadAttribute &axis,
- const FloatReadAttribute &angles,
+ const VArray<float3> &axis,
+ const VArray<float> &angles,
MutableSpan<float3> rotations)
{
for (const int i : IndexRange(domain_size)) {
@@ -76,8 +76,8 @@ static void point_rotate__axis_angle__object_space(const int domain_size,
}
static void point_rotate__axis_angle__point_space(const int domain_size,
- const Float3ReadAttribute &axis,
- const FloatReadAttribute &angles,
+ const VArray<float3> &axis,
+ const VArray<float> &angles,
MutableSpan<float3> rotations)
{
for (const int i : IndexRange(domain_size)) {
@@ -92,7 +92,7 @@ static void point_rotate__axis_angle__point_space(const int domain_size,
}
static void point_rotate__euler__object_space(const int domain_size,
- const Float3ReadAttribute &eulers,
+ const VArray<float3> &eulers,
MutableSpan<float3> rotations)
{
for (const int i : IndexRange(domain_size)) {
@@ -107,7 +107,7 @@ static void point_rotate__euler__object_space(const int domain_size,
}
static void point_rotate__euler__point_space(const int domain_size,
- const Float3ReadAttribute &eulers,
+ const VArray<float3> &eulers,
MutableSpan<float3> rotations)
{
for (const int i : IndexRange(domain_size)) {
@@ -127,19 +127,19 @@ static void point_rotate_on_component(GeometryComponent &component,
const bNode &node = params.node();
const NodeGeometryRotatePoints &storage = *(const NodeGeometryRotatePoints *)node.storage;
- OutputAttributePtr rotation_attribute = component.attribute_try_get_for_output(
- "rotation", ATTR_DOMAIN_POINT, CD_PROP_FLOAT3);
+ OutputAttribute_Typed<float3> rotation_attribute =
+ component.attribute_try_get_for_output<float3>("rotation", ATTR_DOMAIN_POINT, {0, 0, 0});
if (!rotation_attribute) {
return;
}
- MutableSpan<float3> rotations = rotation_attribute->get_span<float3>();
+ MutableSpan<float3> rotations = rotation_attribute.as_span();
const int domain_size = rotations.size();
if (storage.type == GEO_NODE_POINT_ROTATE_TYPE_AXIS_ANGLE) {
- Float3ReadAttribute axis = params.get_input_attribute<float3>(
+ GVArray_Typed<float3> axis = params.get_input_attribute<float3>(
"Axis", component, ATTR_DOMAIN_POINT, {0, 0, 1});
- FloatReadAttribute angles = params.get_input_attribute<float>(
+ GVArray_Typed<float> angles = params.get_input_attribute<float>(
"Angle", component, ATTR_DOMAIN_POINT, 0);
if (storage.space == GEO_NODE_POINT_ROTATE_SPACE_OBJECT) {
@@ -150,7 +150,7 @@ static void point_rotate_on_component(GeometryComponent &component,
}
}
else {
- Float3ReadAttribute eulers = params.get_input_attribute<float3>(
+ GVArray_Typed<float3> eulers = params.get_input_attribute<float3>(
"Rotation", component, ATTR_DOMAIN_POINT, {0, 0, 0});
if (storage.space == GEO_NODE_POINT_ROTATE_SPACE_OBJECT) {
@@ -161,7 +161,7 @@ static void point_rotate_on_component(GeometryComponent &component,
}
}
- rotation_attribute.apply_span_and_save();
+ rotation_attribute.save();
}
static void geo_node_point_rotate_exec(GeoNodeExecParams params)
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 113e2c620f6..2ef21fb085b 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_point_scale.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_point_scale.cc
@@ -50,7 +50,7 @@ static void execute_on_component(GeoNodeExecParams params, GeometryComponent &co
* for the factor. But for it's simpler to simply always use float3, since that is usually
* expected anyway. */
static const float3 scale_default = float3(1.0f);
- OutputAttributePtr scale_attribute = component.attribute_try_get_for_output(
+ OutputAttribute_Typed<float3> scale_attribute = component.attribute_try_get_for_output(
"scale", ATTR_DOMAIN_POINT, CD_PROP_FLOAT3, &scale_default);
if (!scale_attribute) {
return;
@@ -63,27 +63,27 @@ static void execute_on_component(GeoNodeExecParams params, GeometryComponent &co
const CustomDataType data_type = (input_type == GEO_NODE_ATTRIBUTE_INPUT_FLOAT) ? CD_PROP_FLOAT :
CD_PROP_FLOAT3;
- ReadAttributePtr attribute = params.get_input_attribute(
+ GVArrayPtr attribute = params.get_input_attribute(
"Factor", component, ATTR_DOMAIN_POINT, data_type, nullptr);
if (!attribute) {
return;
}
- MutableSpan<float3> scale_span = scale_attribute->get_span<float3>();
+ MutableSpan<float3> scale_span = scale_attribute.as_span();
if (data_type == CD_PROP_FLOAT) {
- Span<float> factors = attribute->get_span<float>();
+ GVArray_Typed<float> factors{*attribute};
for (const int i : scale_span.index_range()) {
scale_span[i] = scale_span[i] * factors[i];
}
}
else if (data_type == CD_PROP_FLOAT3) {
- Span<float3> factors = attribute->get_span<float3>();
+ GVArray_Typed<float3> factors{*attribute};
for (const int i : scale_span.index_range()) {
scale_span[i] = scale_span[i] * factors[i];
}
}
- scale_attribute.apply_span_and_save();
+ scale_attribute.save();
}
static void geo_node_point_scale_exec(GeoNodeExecParams params)
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 522dea4aa0e..6541d982629 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_point_separate.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_point_separate.cc
@@ -58,27 +58,27 @@ static void copy_attributes_based_on_mask(const GeometryComponent &in_component,
const bool invert)
{
for (const std::string &name : in_component.attribute_names()) {
- ReadAttributePtr attribute = in_component.attribute_try_get_for_read(name);
- const CustomDataType data_type = attribute->custom_data_type();
+ ReadAttributeLookup attribute = in_component.attribute_try_get_for_read(name);
+ const CustomDataType data_type = bke::cpp_type_to_custom_data_type(attribute.varray->type());
/* Only copy point attributes. Theoretically this could interpolate attributes on other
* domains to the point domain, but that would conflict with attributes that are built-in
* on other domains, which causes creating the attributes to fail. */
- if (attribute->domain() != ATTR_DOMAIN_POINT) {
+ if (attribute.domain != ATTR_DOMAIN_POINT) {
continue;
}
- OutputAttributePtr result_attribute = result_component.attribute_try_get_for_output(
+ OutputAttribute result_attribute = result_component.attribute_try_get_for_output_only(
name, ATTR_DOMAIN_POINT, data_type);
attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
using T = decltype(dummy);
- Span<T> span = attribute->get_span<T>();
- MutableSpan<T> out_span = result_attribute->get_span_for_write_only<T>();
+ GVArray_Span<T> span{*attribute.varray};
+ MutableSpan<T> out_span = result_attribute.as_span<T>();
copy_data_based_on_mask(span, masks, invert, out_span);
});
- result_attribute.apply_span_and_save();
+ result_attribute.save();
}
}
@@ -107,9 +107,9 @@ static void separate_points_from_component(const GeometryComponent &in_component
return;
}
- const BooleanReadAttribute mask_attribute = in_component.attribute_get_for_read<bool>(
+ const GVArray_Typed<bool> mask_attribute = in_component.attribute_get_for_read<bool>(
mask_name, ATTR_DOMAIN_POINT, false);
- Span<bool> masks = mask_attribute.get_span();
+ VArray_Span<bool> masks{mask_attribute};
const int total = masks.count(!invert);
if (total == 0) {
diff --git a/source/blender/nodes/geometry/nodes/node_geo_point_translate.cc b/source/blender/nodes/geometry/nodes/node_geo_point_translate.cc
index 8c7387f7d9b..44203228899 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_point_translate.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_point_translate.cc
@@ -42,24 +42,19 @@ namespace blender::nodes {
static void execute_on_component(GeoNodeExecParams params, GeometryComponent &component)
{
- OutputAttributePtr position_attribute = component.attribute_try_get_for_output(
- "position", ATTR_DOMAIN_POINT, CD_PROP_FLOAT3);
+ OutputAttribute_Typed<float3> position_attribute =
+ component.attribute_try_get_for_output<float3>("position", ATTR_DOMAIN_POINT, {0, 0, 0});
if (!position_attribute) {
return;
}
- ReadAttributePtr attribute = params.get_input_attribute(
- "Translation", component, ATTR_DOMAIN_POINT, CD_PROP_FLOAT3, nullptr);
- if (!attribute) {
- return;
- }
+ GVArray_Typed<float3> attribute = params.get_input_attribute<float3>(
+ "Translation", component, ATTR_DOMAIN_POINT, {0, 0, 0});
- Span<float3> data = attribute->get_span<float3>();
- MutableSpan<float3> scale_span = position_attribute->get_span<float3>();
- for (const int i : scale_span.index_range()) {
- scale_span[i] = scale_span[i] + data[i];
+ for (const int i : IndexRange(attribute.size())) {
+ position_attribute->set(i, position_attribute->get(i) + attribute[i]);
}
- position_attribute.apply_span_and_save();
+ position_attribute.save();
}
static void geo_node_point_translate_exec(GeoNodeExecParams params)
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 a9eb136597e..e85f6aaee54 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
@@ -147,13 +147,15 @@ static void gather_point_data_from_component(const GeoNodeExecParams &params,
Vector<float3> &r_positions,
Vector<float> &r_radii)
{
- Float3ReadAttribute positions = component.attribute_get_for_read<float3>(
+ GVArray_Typed<float3> positions = component.attribute_get_for_read<float3>(
"position", ATTR_DOMAIN_POINT, {0, 0, 0});
- FloatReadAttribute radii = params.get_input_attribute<float>(
+ GVArray_Typed<float> radii = params.get_input_attribute<float>(
"Radius", component, ATTR_DOMAIN_POINT, 0.0f);
- r_positions.extend(positions.get_span());
- r_radii.extend(radii.get_span());
+ for (const int i : IndexRange(positions.size())) {
+ r_positions.append(positions[i]);
+ r_radii.append(radii[i]);
+ }
}
static void convert_to_grid_index_space(const float voxel_size,
@@ -184,6 +186,10 @@ static void initialize_volume_component_from_points(const GeometrySet &geometry_
gather_point_data_from_component(
params, *geometry_set_in.get_component_for_read<PointCloudComponent>(), positions, radii);
}
+ if (geometry_set_in.has<CurveComponent>()) {
+ gather_point_data_from_component(
+ params, *geometry_set_in.get_component_for_read<CurveComponent>(), positions, radii);
+ }
const float max_radius = *std::max_element(radii.begin(), radii.end());
const float voxel_size = compute_voxel_size(params, positions, max_radius);
diff --git a/source/blender/nodes/geometry/nodes/node_geo_switch.cc b/source/blender/nodes/geometry/nodes/node_geo_switch.cc
new file mode 100644
index 00000000000..f27c2163188
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_switch.cc
@@ -0,0 +1,163 @@
+/*
+ * 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"
+
+static bNodeSocketTemplate geo_node_switch_in[] = {
+ {SOCK_BOOLEAN, N_("Switch")},
+
+ {SOCK_FLOAT, N_("A"), 0.0, 0.0, 0.0, 0.0, -FLT_MAX, FLT_MAX},
+ {SOCK_FLOAT, N_("B"), 0.0, 0.0, 0.0, 0.0, -FLT_MAX, FLT_MAX},
+ {SOCK_INT, N_("A"), 0, 0, 0, 0, -100000, 100000},
+ {SOCK_INT, N_("B"), 0, 0, 0, 0, -100000, 100000},
+ {SOCK_BOOLEAN, N_("A")},
+ {SOCK_BOOLEAN, N_("B")},
+ {SOCK_VECTOR, N_("A"), 0.0, 0.0, 0.0, 0.0, -FLT_MAX, FLT_MAX},
+ {SOCK_VECTOR, N_("B"), 0.0, 0.0, 0.0, 0.0, -FLT_MAX, FLT_MAX},
+ {SOCK_RGBA, N_("A"), 0.8, 0.8, 0.8, 1.0},
+ {SOCK_RGBA, N_("B"), 0.8, 0.8, 0.8, 1.0},
+ {SOCK_STRING, N_("A")},
+ {SOCK_STRING, N_("B")},
+ {SOCK_GEOMETRY, N_("A")},
+ {SOCK_GEOMETRY, N_("B")},
+ {SOCK_OBJECT, N_("A")},
+ {SOCK_OBJECT, N_("B")},
+ {SOCK_COLLECTION, N_("A")},
+ {SOCK_COLLECTION, N_("B")},
+ {-1, ""},
+};
+
+static bNodeSocketTemplate geo_node_switch_out[] = {
+ {SOCK_FLOAT, N_("Output")},
+ {SOCK_INT, N_("Output")},
+ {SOCK_BOOLEAN, N_("Output")},
+ {SOCK_VECTOR, N_("Output")},
+ {SOCK_RGBA, N_("Output")},
+ {SOCK_STRING, N_("Output")},
+ {SOCK_GEOMETRY, N_("Output")},
+ {SOCK_OBJECT, N_("Output")},
+ {SOCK_COLLECTION, N_("Output")},
+ {-1, ""},
+};
+
+static void geo_node_switch_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
+{
+ uiItemR(layout, ptr, "input_type", 0, "", ICON_NONE);
+}
+
+static void geo_node_switch_init(bNodeTree *UNUSED(tree), bNode *node)
+{
+ NodeSwitch *data = (NodeSwitch *)MEM_callocN(sizeof(NodeSwitch), __func__);
+ data->input_type = SOCK_FLOAT;
+ node->storage = data;
+}
+
+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;
+ int index = 0;
+ LISTBASE_FOREACH (bNodeSocket *, socket, &node->inputs) {
+ nodeSetSocketAvailability(
+ socket, index == 0 || socket->type == (eNodeSocketDatatype)node_storage->input_type);
+ index++;
+ }
+ LISTBASE_FOREACH (bNodeSocket *, socket, &node->outputs) {
+ nodeSetSocketAvailability(socket,
+ socket->type == (eNodeSocketDatatype)node_storage->input_type);
+ }
+}
+
+static void geo_node_switch_exec(GeoNodeExecParams params)
+{
+ const NodeSwitch &storage = *(const NodeSwitch *)params.node().storage;
+ const bool input = params.extract_input<bool>("Switch");
+ switch ((eNodeSocketDatatype)storage.input_type) {
+ case SOCK_FLOAT: {
+ output_input<float>(params, input, "", "Output");
+ break;
+ }
+ case SOCK_INT: {
+ output_input<int>(params, input, "_001", "Output_001");
+ break;
+ }
+ case SOCK_BOOLEAN: {
+ output_input<bool>(params, input, "_002", "Output_002");
+ break;
+ }
+ case SOCK_VECTOR: {
+ output_input<float3>(params, input, "_003", "Output_003");
+ break;
+ }
+ case SOCK_RGBA: {
+ output_input<Color4f>(params, input, "_004", "Output_004");
+ break;
+ }
+ case SOCK_STRING: {
+ output_input<std::string>(params, input, "_005", "Output_005");
+ break;
+ }
+ case SOCK_GEOMETRY: {
+ output_input<GeometrySet>(params, input, "_006", "Output_006");
+ break;
+ }
+ case SOCK_OBJECT: {
+ output_input<Object *>(params, input, "_007", "Output_007");
+ break;
+ }
+ case SOCK_COLLECTION: {
+ output_input<Collection *>(params, input, "_008", "Output_008");
+ break;
+ }
+ default:
+ BLI_assert_unreachable();
+ break;
+ }
+}
+
+} // namespace blender::nodes
+
+void register_node_type_geo_switch()
+{
+ static bNodeType ntype;
+
+ geo_node_type_base(&ntype, GEO_NODE_SWITCH, "Switch", NODE_CLASS_GEOMETRY, 0);
+ node_type_socket_templates(&ntype, geo_node_switch_in, geo_node_switch_out);
+ node_type_init(&ntype, geo_node_switch_init);
+ 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.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 d54982d16c2..ce52dc90668 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_transform.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_transform.cc
@@ -24,6 +24,7 @@
#include "DNA_volume_types.h"
#include "BKE_mesh.h"
+#include "BKE_spline.hh"
#include "BKE_volume.h"
#include "DEG_depsgraph_query.h"
@@ -100,7 +101,7 @@ static void transform_instances(InstancesComponent &instances,
const float3 rotation,
const float3 scale)
{
- MutableSpan<float4x4> transforms = instances.transforms();
+ MutableSpan<float4x4> transforms = instances.instance_transforms();
/* Use only translation if rotation and scale don't apply. */
if (use_translate(rotation, scale)) {
@@ -152,6 +153,21 @@ static void transform_volume(Volume *volume,
#endif
}
+static void transform_curve(CurveEval &curve,
+ const float3 translation,
+ const float3 rotation,
+ const float3 scale)
+{
+
+ if (use_translate(rotation, scale)) {
+ curve.translate(translation);
+ }
+ else {
+ const float4x4 matrix = float4x4::from_loc_eul_scale(translation, rotation, scale);
+ curve.transform(matrix);
+ }
+}
+
static void geo_node_transform_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
@@ -179,6 +195,11 @@ static void geo_node_transform_exec(GeoNodeExecParams params)
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);
+ }
+
params.set_output("Geometry", std::move(geometry_set));
}
} // namespace blender::nodes