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')
-rw-r--r--source/blender/nodes/NOD_composite.h2
-rw-r--r--source/blender/nodes/NOD_geometry.h1
-rw-r--r--source/blender/nodes/NOD_geometry_exec.hh22
-rw-r--r--source/blender/nodes/NOD_geometry_nodes_eval_log.hh5
-rw-r--r--source/blender/nodes/NOD_static_types.h371
-rw-r--r--source/blender/nodes/composite/node_composite_tree.cc1
-rw-r--r--source/blender/nodes/composite/node_composite_util.hh12
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_cryptomatte.cc3
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_curves.cc2
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_diff_matte.cc2
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_huecorrect.cc2
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_image.cc2
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_keyingscreen.cc22
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_map_value.cc2
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_moviedistortion.cc1
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_output_file.cc2
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_planetrackdeform.cc32
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_trackpos.cc31
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_val_to_rgb.cc4
-rw-r--r--source/blender/nodes/function/nodes/node_fn_align_euler_to_vector.cc2
-rw-r--r--source/blender/nodes/geometry/CMakeLists.txt1
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_accumulate_field.cc34
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_capture.cc17
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_domain_size.cc42
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_statistic.cc20
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_boolean.cc15
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_bounding_box.cc4
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc70
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_endpoint_selection.cc4
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc596
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_primitive_star.cc12
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc20
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_reverse.cc4
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_sample.cc318
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_set_handle_type.cc4
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_spline_type.cc679
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc322
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc5
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc21
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc17
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_deform_curves_on_surface.cc416
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc136
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc72
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_dual_mesh.cc20
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_duplicate_elements.cc338
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_edge_split.cc4
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_extrude_mesh.cc71
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_field_at_index.cc4
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_field_on_domain.cc11
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_flip_faces.cc17
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_input_curve_handles.cc14
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_input_mesh_edge_angle.cc4
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_input_mesh_edge_neighbors.cc2
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_input_mesh_edge_vertices.cc4
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_input_mesh_face_area.cc2
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_input_mesh_face_is_planar.cc4
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_input_mesh_face_neighbors.cc4
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_input_mesh_island.cc5
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_input_spline_length.cc2
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_input_tangent.cc4
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_instance_on_points.cc10
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_instances_to_points.cc45
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc18
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_material_selection.cc2
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_merge_by_distance.cc8
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cone.cc42
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_grid.cc15
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_uv_sphere.cc153
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc14
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_mesh_to_points.cc32
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_mesh_to_volume.cc2
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_object_info.cc2
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_points.cc14
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_points_to_vertices.cc32
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_points_to_volume.cc9
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_raycast.cc4
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_remove_attribute.cc4
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_rotate_instances.cc6
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_scale_instances.cc6
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_set_curve_handles.cc14
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_set_curve_radius.cc19
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_set_curve_tilt.cc18
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_set_id.cc28
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_set_material_index.cc17
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_set_point_radius.cc17
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_set_position.cc56
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_set_shade_smooth.cc18
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_set_spline_cyclic.cc18
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_set_spline_resolution.cc18
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_store_named_attribute.cc74
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_string_to_curves.cc45
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc14
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_transfer_attribute.cc10
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_transform.cc55
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_translate_instances.cc4
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_triangulate.cc4
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_uv_pack_islands.cc6
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_uv_unwrap.cc10
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_volume_to_mesh.cc2
-rw-r--r--source/blender/nodes/intern/geometry_nodes_eval_log.cc24
-rw-r--r--source/blender/nodes/intern/node_common.cc15
-rw-r--r--source/blender/nodes/intern/node_geometry_exec.cc17
-rw-r--r--source/blender/nodes/intern/node_tree_ref.cc1
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_bsdf_hair_principled.cc3
104 files changed, 2067 insertions, 2718 deletions
diff --git a/source/blender/nodes/NOD_composite.h b/source/blender/nodes/NOD_composite.h
index 5d782674f16..58126c5722d 100644
--- a/source/blender/nodes/NOD_composite.h
+++ b/source/blender/nodes/NOD_composite.h
@@ -169,7 +169,7 @@ void ntreeCompositClearTags(struct bNodeTree *ntree);
struct bNodeSocket *ntreeCompositOutputFileAddSocket(struct bNodeTree *ntree,
struct bNode *node,
const char *name,
- struct ImageFormatData *im_format);
+ const struct ImageFormatData *im_format);
int ntreeCompositOutputFileRemoveActiveSocket(struct bNodeTree *ntree, struct bNode *node);
void ntreeCompositOutputFileSetPath(struct bNode *node,
diff --git a/source/blender/nodes/NOD_geometry.h b/source/blender/nodes/NOD_geometry.h
index 8f15add33fd..86c276fbd6f 100644
--- a/source/blender/nodes/NOD_geometry.h
+++ b/source/blender/nodes/NOD_geometry.h
@@ -47,6 +47,7 @@ void register_node_type_geo_curve_subdivide(void);
void register_node_type_geo_curve_to_mesh(void);
void register_node_type_geo_curve_to_points(void);
void register_node_type_geo_curve_trim(void);
+void register_node_type_geo_deform_curves_on_surface(void);
void register_node_type_geo_delete_geometry(void);
void register_node_type_geo_duplicate_elements(void);
void register_node_type_geo_distribute_points_on_faces(void);
diff --git a/source/blender/nodes/NOD_geometry_exec.hh b/source/blender/nodes/NOD_geometry_exec.hh
index b82c05f33be..c5bc42b059d 100644
--- a/source/blender/nodes/NOD_geometry_exec.hh
+++ b/source/blender/nodes/NOD_geometry_exec.hh
@@ -5,34 +5,36 @@
#include "FN_field.hh"
#include "FN_multi_function_builder.hh"
-#include "BKE_attribute_access.hh"
#include "BKE_geometry_fields.hh"
#include "BKE_geometry_set.hh"
-#include "BKE_geometry_set_instances.hh"
#include "DNA_node_types.h"
#include "NOD_derived_node_tree.hh"
#include "NOD_geometry_nodes_eval_log.hh"
-#include "GEO_realize_instances.hh"
-
struct Depsgraph;
struct ModifierData;
namespace blender::nodes {
using bke::AnonymousAttributeFieldInput;
+using bke::AttributeAccessor;
using bke::AttributeFieldInput;
using bke::AttributeIDRef;
+using bke::AttributeKind;
+using bke::AttributeMetaData;
+using bke::AttributeReader;
+using bke::AttributeWriter;
+using bke::GAttributeReader;
+using bke::GAttributeWriter;
using bke::GeometryComponentFieldContext;
using bke::GeometryFieldInput;
-using bke::OutputAttribute;
-using bke::OutputAttribute_Typed;
-using bke::ReadAttributeLookup;
+using bke::GSpanAttributeWriter;
+using bke::MutableAttributeAccessor;
+using bke::SpanAttributeWriter;
using bke::StrongAnonymousAttributeID;
using bke::WeakAnonymousAttributeID;
-using bke::WriteAttributeLookup;
using fn::Field;
using fn::FieldContext;
using fn::FieldEvaluator;
@@ -161,6 +163,7 @@ class GeoNodeExecParams {
}
void check_input_geometry_set(StringRef identifier, const GeometrySet &geometry_set) const;
+ void check_output_geometry_set(const GeometrySet &geometry_set) const;
/**
* Get input as vector for multi input socket with the given identifier.
@@ -229,6 +232,9 @@ class GeoNodeExecParams {
#ifdef DEBUG
this->check_output_access(identifier, type);
#endif
+ if constexpr (std::is_same_v<StoredT, GeometrySet>) {
+ this->check_output_geometry_set(value);
+ }
GMutablePointer gvalue = provider_->alloc_output_value(type);
new (gvalue.get()) StoredT(std::forward<T>(value));
provider_->set_output(identifier, gvalue);
diff --git a/source/blender/nodes/NOD_geometry_nodes_eval_log.hh b/source/blender/nodes/NOD_geometry_nodes_eval_log.hh
index 05c97c3903d..46ba72d14d8 100644
--- a/source/blender/nodes/NOD_geometry_nodes_eval_log.hh
+++ b/source/blender/nodes/NOD_geometry_nodes_eval_log.hh
@@ -115,11 +115,16 @@ class GeometryValueLog : public ValueLog {
struct InstancesInfo {
int instances_num;
};
+ struct EditDataInfo {
+ bool has_deformed_positions;
+ bool has_deform_matrices;
+ };
std::optional<MeshInfo> mesh_info;
std::optional<CurveInfo> curve_info;
std::optional<PointCloudInfo> pointcloud_info;
std::optional<InstancesInfo> instances_info;
+ std::optional<EditDataInfo> edit_data_info;
GeometryValueLog(const GeometrySet &geometry_set, bool log_full_geometry = false);
diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h
index fe5bb2d9b21..f1e4729143d 100644
--- a/source/blender/nodes/NOD_static_types.h
+++ b/source/blender/nodes/NOD_static_types.h
@@ -17,111 +17,111 @@
/* WARNING! If you edit those strings, please do the same in relevant nodes files (under blender/nodes/...)! */
/* Tree type Node ID RNA def function Enum name Struct name UI Name UI Description */
-DefNode(Node, NODE_FRAME, def_frame, "FRAME", Frame, "Frame", "Collect related nodes together in a common area.\nFrames are useful when a node setup becomes large and confusing yet the re-usability of a Node Group is not required")
+DefNode(Node, NODE_FRAME, def_frame, "FRAME", Frame, "Frame", "Collect related nodes together in a common area. Useful for organization when the re-usability of a node group is not required")
DefNode(Node, NODE_GROUP, def_group, "GROUP", Group, "Group", "")
-DefNode(Node, NODE_GROUP_INPUT, def_group_input, "GROUP_INPUT", GroupInput, "Group Input", "While inside a group, expose connected data as sockets in the node gruop interface")
-DefNode(Node, NODE_GROUP_OUTPUT, def_group_output, "GROUP_OUTPUT", GroupOutput, "Group Output", "While inside a group, output connected data")
-DefNode(Node, NODE_REROUTE, 0, "REROUTE", Reroute, "Reroute", "Used for organizing.\nReroute looks and behaves much like a socket on other nodes in that it supports one input connection while allowing multiple output connections")
+DefNode(Node, NODE_GROUP_INPUT, def_group_input, "GROUP_INPUT", GroupInput, "Group Input", "Expose connected data from inside a node group as inputs to its interface")
+DefNode(Node, NODE_GROUP_OUTPUT, def_group_output, "GROUP_OUTPUT", GroupOutput, "Group Output", "Output data from inside of a node group")
+DefNode(Node, NODE_REROUTE, 0, "REROUTE", Reroute, "Reroute", "A single-socket organization tool that supports one input and multiple outputs")
DefNode(ShaderNode, SH_NODE_RGB, 0, "RGB", RGB, "RGB", "A color picker")
-DefNode(ShaderNode, SH_NODE_VALUE, 0, "VALUE", Value, "Value", "A node to input numerical values to other nodes in the tree")
-DefNode(ShaderNode, SH_NODE_MIX_RGB, def_mix_rgb, "MIX_RGB", MixRGB, "MixRGB", "Mix colors by working on the individual and corresponding pixels of the two input colors")
+DefNode(ShaderNode, SH_NODE_VALUE, 0, "VALUE", Value, "Value", "Used to Input numerical values to other nodes in the tree")
+DefNode(ShaderNode, SH_NODE_MIX_RGB, def_mix_rgb, "MIX_RGB", MixRGB, "MixRGB", "Mix two input colors")
DefNode(ShaderNode, SH_NODE_VALTORGB, def_colorramp, "VALTORGB", ValToRGB, "ColorRamp", "Map values to colors with the use of a gradient")
-DefNode(ShaderNode, SH_NODE_RGBTOBW, 0, "RGBTOBW", RGBToBW, "RGB to BW", "Convert an RGB color image to a grayscale by the luminance")
-DefNode(ShaderNode, SH_NODE_SHADERTORGB, 0, "SHADERTORGB", ShaderToRGB, "Shader to RGB", "Convert rendering effect (such as light and shadow) to color.\nTypically used for non-photorealistic rendering, to apply additional effects on the output of BSDFs.\nFor example, a color ramp on the output of a diffuse BSDF can be used to create a flexible toon shader.\nNote: only supported for Eevee")
+DefNode(ShaderNode, SH_NODE_RGBTOBW, 0, "RGBTOBW", RGBToBW, "RGB to BW", "Convert a color's luminance to a grayscale value")
+DefNode(ShaderNode, SH_NODE_SHADERTORGB, 0, "SHADERTORGB", ShaderToRGB, "Shader to RGB", "Convert rendering effect (such as light and shadow) to color. Typically used for non-photorealistic rendering, to apply additional effects on the output of BSDFs.\nNote: only supported for Eevee")
DefNode(ShaderNode, SH_NODE_NORMAL, 0, "NORMAL", Normal, "Normal", "Generate a normal vector and a dot product")
DefNode(ShaderNode, SH_NODE_GAMMA, 0, "GAMMA", Gamma, "Gamma", "Apply a gamma correction")
DefNode(ShaderNode, SH_NODE_BRIGHTCONTRAST, 0, "BRIGHTCONTRAST", BrightContrast, "Bright Contrast", "Control the brightness and contrast of the input color")
-DefNode(ShaderNode, SH_NODE_MAPPING, def_sh_mapping, "MAPPING", Mapping, "Mapping", "Transform the input vector by applying translation, rotation, and scaling")
-DefNode(ShaderNode, SH_NODE_CURVE_VEC, def_vector_curve, "CURVE_VEC", VectorCurve, "Vector Curves", "Map an input vector component to a curve, Used to fine-tune the interpolation of the input")
+DefNode(ShaderNode, SH_NODE_MAPPING, def_sh_mapping, "MAPPING", Mapping, "Mapping", "Transform the input vector by applying translation, rotation, and scale")
+DefNode(ShaderNode, SH_NODE_CURVE_VEC, def_vector_curve, "CURVE_VEC", VectorCurve, "Vector Curves", "Map an input vectors to curves, used to fine-tune the interpolation of the input")
DefNode(ShaderNode, SH_NODE_CURVE_RGB, def_rgb_curve, "CURVE_RGB", RGBCurve, "RGB Curves", "Apply color corrections for each color channel")
-DefNode(ShaderNode, SH_NODE_CAMERA, 0, "CAMERA", CameraData, "Camera Data", "Use to get information about the position of the object relative to the camera.\nFor Example: To change the shading of objects further away from the camera, or make custom fog effects")
+DefNode(ShaderNode, SH_NODE_CAMERA, 0, "CAMERA", CameraData, "Camera Data", "Retrieve information about the camera and how it relates to the current shading point's position")
DefNode(ShaderNode, SH_NODE_MAP_RANGE, def_map_range, "MAP_RANGE", MapRange, "Map Range", "Remap a value from a range to a target range")
DefNode(ShaderNode, SH_NODE_CLAMP, def_clamp, "CLAMP", Clamp, "Clamp", "Clamp a value between a minimum and a maximum")
DefNode(ShaderNode, SH_NODE_MATH, def_math, "MATH", Math, "Math", "Perform math operations")
DefNode(ShaderNode, SH_NODE_VECTOR_MATH, def_vector_math, "VECT_MATH", VectorMath, "Vector Math", "Perform vector math operation")
DefNode(ShaderNode, SH_NODE_SQUEEZE, 0, "SQUEEZE", Squeeze, "Squeeze Value", "")
-DefNode(ShaderNode, SH_NODE_INVERT, 0, "INVERT", Invert, "Invert", "Inverts a color, producing a negative")
-DefNode(ShaderNode, SH_NODE_SEPRGB_LEGACY, 0, "SEPRGB", SeparateRGB, "Separate RGB", "Split an image into its red, green and blue channels")
-DefNode(ShaderNode, SH_NODE_COMBRGB_LEGACY, 0, "COMBRGB", CombineRGB, "Combine RGB", "Combine an image from its red, green and blue channels")
-DefNode(ShaderNode, SH_NODE_HUE_SAT, 0, "HUE_SAT", HueSaturation, "Hue Saturation Value","Apply a color transformation in the HSV Color Model")
+DefNode(ShaderNode, SH_NODE_INVERT, 0, "INVERT", Invert, "Invert", "Invert a color, producing a negative")
+DefNode(ShaderNode, SH_NODE_SEPRGB_LEGACY, 0, "SEPRGB", SeparateRGB, "Separate RGB", "Split a color into its red, green, and blue channels (Deprecated)")
+DefNode(ShaderNode, SH_NODE_COMBRGB_LEGACY, 0, "COMBRGB", CombineRGB, "Combine RGB", "Generate a color from its red, green, and blue channels (Deprecated)")
+DefNode(ShaderNode, SH_NODE_HUE_SAT, 0, "HUE_SAT", HueSaturation, "Hue Saturation Value","Apply a color transformation in the HSV color model")
-DefNode(ShaderNode, SH_NODE_OUTPUT_MATERIAL, def_sh_output, "OUTPUT_MATERIAL", OutputMaterial, "Material Output", "Output surface material information to a surface object")
-DefNode(ShaderNode, SH_NODE_EEVEE_SPECULAR, 0, "EEVEE_SPECULAR", EeveeSpecular, "Specular BSDF", "Similar to the Principled BSDF node but uses the specular workflow instead of the metallic.\nThe specular workflow functions by specifying the facing (along normal) reflection color The result may not be physically plausible because there is no energy conservation")
+DefNode(ShaderNode, SH_NODE_OUTPUT_MATERIAL, def_sh_output, "OUTPUT_MATERIAL", OutputMaterial, "Material Output", "Output surface material information for use in rendering")
+DefNode(ShaderNode, SH_NODE_EEVEE_SPECULAR, 0, "EEVEE_SPECULAR", EeveeSpecular, "Specular BSDF", "Similar to the Principled BSDF node but uses the specular workflow instead of metallic, which functions by specifying the facing (along normal) reflection color. Energy is not conserved, so the result may not be physically accurate")
DefNode(ShaderNode, SH_NODE_OUTPUT_LIGHT, def_sh_output, "OUTPUT_LIGHT", OutputLight, "Light Output", "Output light information to a light object")
-DefNode(ShaderNode, SH_NODE_OUTPUT_WORLD, def_sh_output, "OUTPUT_WORLD", OutputWorld, "World Output", "Output light color information to the scene’s World")
+DefNode(ShaderNode, SH_NODE_OUTPUT_WORLD, def_sh_output, "OUTPUT_WORLD", OutputWorld, "World Output", "Output light color information to the scene's World")
DefNode(ShaderNode, SH_NODE_OUTPUT_LINESTYLE, def_sh_output_linestyle,"OUTPUT_LINESTYLE", OutputLineStyle, "Line Style Output", "")
-DefNode(ShaderNode, SH_NODE_FRESNEL, 0, "FRESNEL", Fresnel, "Fresnel", "Produce a blending factor depending on the angle between the surface normal and the viewing direction using Fresnel equations.\nFaces facing the viewer result in darker values, and perpendicular faces produce lighter values.\nTypically used for mixing realistic reflections at grazing angles")
-DefNode(ShaderNode, SH_NODE_LAYER_WEIGHT, 0, "LAYER_WEIGHT", LayerWeight, "Layer Weight", "Produce a blending factor depending on the angle between the surface normal and the viewing direction.\nDirectly facing the viewer results in darker values and perpendicular faces will be lighter.\nTypically used for layering shaders with the Mix Shader node")
-DefNode(ShaderNode, SH_NODE_MIX_SHADER, 0, "MIX_SHADER", MixShader, "Mix Shader", "Mix two shaders together.\nTypically used for material layering")
+DefNode(ShaderNode, SH_NODE_FRESNEL, 0, "FRESNEL", Fresnel, "Fresnel", "Produce a blending factor depending on the angle between the surface normal and the view direction using Fresnel equations.\nTypically used for mixing reflections at grazing angles")
+DefNode(ShaderNode, SH_NODE_LAYER_WEIGHT, 0, "LAYER_WEIGHT", LayerWeight, "Layer Weight", "Produce a blending factor depending on the angle between the surface normal and the view direction.\nTypically used for layering shaders with the Mix Shader node")
+DefNode(ShaderNode, SH_NODE_MIX_SHADER, 0, "MIX_SHADER", MixShader, "Mix Shader", "Mix two shaders together. Typically used for material layering")
DefNode(ShaderNode, SH_NODE_ADD_SHADER, 0, "ADD_SHADER", AddShader, "Add Shader", "Add two Shaders together")
-DefNode(ShaderNode, SH_NODE_ATTRIBUTE, def_sh_attribute, "ATTRIBUTE", Attribute, "Attribute", "Retrieve attributes attached to an object or mesh")
-DefNode(ShaderNode, SH_NODE_AMBIENT_OCCLUSION, def_sh_ambient_occlusion,"AMBIENT_OCCLUSION", AmbientOcclusion, "Ambient Occlusion", "Computes how much the hemisphere above the shading point is occluded.\nThis can be used for procedural texturing, for example, to add weathering effects to corners only.\nNote: For Cycle, This is an expensive shader and may slow down render significantly")
+DefNode(ShaderNode, SH_NODE_ATTRIBUTE, def_sh_attribute, "ATTRIBUTE", Attribute, "Attribute", "Retrieve attributes attached to objects or geometry")
+DefNode(ShaderNode, SH_NODE_AMBIENT_OCCLUSION, def_sh_ambient_occlusion,"AMBIENT_OCCLUSION", AmbientOcclusion, "Ambient Occlusion", "Compute how much the hemisphere above the shading point is occluded, for example to add weathering effects to corners.\nNote: For Cycles, this may slow down renders significantly")
DefNode(ShaderNode, SH_NODE_BACKGROUND, 0, "BACKGROUND", Background, "Background", "Add background light emission.\nNote: This node should only be used for the world surface output")
-DefNode(ShaderNode, SH_NODE_HOLDOUT, 0, "HOLDOUT", Holdout, "Holdout", "Create a “hole” in the image with zero alpha transparency, which is useful for compositing.\nNote: the holdout shader can only create alpha when: Properties ‣ Render ‣ Film ‣ Transparent is enabled")
+DefNode(ShaderNode, SH_NODE_HOLDOUT, 0, "HOLDOUT", Holdout, "Holdout", "Create a \"hole\" in the image with zero alpha transparency, which is useful for compositing.\nNote: the holdout shader can only create alpha when transparency is enabled in the film settings")
DefNode(ShaderNode, SH_NODE_BSDF_ANISOTROPIC, def_anisotropic, "BSDF_ANISOTROPIC", BsdfAnisotropic, "Anisotropic BSDF", "Glossy reflection with separate control over U and V direction roughness")
DefNode(ShaderNode, SH_NODE_BSDF_DIFFUSE, 0, "BSDF_DIFFUSE", BsdfDiffuse, "Diffuse BSDF", "Lambertian and Oren-Nayar diffuse reflection")
-DefNode(ShaderNode, SH_NODE_BSDF_PRINCIPLED, def_principled, "BSDF_PRINCIPLED", BsdfPrincipled, "Principled BSDF", "Physically-based, easy-to-use shader for rendering surface materials.\nIt is based on the Disney principled model also known as the “PBR” shader, making it compatible with other software.\nTextures painted or baked from other softwares may be directly linked to the corresponding parameters in this shader")
+DefNode(ShaderNode, SH_NODE_BSDF_PRINCIPLED, def_principled, "BSDF_PRINCIPLED", BsdfPrincipled, "Principled BSDF", "Physically-based, easy-to-use shader for rendering surface materials, based on the Disney principled model also known as the \"PBR\" shader")
DefNode(ShaderNode, SH_NODE_BSDF_GLOSSY, def_glossy, "BSDF_GLOSSY", BsdfGlossy, "Glossy BSDF", "Reflection with microfacet distribution, used for materials such as metal or mirrors")
DefNode(ShaderNode, SH_NODE_BSDF_GLASS, def_glass, "BSDF_GLASS", BsdfGlass, "Glass BSDF", "Glass-like shader mixing refraction and reflection at grazing angles")
-DefNode(ShaderNode, SH_NODE_BSDF_REFRACTION, def_refraction, "BSDF_REFRACTION", BsdfRefraction, "Refraction BSDF", "Glossy refraction with sharp or microfacet distribution,.\nTypically used for materials that transmit light")
+DefNode(ShaderNode, SH_NODE_BSDF_REFRACTION, def_refraction, "BSDF_REFRACTION", BsdfRefraction, "Refraction BSDF", "Glossy refraction with sharp or microfacet distribution, typically used for materials that transmit light")
DefNode(ShaderNode, SH_NODE_BSDF_TRANSLUCENT, 0, "BSDF_TRANSLUCENT", BsdfTranslucent, "Translucent BSDF", "Lambertian diffuse transmission")
-DefNode(ShaderNode, SH_NODE_BSDF_TRANSPARENT, 0, "BSDF_TRANSPARENT", BsdfTransparent, "Transparent BSDF", "Transparency without refraction, passing straight through the surface as if there were no geometry there")
-DefNode(ShaderNode, SH_NODE_BSDF_VELVET, 0, "BSDF_VELVET", BsdfVelvet, "Velvet BSDF", "Reflection for materials such as cloth.\nTypically used together with other shaders (such as a Diffuse Shader) and is not particularly useful on its own")
+DefNode(ShaderNode, SH_NODE_BSDF_TRANSPARENT, 0, "BSDF_TRANSPARENT", BsdfTransparent, "Transparent BSDF", "Transparency without refraction, passing straight through the surface as if there were no geometry")
+DefNode(ShaderNode, SH_NODE_BSDF_VELVET, 0, "BSDF_VELVET", BsdfVelvet, "Velvet BSDF", "Reflection for materials such as cloth.\nTypically mixed with other shaders (such as a Diffuse Shader) and is not particularly useful on its own")
DefNode(ShaderNode, SH_NODE_BSDF_TOON, def_toon, "BSDF_TOON", BsdfToon, "Toon BSDF", "Diffuse and Glossy shaders with cartoon light effects")
DefNode(ShaderNode, SH_NODE_BSDF_HAIR, def_hair, "BSDF_HAIR", BsdfHair, "Hair BSDF", "Reflection and transmission shaders optimized for hair rendering")
DefNode(ShaderNode, SH_NODE_BSDF_HAIR_PRINCIPLED, def_hair_principled, "BSDF_HAIR_PRINCIPLED", BsdfHairPrincipled, "Principled Hair BSDF", "Physically-based, easy-to-use shader for rendering hair and fur")
-DefNode(ShaderNode, SH_NODE_SUBSURFACE_SCATTERING, def_sh_subsurface, "SUBSURFACE_SCATTERING",SubsurfaceScattering,"Subsurface Scattering","Subsurface multiple scattering shader.\nRather than light being reflected directly off the surface, it will penetrate the surface and bounce around internally.\nTypically used for materials such as skin, wax, marble or milk")
+DefNode(ShaderNode, SH_NODE_SUBSURFACE_SCATTERING, def_sh_subsurface, "SUBSURFACE_SCATTERING",SubsurfaceScattering,"Subsurface Scattering","Subsurface multiple scattering shader to simulate light entering the surface and bouncing internally.\nTypically used for materials such as skin, wax, marble or milk")
DefNode(ShaderNode, SH_NODE_VOLUME_ABSORPTION, 0, "VOLUME_ABSORPTION", VolumeAbsorption, "Volume Absorption", "Absorb light as it passes through the volume")
-DefNode(ShaderNode, SH_NODE_VOLUME_SCATTER, 0, "VOLUME_SCATTER", VolumeScatter, "Volume Scatter", "Scatter light as it passes through the volume.\nTypically usage would be to add fog to a scene")
+DefNode(ShaderNode, SH_NODE_VOLUME_SCATTER, 0, "VOLUME_SCATTER", VolumeScatter, "Volume Scatter", "Scatter light as it passes through the volume, often used to add fog to a scene")
DefNode(ShaderNode, SH_NODE_VOLUME_PRINCIPLED, 0, "PRINCIPLED_VOLUME", VolumePrincipled, "Principled Volume", "Combine all volume shading components into a single easy to use node")
DefNode(ShaderNode, SH_NODE_EMISSION, 0, "EMISSION", Emission, "Emission", "Lambertian emission shader")
-DefNode(ShaderNode, SH_NODE_NEW_GEOMETRY, 0, "NEW_GEOMETRY", NewGeometry, "Geometry", "Geometric information about the current shading point")
-DefNode(ShaderNode, SH_NODE_LIGHT_PATH, 0, "LIGHT_PATH", LightPath, "Light Path", "Find out for which kind of incoming ray the shader is being executed.\nTypically used for non-physically-based tricks, For example: an invisible object that still affects parts of the scene")
-DefNode(ShaderNode, SH_NODE_LIGHT_FALLOFF, 0, "LIGHT_FALLOFF", LightFalloff, "Light Falloff", "Manipulate how light intensity decreases over distance.\nTypically used for non-physically-based effects, in reality, the light will always fall off quadratically")
+DefNode(ShaderNode, SH_NODE_NEW_GEOMETRY, 0, "NEW_GEOMETRY", NewGeometry, "Geometry", "Retrieve geometric information about the current shading point")
+DefNode(ShaderNode, SH_NODE_LIGHT_PATH, 0, "LIGHT_PATH", LightPath, "Light Path", "Retrieve the type of incoming ray for which the shader is being executed.\nTypically used for non-physically-based tricks")
+DefNode(ShaderNode, SH_NODE_LIGHT_FALLOFF, 0, "LIGHT_FALLOFF", LightFalloff, "Light Falloff", "Manipulate how light intensity decreases over distance. Typically used for non-physically-based effects; in reality light always falls off quadratically")
DefNode(ShaderNode, SH_NODE_OBJECT_INFO, 0, "OBJECT_INFO", ObjectInfo, "Object Info", "Retrieve information about the object instance")
-DefNode(ShaderNode, SH_NODE_PARTICLE_INFO, 0, "PARTICLE_INFO", ParticleInfo, "Particle Info", "Retrieve the data of the particle that spawned the object instance.\nIt can be useful to give some variation to a single material assigned to multiple instances of instancing object")
+DefNode(ShaderNode, SH_NODE_PARTICLE_INFO, 0, "PARTICLE_INFO", ParticleInfo, "Particle Info", "Retrieve the data of the particle that spawned the object instance, for example to give variation to multiple instances of an object")
DefNode(ShaderNode, SH_NODE_HAIR_INFO, 0, "HAIR_INFO", HairInfo, "Curves Info", "Retrieve hair curve information")
DefNode(ShaderNode, SH_NODE_POINT_INFO, 0, "POINT_INFO", PointInfo, "Point Info", "Retrieve information about points in a point cloud")
-DefNode(ShaderNode, SH_NODE_VOLUME_INFO, 0, "VOLUME_INFO", VolumeInfo, "Volume Info", "Read volume data attributes found from smoke domains and other volumes")
-DefNode(ShaderNode, SH_NODE_WIREFRAME, def_sh_tex_wireframe, "WIREFRAME", Wireframe, "Wireframe", "Retrieve the edges of an object as it appears to Cycles.\nNote: as meshes are triangulated before being processed by Cycles, topology will always appear triangulated when viewed with the Wireframe node")
+DefNode(ShaderNode, SH_NODE_VOLUME_INFO, 0, "VOLUME_INFO", VolumeInfo, "Volume Info", "Read volume data attributes from volume grids")
+DefNode(ShaderNode, SH_NODE_WIREFRAME, def_sh_tex_wireframe, "WIREFRAME", Wireframe, "Wireframe", "Retrieve the edges of an object as it appears to Cycles.\nNote: as meshes are triangulated before being processed by Cycles, topology will always appear triangulated")
DefNode(ShaderNode, SH_NODE_WAVELENGTH, 0, "WAVELENGTH", Wavelength, "Wavelength", "Convert a wavelength value to an RGB value")
DefNode(ShaderNode, SH_NODE_BLACKBODY, 0, "BLACKBODY", Blackbody, "Blackbody", "Convert a blackbody temperature to an RGB value")
-DefNode(ShaderNode, SH_NODE_BUMP, def_sh_bump, "BUMP", Bump, "Bump", "Generate a perturbed normal from a height texture, for bump mapping.\nTypically used for faking high detailed surfaces")
-DefNode(ShaderNode, SH_NODE_NORMAL_MAP, def_sh_normal_map, "NORMAL_MAP", NormalMap, "Normal Map", "Generate a perturbed normal from an RGB normal map image.\nTypically used for faking high detailed surfaces")
+DefNode(ShaderNode, SH_NODE_BUMP, def_sh_bump, "BUMP", Bump, "Bump", "Generate a perturbed normal from a height texture for bump mapping. Typically used for faking highly detailed surfaces")
+DefNode(ShaderNode, SH_NODE_NORMAL_MAP, def_sh_normal_map, "NORMAL_MAP", NormalMap, "Normal Map", "Generate a perturbed normal from an RGB normal map image. Typically used for faking highly detailed surfaces")
DefNode(ShaderNode, SH_NODE_TANGENT, def_sh_tangent, "TANGENT", Tangent, "Tangent", "Generate a tangent direction for the Anisotropic BSDF")
-DefNode(ShaderNode, SH_NODE_SCRIPT, def_sh_script, "SCRIPT", Script, "Script", "Generate an OSL shader from a file or text block.\nNote: there is no support for running OSL code on the GPU")
-DefNode(ShaderNode, SH_NODE_TEX_IMAGE, def_sh_tex_image, "TEX_IMAGE", TexImage, "Image Texture", "Add an image file as a texture")
-DefNode(ShaderNode, SH_NODE_TEX_ENVIRONMENT, def_sh_tex_environment, "TEX_ENVIRONMENT", TexEnvironment, "Environment Texture","Add an image file as an environment texture.\nTypically used to light the scene with the background node")
+DefNode(ShaderNode, SH_NODE_SCRIPT, def_sh_script, "SCRIPT", Script, "Script", "Generate an OSL shader from a file or text data-block.\nNote: OSL shaders are not supported on the GPU")
+DefNode(ShaderNode, SH_NODE_TEX_IMAGE, def_sh_tex_image, "TEX_IMAGE", TexImage, "Image Texture", "Sample an image file as a texture")
+DefNode(ShaderNode, SH_NODE_TEX_ENVIRONMENT, def_sh_tex_environment, "TEX_ENVIRONMENT", TexEnvironment, "Environment Texture","Sample an image file as an environment texture. Typically used to light the scene with the background node")
DefNode(ShaderNode, SH_NODE_TEX_SKY, def_sh_tex_sky, "TEX_SKY", TexSky, "Sky Texture", "Generate a procedural sky texture")
-DefNode(ShaderNode, SH_NODE_TEX_GRADIENT, def_sh_tex_gradient, "TEX_GRADIENT", TexGradient, "Gradient Texture", "Generate an interpolated color and intensity values based on the input vector")
-DefNode(ShaderNode, SH_NODE_TEX_NOISE, def_sh_tex_noise, "TEX_NOISE", TexNoise, "Noise Texture", "Generate a fractal Perlin noise")
+DefNode(ShaderNode, SH_NODE_TEX_GRADIENT, def_sh_tex_gradient, "TEX_GRADIENT", TexGradient, "Gradient Texture", "Generate interpolated color and intensity values based on the input vector")
+DefNode(ShaderNode, SH_NODE_TEX_NOISE, def_sh_tex_noise, "TEX_NOISE", TexNoise, "Noise Texture", "Generate fractal Perlin noise")
DefNode(ShaderNode, SH_NODE_TEX_MAGIC, def_sh_tex_magic, "TEX_MAGIC", TexMagic, "Magic Texture", "Generate a psychedelic color texture")
DefNode(ShaderNode, SH_NODE_TEX_WAVE, def_sh_tex_wave, "TEX_WAVE", TexWave, "Wave Texture", "Generate procedural bands or rings with noise")
-DefNode(ShaderNode, SH_NODE_TEX_MUSGRAVE, def_sh_tex_musgrave, "TEX_MUSGRAVE", TexMusgrave, "Musgrave Texture", "Generate a fractal Perlin noise.\nUnlike the Noise Texture, which is also a fractal Perlin noise, the Musgrave Texture allows greater control over how octaves are combined")
-DefNode(ShaderNode, SH_NODE_TEX_VORONOI, def_sh_tex_voronoi, "TEX_VORONOI", TexVoronoi, "Voronoi Texture", "Generate a Worley noise.\nTypically used to generate textures such as stones, water, or biological cells")
+DefNode(ShaderNode, SH_NODE_TEX_MUSGRAVE, def_sh_tex_musgrave, "TEX_MUSGRAVE", TexMusgrave, "Musgrave Texture", "Generate fractal Perlin noise. Allows for greater control over how octaves are combined than the Noise Texture node")
+DefNode(ShaderNode, SH_NODE_TEX_VORONOI, def_sh_tex_voronoi, "TEX_VORONOI", TexVoronoi, "Voronoi Texture", "Generate Worley noise based on the distance to random points. Typically used to generate textures such as stones, water, or biological cells")
DefNode(ShaderNode, SH_NODE_TEX_CHECKER, def_sh_tex_checker, "TEX_CHECKER", TexChecker, "Checker Texture", "Generate a checkerboard texture")
DefNode(ShaderNode, SH_NODE_TEX_BRICK, def_sh_tex_brick, "TEX_BRICK", TexBrick, "Brick Texture", "Generate a procedural texture producing bricks")
DefNode(ShaderNode, SH_NODE_TEX_POINTDENSITY, def_sh_tex_pointdensity,"TEX_POINTDENSITY", TexPointDensity, "Point Density", "Generate a volumetric point for each particle or vertex of another object")
DefNode(ShaderNode, SH_NODE_TEX_COORD, def_sh_tex_coord, "TEX_COORD", TexCoord, "Texture Coordinate","Retrieve multiple types of texture coordinates.\nTypically used as inputs for texture nodes")
DefNode(ShaderNode, SH_NODE_VECTOR_ROTATE, def_sh_vector_rotate, "VECTOR_ROTATE", VectorRotate, "Vector Rotate", "Rotate a vector around a pivot point (center)")
DefNode(ShaderNode, SH_NODE_VECT_TRANSFORM, def_sh_vect_transform, "VECT_TRANSFORM", VectorTransform, "Vector Transform", "Convert a vector, point, or normal between world, camera, and object coordinate space")
-DefNode(ShaderNode, SH_NODE_SEPHSV_LEGACY, 0, "SEPHSV", SeparateHSV, "Separate HSV", "Split an image into its hue, saturation, and value channels")
-DefNode(ShaderNode, SH_NODE_COMBHSV_LEGACY, 0, "COMBHSV", CombineHSV, "Combine HSV", "Combine an image from its hue, saturation, and value channels")
-DefNode(ShaderNode, SH_NODE_UVMAP, def_sh_uvmap, "UVMAP", UVMap, "UV Map", "Retrieve a specified UV map")
-DefNode(ShaderNode, SH_NODE_VERTEX_COLOR, def_sh_vertex_color, "VERTEX_COLOR", VertexColor, "Color Attribute", "Retrieve a specified color attribute")
+DefNode(ShaderNode, SH_NODE_SEPHSV_LEGACY, 0, "SEPHSV", SeparateHSV, "Separate HSV", "Split a color into its hue, saturation, and value channels")
+DefNode(ShaderNode, SH_NODE_COMBHSV_LEGACY, 0, "COMBHSV", CombineHSV, "Combine HSV", "Create a color from its hue, saturation, and value channels")
+DefNode(ShaderNode, SH_NODE_UVMAP, def_sh_uvmap, "UVMAP", UVMap, "UV Map", "Retrieve a UV map from the geometry, or the default fallback if none is specified")
+DefNode(ShaderNode, SH_NODE_VERTEX_COLOR, def_sh_vertex_color, "VERTEX_COLOR", VertexColor, "Color Attribute", "Retrieve a color attribute, or the default fallback if none is specified")
DefNode(ShaderNode, SH_NODE_UVALONGSTROKE, def_sh_uvalongstroke, "UVALONGSTROKE", UVAlongStroke, "UV Along Stroke", "")
-DefNode(ShaderNode, SH_NODE_SEPXYZ, 0, "SEPXYZ", SeparateXYZ, "Separate XYZ", "Split an image into its X, Y, and Z channels")
-DefNode(ShaderNode, SH_NODE_COMBXYZ, 0, "COMBXYZ", CombineXYZ, "Combine XYZ", "Combine an image from its X, Y, and Z channels")
-DefNode(ShaderNode, SH_NODE_BEVEL, def_sh_bevel, "BEVEL", Bevel, "Bevel", "Generates normals with round corners.\nNote: this is only supported in Cycles, and is expensive")
+DefNode(ShaderNode, SH_NODE_SEPXYZ, 0, "SEPXYZ", SeparateXYZ, "Separate XYZ", "Split a vector into its X, Y, and Z components")
+DefNode(ShaderNode, SH_NODE_COMBXYZ, 0, "COMBXYZ", CombineXYZ, "Combine XYZ", "Create a vector from X, Y, and Z components")
+DefNode(ShaderNode, SH_NODE_BEVEL, def_sh_bevel, "BEVEL", Bevel, "Bevel", "Generates normals with round corners.\nNote: only supported in Cycles, and may slow down renders")
DefNode(ShaderNode, SH_NODE_DISPLACEMENT, def_sh_displacement, "DISPLACEMENT", Displacement, "Displacement", "Displace the surface along the surface normal")
-DefNode(ShaderNode, SH_NODE_VECTOR_DISPLACEMENT,def_sh_vector_displacement,"VECTOR_DISPLACEMENT",VectorDisplacement,"Vector Displacement","Displace the surface along arbitrary directions")
-DefNode(ShaderNode, SH_NODE_TEX_IES, def_sh_tex_ies, "TEX_IES", TexIES, "IES Texture", "Used to match real world lights based on IES files (IES).\nIES files store the directional intensity distribution of light sources")
-DefNode(ShaderNode, SH_NODE_TEX_WHITE_NOISE, def_sh_tex_white_noise, "TEX_WHITE_NOISE", TexWhiteNoise, "White Noise", "Return a random number based on an input seed")
-DefNode(ShaderNode, SH_NODE_OUTPUT_AOV, def_sh_output_aov, "OUTPUT_AOV", OutputAOV, "AOV Output", "Arbitrary Output Variables.\nProvide custom render passes for arbitrary shader node components")
+DefNode(ShaderNode, SH_NODE_VECTOR_DISPLACEMENT,def_sh_vector_displacement,"VECTOR_DISPLACEMENT",VectorDisplacement,"Vector Displacement","Displace the surface along an arbitrary direction")
+DefNode(ShaderNode, SH_NODE_TEX_IES, def_sh_tex_ies, "TEX_IES", TexIES, "IES Texture", "Used to match real world lights with IES files, which store the directional intensity distribution of light sources")
+DefNode(ShaderNode, SH_NODE_TEX_WHITE_NOISE, def_sh_tex_white_noise, "TEX_WHITE_NOISE", TexWhiteNoise, "White Noise", "Return a random value or color based on an input seed")
+DefNode(ShaderNode, SH_NODE_OUTPUT_AOV, def_sh_output_aov, "OUTPUT_AOV", OutputAOV, "AOV Output", "Arbitrary Output Variables.\nProvide custom render passes for arbitrary shader node outputs")
DefNode(ShaderNode, SH_NODE_CURVE_FLOAT, def_float_curve, "CURVE_FLOAT", FloatCurve, "Float Curve", "Map an input float to a curve and outputs a float value")
-DefNode(ShaderNode, SH_NODE_COMBINE_COLOR, def_sh_combsep_color, "COMBINE_COLOR", CombineColor, "Combine Color", "Combine an image from its red, green, and blue channels")
-DefNode(ShaderNode, SH_NODE_SEPARATE_COLOR, def_sh_combsep_color, "SEPARATE_COLOR", SeparateColor, "Separate Color", "Split an image into its red, green, and blue channels")
+DefNode(ShaderNode, SH_NODE_COMBINE_COLOR, def_sh_combsep_color, "COMBINE_COLOR", CombineColor, "Combine Color", "Create a color from individual components using multiple models")
+DefNode(ShaderNode, SH_NODE_SEPARATE_COLOR, def_sh_combsep_color, "SEPARATE_COLOR", SeparateColor, "Separate Color", "Split a color into its individual components using multiple models")
DefNode(CompositorNode, CMP_NODE_VIEWER, def_cmp_viewer, "VIEWER", Viewer, "Viewer", "" )
DefNode(CompositorNode, CMP_NODE_RGB, 0, "RGB", RGB, "RGB", "" )
@@ -260,8 +260,8 @@ DefNode(TextureNode, TEX_NODE_PROC+TEX_NOISE, 0, "TEX_NO
DefNode(TextureNode, TEX_NODE_PROC+TEX_STUCCI, 0, "TEX_STUCCI", TexStucci, "Stucci", "" )
DefNode(TextureNode, TEX_NODE_PROC+TEX_DISTNOISE, 0, "TEX_DISTNOISE", TexDistNoise, "Distorted Noise", "" )
-DefNode(FunctionNode, FN_NODE_ALIGN_EULER_TO_VECTOR, def_fn_align_euler_to_vector, "ALIGN_EULER_TO_VECTOR", AlignEulerToVector, "Align Euler To Vector", "")
-DefNode(FunctionNode, FN_NODE_BOOLEAN_MATH, def_boolean_math, "BOOLEAN_MATH", BooleanMath, "Boolean Math", "")
+DefNode(FunctionNode, FN_NODE_ALIGN_EULER_TO_VECTOR, def_fn_align_euler_to_vector, "ALIGN_EULER_TO_VECTOR", AlignEulerToVector, "Align Euler to Vector", "")
+DefNode(FunctionNode, FN_NODE_BOOLEAN_MATH, def_boolean_math, "BOOLEAN_MATH", BooleanMath, "Boolean Math", "")
DefNode(FunctionNode, FN_NODE_COMBINE_COLOR, def_fn_combsep_color, "COMBINE_COLOR", CombineColor, "Combine Color", "")
DefNode(FunctionNode, FN_NODE_COMPARE, def_compare, "COMPARE", Compare, "Compare", "")
DefNode(FunctionNode, FN_NODE_FLOAT_TO_INT, def_float_to_int, "FLOAT_TO_INT", FloatToInt, "Float to Integer", "")
@@ -279,131 +279,132 @@ DefNode(FunctionNode, FN_NODE_SLICE_STRING, 0, "SLICE_STRING", SliceString, "Sli
DefNode(FunctionNode, FN_NODE_STRING_LENGTH, 0, "STRING_LENGTH", StringLength, "String Length", "")
DefNode(FunctionNode, FN_NODE_VALUE_TO_STRING, 0, "VALUE_TO_STRING", ValueToString, "Value to String", "")
-DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_DOMAIN_SIZE, def_geo_attribute_domain_size, "ATTRIBUTE_DOMAIN_SIZE", AttributeDomainSize, "Domain Size", "")
-DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_STATISTIC, def_geo_attribute_statistic, "ATTRIBUTE_STATISTIC", AttributeStatistic, "Attribute Statistic", "")
-DefNode(GeometryNode, GEO_NODE_BOUNDING_BOX, 0, "BOUNDING_BOX", BoundBox, "Bounding Box", "")
-DefNode(GeometryNode, GEO_NODE_CAPTURE_ATTRIBUTE, def_geo_attribute_capture, "CAPTURE_ATTRIBUTE", CaptureAttribute, "Capture Attribute", "")
-DefNode(GeometryNode, GEO_NODE_COLLECTION_INFO, def_geo_collection_info, "COLLECTION_INFO", CollectionInfo, "Collection Info", "")
-DefNode(GeometryNode, GEO_NODE_CONVEX_HULL, 0, "CONVEX_HULL", ConvexHull, "Convex Hull", "")
-DefNode(GeometryNode, GEO_NODE_CURVE_ENDPOINT_SELECTION, 0, "CURVE_ENDPOINT_SELECTION", CurveEndpointSelection, "Endpoint Selection", "")
-DefNode(GeometryNode, GEO_NODE_CURVE_HANDLE_TYPE_SELECTION, def_geo_curve_handle_type_selection, "CURVE_HANDLE_TYPE_SELECTION", CurveHandleTypeSelection, "Handle Type Selection", "")
-DefNode(GeometryNode, GEO_NODE_CURVE_LENGTH, 0, "CURVE_LENGTH", CurveLength, "Curve Length", "")
-DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_ARC, def_geo_curve_primitive_arc, "CURVE_PRIMITIVE_ARC", CurveArc, "Arc", "")
-DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_BEZIER_SEGMENT, def_geo_curve_primitive_bezier_segment, "CURVE_PRIMITIVE_BEZIER_SEGMENT", CurvePrimitiveBezierSegment, "Bezier Segment", "")
-DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_CIRCLE, def_geo_curve_primitive_circle, "CURVE_PRIMITIVE_CIRCLE", CurvePrimitiveCircle, "Curve Circle", "")
-DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_LINE, def_geo_curve_primitive_line, "CURVE_PRIMITIVE_LINE", CurvePrimitiveLine, "Curve Line", "")
-DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_QUADRATIC_BEZIER, 0, "CURVE_PRIMITIVE_QUADRATIC_BEZIER", CurveQuadraticBezier, "Quadratic Bezier", "")
-DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_QUADRILATERAL, def_geo_curve_primitive_quadrilateral, "CURVE_PRIMITIVE_QUADRILATERAL", CurvePrimitiveQuadrilateral, "Quadrilateral", "")
-DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_SPIRAL, 0, "CURVE_PRIMITIVE_SPIRAL", CurveSpiral, "Curve Spiral", "")
-DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_STAR, 0, "CURVE_PRIMITIVE_STAR", CurveStar, "Star", "")
-DefNode(GeometryNode, GEO_NODE_CURVE_SET_HANDLE_TYPE, def_geo_curve_set_handle_type, "CURVE_SET_HANDLES", CurveSetHandles, "Set Handle Type", "")
-DefNode(GeometryNode, GEO_NODE_CURVE_SPLINE_PARAMETER, 0, "SPLINE_PARAMETER", SplineParameter, "Spline Parameter", "")
-DefNode(GeometryNode, GEO_NODE_CURVE_SPLINE_TYPE, def_geo_curve_spline_type, "CURVE_SPLINE_TYPE", CurveSplineType, "Set Spline Type", "")
-DefNode(GeometryNode, GEO_NODE_CURVE_TO_MESH, 0, "CURVE_TO_MESH", CurveToMesh, "Curve to Mesh", "")
-DefNode(GeometryNode, GEO_NODE_CURVE_TO_POINTS, def_geo_curve_to_points, "CURVE_TO_POINTS", CurveToPoints, "Curve to Points", "")
-DefNode(GeometryNode, GEO_NODE_DELETE_GEOMETRY, def_geo_delete_geometry, "DELETE_GEOMETRY", DeleteGeometry, "Delete Geometry", "")
-DefNode(GeometryNode, GEO_NODE_DUPLICATE_ELEMENTS, def_geo_duplicate_elements, "DUPLICATE_ELEMENTS", DuplicateElements, "Duplicate Elements", "")
-DefNode(GeometryNode, GEO_NODE_DISTRIBUTE_POINTS_ON_FACES, def_geo_distribute_points_on_faces, "DISTRIBUTE_POINTS_ON_FACES", DistributePointsOnFaces, "Distribute Points on Faces", "")
-DefNode(GeometryNode, GEO_NODE_ACCUMULATE_FIELD, def_geo_accumulate_field, "ACCUMULATE_FIELD", AccumulateField, "Accumulate Field", "")
-DefNode(GeometryNode, GEO_NODE_DUAL_MESH, 0, "DUAL_MESH", DualMesh, "Dual Mesh", "")
-DefNode(GeometryNode, GEO_NODE_EXTRUDE_MESH, def_geo_extrude_mesh, "EXTRUDE_MESH", ExtrudeMesh, "Extrude Mesh", "")
-DefNode(GeometryNode, GEO_NODE_FIELD_AT_INDEX, def_geo_field_at_index, "FIELD_AT_INDEX", FieldAtIndex, "Field at Index", "")
-DefNode(GeometryNode, GEO_NODE_FIELD_ON_DOMAIN, def_geo_field_on_domain, "FIELD_ON_DOMAIN", FieldOnDomain, "Field on Domain", "")
-DefNode(GeometryNode, GEO_NODE_FILL_CURVE, def_geo_curve_fill, "FILL_CURVE", FillCurve, "Fill Curve", "")
-DefNode(GeometryNode, GEO_NODE_FILLET_CURVE, def_geo_curve_fillet, "FILLET_CURVE", FilletCurve, "Fillet Curve", "")
-DefNode(GeometryNode, GEO_NODE_FLIP_FACES, 0, "FLIP_FACES", FlipFaces, "Flip Faces", "")
-DefNode(GeometryNode, GEO_NODE_GEOMETRY_TO_INSTANCE, 0, "GEOMETRY_TO_INSTANCE", GeometryToInstance, "Geometry to Instance", "")
-DefNode(GeometryNode, GEO_NODE_IMAGE_TEXTURE, def_geo_image_texture, "IMAGE_TEXTURE", ImageTexture, "Image Texture", "")
-DefNode(GeometryNode, GEO_NODE_INPUT_NAMED_ATTRIBUTE, def_geo_input_named_attribute, "INPUT_ATTRIBUTE", InputNamedAttribute, "Named Attribute", "")
-DefNode(GeometryNode, GEO_NODE_INPUT_CURVE_HANDLES, 0, "INPUT_CURVE_HANDLES", InputCurveHandlePositions, "Curve Handle Positions", "")
-DefNode(GeometryNode, GEO_NODE_INPUT_CURVE_TILT, 0, "INPUT_CURVE_TILT", InputCurveTilt, "Curve Tilt", "")
-DefNode(GeometryNode, GEO_NODE_INPUT_ID, 0, "INPUT_ID", InputID, "ID", "")
-DefNode(GeometryNode, GEO_NODE_INPUT_INDEX, 0, "INDEX", InputIndex, "Index", "")
-DefNode(GeometryNode, GEO_NODE_INPUT_INSTANCE_ROTATION, 0, "INPUT_INSTANCE_ROTATION", InputInstanceRotation, "Instance Rotation", "")
-DefNode(GeometryNode, GEO_NODE_INPUT_INSTANCE_SCALE, 0, "INPUT_INSTANCE_SCALE", InputInstanceScale, "Instance Scale", "")
-DefNode(GeometryNode, GEO_NODE_INPUT_MATERIAL_INDEX, 0, "INPUT_MATERIAL_INDEX", InputMaterialIndex, "Material Index", "")
-DefNode(GeometryNode, GEO_NODE_INPUT_MATERIAL, def_geo_input_material, "INPUT_MATERIAL", InputMaterial, "Material", "")
-DefNode(GeometryNode, GEO_NODE_INPUT_MESH_EDGE_ANGLE, 0, "MESH_EDGE_ANGLE", InputMeshEdgeAngle, "Edge Angle", "")
-DefNode(GeometryNode, GEO_NODE_INPUT_MESH_EDGE_NEIGHBORS, 0, "MESH_EDGE_NEIGHBORS", InputMeshEdgeNeighbors, "Edge Neighbors", "")
-DefNode(GeometryNode, GEO_NODE_INPUT_MESH_EDGE_VERTICES, 0, "MESH_EDGE_VERTICES", InputMeshEdgeVertices, "Edge Vertices", "")
-DefNode(GeometryNode, GEO_NODE_INPUT_MESH_FACE_AREA, 0, "MESH_FACE_AREA", InputMeshFaceArea, "Face Area", "")
-DefNode(GeometryNode, GEO_NODE_INPUT_MESH_FACE_IS_PLANAR, 0, "MESH_FACE_IS_PLANAR", InputMeshFaceIsPlanar, "Face is Planar", "")
-DefNode(GeometryNode, GEO_NODE_INPUT_MESH_FACE_NEIGHBORS, 0, "MESH_FACE_NEIGHBORS", InputMeshFaceNeighbors, "Face Neighbors", "")
-DefNode(GeometryNode, GEO_NODE_INPUT_MESH_ISLAND, 0, "MESH_ISLAND", InputMeshIsland, "Mesh Island", "")
-DefNode(GeometryNode, GEO_NODE_INPUT_MESH_VERTEX_NEIGHBORS, 0, "MESH_VERTEX_NEIGHBORS", InputMeshVertexNeighbors, "Vertex Neighbors", "")
-DefNode(GeometryNode, GEO_NODE_INPUT_NORMAL, 0, "INPUT_NORMAL", InputNormal, "Normal", "")
-DefNode(GeometryNode, GEO_NODE_INPUT_POSITION, 0, "POSITION", InputPosition, "Position", "")
-DefNode(GeometryNode, GEO_NODE_INPUT_RADIUS, 0, "INPUT_RADIUS", InputRadius, "Radius", "")
-DefNode(GeometryNode, GEO_NODE_INPUT_SCENE_TIME, 0, "INPUT_SCENE_TIME", InputSceneTime, "Scene Time", "")
-DefNode(GeometryNode, GEO_NODE_INPUT_SHADE_SMOOTH, 0, "INPUT_SHADE_SMOOTH", InputShadeSmooth, "Is Shade Smooth", "")
-DefNode(GeometryNode, GEO_NODE_INPUT_SPLINE_CYCLIC, 0, "INPUT_SPLINE_CYCLIC", InputSplineCyclic, "Is Spline Cyclic", "")
-DefNode(GeometryNode, GEO_NODE_INPUT_SPLINE_LENGTH, 0, "SPLINE_LENGTH", SplineLength, "Spline Length", "")
-DefNode(GeometryNode, GEO_NODE_INPUT_SPLINE_RESOLUTION, 0, "INPUT_SPLINE_RESOLUTION", InputSplineResolution, "Spline Resolution", "")
-DefNode(GeometryNode, GEO_NODE_INPUT_TANGENT, 0, "INPUT_TANGENT", InputTangent, "Curve Tangent", "")
-DefNode(GeometryNode, GEO_NODE_INSTANCE_ON_POINTS, 0, "INSTANCE_ON_POINTS", InstanceOnPoints, "Instance on Points", "")
-DefNode(GeometryNode, GEO_NODE_INSTANCES_TO_POINTS, 0, "INSTANCES_TO_POINTS", InstancesToPoints, "Instances to Points", "")
-DefNode(GeometryNode, GEO_NODE_IS_VIEWPORT, 0, "IS_VIEWPORT", IsViewport, "Is Viewport", "")
-DefNode(GeometryNode, GEO_NODE_JOIN_GEOMETRY, 0, "JOIN_GEOMETRY", JoinGeometry, "Join Geometry", "")
-DefNode(GeometryNode, GEO_NODE_MATERIAL_SELECTION, 0, "MATERIAL_SELECTION", MaterialSelection, "Material Selection", "")
-DefNode(GeometryNode, GEO_NODE_MERGE_BY_DISTANCE, def_geo_merge_by_distance, "MERGE_BY_DISTANCE", MergeByDistance, "Merge by Distance", "")
-DefNode(GeometryNode, GEO_NODE_MESH_BOOLEAN, def_geo_boolean, "MESH_BOOLEAN", MeshBoolean, "Mesh Boolean", "")
-DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_CIRCLE, def_geo_mesh_circle, "MESH_PRIMITIVE_CIRCLE", MeshCircle, "Mesh Circle", "")
-DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_CONE, def_geo_mesh_cone, "MESH_PRIMITIVE_CONE", MeshCone, "Cone", "")
-DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_CUBE, 0, "MESH_PRIMITIVE_CUBE", MeshCube, "Cube", "")
-DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_CYLINDER, def_geo_mesh_cylinder, "MESH_PRIMITIVE_CYLINDER", MeshCylinder, "Cylinder", "")
-DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_GRID, 0, "MESH_PRIMITIVE_GRID", MeshGrid, "Grid", "")
-DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_ICO_SPHERE, 0, "MESH_PRIMITIVE_ICO_SPHERE", MeshIcoSphere, "Ico Sphere", "")
-DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_LINE, def_geo_mesh_line, "MESH_PRIMITIVE_LINE", MeshLine, "Mesh Line", "")
-DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_UV_SPHERE, 0, "MESH_PRIMITIVE_UV_SPHERE", MeshUVSphere, "UV Sphere", "")
-DefNode(GeometryNode, GEO_NODE_MESH_TO_CURVE, 0, "MESH_TO_CURVE", MeshToCurve, "Mesh to Curve", "")
-DefNode(GeometryNode, GEO_NODE_MESH_TO_POINTS, def_geo_mesh_to_points, "MESH_TO_POINTS", MeshToPoints, "Mesh to Points", "")
-DefNode(GeometryNode, GEO_NODE_MESH_TO_VOLUME, def_geo_mesh_to_volume, "MESH_TO_VOLUME", MeshToVolume, "Mesh To Volume", "")
-DefNode(GeometryNode, GEO_NODE_OBJECT_INFO, def_geo_object_info, "OBJECT_INFO", ObjectInfo, "Object Info", "")
-DefNode(GeometryNode, GEO_NODE_POINTS, 0, "POINTS", Points, "Points", "")
-DefNode(GeometryNode, GEO_NODE_POINTS_TO_VERTICES, 0, "POINTS_TO_VERTICES", PointsToVertices, "Points to Vertices", "")
-DefNode(GeometryNode, GEO_NODE_POINTS_TO_VOLUME, def_geo_points_to_volume, "POINTS_TO_VOLUME", PointsToVolume, "Points to Volume", "")
-DefNode(GeometryNode, GEO_NODE_PROXIMITY, def_geo_proximity, "PROXIMITY", Proximity, "Geometry Proximity", "")
-DefNode(GeometryNode, GEO_NODE_RAYCAST, def_geo_raycast, "RAYCAST", Raycast, "Raycast", "")
-DefNode(GeometryNode, GEO_NODE_REMOVE_ATTRIBUTE, 0, "REMOVE_ATTRIBUTE", RemoveAttribute, "Remove Named Attribute", "")
-DefNode(GeometryNode, GEO_NODE_REALIZE_INSTANCES, def_geo_realize_instances, "REALIZE_INSTANCES", RealizeInstances, "Realize Instances", "")
-DefNode(GeometryNode, GEO_NODE_REPLACE_MATERIAL, 0, "REPLACE_MATERIAL", ReplaceMaterial, "Replace Material", "")
-DefNode(GeometryNode, GEO_NODE_RESAMPLE_CURVE, def_geo_curve_resample, "RESAMPLE_CURVE", ResampleCurve, "Resample Curve", "")
-DefNode(GeometryNode, GEO_NODE_REVERSE_CURVE, 0, "REVERSE_CURVE", ReverseCurve, "Reverse Curve", "")
-DefNode(GeometryNode, GEO_NODE_ROTATE_INSTANCES, 0, "ROTATE_INSTANCES", RotateInstances, "Rotate Instances", "")
-DefNode(GeometryNode, GEO_NODE_SAMPLE_CURVE, def_geo_curve_sample, "SAMPLE_CURVE", SampleCurve, "Sample Curve", "")
-DefNode(GeometryNode, GEO_NODE_SCALE_ELEMENTS, def_geo_scale_elements, "SCALE_ELEMENTS", ScaleElements, "Scale Elements", "")
-DefNode(GeometryNode, GEO_NODE_SCALE_INSTANCES, 0, "SCALE_INSTANCES", ScaleInstances, "Scale Instances", "")
-DefNode(GeometryNode, GEO_NODE_SEPARATE_COMPONENTS, 0, "SEPARATE_COMPONENTS", SeparateComponents, "Separate Components", "")
-DefNode(GeometryNode, GEO_NODE_SEPARATE_GEOMETRY, def_geo_separate_geometry, "SEPARATE_GEOMETRY", SeparateGeometry, "Separate Geometry", "")
-DefNode(GeometryNode, GEO_NODE_SET_CURVE_HANDLES, def_geo_curve_set_handle_positions, "SET_CURVE_HANDLES", SetCurveHandlePositions, "Set Handle Positions", "")
-DefNode(GeometryNode, GEO_NODE_SET_CURVE_RADIUS, 0, "SET_CURVE_RADIUS", SetCurveRadius, "Set Curve Radius", "")
-DefNode(GeometryNode, GEO_NODE_SET_CURVE_TILT, 0, "SET_CURVE_TILT", SetCurveTilt, "Set Curve Tilt", "")
-DefNode(GeometryNode, GEO_NODE_SET_ID, 0, "SET_ID", SetID, "Set ID", "")
-DefNode(GeometryNode, GEO_NODE_SET_MATERIAL_INDEX, 0, "SET_MATERIAL_INDEX", SetMaterialIndex, "Set Material Index", "")
-DefNode(GeometryNode, GEO_NODE_SET_MATERIAL, 0, "SET_MATERIAL", SetMaterial, "Set Material", "")
-DefNode(GeometryNode, GEO_NODE_SET_POINT_RADIUS, 0, "SET_POINT_RADIUS", SetPointRadius, "Set Point Radius", "")
-DefNode(GeometryNode, GEO_NODE_SET_POSITION, 0, "SET_POSITION", SetPosition, "Set Position", "")
-DefNode(GeometryNode, GEO_NODE_SET_SHADE_SMOOTH, 0, "SET_SHADE_SMOOTH", SetShadeSmooth, "Set Shade Smooth", "")
-DefNode(GeometryNode, GEO_NODE_SET_SPLINE_CYCLIC, 0, "SET_SPLINE_CYCLIC", SetSplineCyclic, "Set Spline Cyclic", "")
-DefNode(GeometryNode, GEO_NODE_SET_SPLINE_RESOLUTION, 0, "SET_SPLINE_RESOLUTION", SetSplineResolution, "Set Spline Resolution", "")
-DefNode(GeometryNode, GEO_NODE_SPLIT_EDGES, 0, "SPLIT_EDGES", SplitEdges, "Split Edges", "")
-DefNode(GeometryNode, GEO_NODE_STORE_NAMED_ATTRIBUTE, def_geo_store_named_attribute, "STORE_NAMED_ATTRIBUTE", StoreNamedAttribute, "Store Named Attribute", "")
-DefNode(GeometryNode, GEO_NODE_STRING_JOIN, 0, "STRING_JOIN", StringJoin, "Join Strings", "")
-DefNode(GeometryNode, GEO_NODE_STRING_TO_CURVES, def_geo_string_to_curves, "STRING_TO_CURVES", StringToCurves, "String to Curves", "")
-DefNode(GeometryNode, GEO_NODE_SUBDIVIDE_CURVE, 0, "SUBDIVIDE_CURVE", SubdivideCurve, "Subdivide Curve", "")
-DefNode(GeometryNode, GEO_NODE_SUBDIVIDE_MESH, 0, "SUBDIVIDE_MESH", SubdivideMesh, "Subdivide Mesh", "")
-DefNode(GeometryNode, GEO_NODE_SUBDIVISION_SURFACE, def_geo_subdivision_surface, "SUBDIVISION_SURFACE", SubdivisionSurface, "Subdivision Surface", "")
-DefNode(GeometryNode, GEO_NODE_SWITCH, def_geo_switch, "SWITCH", Switch, "Switch", "")
-DefNode(GeometryNode, GEO_NODE_TRANSFER_ATTRIBUTE, def_geo_transfer_attribute, "ATTRIBUTE_TRANSFER", AttributeTransfer, "Transfer Attribute", "")
-DefNode(GeometryNode, GEO_NODE_TRANSFORM, 0, "TRANSFORM", Transform, "Transform", "")
-DefNode(GeometryNode, GEO_NODE_TRANSLATE_INSTANCES, 0, "TRANSLATE_INSTANCES", TranslateInstances, "Translate Instances", "")
-DefNode(GeometryNode, GEO_NODE_TRIANGULATE, def_geo_triangulate, "TRIANGULATE", Triangulate, "Triangulate", "")
-DefNode(GeometryNode, GEO_NODE_TRIM_CURVE, def_geo_curve_trim, "TRIM_CURVE", TrimCurve, "Trim Curve", "")
-DefNode(GeometryNode, GEO_NODE_VIEWER, def_geo_viewer, "VIEWER", Viewer, "Viewer", "")
-DefNode(GeometryNode, GEO_NODE_VOLUME_CUBE, 0, "VOLUME_CUBE", VolumeCube, "Volume Cube", "")
-DefNode(GeometryNode, GEO_NODE_VOLUME_TO_MESH, def_geo_volume_to_mesh, "VOLUME_TO_MESH", VolumeToMesh, "Volume to Mesh", "")
-DefNode(GeometryNode, GEO_NODE_UV_PACK_ISLANDS, 0, "UV_PACK_ISLANDS", UVPackIslands, "Pack UV Islands", "")
-DefNode(GeometryNode, GEO_NODE_UV_UNWRAP, def_geo_uv_unwrap, "UV_UNWRAP", UVUnwrap, "UV Unwrap", "")
+DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_DOMAIN_SIZE, def_geo_attribute_domain_size, "ATTRIBUTE_DOMAIN_SIZE", AttributeDomainSize, "Domain Size", "Retrieve the number of elements in a geometry for each attribute domain")
+DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_STATISTIC, def_geo_attribute_statistic, "ATTRIBUTE_STATISTIC",AttributeStatistic, "Attribute Statistic","Calculate statistics about a data set from a field evaluated on a geometry")
+DefNode(GeometryNode, GEO_NODE_BOUNDING_BOX, 0, "BOUNDING_BOX", BoundBox, "Bounding Box", "Calculate the limits of a geometry's positions and generate a box mesh with those dimensions")
+DefNode(GeometryNode, GEO_NODE_CAPTURE_ATTRIBUTE, def_geo_attribute_capture,"CAPTURE_ATTRIBUTE", CaptureAttribute, "Capture Attribute", "Store the result of a field on a geometry and output the data as a node socket. Allows remembering or interpolating data as the geometry changes, such as positions before deformation")
+DefNode(GeometryNode, GEO_NODE_COLLECTION_INFO, def_geo_collection_info, "COLLECTION_INFO", CollectionInfo, "Collection Info", "Retrieve geometry from a collection")
+DefNode(GeometryNode, GEO_NODE_CONVEX_HULL, 0, "CONVEX_HULL", ConvexHull, "Convex Hull", "Create a mesh that encloses all points in the input geometry with the smallest number of points")
+DefNode(GeometryNode, GEO_NODE_CURVE_ENDPOINT_SELECTION, 0, "CURVE_ENDPOINT_SELECTION", CurveEndpointSelection, "Endpoint Selection", "Provide a selection for an arbitrary number of endpoints in each spline")
+DefNode(GeometryNode, GEO_NODE_CURVE_HANDLE_TYPE_SELECTION, def_geo_curve_handle_type_selection, "CURVE_HANDLE_TYPE_SELECTION", CurveHandleTypeSelection, "Handle Type Selection", "Provide a selection based on the handle types of Bézier control points")
+DefNode(GeometryNode, GEO_NODE_CURVE_LENGTH, 0, "CURVE_LENGTH", CurveLength, "Curve Length", "Retrieve the length of all splines added together")
+DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_ARC, def_geo_curve_primitive_arc, "CURVE_PRIMITIVE_ARC",CurveArc, "Arc", "Generate a poly spline arc")
+DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_BEZIER_SEGMENT, def_geo_curve_primitive_bezier_segment, "CURVE_PRIMITIVE_BEZIER_SEGMENT", CurvePrimitiveBezierSegment, "Bezier Segment", "Generate a 2D Bézier spline from the given control points and handles")
+DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_CIRCLE,def_geo_curve_primitive_circle, "CURVE_PRIMITIVE_CIRCLE", CurvePrimitiveCircle, "Curve Circle", "Generate a poly spline circle")
+DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_LINE, def_geo_curve_primitive_line, "CURVE_PRIMITIVE_LINE", CurvePrimitiveLine, "Curve Line", "Generate a poly spline line with two points")
+DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_QUADRATIC_BEZIER, 0, "CURVE_PRIMITIVE_QUADRATIC_BEZIER", CurveQuadraticBezier, "Quadratic Bezier", "Generate a poly spline in a parabola shape with control points positions")
+DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_QUADRILATERAL, def_geo_curve_primitive_quadrilateral, "CURVE_PRIMITIVE_QUADRILATERAL", CurvePrimitiveQuadrilateral, "Quadrilateral", "Generate a polygon with four points")
+DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_SPIRAL,0, "CURVE_PRIMITIVE_SPIRAL", CurveSpiral, "Curve Spiral", "Generate a poly spline in a spiral shape")
+DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_STAR, 0, "CURVE_PRIMITIVE_STAR", CurveStar, "Star", "Generate a poly spline in a star pattern by connecting alternating points of two circles")
+DefNode(GeometryNode, GEO_NODE_CURVE_SET_HANDLE_TYPE, def_geo_curve_set_handle_type, "CURVE_SET_HANDLES", CurveSetHandles, "Set Handle Type", "Set the handle type for the control points of a Bézier curve")
+DefNode(GeometryNode, GEO_NODE_CURVE_SPLINE_PARAMETER,0, "SPLINE_PARAMETER", SplineParameter, "Spline Parameter", "Retrieve how far along each spline a control point is")
+DefNode(GeometryNode, GEO_NODE_CURVE_SPLINE_TYPE, def_geo_curve_spline_type,"CURVE_SPLINE_TYPE", CurveSplineType, "Set Spline Type", "Change the type of curves")
+DefNode(GeometryNode, GEO_NODE_CURVE_TO_MESH, 0, "CURVE_TO_MESH", CurveToMesh, "Curve to Mesh", "Convert curves into a mesh, optionally with a custom profile shape defined by curves")
+DefNode(GeometryNode, GEO_NODE_CURVE_TO_POINTS, def_geo_curve_to_points, "CURVE_TO_POINTS", CurveToPoints, "Curve to Points", "Generate a point cloud by sampling positions along curves")
+DefNode(GeometryNode, GEO_NODE_DEFORM_CURVES_ON_SURFACE, 0, "DEFORM_CURVES_ON_SURFACE", DeformCurvesOnSurface, "Deform Curves on Surface", "Translate and rotate curves based on changes between the object's original and evaluated surface mesh")
+DefNode(GeometryNode, GEO_NODE_DELETE_GEOMETRY, def_geo_delete_geometry, "DELETE_GEOMETRY", DeleteGeometry, "Delete Geometry", "Remove selected elements of a geometry")
+DefNode(GeometryNode, GEO_NODE_DUPLICATE_ELEMENTS, def_geo_duplicate_elements, "DUPLICATE_ELEMENTS", DuplicateElements, "Duplicate Elements", "Generate an arbitrary number copies of each selected input element")
+DefNode(GeometryNode, GEO_NODE_DISTRIBUTE_POINTS_ON_FACES, def_geo_distribute_points_on_faces, "DISTRIBUTE_POINTS_ON_FACES", DistributePointsOnFaces, "Distribute Points on Faces", "Generate points spread out on the surface of a mesh")
+DefNode(GeometryNode, GEO_NODE_ACCUMULATE_FIELD, def_geo_accumulate_field, "ACCUMULATE_FIELD", AccumulateField, "Accumulate Field", "Add the values of an evaluated field together and output the running total for each element")
+DefNode(GeometryNode, GEO_NODE_DUAL_MESH, 0, "DUAL_MESH", DualMesh, "Dual Mesh", "Convert Faces into vertices and vertices into faces")
+DefNode(GeometryNode, GEO_NODE_EXTRUDE_MESH, def_geo_extrude_mesh, "EXTRUDE_MESH", ExtrudeMesh, "Extrude Mesh", "Generate new vertices, edges, or faces from selected elements and move them based on an offset while keeping them connected by their boundary")
+DefNode(GeometryNode, GEO_NODE_FIELD_AT_INDEX, def_geo_field_at_index, "FIELD_AT_INDEX", FieldAtIndex, "Field at Index", "Retrieve data of other elements in the context's geometry")
+DefNode(GeometryNode, GEO_NODE_FIELD_ON_DOMAIN, def_geo_field_on_domain, "FIELD_ON_DOMAIN", FieldOnDomain, "Field on Domain", "Retrieve values from a field on a different domain besides the domain from the context")
+DefNode(GeometryNode, GEO_NODE_FILL_CURVE, def_geo_curve_fill, "FILL_CURVE", FillCurve, "Fill Curve", "Generate a mesh on the XY plane with faces on the inside of input curves")
+DefNode(GeometryNode, GEO_NODE_FILLET_CURVE, def_geo_curve_fillet, "FILLET_CURVE", FilletCurve, "Fillet Curve", "Round corners by generating circular arcs on each control point")
+DefNode(GeometryNode, GEO_NODE_FLIP_FACES, 0, "FLIP_FACES", FlipFaces, "Flip Faces", "Reverse the order of the vertices and edges of selected faces, flipping their normal direction")
+DefNode(GeometryNode, GEO_NODE_GEOMETRY_TO_INSTANCE, 0, "GEOMETRY_TO_INSTANCE", GeometryToInstance, "Geometry to Instance", "Convert each input geometry into an instance, which can be much faster than the Join Geometry node when the inputs are large")
+DefNode(GeometryNode, GEO_NODE_IMAGE_TEXTURE, def_geo_image_texture, "IMAGE_TEXTURE", ImageTexture, "Image Texture", "Sample values from an image texture")
+DefNode(GeometryNode, GEO_NODE_INPUT_NAMED_ATTRIBUTE, def_geo_input_named_attribute, "INPUT_ATTRIBUTE", InputNamedAttribute, "Named Attribute", "Retrieve the data of a specified attribute")
+DefNode(GeometryNode, GEO_NODE_INPUT_CURVE_HANDLES, 0, "INPUT_CURVE_HANDLES",InputCurveHandlePositions,"Curve Handle Positions", "Retrieve the position of each Bézier control point's handles")
+DefNode(GeometryNode, GEO_NODE_INPUT_CURVE_TILT, 0, "INPUT_CURVE_TILT", InputCurveTilt, "Curve Tilt", "Retrieve the angle at each control point used to twist the curve's normal around its tangent")
+DefNode(GeometryNode, GEO_NODE_INPUT_ID, 0, "INPUT_ID", InputID, "ID", "Retrieve a stable random identifier value from the \"id\" attribute on the point domain, or the index if the attribute does not exist")
+DefNode(GeometryNode, GEO_NODE_INPUT_INDEX, 0, "INDEX", InputIndex, "Index", "Retrieve an integer value indicating the position of each element in the list, starting at zero")
+DefNode(GeometryNode, GEO_NODE_INPUT_INSTANCE_ROTATION, 0, "INPUT_INSTANCE_ROTATION", InputInstanceRotation, "Instance Rotation", "Retrieve the rotation of each instance in the geometry")
+DefNode(GeometryNode, GEO_NODE_INPUT_INSTANCE_SCALE, 0, "INPUT_INSTANCE_SCALE", InputInstanceScale, "Instance Scale", "Retrieve the scale of each instance in the geometry")
+DefNode(GeometryNode, GEO_NODE_INPUT_MATERIAL_INDEX, 0, "INPUT_MATERIAL_INDEX", InputMaterialIndex, "Material Index", "Retrieve the index of the material used for each element in the geometry's list of materials")
+DefNode(GeometryNode, GEO_NODE_INPUT_MATERIAL, def_geo_input_material, "INPUT_MATERIAL", InputMaterial, "Material", "Output a single material")
+DefNode(GeometryNode, GEO_NODE_INPUT_MESH_EDGE_ANGLE, 0, "MESH_EDGE_ANGLE", InputMeshEdgeAngle, "Edge Angle", "Calculate the surface area of each face in a mesh")
+DefNode(GeometryNode, GEO_NODE_INPUT_MESH_EDGE_NEIGHBORS, 0, "MESH_EDGE_NEIGHBORS",InputMeshEdgeNeighbors, "Edge Neighbors", "Retrieve the number of faces that use each edge as one of their sides")
+DefNode(GeometryNode, GEO_NODE_INPUT_MESH_EDGE_VERTICES, 0, "MESH_EDGE_VERTICES", InputMeshEdgeVertices, "Edge Vertices", "Retrieve topology information relating to each edge of a mesh")
+DefNode(GeometryNode, GEO_NODE_INPUT_MESH_FACE_AREA, 0, "MESH_FACE_AREA", InputMeshFaceArea, "Face Area", "Calculate the surface area of a mesh's faces")
+DefNode(GeometryNode, GEO_NODE_INPUT_MESH_FACE_IS_PLANAR, 0, "MESH_FACE_IS_PLANAR",InputMeshFaceIsPlanar, "Face is Planar", "Retrieve whether all triangles in a face are on the same plane, i.e. whether have the same normal")
+DefNode(GeometryNode, GEO_NODE_INPUT_MESH_FACE_NEIGHBORS, 0, "MESH_FACE_NEIGHBORS",InputMeshFaceNeighbors, "Face Neighbors", "Retrieve topology information relating to each face of a mesh")
+DefNode(GeometryNode, GEO_NODE_INPUT_MESH_ISLAND, 0, "MESH_ISLAND", InputMeshIsland, "Mesh Island", "Retrieve information about separate connected regions in a mesh")
+DefNode(GeometryNode, GEO_NODE_INPUT_MESH_VERTEX_NEIGHBORS, 0, "MESH_VERTEX_NEIGHBORS", InputMeshVertexNeighbors, "Vertex Neighbors", "Retrieve topology information relating to each vertex of a mesh")
+DefNode(GeometryNode, GEO_NODE_INPUT_NORMAL, 0, "INPUT_NORMAL", InputNormal, "Normal", "Retrieve a unit length vector indicating the direction pointing away from the geometry at each element")
+DefNode(GeometryNode, GEO_NODE_INPUT_POSITION, 0, "POSITION", InputPosition, "Position", "Retrieve a vector indicating the location of each element")
+DefNode(GeometryNode, GEO_NODE_INPUT_RADIUS, 0, "INPUT_RADIUS", InputRadius, "Radius", "Retrieve the radius at each point on curve or point cloud geometry")
+DefNode(GeometryNode, GEO_NODE_INPUT_SCENE_TIME, 0, "INPUT_SCENE_TIME", InputSceneTime, "Scene Time", "Retrieve the current time in the scene's animation in units of seconds or frames")
+DefNode(GeometryNode, GEO_NODE_INPUT_SHADE_SMOOTH, 0, "INPUT_SHADE_SMOOTH", InputShadeSmooth, "Is Shade Smooth", "Retrieve whether each face is marked for smooth shading")
+DefNode(GeometryNode, GEO_NODE_INPUT_SPLINE_CYCLIC, 0, "INPUT_SPLINE_CYCLIC",InputSplineCyclic, "Is Spline Cyclic", "Retrieve whether each spline endpoint connects to the beginning")
+DefNode(GeometryNode, GEO_NODE_INPUT_SPLINE_LENGTH, 0, "SPLINE_LENGTH", SplineLength, "Spline Length", "Retrieve the total length of each spline, as a distance or as a number of points")
+DefNode(GeometryNode, GEO_NODE_INPUT_SPLINE_RESOLUTION, 0, "INPUT_SPLINE_RESOLUTION", InputSplineResolution, "Spline Resolution", "Retrieve the number of evaluated points that will be generated for every control point on curves")
+DefNode(GeometryNode, GEO_NODE_INPUT_TANGENT, 0, "INPUT_TANGENT", InputTangent, "Curve Tangent", "Retrieve the direction of curves at each control point")
+DefNode(GeometryNode, GEO_NODE_INSTANCE_ON_POINTS, 0, "INSTANCE_ON_POINTS", InstanceOnPoints, "Instance on Points", "Generate a reference to geometry at each of the input points, without duplicating its underlying data")
+DefNode(GeometryNode, GEO_NODE_INSTANCES_TO_POINTS, 0, "INSTANCES_TO_POINTS",InstancesToPoints, "Instances to Points","Generate points at the origins of instances.\nNote: Nested instances are not affected by this node")
+DefNode(GeometryNode, GEO_NODE_IS_VIEWPORT, 0, "IS_VIEWPORT", IsViewport, "Is Viewport", "Retrieve whether the nodes are being evaluated for the viewport rather than the final render")
+DefNode(GeometryNode, GEO_NODE_JOIN_GEOMETRY, 0, "JOIN_GEOMETRY", JoinGeometry, "Join Geometry", "Merge separately generated geometries into a single one")
+DefNode(GeometryNode, GEO_NODE_MATERIAL_SELECTION, 0, "MATERIAL_SELECTION", MaterialSelection, "Material Selection", "Provide a selection of faces that use the specified material")
+DefNode(GeometryNode, GEO_NODE_MERGE_BY_DISTANCE, def_geo_merge_by_distance,"MERGE_BY_DISTANCE", MergeByDistance, "Merge by Distance", "Merge vertices or points within a given distance")
+DefNode(GeometryNode, GEO_NODE_MESH_BOOLEAN, def_geo_boolean, "MESH_BOOLEAN", MeshBoolean, "Mesh Boolean", "Cut, subtract, or join multiple mesh inputs")
+DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_CIRCLE, def_geo_mesh_circle, "MESH_PRIMITIVE_CIRCLE", MeshCircle, "Mesh Circle", "Generate a circular ring of edges")
+DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_CONE, def_geo_mesh_cone, "MESH_PRIMITIVE_CONE",MeshCone, "Cone", "Generate a cone mesh")
+DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_CUBE, 0, "MESH_PRIMITIVE_CUBE",MeshCube, "Cube", "Generate a cuboid mesh with variable side lengths and subdivisions")
+DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_CYLINDER, def_geo_mesh_cylinder, "MESH_PRIMITIVE_CYLINDER", MeshCylinder, "Cylinder", "Generate a cylinder mesh")
+DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_GRID, 0, "MESH_PRIMITIVE_GRID",MeshGrid, "Grid", "Generate a planar mesh on the XY plane")
+DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_ICO_SPHERE, 0, "MESH_PRIMITIVE_ICO_SPHERE", MeshIcoSphere, "Ico Sphere", "Generate a spherical mesh that consists of equally sized triangles")
+DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_LINE, def_geo_mesh_line, "MESH_PRIMITIVE_LINE",MeshLine, "Mesh Line", "Generate vertices in a line and connect them with edges")
+DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_UV_SPHERE, 0, "MESH_PRIMITIVE_UV_SPHERE", MeshUVSphere, "UV Sphere", "Generate a spherical mesh with quads, except for triangles at the top and bottom")
+DefNode(GeometryNode, GEO_NODE_MESH_TO_CURVE, 0, "MESH_TO_CURVE", MeshToCurve, "Mesh to Curve", "Generate a curve from a mesh")
+DefNode(GeometryNode, GEO_NODE_MESH_TO_POINTS, def_geo_mesh_to_points, "MESH_TO_POINTS", MeshToPoints, "Mesh to Points", "Generate a point cloud from a mesh's vertices")
+DefNode(GeometryNode, GEO_NODE_MESH_TO_VOLUME, def_geo_mesh_to_volume, "MESH_TO_VOLUME", MeshToVolume, "Mesh To Volume", "Create a fog volume with the shape of the input mesh's surface")
+DefNode(GeometryNode, GEO_NODE_OBJECT_INFO, def_geo_object_info, "OBJECT_INFO", ObjectInfo, "Object Info", "Retrieve information from an object")
+DefNode(GeometryNode, GEO_NODE_POINTS, 0, "POINTS", Points, "Points", "Generate a point cloud with positions and radii defined by fields")
+DefNode(GeometryNode, GEO_NODE_POINTS_TO_VERTICES, 0, "POINTS_TO_VERTICES", PointsToVertices, "Points to Vertices", "Generate a mesh vertex for each point cloud point")
+DefNode(GeometryNode, GEO_NODE_POINTS_TO_VOLUME, def_geo_points_to_volume, "POINTS_TO_VOLUME", PointsToVolume, "Points to Volume", "Generate a fog volume sphere around every point")
+DefNode(GeometryNode, GEO_NODE_PROXIMITY, def_geo_proximity, "PROXIMITY", Proximity, "Geometry Proximity", "Compute the closest location on the target geometry")
+DefNode(GeometryNode, GEO_NODE_RAYCAST, def_geo_raycast, "RAYCAST", Raycast, "Raycast", "Cast rays from the context geometry onto a target geometry, and retrieve information from each hit point")
+DefNode(GeometryNode, GEO_NODE_REMOVE_ATTRIBUTE, 0, "REMOVE_ATTRIBUTE", RemoveAttribute, "Remove Named Attribute", "Delete an attribute with a specified name from a geometry. Typically used to optimize performance")
+DefNode(GeometryNode, GEO_NODE_REALIZE_INSTANCES, def_geo_realize_instances,"REALIZE_INSTANCES", RealizeInstances, "Realize Instances", "Change the direction of the curve by swapping each spline's start and end data")
+DefNode(GeometryNode, GEO_NODE_REPLACE_MATERIAL, 0, "REPLACE_MATERIAL", ReplaceMaterial, "Replace Material", "Swap one material with another")
+DefNode(GeometryNode, GEO_NODE_RESAMPLE_CURVE, def_geo_curve_resample, "RESAMPLE_CURVE", ResampleCurve, "Resample Curve", "Generate a poly spline for each input spline")
+DefNode(GeometryNode, GEO_NODE_REVERSE_CURVE, 0, "REVERSE_CURVE", ReverseCurve, "Reverse Curve", "Swap the start and end of splines")
+DefNode(GeometryNode, GEO_NODE_ROTATE_INSTANCES, 0, "ROTATE_INSTANCES", RotateInstances, "Rotate Instances", "Rotate geometry instances in local or global space")
+DefNode(GeometryNode, GEO_NODE_SAMPLE_CURVE, def_geo_curve_sample, "SAMPLE_CURVE", SampleCurve, "Sample Curve", "Retrieve data from a point on a curve at a certain distance from its start")
+DefNode(GeometryNode, GEO_NODE_SCALE_ELEMENTS, def_geo_scale_elements, "SCALE_ELEMENTS", ScaleElements, "Scale Elements", "Scale groups of connected edges and faces")
+DefNode(GeometryNode, GEO_NODE_SCALE_INSTANCES, 0, "SCALE_INSTANCES", ScaleInstances, "Scale Instances", "Scale geometry instances in local or global space")
+DefNode(GeometryNode, GEO_NODE_SEPARATE_COMPONENTS, 0, "SEPARATE_COMPONENTS",SeparateComponents, "Separate Components","Split a geometry into a separate output for each type of data in the geometry")
+DefNode(GeometryNode, GEO_NODE_SEPARATE_GEOMETRY, def_geo_separate_geometry,"SEPARATE_GEOMETRY", SeparateGeometry, "Separate Geometry", "Split a geometry into two geometry outputs based on a selection")
+DefNode(GeometryNode, GEO_NODE_SET_CURVE_HANDLES, def_geo_curve_set_handle_positions, "SET_CURVE_HANDLES", SetCurveHandlePositions, "Set Handle Positions", "Set the positions for the handles of Bézier curves")
+DefNode(GeometryNode, GEO_NODE_SET_CURVE_RADIUS, 0, "SET_CURVE_RADIUS", SetCurveRadius, "Set Curve Radius", "Set the radius of the curve at each control point")
+DefNode(GeometryNode, GEO_NODE_SET_CURVE_TILT, 0, "SET_CURVE_TILT", SetCurveTilt, "Set Curve Tilt", "Set the tilt angle at each curve control point")
+DefNode(GeometryNode, GEO_NODE_SET_ID, 0, "SET_ID", SetID, "Set ID", "Set the id attribute on the input geometry, mainly used internally for randomizing")
+DefNode(GeometryNode, GEO_NODE_SET_MATERIAL_INDEX, 0, "SET_MATERIAL_INDEX", SetMaterialIndex, "Set Material Index", "Set the material index for each selected geometry element")
+DefNode(GeometryNode, GEO_NODE_SET_MATERIAL, 0, "SET_MATERIAL", SetMaterial, "Set Material", "Assign a material to geometry elements")
+DefNode(GeometryNode, GEO_NODE_SET_POINT_RADIUS, 0, "SET_POINT_RADIUS", SetPointRadius, "Set Point Radius", "Set the display size of point cloud points")
+DefNode(GeometryNode, GEO_NODE_SET_POSITION, 0, "SET_POSITION", SetPosition, "Set Position", "Set the location of each point")
+DefNode(GeometryNode, GEO_NODE_SET_SHADE_SMOOTH, 0, "SET_SHADE_SMOOTH", SetShadeSmooth, "Set Shade Smooth", "Control the smoothness of mesh normals around each face by changing the \"shade smooth\" attribute")
+DefNode(GeometryNode, GEO_NODE_SET_SPLINE_CYCLIC, 0, "SET_SPLINE_CYCLIC", SetSplineCyclic, "Set Spline Cyclic", "Control whether each spline loops back on itself by changing the \"cyclic\" attribute")
+DefNode(GeometryNode, GEO_NODE_SET_SPLINE_RESOLUTION, 0, "SET_SPLINE_RESOLUTION", SetSplineResolution, "Set Spline Resolution", "Control how many evaluated points should be generated on every curve segment")
+DefNode(GeometryNode, GEO_NODE_SPLIT_EDGES, 0, "SPLIT_EDGES", SplitEdges, "Split Edges", "Duplicate mesh edges and break connections with the surrounding faces")
+DefNode(GeometryNode, GEO_NODE_STORE_NAMED_ATTRIBUTE, def_geo_store_named_attribute, "STORE_NAMED_ATTRIBUTE", StoreNamedAttribute, "Store Named Attribute", "Store the result of a field on a geometry as an attribute with the specified name")
+DefNode(GeometryNode, GEO_NODE_STRING_JOIN, 0, "STRING_JOIN", StringJoin, "Join Strings", "Combine any number of input strings")
+DefNode(GeometryNode, GEO_NODE_STRING_TO_CURVES, def_geo_string_to_curves, "STRING_TO_CURVES", StringToCurves, "String to Curves", "Generate a paragraph of text with a specific font, using a curve instance to store each character")
+DefNode(GeometryNode, GEO_NODE_SUBDIVIDE_CURVE, 0, "SUBDIVIDE_CURVE", SubdivideCurve, "Subdivide Curve", "Dividing each curve segment into a specified number of pieces")
+DefNode(GeometryNode, GEO_NODE_SUBDIVIDE_MESH, 0, "SUBDIVIDE_MESH", SubdivideMesh, "Subdivide Mesh", "Divide mesh faces into smaller ones without changing the shape or volume, using linear interpolation to place the new vertices")
+DefNode(GeometryNode, GEO_NODE_SUBDIVISION_SURFACE, def_geo_subdivision_surface, "SUBDIVISION_SURFACE",SubdivisionSurface, "Subdivision Surface","Divide mesh faces to form a smooth surface, using the Catmull-Clark subdivision method")
+DefNode(GeometryNode, GEO_NODE_SWITCH, def_geo_switch, "SWITCH", Switch, "Switch", "Switch between two inputs")
+DefNode(GeometryNode, GEO_NODE_TRANSFER_ATTRIBUTE, def_geo_transfer_attribute, "ATTRIBUTE_TRANSFER", AttributeTransfer, "Transfer Attribute", "Retrieve values from a source geometry and provides them as a field by interpolating them with the context geomerty")
+DefNode(GeometryNode, GEO_NODE_TRANSFORM, 0, "TRANSFORM", Transform, "Transform", "Translate, rotate or scale the geometry")
+DefNode(GeometryNode, GEO_NODE_TRANSLATE_INSTANCES, 0, "TRANSLATE_INSTANCES",TranslateInstances, "Translate Instances","Move top-level geometry instances in local or global space")
+DefNode(GeometryNode, GEO_NODE_TRIANGULATE, def_geo_triangulate, "TRIANGULATE", Triangulate, "Triangulate", "Convert all faces in a mesh to triangular faces")
+DefNode(GeometryNode, GEO_NODE_TRIM_CURVE, def_geo_curve_trim, "TRIM_CURVE", TrimCurve, "Trim Curve", "Shorten curves by removing portions at the start or end")
+DefNode(GeometryNode, GEO_NODE_VIEWER, def_geo_viewer, "VIEWER", Viewer, "Viewer", "Display the input data in the Spreadsheet Editor")
+DefNode(GeometryNode, GEO_NODE_VOLUME_CUBE, 0, "VOLUME_CUBE", VolumeCube, "Volume Cube", "Generate a dense volume with a field that controls the density at each grid voxel based on its position")
+DefNode(GeometryNode, GEO_NODE_VOLUME_TO_MESH, def_geo_volume_to_mesh, "VOLUME_TO_MESH", VolumeToMesh, "Volume to Mesh", "Generate a mesh on the \"surface\" of a volume")
+DefNode(GeometryNode, GEO_NODE_UV_PACK_ISLANDS, 0, "UV_PACK_ISLANDS", UVPackIslands, "Pack UV Islands", "Scale islands of a UV map and move them so they fill the UV space as much as possible")
+DefNode(GeometryNode, GEO_NODE_UV_UNWRAP, def_geo_uv_unwrap, "UV_UNWRAP", UVUnwrap, "UV Unwrap", "Generate a UV map islands based on seam edges")
DefNode(SimulationNode, SIMULATION_NODE_ADD_COLLISION_SHAPES, def_simulation_add_collision_shapes, "ADD_COLLISION_SHAPES", AddCollisionShapes, "Add Collision Shapes", "")
DefNode(SimulationNode, SIMULATION_NODE_ADD_RIGID_BODIES, 0, "ADD_RIGID_BODIES", AddRigidBodies, "Add Rigid Bodies", "")
diff --git a/source/blender/nodes/composite/node_composite_tree.cc b/source/blender/nodes/composite/node_composite_tree.cc
index 0b75dd9cef3..32b5d98a556 100644
--- a/source/blender/nodes/composite/node_composite_tree.cc
+++ b/source/blender/nodes/composite/node_composite_tree.cc
@@ -15,6 +15,7 @@
#include "BKE_context.h"
#include "BKE_global.h"
+#include "BKE_image.h"
#include "BKE_main.h"
#include "BKE_node.h"
#include "BKE_node_tree_update.h"
diff --git a/source/blender/nodes/composite/node_composite_util.hh b/source/blender/nodes/composite/node_composite_util.hh
index 3e9c43aa7d2..14210cedc95 100644
--- a/source/blender/nodes/composite/node_composite_util.hh
+++ b/source/blender/nodes/composite/node_composite_util.hh
@@ -8,24 +8,12 @@
#pragma once
#include "DNA_ID.h"
-#include "DNA_movieclip_types.h"
#include "DNA_node_types.h"
#include "BLT_translation.h"
-#include "BKE_colorband.h"
-#include "BKE_colortools.h"
-#include "BKE_image.h"
-#include "BKE_texture.h"
-#include "BKE_tracking.h"
-
#include "node_util.h"
-#include "IMB_imbuf.h"
-#include "IMB_imbuf_types.h"
-
-#include "RE_pipeline.h"
-
#include "NOD_composite.h"
#include "NOD_socket.h"
#include "NOD_socket_declarations.hh"
diff --git a/source/blender/nodes/composite/nodes/node_composite_cryptomatte.cc b/source/blender/nodes/composite/nodes/node_composite_cryptomatte.cc
index 9193f91087a..5462441660c 100644
--- a/source/blender/nodes/composite/nodes/node_composite_cryptomatte.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_cryptomatte.cc
@@ -17,12 +17,15 @@
#include "BKE_context.h"
#include "BKE_cryptomatte.hh"
#include "BKE_global.h"
+#include "BKE_image.h"
#include "BKE_lib_id.h"
#include "BKE_library.h"
#include "BKE_main.h"
#include "MEM_guardedalloc.h"
+#include "RE_pipeline.h"
+
#include <optional>
/* -------------------------------------------------------------------- */
diff --git a/source/blender/nodes/composite/nodes/node_composite_curves.cc b/source/blender/nodes/composite/nodes/node_composite_curves.cc
index fff0d467f75..802664d7934 100644
--- a/source/blender/nodes/composite/nodes/node_composite_curves.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_curves.cc
@@ -5,6 +5,8 @@
* \ingroup cmpnodes
*/
+#include "BKE_colortools.h"
+
#include "UI_interface.h"
#include "UI_resources.h"
diff --git a/source/blender/nodes/composite/nodes/node_composite_diff_matte.cc b/source/blender/nodes/composite/nodes/node_composite_diff_matte.cc
index 20dd61a9725..b87bbe439db 100644
--- a/source/blender/nodes/composite/nodes/node_composite_diff_matte.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_diff_matte.cc
@@ -19,7 +19,7 @@ static void cmp_node_diff_matte_declare(NodeDeclarationBuilder &b)
b.add_input<decl::Color>(N_("Image 1")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
b.add_input<decl::Color>(N_("Image 2")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
b.add_output<decl::Color>(N_("Image"));
- b.add_output<decl::Color>(N_("Matte"));
+ b.add_output<decl::Float>(N_("Matte"));
}
static void node_composit_init_diff_matte(bNodeTree *UNUSED(ntree), bNode *node)
diff --git a/source/blender/nodes/composite/nodes/node_composite_huecorrect.cc b/source/blender/nodes/composite/nodes/node_composite_huecorrect.cc
index bb5e6bf06a8..d252d96f8c3 100644
--- a/source/blender/nodes/composite/nodes/node_composite_huecorrect.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_huecorrect.cc
@@ -7,6 +7,8 @@
#include "node_composite_util.hh"
+#include "BKE_colortools.h"
+
namespace blender::nodes::node_composite_huecorrect_cc {
static void cmp_node_huecorrect_declare(NodeDeclarationBuilder &b)
diff --git a/source/blender/nodes/composite/nodes/node_composite_image.cc b/source/blender/nodes/composite/nodes/node_composite_image.cc
index d071e9f13db..d75aa575395 100644
--- a/source/blender/nodes/composite/nodes/node_composite_image.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_image.cc
@@ -12,6 +12,7 @@
#include "BKE_context.h"
#include "BKE_global.h"
+#include "BKE_image.h"
#include "BKE_lib_id.h"
#include "BKE_main.h"
#include "BKE_scene.h"
@@ -19,6 +20,7 @@
#include "DNA_scene_types.h"
#include "RE_engine.h"
+#include "RE_pipeline.h"
#include "RNA_access.h"
diff --git a/source/blender/nodes/composite/nodes/node_composite_keyingscreen.cc b/source/blender/nodes/composite/nodes/node_composite_keyingscreen.cc
index c3ed5cd7aa8..e835ee9e721 100644
--- a/source/blender/nodes/composite/nodes/node_composite_keyingscreen.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_keyingscreen.cc
@@ -6,10 +6,15 @@
*/
#include "DNA_movieclip_types.h"
+#include "DNA_tracking_types.h"
#include "BLI_math_base.h"
#include "BLI_math_color.h"
+#include "BKE_context.h"
+#include "BKE_lib_id.h"
+#include "BKE_tracking.h"
+
#include "RNA_access.h"
#include "RNA_prototypes.h"
@@ -27,10 +32,23 @@ static void cmp_node_keyingscreen_declare(NodeDeclarationBuilder &b)
b.add_output<decl::Color>(N_("Screen"));
}
-static void node_composit_init_keyingscreen(bNodeTree *UNUSED(ntree), bNode *node)
+static void node_composit_init_keyingscreen(const bContext *C, PointerRNA *ptr)
{
+ bNode *node = (bNode *)ptr->data;
+
NodeKeyingScreenData *data = MEM_cnew<NodeKeyingScreenData>(__func__);
node->storage = data;
+
+ const Scene *scene = CTX_data_scene(C);
+ if (scene->clip) {
+ MovieClip *clip = scene->clip;
+
+ node->id = &clip->id;
+ id_us_plus(&clip->id);
+
+ const MovieTrackingObject *tracking_object = BKE_tracking_object_get_active(&clip->tracking);
+ BLI_strncpy(data->tracking_object, tracking_object->name, sizeof(data->tracking_object));
+ }
}
static void node_composit_buts_keyingscreen(uiLayout *layout, bContext *C, PointerRNA *ptr)
@@ -71,7 +89,7 @@ void register_node_type_cmp_keyingscreen()
cmp_node_type_base(&ntype, CMP_NODE_KEYINGSCREEN, "Keying Screen", NODE_CLASS_MATTE);
ntype.declare = file_ns::cmp_node_keyingscreen_declare;
ntype.draw_buttons = file_ns::node_composit_buts_keyingscreen;
- node_type_init(&ntype, file_ns::node_composit_init_keyingscreen);
+ ntype.initfunc_api = file_ns::node_composit_init_keyingscreen;
node_type_storage(
&ntype, "NodeKeyingScreenData", node_free_standard_storage, node_copy_standard_storage);
diff --git a/source/blender/nodes/composite/nodes/node_composite_map_value.cc b/source/blender/nodes/composite/nodes/node_composite_map_value.cc
index b069cce93fc..bb42628ed3d 100644
--- a/source/blender/nodes/composite/nodes/node_composite_map_value.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_map_value.cc
@@ -5,6 +5,8 @@
* \ingroup cmpnodes
*/
+#include "BKE_texture.h"
+
#include "RNA_access.h"
#include "UI_interface.h"
diff --git a/source/blender/nodes/composite/nodes/node_composite_moviedistortion.cc b/source/blender/nodes/composite/nodes/node_composite_moviedistortion.cc
index 9c6c6a40b2c..4d52a767b8a 100644
--- a/source/blender/nodes/composite/nodes/node_composite_moviedistortion.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_moviedistortion.cc
@@ -7,6 +7,7 @@
#include "BKE_context.h"
#include "BKE_lib_id.h"
+#include "BKE_tracking.h"
#include "UI_interface.h"
#include "UI_resources.h"
diff --git a/source/blender/nodes/composite/nodes/node_composite_output_file.cc b/source/blender/nodes/composite/nodes/node_composite_output_file.cc
index f1621f83ac3..84235b085a4 100644
--- a/source/blender/nodes/composite/nodes/node_composite_output_file.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_output_file.cc
@@ -114,7 +114,7 @@ void ntreeCompositOutputFileUniqueLayer(ListBase *list,
bNodeSocket *ntreeCompositOutputFileAddSocket(bNodeTree *ntree,
bNode *node,
const char *name,
- ImageFormatData *im_format)
+ const ImageFormatData *im_format)
{
NodeImageMultiFile *nimf = (NodeImageMultiFile *)node->storage;
bNodeSocket *sock = nodeAddStaticSocket(
diff --git a/source/blender/nodes/composite/nodes/node_composite_planetrackdeform.cc b/source/blender/nodes/composite/nodes/node_composite_planetrackdeform.cc
index fb0c03579a2..6557478fc4b 100644
--- a/source/blender/nodes/composite/nodes/node_composite_planetrackdeform.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_planetrackdeform.cc
@@ -5,6 +5,13 @@
* \ingroup cmpnodes
*/
+#include "DNA_movieclip_types.h"
+#include "DNA_tracking_types.h"
+
+#include "BKE_context.h"
+#include "BKE_lib_id.h"
+#include "BKE_tracking.h"
+
#include "RNA_access.h"
#include "RNA_prototypes.h"
@@ -22,12 +29,33 @@ static void cmp_node_planetrackdeform_declare(NodeDeclarationBuilder &b)
b.add_output<decl::Float>(N_("Plane"));
}
-static void init(bNodeTree *UNUSED(ntree), bNode *node)
+static void init(const bContext *C, PointerRNA *ptr)
{
+ bNode *node = (bNode *)ptr->data;
+
NodePlaneTrackDeformData *data = MEM_cnew<NodePlaneTrackDeformData>(__func__);
data->motion_blur_samples = 16;
data->motion_blur_shutter = 0.5f;
node->storage = data;
+
+ const Scene *scene = CTX_data_scene(C);
+ if (scene->clip) {
+ MovieClip *clip = scene->clip;
+ MovieTracking *tracking = &clip->tracking;
+
+ node->id = &clip->id;
+ id_us_plus(&clip->id);
+
+ const MovieTrackingObject *tracking_object = BKE_tracking_object_get_active(tracking);
+ BLI_strncpy(data->tracking_object, tracking_object->name, sizeof(data->tracking_object));
+
+ const MovieTrackingPlaneTrack *active_plane_track = BKE_tracking_plane_track_get_active(
+ tracking);
+ if (active_plane_track) {
+ BLI_strncpy(
+ data->plane_track_name, active_plane_track->name, sizeof(data->plane_track_name));
+ }
+ }
}
static void node_composit_buts_planetrackdeform(uiLayout *layout, bContext *C, PointerRNA *ptr)
@@ -90,7 +118,7 @@ void register_node_type_cmp_planetrackdeform()
cmp_node_type_base(&ntype, CMP_NODE_PLANETRACKDEFORM, "Plane Track Deform", NODE_CLASS_DISTORT);
ntype.declare = file_ns::cmp_node_planetrackdeform_declare;
ntype.draw_buttons = file_ns::node_composit_buts_planetrackdeform;
- node_type_init(&ntype, file_ns::init);
+ ntype.initfunc_api = file_ns::init;
node_type_storage(
&ntype, "NodePlaneTrackDeformData", node_free_standard_storage, node_copy_standard_storage);
diff --git a/source/blender/nodes/composite/nodes/node_composite_trackpos.cc b/source/blender/nodes/composite/nodes/node_composite_trackpos.cc
index 17a086f306f..0e99ff59327 100644
--- a/source/blender/nodes/composite/nodes/node_composite_trackpos.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_trackpos.cc
@@ -5,6 +5,13 @@
* \ingroup cmpnodes
*/
+#include "DNA_movieclip_types.h"
+#include "DNA_tracking_types.h"
+
+#include "BKE_context.h"
+#include "BKE_lib_id.h"
+#include "BKE_tracking.h"
+
#include "RNA_access.h"
#include "RNA_prototypes.h"
@@ -22,11 +29,29 @@ static void cmp_node_trackpos_declare(NodeDeclarationBuilder &b)
b.add_output<decl::Vector>(N_("Speed")).subtype(PROP_VELOCITY);
}
-static void init(bNodeTree *UNUSED(ntree), bNode *node)
+static void init(const bContext *C, PointerRNA *ptr)
{
- NodeTrackPosData *data = MEM_cnew<NodeTrackPosData>(__func__);
+ bNode *node = (bNode *)ptr->data;
+ NodeTrackPosData *data = MEM_cnew<NodeTrackPosData>(__func__);
node->storage = data;
+
+ const Scene *scene = CTX_data_scene(C);
+ if (scene->clip) {
+ MovieClip *clip = scene->clip;
+ MovieTracking *tracking = &clip->tracking;
+
+ node->id = &clip->id;
+ id_us_plus(&clip->id);
+
+ const MovieTrackingObject *tracking_object = BKE_tracking_object_get_active(tracking);
+ BLI_strncpy(data->tracking_object, tracking_object->name, sizeof(data->tracking_object));
+
+ const MovieTrackingTrack *active_track = BKE_tracking_track_get_active(tracking);
+ if (active_track) {
+ BLI_strncpy(data->track_name, active_track->name, sizeof(data->track_name));
+ }
+ }
}
static void node_composit_buts_trackpos(uiLayout *layout, bContext *C, PointerRNA *ptr)
@@ -88,7 +113,7 @@ void register_node_type_cmp_trackpos()
cmp_node_type_base(&ntype, CMP_NODE_TRACKPOS, "Track Position", NODE_CLASS_INPUT);
ntype.declare = file_ns::cmp_node_trackpos_declare;
ntype.draw_buttons = file_ns::node_composit_buts_trackpos;
- node_type_init(&ntype, file_ns::init);
+ ntype.initfunc_api = file_ns::init;
node_type_storage(
&ntype, "NodeTrackPosData", node_free_standard_storage, node_copy_standard_storage);
diff --git a/source/blender/nodes/composite/nodes/node_composite_val_to_rgb.cc b/source/blender/nodes/composite/nodes/node_composite_val_to_rgb.cc
index f71028bf8c1..df669d5beda 100644
--- a/source/blender/nodes/composite/nodes/node_composite_val_to_rgb.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_val_to_rgb.cc
@@ -7,6 +7,8 @@
#include "node_composite_util.hh"
+#include "BKE_colorband.h"
+
/* **************** VALTORGB ******************** */
namespace blender::nodes::node_composite_val_to_rgb_cc {
@@ -47,7 +49,7 @@ namespace blender::nodes::node_composite_val_to_rgb_cc {
static void cmp_node_rgbtobw_declare(NodeDeclarationBuilder &b)
{
b.add_input<decl::Color>(N_("Image")).default_value({0.8f, 0.8f, 0.8f, 1.0f});
- b.add_output<decl::Color>(N_("Val"));
+ b.add_output<decl::Float>(N_("Val"));
}
} // namespace blender::nodes::node_composite_val_to_rgb_cc
diff --git a/source/blender/nodes/function/nodes/node_fn_align_euler_to_vector.cc b/source/blender/nodes/function/nodes/node_fn_align_euler_to_vector.cc
index 2cb455832e5..7d08d57c503 100644
--- a/source/blender/nodes/function/nodes/node_fn_align_euler_to_vector.cc
+++ b/source/blender/nodes/function/nodes/node_fn_align_euler_to_vector.cc
@@ -150,7 +150,7 @@ class MF_AlignEulerToVector : public fn::MultiFunction {
static fn::MFSignature create_signature()
{
- fn::MFSignatureBuilder signature{"Align Euler To Vector"};
+ fn::MFSignatureBuilder signature{"Align Euler to Vector"};
signature.single_input<float3>("Rotation");
signature.single_input<float>("Factor");
signature.single_input<float3>("Vector");
diff --git a/source/blender/nodes/geometry/CMakeLists.txt b/source/blender/nodes/geometry/CMakeLists.txt
index 950124f75d0..d87f0312958 100644
--- a/source/blender/nodes/geometry/CMakeLists.txt
+++ b/source/blender/nodes/geometry/CMakeLists.txt
@@ -57,6 +57,7 @@ set(SRC
nodes/node_geo_curve_to_mesh.cc
nodes/node_geo_curve_to_points.cc
nodes/node_geo_curve_trim.cc
+ nodes/node_geo_deform_curves_on_surface.cc
nodes/node_geo_delete_geometry.cc
nodes/node_geo_distribute_points_on_faces.cc
nodes/node_geo_dual_mesh.cc
diff --git a/source/blender/nodes/geometry/nodes/node_geo_accumulate_field.cc b/source/blender/nodes/geometry/nodes/node_geo_accumulate_field.cc
index a7404af8564..58fbfb5a000 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_accumulate_field.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_accumulate_field.cc
@@ -217,16 +217,20 @@ template<typename T> class AccumulateFieldInput final : public GeometryFieldInpu
IndexMask UNUSED(mask)) const final
{
const GeometryComponentFieldContext field_context{component, source_domain_};
- const int domain_num = component.attribute_domain_num(field_context.domain());
+ const int domain_size = component.attribute_domain_size(field_context.domain());
+ if (domain_size == 0) {
+ return {};
+ }
+ const AttributeAccessor attributes = *component.attributes();
- fn::FieldEvaluator evaluator{field_context, domain_num};
+ fn::FieldEvaluator evaluator{field_context, domain_size};
evaluator.add(input_);
evaluator.add(group_index_);
evaluator.evaluate();
- const VArray<T> &values = evaluator.get_evaluated<T>(0);
- const VArray<int> &group_indices = evaluator.get_evaluated<int>(1);
+ const VArray<T> values = evaluator.get_evaluated<T>(0);
+ const VArray<int> group_indices = evaluator.get_evaluated<int>(1);
- Array<T> accumulations_out(domain_num);
+ Array<T> accumulations_out(domain_size);
if (group_indices.is_single()) {
T accumulation = T();
@@ -261,7 +265,7 @@ template<typename T> class AccumulateFieldInput final : public GeometryFieldInpu
}
}
- return component.attribute_try_adapt_domain<T>(
+ return attributes.adapt_domain<T>(
VArray<T>::ForContainer(std::move(accumulations_out)), source_domain_, domain);
}
@@ -303,24 +307,28 @@ template<typename T> class TotalFieldInput final : public GeometryFieldInput {
IndexMask UNUSED(mask)) const final
{
const GeometryComponentFieldContext field_context{component, source_domain_};
- const int domain_num = component.attribute_domain_num(field_context.domain());
+ const int domain_size = component.attribute_domain_size(field_context.domain());
+ if (domain_size == 0) {
+ return {};
+ }
+ const AttributeAccessor attributes = *component.attributes();
- fn::FieldEvaluator evaluator{field_context, domain_num};
+ fn::FieldEvaluator evaluator{field_context, domain_size};
evaluator.add(input_);
evaluator.add(group_index_);
evaluator.evaluate();
- const VArray<T> &values = evaluator.get_evaluated<T>(0);
- const VArray<int> &group_indices = evaluator.get_evaluated<int>(1);
+ const VArray<T> values = evaluator.get_evaluated<T>(0);
+ const VArray<int> group_indices = evaluator.get_evaluated<int>(1);
if (group_indices.is_single()) {
T accumulation = T();
for (const int i : values.index_range()) {
accumulation = values[i] + accumulation;
}
- return VArray<T>::ForSingle(accumulation, domain_num);
+ return VArray<T>::ForSingle(accumulation, domain_size);
}
- Array<T> accumulations_out(domain_num);
+ Array<T> accumulations_out(domain_size);
Map<int, T> accumulations;
for (const int i : values.index_range()) {
T &value = accumulations.lookup_or_add_default(group_indices[i]);
@@ -330,7 +338,7 @@ template<typename T> class TotalFieldInput final : public GeometryFieldInput {
accumulations_out[i] = accumulations.lookup(group_indices[i]);
}
- return component.attribute_try_adapt_domain<T>(
+ return attributes.adapt_domain<T>(
VArray<T>::ForContainer(std::move(accumulations_out)), source_domain_, domain);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_capture.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_capture.cc
index 496fb081d6b..9f317075bb5 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_attribute_capture.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_capture.cc
@@ -111,19 +111,26 @@ static void try_capture_field_on_geometry(GeometryComponent &component,
const eAttrDomain domain,
const GField &field)
{
+ const int domain_size = component.attribute_domain_size(domain);
+ if (domain_size == 0) {
+ return;
+ }
GeometryComponentFieldContext field_context{component, domain};
- const int domain_num = component.attribute_domain_num(domain);
- const IndexMask mask{IndexMask(domain_num)};
+ MutableAttributeAccessor attributes = *component.attributes_for_write();
+ const IndexMask mask{IndexMask(domain_size)};
const eCustomDataType data_type = bke::cpp_type_to_custom_data_type(field.cpp_type());
- OutputAttribute output_attribute = component.attribute_try_get_for_output_only(
+ GAttributeWriter output_attribute = attributes.lookup_or_add_for_write(
attribute_id, domain, data_type);
+ if (!output_attribute) {
+ return;
+ }
fn::FieldEvaluator evaluator{field_context, &mask};
- evaluator.add_with_destination(field, output_attribute.varray());
+ evaluator.add_with_destination(field, output_attribute.varray);
evaluator.evaluate();
- output_attribute.save();
+ output_attribute.finish();
}
static StringRefNull identifier_suffix(eCustomDataType data_type)
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_domain_size.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_domain_size.cc
index 8ab0eb678e7..f6ea6073459 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_attribute_domain_size.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_domain_size.cc
@@ -65,18 +65,17 @@ static void node_update(bNodeTree *ntree, bNode *node)
static void node_geo_exec(GeoNodeExecParams params)
{
- GeometryComponentType component = (GeometryComponentType)params.node().custom1;
- GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
+ const GeometryComponentType component = (GeometryComponentType)params.node().custom1;
+ const GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
switch (component) {
case GEO_COMPONENT_TYPE_MESH: {
- if (geometry_set.has_mesh()) {
- const MeshComponent *component = geometry_set.get_component_for_read<MeshComponent>();
- params.set_output("Point Count", component->attribute_domain_num(ATTR_DOMAIN_POINT));
- params.set_output("Edge Count", component->attribute_domain_num(ATTR_DOMAIN_EDGE));
- params.set_output("Face Count", component->attribute_domain_num(ATTR_DOMAIN_FACE));
- params.set_output("Face Corner Count",
- component->attribute_domain_num(ATTR_DOMAIN_CORNER));
+ if (const MeshComponent *component = geometry_set.get_component_for_read<MeshComponent>()) {
+ const AttributeAccessor attributes = *component->attributes();
+ params.set_output("Point Count", attributes.domain_size(ATTR_DOMAIN_POINT));
+ params.set_output("Edge Count", attributes.domain_size(ATTR_DOMAIN_EDGE));
+ params.set_output("Face Count", attributes.domain_size(ATTR_DOMAIN_FACE));
+ params.set_output("Face Corner Count", attributes.domain_size(ATTR_DOMAIN_CORNER));
}
else {
params.set_default_remaining_outputs();
@@ -84,10 +83,11 @@ static void node_geo_exec(GeoNodeExecParams params)
break;
}
case GEO_COMPONENT_TYPE_CURVE: {
- if (geometry_set.has_curves()) {
- const CurveComponent *component = geometry_set.get_component_for_read<CurveComponent>();
- params.set_output("Point Count", component->attribute_domain_num(ATTR_DOMAIN_POINT));
- params.set_output("Spline Count", component->attribute_domain_num(ATTR_DOMAIN_CURVE));
+ if (const CurveComponent *component =
+ geometry_set.get_component_for_read<CurveComponent>()) {
+ const AttributeAccessor attributes = *component->attributes();
+ params.set_output("Point Count", attributes.domain_size(ATTR_DOMAIN_POINT));
+ params.set_output("Spline Count", attributes.domain_size(ATTR_DOMAIN_CURVE));
}
else {
params.set_default_remaining_outputs();
@@ -95,10 +95,10 @@ static void node_geo_exec(GeoNodeExecParams params)
break;
}
case GEO_COMPONENT_TYPE_POINT_CLOUD: {
- if (geometry_set.has_pointcloud()) {
- const PointCloudComponent *component =
- geometry_set.get_component_for_read<PointCloudComponent>();
- params.set_output("Point Count", component->attribute_domain_num(ATTR_DOMAIN_POINT));
+ if (const PointCloudComponent *component =
+ geometry_set.get_component_for_read<PointCloudComponent>()) {
+ const AttributeAccessor attributes = *component->attributes();
+ params.set_output("Point Count", attributes.domain_size(ATTR_DOMAIN_POINT));
}
else {
params.set_default_remaining_outputs();
@@ -106,10 +106,10 @@ static void node_geo_exec(GeoNodeExecParams params)
break;
}
case GEO_COMPONENT_TYPE_INSTANCES: {
- if (geometry_set.has_instances()) {
- const InstancesComponent *component =
- geometry_set.get_component_for_read<InstancesComponent>();
- params.set_output("Instance Count", component->attribute_domain_num(ATTR_DOMAIN_INSTANCE));
+ if (const InstancesComponent *component =
+ geometry_set.get_component_for_read<InstancesComponent>()) {
+ const AttributeAccessor attributes = *component->attributes();
+ params.set_output("Instance Count", attributes.domain_size(ATTR_DOMAIN_INSTANCE));
}
else {
params.set_default_remaining_outputs();
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_statistic.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_statistic.cc
index 35404725998..34e28e50c81 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_attribute_statistic.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_statistic.cc
@@ -195,15 +195,19 @@ static void node_geo_exec(GeoNodeExecParams params)
const Field<float> input_field = params.get_input<Field<float>>("Attribute");
Vector<float> data;
for (const GeometryComponent *component : components) {
- if (component->attribute_domain_supported(domain)) {
+ const std::optional<AttributeAccessor> attributes = component->attributes();
+ if (!attributes.has_value()) {
+ continue;
+ }
+ if (attributes->domain_supported(domain)) {
GeometryComponentFieldContext field_context{*component, domain};
- const int domain_num = component->attribute_domain_num(domain);
+ const int domain_num = attributes->domain_size(domain);
fn::FieldEvaluator data_evaluator{field_context, domain_num};
data_evaluator.add(input_field);
data_evaluator.set_selection(selection_field);
data_evaluator.evaluate();
- const VArray<float> &component_data = data_evaluator.get_evaluated<float>(0);
+ const VArray<float> component_data = data_evaluator.get_evaluated<float>(0);
const IndexMask selection = data_evaluator.get_evaluated_selection_as_mask();
const int next_data_index = data.size();
@@ -273,15 +277,19 @@ static void node_geo_exec(GeoNodeExecParams params)
const Field<float3> input_field = params.get_input<Field<float3>>("Attribute_001");
Vector<float3> data;
for (const GeometryComponent *component : components) {
- if (component->attribute_domain_supported(domain)) {
+ const std::optional<AttributeAccessor> attributes = component->attributes();
+ if (!attributes.has_value()) {
+ continue;
+ }
+ if (attributes->domain_supported(domain)) {
GeometryComponentFieldContext field_context{*component, domain};
- const int domain_num = component->attribute_domain_num(domain);
+ const int domain_num = attributes->domain_size(domain);
fn::FieldEvaluator data_evaluator{field_context, domain_num};
data_evaluator.add(input_field);
data_evaluator.set_selection(selection_field);
data_evaluator.evaluate();
- const VArray<float3> &component_data = data_evaluator.get_evaluated<float3>(0);
+ const VArray<float3> component_data = data_evaluator.get_evaluated<float3>(0);
const IndexMask selection = data_evaluator.get_evaluated_selection_as_mask();
const int next_data_index = data.size();
diff --git a/source/blender/nodes/geometry/nodes/node_geo_boolean.cc b/source/blender/nodes/geometry/nodes/node_geo_boolean.cc
index daeca311e08..69938f3e447 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_boolean.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_boolean.cc
@@ -2,6 +2,7 @@
#include "DNA_mesh_types.h"
+#include "BKE_geometry_set_instances.hh"
#include "BKE_mesh_boolean_convert.hh"
#include "UI_interface.h"
@@ -153,17 +154,15 @@ static void node_geo_exec(GeoNodeExecParams params)
/* Store intersecting edges in attribute. */
if (attribute_outputs.intersecting_edges_id) {
- MeshComponent mesh_component;
- mesh_component.replace(result, GeometryOwnershipType::Editable);
- OutputAttribute_Typed<bool> attribute = mesh_component.attribute_try_get_for_output_only<bool>(
+ MutableAttributeAccessor attributes = bke::mesh_attributes_for_write(*result);
+ SpanAttributeWriter<bool> selection = attributes.lookup_or_add_for_write_only_span<bool>(
attribute_outputs.intersecting_edges_id.get(), ATTR_DOMAIN_EDGE);
- MutableSpan<bool> selection = attribute.as_span();
- selection.fill(false);
+
+ selection.span.fill(false);
for (const int i : intersecting_edges) {
- selection[i] = true;
+ selection.span[i] = true;
}
-
- attribute.save();
+ selection.finish();
params.set_output(
"Intersecting Edges",
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 00b10cc8a2f..75c198f7bdd 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_bounding_box.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_bounding_box.cc
@@ -50,7 +50,7 @@ static void node_geo_exec(GeoNodeExecParams params)
}
if (sub_min == float3(FLT_MAX)) {
- sub_geometry.keep_only({GEO_COMPONENT_TYPE_INSTANCES});
+ sub_geometry.remove_geometry_during_modify();
}
else {
const float3 scale = sub_max - sub_min;
@@ -58,7 +58,7 @@ static void node_geo_exec(GeoNodeExecParams params)
Mesh *mesh = geometry::create_cuboid_mesh(scale, 2, 2, 2, "uv_map");
transform_mesh(*mesh, center, float3(0), float3(1));
sub_geometry.replace_mesh(mesh);
- sub_geometry.keep_only({GEO_COMPONENT_TYPE_MESH, GEO_COMPONENT_TYPE_INSTANCES});
+ sub_geometry.keep_only_during_modify({GEO_COMPONENT_TYPE_MESH});
}
});
diff --git a/source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc b/source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc
index 31f706c497c..03892501c89 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc
@@ -22,8 +22,6 @@ static void node_declare(NodeDeclarationBuilder &b)
b.add_output<decl::Geometry>(N_("Convex Hull"));
}
-using bke::GeometryInstanceGroup;
-
#ifdef WITH_BULLET
static Mesh *hull_from_bullet(const Mesh *mesh, Span<float3> coords)
@@ -142,28 +140,35 @@ static Mesh *compute_hull(const GeometrySet &geometry_set)
Span<float3> positions_span;
- if (geometry_set.has_mesh()) {
+ if (const MeshComponent *component = geometry_set.get_component_for_read<MeshComponent>()) {
count++;
- const MeshComponent *component = geometry_set.get_component_for_read<MeshComponent>();
- total_num += component->attribute_domain_num(ATTR_DOMAIN_POINT);
+ if (const VArray<float3> positions = component->attributes()->lookup<float3>(
+ "position", ATTR_DOMAIN_POINT)) {
+ if (positions.is_span()) {
+ span_count++;
+ positions_span = positions.get_internal_span();
+ }
+ total_num += positions.size();
+ }
}
- if (geometry_set.has_pointcloud()) {
+ if (const PointCloudComponent *component =
+ geometry_set.get_component_for_read<PointCloudComponent>()) {
count++;
- span_count++;
- const PointCloudComponent *component =
- geometry_set.get_component_for_read<PointCloudComponent>();
- VArray<float3> varray = component->attribute_get_for_read<float3>(
- "position", ATTR_DOMAIN_POINT, {0, 0, 0});
- total_num += varray.size();
- positions_span = varray.get_internal_span();
+ if (const VArray<float3> positions = component->attributes()->lookup<float3>(
+ "position", ATTR_DOMAIN_POINT)) {
+ if (positions.is_span()) {
+ span_count++;
+ positions_span = positions.get_internal_span();
+ }
+ total_num += positions.size();
+ }
}
- if (geometry_set.has_curves()) {
+ if (const Curves *curves_id = geometry_set.get_curves_for_read()) {
count++;
span_count++;
- const Curves &curves_id = *geometry_set.get_curves_for_read();
- const bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id.geometry);
+ const bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id->geometry);
positions_span = curves.evaluated_positions();
total_num += positions_span.size();
}
@@ -181,26 +186,25 @@ static Mesh *compute_hull(const GeometrySet &geometry_set)
Array<float3> positions(total_num);
int offset = 0;
- if (geometry_set.has_mesh()) {
- const MeshComponent *component = geometry_set.get_component_for_read<MeshComponent>();
- VArray<float3> varray = component->attribute_get_for_read<float3>(
- "position", ATTR_DOMAIN_POINT, {0, 0, 0});
- varray.materialize(positions.as_mutable_span().slice(offset, varray.size()));
- offset += varray.size();
+ if (const MeshComponent *component = geometry_set.get_component_for_read<MeshComponent>()) {
+ if (const VArray<float3> varray = component->attributes()->lookup<float3>("position",
+ ATTR_DOMAIN_POINT)) {
+ varray.materialize(positions.as_mutable_span().slice(offset, varray.size()));
+ offset += varray.size();
+ }
}
- if (geometry_set.has_pointcloud()) {
- const PointCloudComponent *component =
- geometry_set.get_component_for_read<PointCloudComponent>();
- VArray<float3> varray = component->attribute_get_for_read<float3>(
- "position", ATTR_DOMAIN_POINT, {0, 0, 0});
- varray.materialize(positions.as_mutable_span().slice(offset, varray.size()));
- offset += varray.size();
+ if (const PointCloudComponent *component =
+ geometry_set.get_component_for_read<PointCloudComponent>()) {
+ if (const VArray<float3> varray = component->attributes()->lookup<float3>("position",
+ ATTR_DOMAIN_POINT)) {
+ varray.materialize(positions.as_mutable_span().slice(offset, varray.size()));
+ offset += varray.size();
+ }
}
- if (geometry_set.has_curves()) {
- const Curves &curves_id = *geometry_set.get_curves_for_read();
- const bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id.geometry);
+ if (const Curves *curves_id = geometry_set.get_curves_for_read()) {
+ const bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id->geometry);
Span<float3> array = curves.evaluated_positions();
positions.as_mutable_span().slice(offset, array.size()).copy_from(array);
offset += array.size();
@@ -220,7 +224,7 @@ static void node_geo_exec(GeoNodeExecParams params)
geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
Mesh *mesh = compute_hull(geometry_set);
geometry_set.replace_mesh(mesh);
- geometry_set.keep_only({GEO_COMPONENT_TYPE_MESH, GEO_COMPONENT_TYPE_INSTANCES});
+ geometry_set.keep_only_during_modify({GEO_COMPONENT_TYPE_MESH});
});
params.set_output("Convex Hull", std::move(geometry_set));
diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_endpoint_selection.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_endpoint_selection.cc
index b52bf2571b5..db3f108aad5 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_curve_endpoint_selection.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_endpoint_selection.cc
@@ -64,8 +64,8 @@ class EndpointFieldInput final : public GeometryFieldInput {
evaluator.add(start_size_);
evaluator.add(end_size_);
evaluator.evaluate();
- const VArray<int> &start_size = evaluator.get_evaluated<int>(0);
- const VArray<int> &end_size = evaluator.get_evaluated<int>(1);
+ const VArray<int> start_size = evaluator.get_evaluated<int>(0);
+ const VArray<int> end_size = evaluator.get_evaluated<int>(1);
Array<bool> selection(curves.points_num(), false);
MutableSpan<bool> selection_span = selection.as_mutable_span();
diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc
index fb8a488ddae..ab1f8269c39 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc
@@ -1,16 +1,12 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
-#include "BLI_task.hh"
-
#include "UI_interface.h"
#include "UI_resources.h"
-#include "DNA_node_types.h"
+#include "GEO_fillet_curves.hh"
#include "node_geometry_util.hh"
-#include "BKE_spline.hh"
-
namespace blender::nodes::node_geo_curve_fillet_cc {
NODE_STORAGE_FUNCS(NodeGeometryCurveFillet)
@@ -44,571 +40,18 @@ static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
static void node_init(bNodeTree *UNUSED(tree), bNode *node)
{
NodeGeometryCurveFillet *data = MEM_cnew<NodeGeometryCurveFillet>(__func__);
-
data->mode = GEO_NODE_CURVE_FILLET_BEZIER;
node->storage = data;
}
-struct FilletParam {
- GeometryNodeCurveFilletMode mode;
-
- /* Number of points to be added. */
- VArray<int> counts;
-
- /* Radii for fillet arc at all vertices. */
- VArray<float> radii;
-
- /* Whether or not fillets are allowed to overlap. */
- bool limit_radius;
-};
-
-/* A data structure used to store fillet data about all vertices to be filleted. */
-struct FilletData {
- Span<float3> positions;
- Array<float3> directions, axes;
- Array<float> radii, angles;
- Array<int> counts;
-};
-
static void node_update(bNodeTree *ntree, bNode *node)
{
const NodeGeometryCurveFillet &storage = node_storage(*node);
const GeometryNodeCurveFilletMode mode = (GeometryNodeCurveFilletMode)storage.mode;
-
bNodeSocket *poly_socket = ((bNodeSocket *)node->inputs.first)->next;
-
nodeSetSocketAvailability(ntree, poly_socket, mode == GEO_NODE_CURVE_FILLET_POLY);
}
-/* Function to get the center of a fillet. */
-static float3 get_center(const float3 vec_pos2prev,
- const float3 pos,
- const float3 axis,
- const float angle)
-{
- float3 vec_pos2center;
- rotate_normalized_v3_v3v3fl(vec_pos2center, vec_pos2prev, axis, M_PI_2 - angle / 2.0f);
- vec_pos2center *= 1.0f / sinf(angle / 2.0f);
-
- return vec_pos2center + pos;
-}
-
-/* Function to get the center of the fillet using fillet data */
-static float3 get_center(const float3 vec_pos2prev, const FilletData &fd, const int index)
-{
- const float angle = fd.angles[index];
- const float3 axis = fd.axes[index];
- const float3 pos = fd.positions[index];
-
- return get_center(vec_pos2prev, pos, axis, angle);
-}
-
-/* Calculate the direction vectors from each vertex to their previous vertex. */
-static Array<float3> calculate_directions(const Span<float3> positions)
-{
- const int num = positions.size();
- Array<float3> directions(num);
-
- for (const int i : IndexRange(num - 1)) {
- directions[i] = math::normalize(positions[i + 1] - positions[i]);
- }
- directions[num - 1] = math::normalize(positions[0] - positions[num - 1]);
-
- return directions;
-}
-
-/* Calculate the axes around which the fillet is built. */
-static Array<float3> calculate_axes(const Span<float3> directions)
-{
- const int num = directions.size();
- Array<float3> axes(num);
-
- axes[0] = math::normalize(math::cross(-directions[num - 1], directions[0]));
- for (const int i : IndexRange(1, num - 1)) {
- axes[i] = math::normalize(math::cross(-directions[i - 1], directions[i]));
- }
-
- return axes;
-}
-
-/* Calculate the angle of the arc formed by the fillet. */
-static Array<float> calculate_angles(const Span<float3> directions)
-{
- const int num = directions.size();
- Array<float> angles(num);
-
- angles[0] = M_PI - angle_v3v3(-directions[num - 1], directions[0]);
- for (const int i : IndexRange(1, num - 1)) {
- angles[i] = M_PI - angle_v3v3(-directions[i - 1], directions[i]);
- }
-
- return angles;
-}
-
-/* Calculate the segment count in each filleted arc. */
-static Array<int> calculate_counts(const FilletParam &fillet_param,
- const int num,
- const int spline_offset,
- const bool cyclic)
-{
- Array<int> counts(num, 1);
- if (fillet_param.mode == GEO_NODE_CURVE_FILLET_POLY) {
- for (const int i : IndexRange(num)) {
- counts[i] = fillet_param.counts[spline_offset + i];
- }
- }
- if (!cyclic) {
- counts[0] = counts[num - 1] = 0;
- }
-
- return counts;
-}
-
-/* Calculate the radii for the vertices to be filleted. */
-static Array<float> calculate_radii(const FilletParam &fillet_param,
- const int num,
- const int spline_offset)
-{
- Array<float> radii(num, 0.0f);
- if (fillet_param.limit_radius) {
- for (const int i : IndexRange(num)) {
- radii[i] = std::max(fillet_param.radii[spline_offset + i], 0.0f);
- }
- }
- else {
- for (const int i : IndexRange(num)) {
- radii[i] = fillet_param.radii[spline_offset + i];
- }
- }
-
- return radii;
-}
-
-/* Calculate the number of vertices added per vertex on the source spline. */
-static int calculate_point_counts(MutableSpan<int> point_counts,
- const Span<float> radii,
- const Span<int> counts)
-{
- int added_count = 0;
- for (const int i : IndexRange(point_counts.size())) {
- /* Calculate number of points to be added for the vertex. */
- if (radii[i] != 0.0f) {
- added_count += counts[i];
- point_counts[i] = counts[i] + 1;
- }
- }
-
- return added_count;
-}
-
-static FilletData calculate_fillet_data(const Spline &spline,
- const FilletParam &fillet_param,
- int &added_count,
- MutableSpan<int> point_counts,
- const int spline_offset)
-{
- const int num = spline.size();
-
- FilletData fd;
- fd.directions = calculate_directions(spline.positions());
- fd.positions = spline.positions();
- fd.axes = calculate_axes(fd.directions);
- fd.angles = calculate_angles(fd.directions);
- fd.counts = calculate_counts(fillet_param, num, spline_offset, spline.is_cyclic());
- fd.radii = calculate_radii(fillet_param, num, spline_offset);
-
- added_count = calculate_point_counts(point_counts, fd.radii, fd.counts);
-
- return fd;
-}
-
-/* Limit the radius based on angle and radii to prevent overlapping. */
-static void limit_radii(FilletData &fd, const bool cyclic)
-{
- MutableSpan<float> radii(fd.radii);
- Span<float> angles(fd.angles);
- Span<float3> positions(fd.positions);
-
- const int num = radii.size();
- const int fillet_count = cyclic ? num : num - 2;
- const int start = cyclic ? 0 : 1;
- Array<float> max_radii(num, FLT_MAX);
-
- if (cyclic) {
- /* Calculate lengths between adjacent control points. */
- const float len_prev = math::distance(positions[0], positions[num - 1]);
- const float len_next = math::distance(positions[0], positions[1]);
-
- /* Calculate tangent lengths of fillets in control points. */
- const float tan_len = radii[0] * tan(angles[0] / 2.0f);
- const float tan_len_prev = radii[num - 1] * tan(angles[num - 1] / 2.0f);
- const float tan_len_next = radii[1] * tan(angles[1] / 2.0f);
-
- float factor_prev = 1.0f, factor_next = 1.0f;
- if (tan_len + tan_len_prev > len_prev) {
- factor_prev = len_prev / (tan_len + tan_len_prev);
- }
- if (tan_len + tan_len_next > len_next) {
- factor_next = len_next / (tan_len + tan_len_next);
- }
-
- /* Scale max radii by calculated factors. */
- max_radii[0] = radii[0] * std::min(factor_next, factor_prev);
- max_radii[1] = radii[1] * factor_next;
- max_radii[num - 1] = radii[num - 1] * factor_prev;
- }
-
- /* Initialize max_radii to largest possible radii. */
- float prev_dist = math::distance(positions[1], positions[0]);
- for (const int i : IndexRange(1, num - 2)) {
- const float temp_dist = math::distance(positions[i], positions[i + 1]);
- max_radii[i] = std::min(prev_dist, temp_dist) / tan(angles[i] / 2.0f);
- prev_dist = temp_dist;
- }
-
- /* Max radii calculations for each index. */
- for (const int i : IndexRange(start, fillet_count - 1)) {
- const float len_next = math::distance(positions[i], positions[i + 1]);
- const float tan_len = radii[i] * tan(angles[i] / 2.0f);
- const float tan_len_next = radii[i + 1] * tan(angles[i + 1] / 2.0f);
-
- /* Scale down radii if too large for segment. */
- float factor = 1.0f;
- if (tan_len + tan_len_next > len_next) {
- factor = len_next / (tan_len + tan_len_next);
- }
- max_radii[i] = std::min(max_radii[i], radii[i] * factor);
- max_radii[i + 1] = std::min(max_radii[i + 1], radii[i + 1] * factor);
- }
-
- /* Assign the max_radii to the fillet data's radii. */
- for (const int i : IndexRange(num)) {
- radii[i] = std::min(radii[i], max_radii[i]);
- }
-}
-
-/*
- * Create a mapping from each vertex in the destination spline to that of the source spline.
- * Used for copying the data from the source spline.
- */
-static Array<int> create_dst_to_src_map(const Span<int> point_counts, const int total_points)
-{
- Array<int> map(total_points);
- MutableSpan<int> map_span{map};
- int index = 0;
-
- for (const int i : point_counts.index_range()) {
- map_span.slice(index, point_counts[i]).fill(i);
- index += point_counts[i];
- }
-
- BLI_assert(index == total_points);
-
- return map;
-}
-
-template<typename T>
-static void copy_attribute_by_mapping(const Span<T> src,
- MutableSpan<T> dst,
- const Span<int> mapping)
-{
- for (const int i : dst.index_range()) {
- dst[i] = src[mapping[i]];
- }
-}
-
-/* Copy radii and tilts from source spline to destination. Positions are handled later in update
- * positions methods. */
-static void copy_common_attributes_by_mapping(const Spline &src,
- Spline &dst,
- const Span<int> mapping)
-{
- copy_attribute_by_mapping(src.radii(), dst.radii(), mapping);
- copy_attribute_by_mapping(src.tilts(), dst.tilts(), mapping);
-
- src.attributes.foreach_attribute(
- [&](const AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) {
- std::optional<GSpan> src_attribute = src.attributes.get_for_read(attribute_id);
- if (dst.attributes.create(attribute_id, meta_data.data_type)) {
- std::optional<GMutableSpan> dst_attribute = dst.attributes.get_for_write(attribute_id);
- if (dst_attribute) {
- attribute_math::convert_to_static_type(dst_attribute->type(), [&](auto dummy) {
- using T = decltype(dummy);
- copy_attribute_by_mapping(
- src_attribute->typed<T>(), dst_attribute->typed<T>(), mapping);
- });
- return true;
- }
- }
- BLI_assert_unreachable();
- return false;
- },
- ATTR_DOMAIN_POINT);
-}
-
-/* Update the vertex positions and handle positions of a Bezier spline based on fillet data. */
-static void update_bezier_positions(const FilletData &fd,
- BezierSpline &dst_spline,
- const BezierSpline &src_spline,
- const Span<int> point_counts)
-{
- Span<float> radii(fd.radii);
- Span<float> angles(fd.angles);
- Span<float3> axes(fd.axes);
- Span<float3> positions(fd.positions);
- Span<float3> directions(fd.directions);
-
- const int num = radii.size();
-
- int i_dst = 0;
- for (const int i_src : IndexRange(num)) {
- const int count = point_counts[i_src];
-
- /* Skip if the point count for the vertex is 1. */
- if (count == 1) {
- dst_spline.positions()[i_dst] = src_spline.positions()[i_src];
- dst_spline.handle_types_left()[i_dst] = src_spline.handle_types_left()[i_src];
- dst_spline.handle_types_right()[i_dst] = src_spline.handle_types_right()[i_src];
- dst_spline.handle_positions_left()[i_dst] = src_spline.handle_positions_left()[i_src];
- dst_spline.handle_positions_right()[i_dst] = src_spline.handle_positions_right()[i_src];
- i_dst++;
- continue;
- }
-
- /* Calculate the angle to be formed between any 2 adjacent vertices within the fillet. */
- const float segment_angle = angles[i_src] / (count - 1);
- /* Calculate the handle length for each added vertex. Equation: L = 4R/3 * tan(A/4) */
- const float handle_length = 4.0f * radii[i_src] / 3.0f * tan(segment_angle / 4.0f);
- /* Calculate the distance by which each vertex should be displaced from their initial position.
- */
- const float displacement = radii[i_src] * tan(angles[i_src] / 2.0f);
-
- /* Position the end points of the arc and their handles. */
- const int end_i = i_dst + count - 1;
- const float3 prev_dir = i_src == 0 ? -directions[num - 1] : -directions[i_src - 1];
- const float3 next_dir = directions[i_src];
- dst_spline.positions()[i_dst] = positions[i_src] + displacement * prev_dir;
- dst_spline.positions()[end_i] = positions[i_src] + displacement * next_dir;
- dst_spline.handle_positions_right()[i_dst] = dst_spline.positions()[i_dst] -
- handle_length * prev_dir;
- dst_spline.handle_positions_left()[end_i] = dst_spline.positions()[end_i] -
- handle_length * next_dir;
- dst_spline.handle_types_right()[i_dst] = dst_spline.handle_types_left()[end_i] =
- BEZIER_HANDLE_ALIGN;
- dst_spline.handle_types_left()[i_dst] = dst_spline.handle_types_right()[end_i] =
- BEZIER_HANDLE_VECTOR;
- dst_spline.mark_cache_invalid();
-
- /* Calculate the center of the radius to be formed. */
- const float3 center = get_center(dst_spline.positions()[i_dst] - positions[i_src], fd, i_src);
- /* Calculate the vector of the radius formed by the first vertex. */
- float3 radius_vec = dst_spline.positions()[i_dst] - center;
- float radius;
- radius_vec = math::normalize_and_get_length(radius_vec, radius);
-
- dst_spline.handle_types_right().slice(1, count - 2).fill(BEZIER_HANDLE_ALIGN);
- dst_spline.handle_types_left().slice(1, count - 2).fill(BEZIER_HANDLE_ALIGN);
-
- /* For each of the vertices in between the end points. */
- for (const int j : IndexRange(1, count - 2)) {
- int index = i_dst + j;
- /* Rotate the radius by the segment angle and determine its tangent (used for getting handle
- * directions). */
- float3 new_radius_vec, tangent_vec;
- rotate_normalized_v3_v3v3fl(new_radius_vec, radius_vec, -axes[i_src], segment_angle);
- rotate_normalized_v3_v3v3fl(tangent_vec, new_radius_vec, axes[i_src], M_PI_2);
- radius_vec = new_radius_vec;
- tangent_vec *= handle_length;
-
- /* Adjust the positions of the respective vertex and its handles. */
- dst_spline.positions()[index] = center + new_radius_vec * radius;
- dst_spline.handle_positions_left()[index] = dst_spline.positions()[index] + tangent_vec;
- dst_spline.handle_positions_right()[index] = dst_spline.positions()[index] - tangent_vec;
- }
-
- i_dst += count;
- }
-}
-
-/* Update the vertex positions of a Poly spline based on fillet data. */
-static void update_poly_positions(const FilletData &fd,
- Spline &dst_spline,
- const Spline &src_spline,
- const Span<int> point_counts)
-{
- Span<float> radii(fd.radii);
- Span<float> angles(fd.angles);
- Span<float3> axes(fd.axes);
- Span<float3> positions(fd.positions);
- Span<float3> directions(fd.directions);
-
- const int num = radii.size();
-
- int i_dst = 0;
- for (const int i_src : IndexRange(num)) {
- const int count = point_counts[i_src];
-
- /* Skip if the point count for the vertex is 1. */
- if (count == 1) {
- dst_spline.positions()[i_dst] = src_spline.positions()[i_src];
- i_dst++;
- continue;
- }
-
- const float segment_angle = angles[i_src] / (count - 1);
- const float displacement = radii[i_src] * tan(angles[i_src] / 2.0f);
-
- /* Position the end points of the arc. */
- const int end_i = i_dst + count - 1;
- const float3 prev_dir = i_src == 0 ? -directions[num - 1] : -directions[i_src - 1];
- const float3 next_dir = directions[i_src];
- dst_spline.positions()[i_dst] = positions[i_src] + displacement * prev_dir;
- dst_spline.positions()[end_i] = positions[i_src] + displacement * next_dir;
-
- /* Calculate the center of the radius to be formed. */
- const float3 center = get_center(dst_spline.positions()[i_dst] - positions[i_src], fd, i_src);
- /* Calculate the vector of the radius formed by the first vertex. */
- float3 radius_vec = dst_spline.positions()[i_dst] - center;
-
- for (const int j : IndexRange(1, count - 2)) {
- /* Rotate the radius by the segment angle */
- float3 new_radius_vec;
- rotate_normalized_v3_v3v3fl(new_radius_vec, radius_vec, -axes[i_src], segment_angle);
- radius_vec = new_radius_vec;
-
- dst_spline.positions()[i_dst + j] = center + new_radius_vec;
- }
-
- i_dst += count;
- }
-}
-
-static SplinePtr fillet_spline(const Spline &spline,
- const FilletParam &fillet_param,
- const int spline_offset)
-{
- const int num = spline.size();
- const bool cyclic = spline.is_cyclic();
-
- if (num < 3) {
- return spline.copy();
- }
-
- /* Initialize the point_counts with 1s (at least one vertex on dst for each vertex on src). */
- Array<int> point_counts(num, 1);
-
- int added_count = 0;
- /* Update point_counts array and added_count. */
- FilletData fd = calculate_fillet_data(
- spline, fillet_param, added_count, point_counts, spline_offset);
- if (fillet_param.limit_radius) {
- limit_radii(fd, cyclic);
- }
-
- const int total_points = added_count + num;
- const Array<int> dst_to_src = create_dst_to_src_map(point_counts, total_points);
- SplinePtr dst_spline_ptr = spline.copy_only_settings();
- (*dst_spline_ptr).resize(total_points);
- copy_common_attributes_by_mapping(spline, *dst_spline_ptr, dst_to_src);
-
- switch (spline.type()) {
- case CURVE_TYPE_BEZIER: {
- const BezierSpline &src_spline = static_cast<const BezierSpline &>(spline);
- BezierSpline &dst_spline = static_cast<BezierSpline &>(*dst_spline_ptr);
- if (fillet_param.mode == GEO_NODE_CURVE_FILLET_POLY) {
- dst_spline.handle_types_left().fill(BEZIER_HANDLE_VECTOR);
- dst_spline.handle_types_right().fill(BEZIER_HANDLE_VECTOR);
- update_poly_positions(fd, dst_spline, src_spline, point_counts);
- }
- else {
- update_bezier_positions(fd, dst_spline, src_spline, point_counts);
- }
- break;
- }
- case CURVE_TYPE_POLY: {
- update_poly_positions(fd, *dst_spline_ptr, spline, point_counts);
- break;
- }
- case CURVE_TYPE_NURBS: {
- const NURBSpline &src_spline = static_cast<const NURBSpline &>(spline);
- NURBSpline &dst_spline = static_cast<NURBSpline &>(*dst_spline_ptr);
- copy_attribute_by_mapping(src_spline.weights(), dst_spline.weights(), dst_to_src);
- update_poly_positions(fd, dst_spline, src_spline, point_counts);
- break;
- }
- case CURVE_TYPE_CATMULL_ROM: {
- BLI_assert_unreachable();
- break;
- }
- }
-
- return dst_spline_ptr;
-}
-
-static std::unique_ptr<CurveEval> fillet_curve(const CurveEval &input_curve,
- const FilletParam &fillet_param)
-{
- Span<SplinePtr> input_splines = input_curve.splines();
-
- std::unique_ptr<CurveEval> output_curve = std::make_unique<CurveEval>();
- const int splines_num = input_splines.size();
- output_curve->resize(splines_num);
- MutableSpan<SplinePtr> output_splines = output_curve->splines();
- Array<int> spline_offsets = input_curve.control_point_offsets();
-
- threading::parallel_for(input_splines.index_range(), 128, [&](IndexRange range) {
- for (const int i : range) {
- output_splines[i] = fillet_spline(*input_splines[i], fillet_param, spline_offsets[i]);
- }
- });
- output_curve->attributes = input_curve.attributes;
-
- return output_curve;
-}
-
-static void calculate_curve_fillet(GeometrySet &geometry_set,
- const GeometryNodeCurveFilletMode mode,
- const Field<float> &radius_field,
- const std::optional<Field<int>> &count_field,
- const bool limit_radius)
-{
- if (!geometry_set.has_curves()) {
- return;
- }
-
- FilletParam fillet_param;
- fillet_param.mode = mode;
-
- CurveComponent &component = geometry_set.get_component_for_write<CurveComponent>();
- GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_POINT};
- const int domain_num = component.attribute_domain_num(ATTR_DOMAIN_POINT);
- fn::FieldEvaluator field_evaluator{field_context, domain_num};
-
- field_evaluator.add(radius_field);
-
- if (mode == GEO_NODE_CURVE_FILLET_POLY) {
- field_evaluator.add(*count_field);
- }
-
- field_evaluator.evaluate();
-
- fillet_param.radii = field_evaluator.get_evaluated<float>(0);
- if (fillet_param.radii.is_single() && fillet_param.radii.get_internal_single() < 0.0f) {
- return;
- }
-
- if (mode == GEO_NODE_CURVE_FILLET_POLY) {
- fillet_param.counts = field_evaluator.get_evaluated<int>(1);
- }
-
- fillet_param.limit_radius = limit_radius;
-
- const std::unique_ptr<CurveEval> input_curve = curves_to_curve_eval(*component.get_for_read());
- std::unique_ptr<CurveEval> output_curve = fillet_curve(*input_curve, fillet_param);
-
- geometry_set.replace_curves(curve_eval_to_curves(*output_curve));
-}
-
static void node_geo_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set = params.extract_input<GeometrySet>("Curve");
@@ -625,7 +68,42 @@ static void node_geo_exec(GeoNodeExecParams params)
}
geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
- calculate_curve_fillet(geometry_set, mode, radius_field, count_field, limit_radius);
+ if (!geometry_set.has_curves()) {
+ return;
+ }
+
+ const CurveComponent &component = *geometry_set.get_component_for_read<CurveComponent>();
+ const Curves &curves_id = *component.get_for_read();
+ const bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id.geometry);
+ GeometryComponentFieldContext context{component, ATTR_DOMAIN_POINT};
+ fn::FieldEvaluator evaluator{context, curves.points_num()};
+ evaluator.add(radius_field);
+
+ switch (mode) {
+ case GEO_NODE_CURVE_FILLET_BEZIER: {
+ evaluator.evaluate();
+ bke::CurvesGeometry dst_curves = geometry::fillet_curves_bezier(
+ curves, curves.curves_range(), evaluator.get_evaluated<float>(0), limit_radius);
+ Curves *dst_curves_id = bke::curves_new_nomain(std::move(dst_curves));
+ bke::curves_copy_parameters(curves_id, *dst_curves_id);
+ geometry_set.replace_curves(dst_curves_id);
+ break;
+ }
+ case GEO_NODE_CURVE_FILLET_POLY: {
+ evaluator.add(*count_field);
+ evaluator.evaluate();
+ bke::CurvesGeometry dst_curves = geometry::fillet_curves_poly(
+ curves,
+ curves.curves_range(),
+ evaluator.get_evaluated<float>(0),
+ evaluator.get_evaluated<int>(1),
+ limit_radius);
+ Curves *dst_curves_id = bke::curves_new_nomain(std::move(dst_curves));
+ bke::curves_copy_parameters(curves_id, *dst_curves_id);
+ geometry_set.replace_curves(dst_curves_id);
+ break;
+ }
+ }
});
params.set_output("Curve", std::move(geometry_set));
diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_star.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_star.cc
index 91ba5f2845f..286d9993d0e 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_star.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_star.cc
@@ -61,13 +61,13 @@ static Curves *create_star_curve(const float inner_radius,
static void create_selection_output(CurveComponent &component,
StrongAnonymousAttributeID &r_attribute)
{
- OutputAttribute_Typed<bool> attribute = component.attribute_try_get_for_output_only<bool>(
- r_attribute.get(), ATTR_DOMAIN_POINT);
- MutableSpan<bool> selection = attribute.as_span();
- for (int i : selection.index_range()) {
- selection[i] = i % 2 == 0;
+ SpanAttributeWriter<bool> selection =
+ component.attributes_for_write()->lookup_or_add_for_write_only_span<bool>(r_attribute.get(),
+ ATTR_DOMAIN_POINT);
+ for (int i : selection.span.index_range()) {
+ selection.span[i] = i % 2 == 0;
}
- attribute.save();
+ selection.finish();
}
static void node_geo_exec(GeoNodeExecParams params)
diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc
index 78a132064ed..37fc6823b9a 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc
@@ -60,13 +60,17 @@ static void node_geo_exec(GeoNodeExecParams params)
const Field<bool> selection = params.extract_input<Field<bool>>("Selection");
+ GeometryComponentEditData::remember_deformed_curve_positions_if_necessary(geometry_set);
+
switch (mode) {
case GEO_NODE_CURVE_RESAMPLE_COUNT: {
Field<int> count = params.extract_input<Field<int>>("Count");
geometry_set.modify_geometry_sets([&](GeometrySet &geometry) {
if (const CurveComponent *component = geometry.get_component_for_read<CurveComponent>()) {
- if (!component->is_empty()) {
- geometry.replace_curves(geometry::resample_to_count(*component, selection, count));
+ if (const Curves *src_curves = component->get_for_read()) {
+ Curves *dst_curves = geometry::resample_to_count(*component, selection, count);
+ bke::curves_copy_parameters(*src_curves, *dst_curves);
+ geometry.replace_curves(dst_curves);
}
}
});
@@ -76,8 +80,10 @@ static void node_geo_exec(GeoNodeExecParams params)
Field<float> length = params.extract_input<Field<float>>("Length");
geometry_set.modify_geometry_sets([&](GeometrySet &geometry) {
if (const CurveComponent *component = geometry.get_component_for_read<CurveComponent>()) {
- if (!component->is_empty()) {
- geometry.replace_curves(geometry::resample_to_length(*component, selection, length));
+ if (const Curves *src_curves = component->get_for_read()) {
+ Curves *dst_curves = geometry::resample_to_length(*component, selection, length);
+ bke::curves_copy_parameters(*src_curves, *dst_curves);
+ geometry.replace_curves(dst_curves);
}
}
});
@@ -86,8 +92,10 @@ static void node_geo_exec(GeoNodeExecParams params)
case GEO_NODE_CURVE_RESAMPLE_EVALUATED:
geometry_set.modify_geometry_sets([&](GeometrySet &geometry) {
if (const CurveComponent *component = geometry.get_component_for_read<CurveComponent>()) {
- if (!component->is_empty()) {
- geometry.replace_curves(geometry::resample_to_evaluated(*component, selection));
+ if (const Curves *src_curves = component->get_for_read()) {
+ Curves *dst_curves = geometry::resample_to_evaluated(*component, selection);
+ bke::curves_copy_parameters(*src_curves, *dst_curves);
+ geometry.replace_curves(dst_curves);
}
}
});
diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_reverse.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_reverse.cc
index 64a174df599..de29735bd2d 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_curve_reverse.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_reverse.cc
@@ -27,9 +27,9 @@ static void node_geo_exec(GeoNodeExecParams params)
Field<bool> selection_field = params.get_input<Field<bool>>("Selection");
const CurveComponent &component = *geometry_set.get_component_for_read<CurveComponent>();
GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_CURVE};
- const int domain_num = component.attribute_domain_num(ATTR_DOMAIN_CURVE);
+ const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_CURVE);
- fn::FieldEvaluator selection_evaluator{field_context, domain_num};
+ fn::FieldEvaluator selection_evaluator{field_context, domain_size};
selection_evaluator.add(selection_field);
selection_evaluator.evaluate();
const IndexMask selection = selection_evaluator.get_evaluated_as_mask(0);
diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_sample.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_sample.cc
index 9077b59254c..e80b600a740 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_curve_sample.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_sample.cc
@@ -1,8 +1,9 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
-#include "BLI_task.hh"
+#include "BLI_devirtualize_parameters.hh"
+#include "BLI_length_parameterize.hh"
-#include "BKE_spline.hh"
+#include "BKE_curves.hh"
#include "UI_interface.h"
#include "UI_resources.h"
@@ -58,34 +59,103 @@ static void node_update(bNodeTree *ntree, bNode *node)
nodeSetSocketAvailability(ntree, length, mode == GEO_NODE_CURVE_SAMPLE_LENGTH);
}
-template<typename T> static T sample_with_lookup(const Spline::LookupResult lookup, Span<T> data)
+static void sample_indices_and_lengths(const Span<float> accumulated_lengths,
+ const Span<float> sample_lengths,
+ const IndexMask mask,
+ MutableSpan<int> r_segment_indices,
+ MutableSpan<float> r_length_in_segment)
{
- return attribute_math::mix2(
- lookup.factor, data[lookup.evaluated_index], data[lookup.next_evaluated_index]);
+ const float total_length = accumulated_lengths.last();
+ length_parameterize::SampleSegmentHint hint;
+
+ mask.to_best_mask_type([&](const auto mask) {
+ for (const int64_t i : mask) {
+ int segment_i;
+ float factor_in_segment;
+ length_parameterize::sample_at_length(accumulated_lengths,
+ std::clamp(sample_lengths[i], 0.0f, total_length),
+ segment_i,
+ factor_in_segment,
+ &hint);
+ const float segment_start = segment_i == 0 ? 0.0f : accumulated_lengths[segment_i - 1];
+ const float segment_end = accumulated_lengths[segment_i];
+ const float segment_length = segment_end - segment_start;
+
+ r_segment_indices[i] = segment_i;
+ r_length_in_segment[i] = factor_in_segment * segment_length;
+ }
+ });
+}
+
+static void sample_indices_and_factors_to_compressed(const Span<float> accumulated_lengths,
+ const Span<float> sample_lengths,
+ const IndexMask mask,
+ MutableSpan<int> r_segment_indices,
+ MutableSpan<float> r_factor_in_segment)
+{
+ const float total_length = accumulated_lengths.last();
+ length_parameterize::SampleSegmentHint hint;
+
+ mask.to_best_mask_type([&](const auto mask) {
+ for (const int64_t i : IndexRange(mask.size())) {
+ const float length = sample_lengths[mask[i]];
+ length_parameterize::sample_at_length(accumulated_lengths,
+ std::clamp(length, 0.0f, total_length),
+ r_segment_indices[i],
+ r_factor_in_segment[i],
+ &hint);
+ }
+ });
}
+/**
+ * Given an array of accumulated lengths, find the segment indices that
+ * sample lengths lie on, and how far along the segment they are.
+ */
+class SampleFloatSegmentsFunction : public fn::MultiFunction {
+ private:
+ Array<float> accumulated_lengths_;
+
+ public:
+ SampleFloatSegmentsFunction(Array<float> accumulated_lengths)
+ : accumulated_lengths_(std::move(accumulated_lengths))
+ {
+ static fn::MFSignature signature = create_signature();
+ this->set_signature(&signature);
+ }
+
+ static fn::MFSignature create_signature()
+ {
+ fn::MFSignatureBuilder signature{"Sample Curve Index"};
+ signature.single_input<float>("Length");
+
+ signature.single_output<int>("Curve Index");
+ signature.single_output<float>("Length in Curve");
+ return signature.build();
+ }
+
+ void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override
+ {
+ const VArraySpan<float> lengths = params.readonly_single_input<float>(0, "Length");
+ MutableSpan<int> indices = params.uninitialized_single_output<int>(1, "Curve Index");
+ MutableSpan<float> lengths_in_segments = params.uninitialized_single_output<float>(
+ 2, "Length in Curve");
+
+ sample_indices_and_lengths(accumulated_lengths_, lengths, mask, indices, lengths_in_segments);
+ }
+};
+
class SampleCurveFunction : public fn::MultiFunction {
private:
/**
- * The function holds a geometry set instead of a curve or a curve component in order to
- * maintain a reference to the geometry while the field tree is being built, so that the
- * curve is not freed before the function can execute.
+ * The function holds a geometry set instead of curves or a curve component reference in order
+ * to maintain a ownership of the geometry while the field tree is being built and used, so
+ * that the curve is not freed before the function can execute.
*/
GeometrySet geometry_set_;
- /**
- * To support factor inputs, the node adds another field operation before this one to multiply by
- * the curve's total length. Since that must calculate the spline lengths anyway, store them to
- * reuse the calculation.
- */
- Array<float> spline_lengths_;
- /** The last member of #spline_lengths_, extracted for convenience. */
- const float total_length_;
public:
- SampleCurveFunction(GeometrySet geometry_set, Array<float> spline_lengths)
- : geometry_set_(std::move(geometry_set)),
- spline_lengths_(std::move(spline_lengths)),
- total_length_(spline_lengths_.last())
+ SampleCurveFunction(GeometrySet geometry_set) : geometry_set_(std::move(geometry_set))
{
static fn::MFSignature signature = create_signature();
this->set_signature(&signature);
@@ -93,7 +163,8 @@ class SampleCurveFunction : public fn::MultiFunction {
static fn::MFSignature create_signature()
{
- blender::fn::MFSignatureBuilder signature{"Curve Sample"};
+ blender::fn::MFSignatureBuilder signature{"Sample Curve"};
+ signature.single_input<int>("Curve Index");
signature.single_input<float>("Length");
signature.single_output<float3>("Position");
signature.single_output<float3>("Tangent");
@@ -104,11 +175,11 @@ class SampleCurveFunction : public fn::MultiFunction {
void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override
{
MutableSpan<float3> sampled_positions = params.uninitialized_single_output_if_required<float3>(
- 1, "Position");
+ 2, "Position");
MutableSpan<float3> sampled_tangents = params.uninitialized_single_output_if_required<float3>(
- 2, "Tangent");
+ 3, "Tangent");
MutableSpan<float3> sampled_normals = params.uninitialized_single_output_if_required<float3>(
- 3, "Normal");
+ 4, "Normal");
auto return_default = [&]() {
if (!sampled_positions.is_empty()) {
@@ -126,61 +197,78 @@ class SampleCurveFunction : public fn::MultiFunction {
return return_default();
}
- const CurveComponent *curve_component = geometry_set_.get_component_for_read<CurveComponent>();
- const std::unique_ptr<CurveEval> curve = curves_to_curve_eval(
- *curve_component->get_for_read());
- Span<SplinePtr> splines = curve->splines();
- if (splines.is_empty()) {
+ const Curves &curves_id = *geometry_set_.get_curves_for_read();
+ const bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id.geometry);
+ if (curves.points_num() == 0) {
return return_default();
}
-
- const VArray<float> &lengths_varray = params.readonly_single_input<float>(0, "Length");
- const VArray_Span lengths{lengths_varray};
-#ifdef DEBUG
- for (const float length : lengths) {
- /* Lengths must be in range of the curve's total length. This is ensured in
- * #get_length_input_field by adding another multi-function before this one
- * to clamp the lengths. */
- BLI_assert(length >= 0.0f && length <= total_length_);
- }
-#endif
-
- Array<int> spline_indices(mask.min_array_size());
- for (const int i : mask) {
- const float *offset = std::lower_bound(
- spline_lengths_.begin(), spline_lengths_.end(), lengths[i]);
- const int index = offset - spline_lengths_.data() - 1;
- spline_indices[i] = std::max(index, 0);
+ Span<float3> evaluated_positions = curves.evaluated_positions();
+ Span<float3> evaluated_tangents;
+ Span<float3> evaluated_normals;
+ if (!sampled_tangents.is_empty()) {
+ evaluated_tangents = curves.evaluated_tangents();
}
-
- /* Storing lookups in an array is unnecessary but will simplify custom attribute transfer. */
- Array<Spline::LookupResult> lookups(mask.min_array_size());
- for (const int i : mask) {
- const float length_in_spline = lengths[i] - spline_lengths_[spline_indices[i]];
- lookups[i] = splines[spline_indices[i]]->lookup_evaluated_length(length_in_spline);
+ if (!sampled_normals.is_empty()) {
+ evaluated_normals = curves.evaluated_normals();
}
- if (!sampled_positions.is_empty()) {
- for (const int i : mask) {
- const Spline::LookupResult &lookup = lookups[i];
- const Span<float3> evaluated_positions = splines[spline_indices[i]]->evaluated_positions();
- sampled_positions[i] = sample_with_lookup(lookup, evaluated_positions);
+ const VArray<int> curve_indices = params.readonly_single_input<int>(0, "Curve Index");
+ const VArraySpan<float> lengths = params.readonly_single_input<float>(1, "Length");
+ const VArray<bool> cyclic = curves.cyclic();
+
+ Array<int> indices;
+ Array<float> factors;
+
+ auto sample_curve = [&](const int curve_i, const IndexMask mask) {
+ /* Store the sampled indices and factors in arrays the size of the mask.
+ * Then, during interpolation, move the results back to the masked indices. */
+ indices.reinitialize(mask.size());
+ factors.reinitialize(mask.size());
+ sample_indices_and_factors_to_compressed(
+ curves.evaluated_lengths_for_curve(curve_i, cyclic[curve_i]),
+ lengths,
+ mask,
+ indices,
+ factors);
+
+ const IndexRange evaluated_points = curves.evaluated_points_for_curve(curve_i);
+ if (!sampled_positions.is_empty()) {
+ length_parameterize::interpolate_to_masked<float3>(
+ evaluated_positions.slice(evaluated_points),
+ indices,
+ factors,
+ mask,
+ sampled_positions);
}
- }
-
- if (!sampled_tangents.is_empty()) {
- for (const int i : mask) {
- const Spline::LookupResult &lookup = lookups[i];
- const Span<float3> evaluated_tangents = splines[spline_indices[i]]->evaluated_tangents();
- sampled_tangents[i] = math::normalize(sample_with_lookup(lookup, evaluated_tangents));
+ if (!sampled_tangents.is_empty()) {
+ length_parameterize::interpolate_to_masked<float3>(
+ evaluated_tangents.slice(evaluated_points), indices, factors, mask, sampled_tangents);
+ for (const int64_t i : mask) {
+ sampled_tangents[i] = math::normalize(sampled_tangents[i]);
+ }
}
- }
+ if (!sampled_normals.is_empty()) {
+ length_parameterize::interpolate_to_masked<float3>(
+ evaluated_normals.slice(evaluated_points), indices, factors, mask, sampled_normals);
+ for (const int64_t i : mask) {
+ sampled_normals[i] = math::normalize(sampled_normals[i]);
+ }
+ }
+ };
- if (!sampled_normals.is_empty()) {
- for (const int i : mask) {
- const Spline::LookupResult &lookup = lookups[i];
- const Span<float3> evaluated_normals = splines[spline_indices[i]]->evaluated_normals();
- sampled_normals[i] = math::normalize(sample_with_lookup(lookup, evaluated_normals));
+ if (curve_indices.is_single()) {
+ sample_curve(curve_indices.get_internal_single(), mask);
+ }
+ else {
+ MultiValueMap<int, int64_t> indices_per_curve;
+ devirtualize_varray(curve_indices, [&](const auto curve_indices) {
+ for (const int64_t i : mask) {
+ indices_per_curve.add(curve_indices[i], i);
+ }
+ });
+
+ for (const int curve_i : indices_per_curve.keys()) {
+ sample_curve(curve_i, IndexMask(indices_per_curve.lookup(curve_i)));
}
}
}
@@ -188,82 +276,82 @@ class SampleCurveFunction : public fn::MultiFunction {
/**
* Pre-process the lengths or factors used for the sampling, turning factors into lengths, and
- * clamping between zero and the total length of the curve. Do this as a separate operation in the
+ * clamping between zero and the total length of the curves. Do this as a separate operation in the
* field tree to make the sampling simpler, and to let the evaluator optimize better.
*
* \todo Use a mutable single input instead when they are supported.
*/
-static Field<float> get_length_input_field(const GeoNodeExecParams &params,
- const float curve_total_length)
+static Field<float> get_length_input_field(GeoNodeExecParams params,
+ const GeometryNodeCurveSampleMode mode,
+ const float curves_total_length)
{
- const NodeGeometryCurveSample &storage = node_storage(params.node());
- const GeometryNodeCurveSampleMode mode = (GeometryNodeCurveSampleMode)storage.mode;
-
if (mode == GEO_NODE_CURVE_SAMPLE_LENGTH) {
- /* Just make sure the length is in bounds of the curve. */
- Field<float> length_field = params.get_input<Field<float>>("Length");
- auto clamp_fn = std::make_unique<fn::CustomMF_SI_SO<float, float>>(
- __func__,
- [curve_total_length](float length) {
- return std::clamp(length, 0.0f, curve_total_length);
- },
- fn::CustomMF_presets::AllSpanOrSingle());
- auto clamp_op = std::make_shared<FieldOperation>(
- FieldOperation(std::move(clamp_fn), {std::move(length_field)}));
-
- return Field<float>(std::move(clamp_op), 0);
+ return params.extract_input<Field<float>>("Length");
}
- /* Convert the factor to a length and clamp it to the bounds of the curve. */
+ /* Convert the factor to a length. */
Field<float> factor_field = params.get_input<Field<float>>("Factor");
auto clamp_fn = std::make_unique<fn::CustomMF_SI_SO<float, float>>(
__func__,
- [curve_total_length](float factor) {
- const float length = factor * curve_total_length;
- return std::clamp(length, 0.0f, curve_total_length);
- },
+ [curves_total_length](float factor) { return factor * curves_total_length; },
fn::CustomMF_presets::AllSpanOrSingle());
- auto process_op = std::make_shared<FieldOperation>(
- FieldOperation(std::move(clamp_fn), {std::move(factor_field)}));
- return Field<float>(std::move(process_op), 0);
+ return Field<float>(FieldOperation::Create(std::move(clamp_fn), {std::move(factor_field)}), 0);
}
-static void node_geo_exec(GeoNodeExecParams params)
+static Array<float> curve_accumulated_lengths(const bke::CurvesGeometry &curves)
{
- GeometrySet geometry_set = params.extract_input<GeometrySet>("Curve");
-
- const CurveComponent *component = geometry_set.get_component_for_read<CurveComponent>();
- if (component == nullptr) {
- params.set_default_remaining_outputs();
- return;
+ curves.ensure_evaluated_lengths();
+
+ Array<float> curve_lengths(curves.curves_num());
+ const VArray<bool> cyclic = curves.cyclic();
+ float length = 0.0f;
+ for (const int i : curves.curves_range()) {
+ length += curves.evaluated_length_total_for_curve(i, cyclic[i]);
+ curve_lengths[i] = length;
}
+ return curve_lengths;
+}
- if (!component->has_curves()) {
+static void node_geo_exec(GeoNodeExecParams params)
+{
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Curve");
+ if (!geometry_set.has_curves()) {
params.set_default_remaining_outputs();
return;
}
- const std::unique_ptr<CurveEval> curve = curves_to_curve_eval(*component->get_for_read());
-
- if (curve->splines().is_empty()) {
+ const Curves &curves_id = *geometry_set.get_curves_for_read();
+ const bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id.geometry);
+ if (curves.points_num() == 0) {
params.set_default_remaining_outputs();
return;
}
- Array<float> spline_lengths = curve->accumulated_spline_lengths();
- const float total_length = spline_lengths.last();
+ Array<float> curve_lengths = curve_accumulated_lengths(curves);
+ const float total_length = curve_lengths.last();
if (total_length == 0.0f) {
params.set_default_remaining_outputs();
return;
}
- Field<float> length_field = get_length_input_field(params, total_length);
+ const NodeGeometryCurveSample &storage = node_storage(params.node());
+ const GeometryNodeCurveSampleMode mode = (GeometryNodeCurveSampleMode)storage.mode;
+ Field<float> length_field = get_length_input_field(params, mode, total_length);
+
+ auto sample_fn = std::make_unique<SampleCurveFunction>(std::move(geometry_set));
- auto sample_fn = std::make_unique<SampleCurveFunction>(std::move(geometry_set),
- std::move(spline_lengths));
- auto sample_op = std::make_shared<FieldOperation>(
- FieldOperation(std::move(sample_fn), {length_field}));
+ std::shared_ptr<FieldOperation> sample_op;
+ if (curves.curves_num() == 1) {
+ sample_op = FieldOperation::Create(std::move(sample_fn),
+ {fn::make_constant_field<int>(0), std::move(length_field)});
+ }
+ else {
+ auto index_fn = std::make_unique<SampleFloatSegmentsFunction>(std::move(curve_lengths));
+ auto index_op = FieldOperation::Create(std::move(index_fn), {std::move(length_field)});
+ sample_op = FieldOperation::Create(std::move(sample_fn),
+ {Field<int>(index_op, 0), Field<float>(index_op, 1)});
+ }
params.set_output("Position", Field<float3>(sample_op, 0));
params.set_output("Tangent", Field<float3>(sample_op, 1));
diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_set_handle_type.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_set_handle_type.cc
index 469d8d8d13b..f7ba724c377 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_curve_set_handle_type.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_set_handle_type.cc
@@ -98,8 +98,8 @@ static void node_geo_exec(GeoNodeExecParams params)
}
has_curves = true;
const CurveComponent &component = *geometry_set.get_component_for_read<CurveComponent>();
- if (!component.attribute_exists("handle_type_left") ||
- !component.attribute_exists("handle_type_right")) {
+ const AttributeAccessor attributes = *component.attributes();
+ if (!attributes.contains("handle_type_left") || !attributes.contains("handle_type_right")) {
return;
}
has_bezier = true;
diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_spline_type.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_spline_type.cc
index 8479d61a890..a92479bc5f1 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_curve_spline_type.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_spline_type.cc
@@ -1,16 +1,12 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
-#include <numeric>
-
-#include "BKE_attribute_math.hh"
#include "BKE_curves.hh"
-#include "BKE_curves_utils.hh"
-
-#include "BLI_task.hh"
#include "UI_interface.h"
#include "UI_resources.h"
+#include "GEO_set_curve_type.hh"
+
#include "node_geometry_util.hh"
namespace blender::nodes::node_geo_curve_spline_type_cc {
@@ -37,631 +33,6 @@ static void node_init(bNodeTree *UNUSED(tree), bNode *node)
node->storage = data;
}
-/**
- * This function answers the question about possible conversion method for NURBS-to-Bezier. In
- * general for 3rd degree NURBS curves there is one-to-one relation with 3rd degree Bezier curves
- * that can be exploit for conversion - Bezier handles sit on NURBS hull segments and in the middle
- * between those handles are Bezier anchor points.
- */
-static bool is_nurbs_to_bezier_one_to_one(const KnotsMode knots_mode)
-{
- if (ELEM(knots_mode, NURBS_KNOT_MODE_NORMAL, NURBS_KNOT_MODE_ENDPOINT)) {
- return true;
- }
- return false;
-}
-
-/**
- * As an optimization, just change the types on a mutable curves data-block when the conversion is
- * simple. This could be expanded to more cases where the number of points doesn't change in the
- * future, though that might require properly initializing some attributes, or removing others.
- */
-static bool conversion_can_change_point_num(const CurveType dst_type)
-{
- if (ELEM(dst_type, CURVE_TYPE_CATMULL_ROM, CURVE_TYPE_POLY)) {
- /* The conversion to Catmull Rom or Poly should never change the number of points, no matter
- * the source type (Bezier to Catmull Rom conversion cannot maintain the same shape anyway). */
- return false;
- }
- return true;
-}
-
-template<typename T>
-static void scale_input_assign(const Span<T> src,
- const int scale,
- const int offset,
- MutableSpan<T> dst)
-{
- for (const int i : dst.index_range()) {
- dst[i] = src[i * scale + offset];
- }
-}
-
-/**
- * The Bezier control point and its handles become three control points on the NURBS curve,
- * so each attribute value is duplicated three times.
- */
-template<typename T> static void bezier_generic_to_nurbs(const Span<T> src, MutableSpan<T> dst)
-{
- for (const int i : src.index_range()) {
- dst[i * 3] = src[i];
- dst[i * 3 + 1] = src[i];
- dst[i * 3 + 2] = src[i];
- }
-}
-
-static void bezier_generic_to_nurbs(const GSpan src, GMutableSpan dst)
-{
- attribute_math::convert_to_static_type(src.type(), [&](auto dummy) {
- using T = decltype(dummy);
- bezier_generic_to_nurbs(src.typed<T>(), dst.typed<T>());
- });
-}
-
-static void bezier_positions_to_nurbs(const Span<float3> src_positions,
- const Span<float3> src_handles_l,
- const Span<float3> src_handles_r,
- MutableSpan<float3> dst_positions)
-{
- for (const int i : src_positions.index_range()) {
- dst_positions[i * 3] = src_handles_l[i];
- dst_positions[i * 3 + 1] = src_positions[i];
- dst_positions[i * 3 + 2] = src_handles_r[i];
- }
-}
-
-static void catmull_rom_to_bezier_handles(const Span<float3> src_positions,
- const bool cyclic,
- MutableSpan<float3> dst_handles_l,
- MutableSpan<float3> dst_handles_r)
-{
- /* Catmull Rom curves are the same as Bezier curves with automatically defined handle positions.
- * This constant defines the portion of the distance between the next/previous points to use for
- * the length of the handles. */
- constexpr float handle_scale = 1.0f / 6.0f;
-
- if (src_positions.size() == 1) {
- dst_handles_l.first() = src_positions.first();
- dst_handles_r.first() = src_positions.first();
- return;
- }
-
- const float3 first_offset = cyclic ? src_positions[1] - src_positions.last() :
- src_positions[1] - src_positions[0];
- dst_handles_r.first() = src_positions.first() + first_offset * handle_scale;
- dst_handles_l.first() = src_positions.first() - first_offset * handle_scale;
-
- const float3 last_offset = cyclic ? src_positions.first() - src_positions.last(1) :
- src_positions.last() - src_positions.last(1);
- dst_handles_l.last() = src_positions.last() - last_offset * handle_scale;
- dst_handles_r.last() = src_positions.last() + last_offset * handle_scale;
-
- for (const int i : src_positions.index_range().drop_front(1).drop_back(1)) {
- const float3 left_offset = src_positions[i - 1] - src_positions[i + 1];
- dst_handles_l[i] = src_positions[i] + left_offset * handle_scale;
-
- const float3 right_offset = src_positions[i + 1] - src_positions[i - 1];
- dst_handles_r[i] = src_positions[i] + right_offset * handle_scale;
- }
-}
-
-static void catmull_rom_to_nurbs_positions(const Span<float3> src_positions,
- const bool cyclic,
- MutableSpan<float3> dst_positions)
-{
- /* Convert the Catmull Rom position data to Bezier handles in order to reuse the Bezier to
- * NURBS positions assignment. If this becomes a bottleneck, this step could be avoided. */
- Array<float3, 32> bezier_handles_l(src_positions.size());
- Array<float3, 32> bezier_handles_r(src_positions.size());
- catmull_rom_to_bezier_handles(src_positions, cyclic, bezier_handles_l, bezier_handles_r);
- bezier_positions_to_nurbs(src_positions, bezier_handles_l, bezier_handles_r, dst_positions);
-}
-
-template<typename T>
-static void nurbs_to_bezier_assign(const Span<T> src,
- const MutableSpan<T> dst,
- const KnotsMode knots_mode)
-{
- switch (knots_mode) {
- case NURBS_KNOT_MODE_NORMAL:
- for (const int i : dst.index_range()) {
- dst[i] = src[(i + 1) % src.size()];
- }
- break;
- case NURBS_KNOT_MODE_ENDPOINT:
- for (const int i : dst.index_range().drop_back(1).drop_front(1)) {
- dst[i] = src[i + 1];
- }
- dst.first() = src.first();
- dst.last() = src.last();
- break;
- default:
- /* Every 3rd NURBS position (starting from index 1) should have its attributes transferred.
- */
- scale_input_assign<T>(src, 3, 1, dst);
- }
-}
-
-static void nurbs_to_bezier_assign(const GSpan src, const KnotsMode knots_mode, GMutableSpan dst)
-{
- attribute_math::convert_to_static_type(src.type(), [&](auto dummy) {
- using T = decltype(dummy);
- nurbs_to_bezier_assign(src.typed<T>(), dst.typed<T>(), knots_mode);
- });
-}
-
-static Vector<float3> create_nurbs_to_bezier_handles(const Span<float3> nurbs_positions,
- const KnotsMode knots_mode)
-{
- const int nurbs_positions_num = nurbs_positions.size();
- Vector<float3> handle_positions;
-
- if (is_nurbs_to_bezier_one_to_one(knots_mode)) {
- const bool is_periodic = knots_mode == NURBS_KNOT_MODE_NORMAL;
- if (is_periodic) {
- handle_positions.append(nurbs_positions[1] +
- ((nurbs_positions[0] - nurbs_positions[1]) / 3));
- }
- else {
- handle_positions.append(2 * nurbs_positions[0] - nurbs_positions[1]);
- handle_positions.append(nurbs_positions[1]);
- }
-
- /* Place Bezier handles on interior NURBS hull segments. Those handles can be either placed on
- * endpoints, midpoints or 1/3 of the distance of a hull segment. */
- const int segments_num = nurbs_positions_num - 1;
- const bool ignore_interior_segment = segments_num == 3 && is_periodic == false;
- if (ignore_interior_segment == false) {
- const float mid_offset = (float)(segments_num - 1) / 2.0f;
- for (const int i : IndexRange(1, segments_num - 2)) {
- /* Divisor can have values: 1, 2 or 3. */
- const int divisor = is_periodic ?
- 3 :
- std::min(3, (int)(-std::abs(i - mid_offset) + mid_offset + 1.0f));
- const float3 &p1 = nurbs_positions[i];
- const float3 &p2 = nurbs_positions[i + 1];
- const float3 displacement = (p2 - p1) / divisor;
- const int num_handles_on_segment = divisor < 3 ? 1 : 2;
- for (int j : IndexRange(1, num_handles_on_segment)) {
- handle_positions.append(p1 + (displacement * j));
- }
- }
- }
-
- const int last_index = nurbs_positions_num - 1;
- if (is_periodic) {
- handle_positions.append(
- nurbs_positions[last_index - 1] +
- ((nurbs_positions[last_index] - nurbs_positions[last_index - 1]) / 3));
- }
- else {
- handle_positions.append(nurbs_positions[last_index - 1]);
- handle_positions.append(2 * nurbs_positions[last_index] - nurbs_positions[last_index - 1]);
- }
- }
- else {
- for (const int i : IndexRange(nurbs_positions_num)) {
- if (i % 3 == 1) {
- continue;
- }
- handle_positions.append(nurbs_positions[i]);
- }
- if (nurbs_positions_num % 3 == 1) {
- handle_positions.pop_last();
- }
- else if (nurbs_positions_num % 3 == 2) {
- const int last_index = nurbs_positions_num - 1;
- handle_positions.append(2 * nurbs_positions[last_index] - nurbs_positions[last_index - 1]);
- }
- }
-
- return handle_positions;
-}
-
-static void create_nurbs_to_bezier_positions(const Span<float3> nurbs_positions,
- const Span<float3> handle_positions,
- const KnotsMode knots_mode,
- MutableSpan<float3> bezier_positions)
-{
- if (is_nurbs_to_bezier_one_to_one(knots_mode)) {
- for (const int i : bezier_positions.index_range()) {
- bezier_positions[i] = math::interpolate(
- handle_positions[i * 2], handle_positions[i * 2 + 1], 0.5f);
- }
- }
- else {
- /* Every 3rd NURBS position (starting from index 1) should be converted to Bezier position. */
- scale_input_assign(nurbs_positions, 3, 1, bezier_positions);
- }
-}
-
-static int to_bezier_size(const CurveType src_type,
- const bool cyclic,
- const KnotsMode knots_mode,
- const int src_size)
-{
- switch (src_type) {
- case CURVE_TYPE_NURBS: {
- if (is_nurbs_to_bezier_one_to_one(knots_mode)) {
- return cyclic ? src_size : src_size - 2;
- }
- return (src_size + 1) / 3;
- }
- default:
- return src_size;
- }
-}
-
-static int to_nurbs_size(const CurveType src_type, const int src_size)
-{
- switch (src_type) {
- case CURVE_TYPE_BEZIER:
- case CURVE_TYPE_CATMULL_ROM:
- return src_size * 3;
- default:
- return src_size;
- }
-}
-
-static void retrieve_curve_sizes(const bke::CurvesGeometry &curves, MutableSpan<int> sizes)
-{
- threading::parallel_for(curves.curves_range(), 4096, [&](IndexRange range) {
- for (const int i : range) {
- sizes[i] = curves.points_for_curve(i).size();
- }
- });
-}
-
-struct GenericAttributes : NonCopyable, NonMovable {
- Vector<GSpan> src;
- Vector<GMutableSpan> dst;
-
- Vector<OutputAttribute> attributes;
-};
-
-static void retrieve_generic_point_attributes(const CurveComponent &src_component,
- CurveComponent &dst_component,
- GenericAttributes &attributes)
-{
- src_component.attribute_foreach(
- [&](const AttributeIDRef &id, const AttributeMetaData meta_data) {
- if (meta_data.domain != ATTR_DOMAIN_POINT) {
- /* Curve domain attributes are all copied directly to the result in one step. */
- return true;
- }
- if (src_component.attribute_is_builtin(id)) {
- if (!(id.is_named() && ELEM(id, "tilt", "radius"))) {
- return true;
- }
- }
-
- GVArray src_attribute = src_component.attribute_try_get_for_read(id, ATTR_DOMAIN_POINT);
- BLI_assert(src_attribute);
- attributes.src.append(src_attribute.get_internal_span());
-
- OutputAttribute dst_attribute = dst_component.attribute_try_get_for_output_only(
- id, ATTR_DOMAIN_POINT, meta_data.data_type);
- attributes.dst.append(dst_attribute.as_span());
- attributes.attributes.append(std::move(dst_attribute));
-
- return true;
- });
-}
-
-static void convert_to_bezier(const CurveComponent &src_component,
- const bke::CurvesGeometry &src_curves,
- const IndexMask selection,
- CurveComponent &dst_component,
- bke::CurvesGeometry &dst_curves)
-{
- const VArray<int8_t> src_knot_modes = src_curves.nurbs_knots_modes();
- const VArray<int8_t> src_types = src_curves.curve_types();
- const VArray<bool> src_cyclic = src_curves.cyclic();
- const Span<float3> src_positions = src_curves.positions();
-
- MutableSpan<int> dst_offsets = dst_curves.offsets_for_write();
- retrieve_curve_sizes(src_curves, dst_curves.offsets_for_write());
- threading::parallel_for(selection.index_range(), 1024, [&](IndexRange range) {
- for (const int i : selection.slice(range)) {
- dst_offsets[i] = to_bezier_size(
- CurveType(src_types[i]), src_cyclic[i], KnotsMode(src_knot_modes[i]), dst_offsets[i]);
- }
- });
- bke::curves::accumulate_counts_to_offsets(dst_offsets);
- dst_curves.resize(dst_offsets.last(), dst_curves.curves_num());
-
- GenericAttributes attributes;
- retrieve_generic_point_attributes(src_component, dst_component, attributes);
-
- MutableSpan<float3> dst_positions = dst_curves.positions_for_write();
- MutableSpan<float3> dst_handles_l = dst_curves.handle_positions_left_for_write();
- MutableSpan<float3> dst_handles_r = dst_curves.handle_positions_right_for_write();
- MutableSpan<int8_t> dst_types_l = dst_curves.handle_types_left_for_write();
- MutableSpan<int8_t> dst_types_r = dst_curves.handle_types_right_for_write();
- MutableSpan<float> dst_weights = dst_curves.nurbs_weights_for_write();
-
- auto catmull_rom_to_bezier = [&](IndexMask selection) {
- bke::curves::fill_points<int8_t>(dst_curves, selection, BEZIER_HANDLE_ALIGN, dst_types_l);
- bke::curves::fill_points<int8_t>(dst_curves, selection, BEZIER_HANDLE_ALIGN, dst_types_r);
- bke::curves::copy_point_data(src_curves, dst_curves, selection, src_positions, dst_positions);
-
- threading::parallel_for(selection.index_range(), 512, [&](IndexRange range) {
- for (const int i : selection.slice(range)) {
- const IndexRange src_points = src_curves.points_for_curve(i);
- const IndexRange dst_points = dst_curves.points_for_curve(i);
- catmull_rom_to_bezier_handles(src_positions.slice(src_points),
- src_cyclic[i],
- dst_handles_l.slice(dst_points),
- dst_handles_r.slice(dst_points));
- }
- });
-
- for (const int i : attributes.src.index_range()) {
- bke::curves::copy_point_data(
- src_curves, dst_curves, selection, attributes.src[i], attributes.dst[i]);
- }
- };
-
- auto poly_to_bezier = [&](IndexMask selection) {
- bke::curves::copy_point_data(src_curves, dst_curves, selection, src_positions, dst_positions);
- bke::curves::fill_points<int8_t>(dst_curves, selection, BEZIER_HANDLE_VECTOR, dst_types_l);
- bke::curves::fill_points<int8_t>(dst_curves, selection, BEZIER_HANDLE_VECTOR, dst_types_r);
- dst_curves.calculate_bezier_auto_handles();
- for (const int i : attributes.src.index_range()) {
- bke::curves::copy_point_data(
- src_curves, dst_curves, selection, attributes.src[i], attributes.dst[i]);
- }
- };
-
- auto bezier_to_bezier = [&](IndexMask selection) {
- const VArray_Span<int8_t> src_types_l = src_curves.handle_types_left();
- const VArray_Span<int8_t> src_types_r = src_curves.handle_types_right();
- const Span<float3> src_handles_l = src_curves.handle_positions_left();
- const Span<float3> src_handles_r = src_curves.handle_positions_right();
-
- bke::curves::copy_point_data(src_curves, dst_curves, selection, src_positions, dst_positions);
- bke::curves::copy_point_data(src_curves, dst_curves, selection, src_handles_l, dst_handles_l);
- bke::curves::copy_point_data(src_curves, dst_curves, selection, src_handles_r, dst_handles_r);
- bke::curves::copy_point_data(src_curves, dst_curves, selection, src_types_l, dst_types_l);
- bke::curves::copy_point_data(src_curves, dst_curves, selection, src_types_r, dst_types_r);
-
- dst_curves.calculate_bezier_auto_handles();
-
- for (const int i : attributes.src.index_range()) {
- bke::curves::copy_point_data(
- src_curves, dst_curves, selection, attributes.src[i], attributes.dst[i]);
- }
- };
-
- auto nurbs_to_bezier = [&](IndexMask selection) {
- bke::curves::fill_points<int8_t>(dst_curves, selection, BEZIER_HANDLE_ALIGN, dst_types_l);
- bke::curves::fill_points<int8_t>(dst_curves, selection, BEZIER_HANDLE_ALIGN, dst_types_r);
- bke::curves::fill_points<float>(dst_curves, selection, 0.0f, dst_weights);
-
- threading::parallel_for(selection.index_range(), 64, [&](IndexRange range) {
- for (const int i : selection.slice(range)) {
- const IndexRange src_points = src_curves.points_for_curve(i);
- const IndexRange dst_points = dst_curves.points_for_curve(i);
- const Span<float3> src_curve_positions = src_positions.slice(src_points);
-
- KnotsMode knots_mode = KnotsMode(src_knot_modes[i]);
- Span<float3> nurbs_positions = src_curve_positions;
- Vector<float3> nurbs_positions_vector;
- if (src_cyclic[i] && is_nurbs_to_bezier_one_to_one(knots_mode)) {
- /* For conversion treat this as periodic closed curve. Extend NURBS hull to first and
- * second point which will act as a skeleton for placing Bezier handles. */
- nurbs_positions_vector.extend(src_curve_positions);
- nurbs_positions_vector.append(src_curve_positions[0]);
- nurbs_positions_vector.append(src_curve_positions[1]);
- nurbs_positions = nurbs_positions_vector;
- knots_mode = NURBS_KNOT_MODE_NORMAL;
- }
-
- const Vector<float3> handle_positions = create_nurbs_to_bezier_handles(nurbs_positions,
- knots_mode);
-
- scale_input_assign(handle_positions.as_span(), 2, 0, dst_handles_l.slice(dst_points));
- scale_input_assign(handle_positions.as_span(), 2, 1, dst_handles_r.slice(dst_points));
-
- create_nurbs_to_bezier_positions(
- nurbs_positions, handle_positions, knots_mode, dst_positions.slice(dst_points));
- }
- });
-
- for (const int i_attribute : attributes.src.index_range()) {
- threading::parallel_for(selection.index_range(), 512, [&](IndexRange range) {
- for (const int i : selection.slice(range)) {
- const IndexRange src_points = src_curves.points_for_curve(i);
- const IndexRange dst_points = dst_curves.points_for_curve(i);
- nurbs_to_bezier_assign(attributes.src[i_attribute].slice(src_points),
- KnotsMode(src_knot_modes[i]),
- attributes.dst[i_attribute].slice(dst_points));
- }
- });
- }
- };
-
- bke::curves::foreach_curve_by_type(src_curves.curve_types(),
- src_curves.curve_type_counts(),
- selection,
- catmull_rom_to_bezier,
- poly_to_bezier,
- bezier_to_bezier,
- nurbs_to_bezier);
-
- const Vector<IndexRange> unselected_ranges = selection.extract_ranges_invert(
- src_curves.curves_range());
-
- for (const int i : attributes.src.index_range()) {
- bke::curves::copy_point_data(
- src_curves, dst_curves, unselected_ranges, attributes.src[i], attributes.dst[i]);
- }
-
- for (OutputAttribute &attribute : attributes.attributes) {
- attribute.save();
- }
-}
-
-static void convert_to_nurbs(const CurveComponent &src_component,
- const bke::CurvesGeometry &src_curves,
- const IndexMask selection,
- CurveComponent &dst_component,
- bke::CurvesGeometry &dst_curves)
-{
- const VArray<int8_t> src_types = src_curves.curve_types();
- const VArray<bool> src_cyclic = src_curves.cyclic();
- const Span<float3> src_positions = src_curves.positions();
-
- MutableSpan<int> dst_offsets = dst_curves.offsets_for_write();
- retrieve_curve_sizes(src_curves, dst_curves.offsets_for_write());
- threading::parallel_for(selection.index_range(), 1024, [&](IndexRange range) {
- for (const int i : selection.slice(range)) {
- dst_offsets[i] = to_nurbs_size(CurveType(src_types[i]), dst_offsets[i]);
- }
- });
- bke::curves::accumulate_counts_to_offsets(dst_offsets);
- dst_curves.resize(dst_offsets.last(), dst_curves.curves_num());
-
- GenericAttributes attributes;
- retrieve_generic_point_attributes(src_component, dst_component, attributes);
-
- MutableSpan<float3> dst_positions = dst_curves.positions_for_write();
-
- auto fill_weights_if_necessary = [&](const IndexMask selection) {
- if (!src_curves.nurbs_weights().is_empty()) {
- bke::curves::fill_points(dst_curves, selection, 1.0f, dst_curves.nurbs_weights_for_write());
- }
- };
-
- auto catmull_rom_to_nurbs = [&](IndexMask selection) {
- dst_curves.nurbs_orders_for_write().fill_indices(selection, 4);
- dst_curves.nurbs_knots_modes_for_write().fill_indices(selection, NURBS_KNOT_MODE_BEZIER);
- fill_weights_if_necessary(selection);
-
- threading::parallel_for(selection.index_range(), 512, [&](IndexRange range) {
- for (const int i : selection.slice(range)) {
- const IndexRange src_points = src_curves.points_for_curve(i);
- const IndexRange dst_points = dst_curves.points_for_curve(i);
- catmull_rom_to_nurbs_positions(
- src_positions.slice(src_points), src_cyclic[i], dst_positions.slice(dst_points));
- }
- });
-
- for (const int i_attribute : attributes.src.index_range()) {
- threading::parallel_for(selection.index_range(), 512, [&](IndexRange range) {
- for (const int i : selection.slice(range)) {
- const IndexRange src_points = src_curves.points_for_curve(i);
- const IndexRange dst_points = dst_curves.points_for_curve(i);
- bezier_generic_to_nurbs(attributes.src[i_attribute].slice(src_points),
- attributes.dst[i_attribute].slice(dst_points));
- }
- });
- }
- };
-
- auto poly_to_nurbs = [&](IndexMask selection) {
- dst_curves.nurbs_orders_for_write().fill_indices(selection, 4);
- bke::curves::copy_point_data(src_curves, dst_curves, selection, src_positions, dst_positions);
- fill_weights_if_necessary(selection);
-
- /* Avoid using "Endpoint" knots modes for cyclic curves, since it adds a sharp point at the
- * start/end. */
- if (src_cyclic.is_single()) {
- dst_curves.nurbs_knots_modes_for_write().fill_indices(
- selection,
- src_cyclic.get_internal_single() ? NURBS_KNOT_MODE_NORMAL : NURBS_KNOT_MODE_ENDPOINT);
- }
- else {
- VArray_Span<bool> cyclic{src_cyclic};
- MutableSpan<int8_t> knots_modes = dst_curves.nurbs_knots_modes_for_write();
- threading::parallel_for(selection.index_range(), 1024, [&](IndexRange range) {
- for (const int i : selection.slice(range)) {
- knots_modes[i] = cyclic[i] ? NURBS_KNOT_MODE_NORMAL : NURBS_KNOT_MODE_ENDPOINT;
- }
- });
- }
-
- for (const int i_attribute : attributes.src.index_range()) {
- bke::curves::copy_point_data(src_curves,
- dst_curves,
- selection,
- attributes.src[i_attribute],
- attributes.dst[i_attribute]);
- }
- };
-
- auto bezier_to_nurbs = [&](IndexMask selection) {
- const Span<float3> src_handles_l = src_curves.handle_positions_left();
- const Span<float3> src_handles_r = src_curves.handle_positions_right();
-
- dst_curves.nurbs_orders_for_write().fill_indices(selection, 4);
- dst_curves.nurbs_knots_modes_for_write().fill_indices(selection, NURBS_KNOT_MODE_BEZIER);
- fill_weights_if_necessary(selection);
-
- threading::parallel_for(selection.index_range(), 512, [&](IndexRange range) {
- for (const int i : selection.slice(range)) {
- const IndexRange src_points = src_curves.points_for_curve(i);
- const IndexRange dst_points = dst_curves.points_for_curve(i);
- bezier_positions_to_nurbs(src_positions.slice(src_points),
- src_handles_l.slice(src_points),
- src_handles_r.slice(src_points),
- dst_positions.slice(dst_points));
- }
- });
-
- for (const int i_attribute : attributes.src.index_range()) {
- threading::parallel_for(selection.index_range(), 512, [&](IndexRange range) {
- for (const int i : selection.slice(range)) {
- const IndexRange src_points = src_curves.points_for_curve(i);
- const IndexRange dst_points = dst_curves.points_for_curve(i);
- bezier_generic_to_nurbs(attributes.src[i_attribute].slice(src_points),
- attributes.dst[i_attribute].slice(dst_points));
- }
- });
- }
- };
-
- auto nurbs_to_nurbs = [&](IndexMask selection) {
- bke::curves::copy_point_data(src_curves, dst_curves, selection, src_positions, dst_positions);
-
- if (!src_curves.nurbs_weights().is_empty()) {
- bke::curves::copy_point_data(src_curves,
- dst_curves,
- selection,
- src_curves.nurbs_weights(),
- dst_curves.nurbs_weights_for_write());
- }
-
- for (const int i_attribute : attributes.src.index_range()) {
- bke::curves::copy_point_data(src_curves,
- dst_curves,
- selection,
- attributes.src[i_attribute],
- attributes.dst[i_attribute]);
- }
- };
-
- bke::curves::foreach_curve_by_type(src_curves.curve_types(),
- src_curves.curve_type_counts(),
- selection,
- catmull_rom_to_nurbs,
- poly_to_nurbs,
- bezier_to_nurbs,
- nurbs_to_nurbs);
-
- const Vector<IndexRange> unselected_ranges = selection.extract_ranges_invert(
- src_curves.curves_range());
-
- for (const int i : attributes.src.index_range()) {
- bke::curves::copy_point_data(
- src_curves, dst_curves, unselected_ranges, attributes.src[i], attributes.dst[i]);
- }
-
- for (OutputAttribute &attribute : attributes.attributes) {
- attribute.save();
- }
-}
-
static void node_geo_exec(GeoNodeExecParams params)
{
const NodeGeometryCurveSplineType &storage = node_storage(params.node());
@@ -682,49 +53,25 @@ static void node_geo_exec(GeoNodeExecParams params)
}
GeometryComponentFieldContext field_context{src_component, ATTR_DOMAIN_CURVE};
- const int domain_size = src_component.attribute_domain_num(ATTR_DOMAIN_CURVE);
-
- fn::FieldEvaluator evaluator{field_context, domain_size};
+ fn::FieldEvaluator evaluator{field_context, src_curves.curves_num()};
evaluator.set_selection(selection_field);
evaluator.evaluate();
const IndexMask selection = evaluator.get_evaluated_selection_as_mask();
- if (!conversion_can_change_point_num(dst_type)) {
- CurveComponent &dst_component = geometry_set.get_component_for_write<CurveComponent>();
- Curves &curves_id = *dst_component.get_for_write();
- bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id.geometry);
- curves.fill_curve_types(selection, dst_type);
- curves.remove_attributes_based_on_types();
+ if (selection.is_empty()) {
return;
}
- Curves *dst_curves_id = bke::curves_new_nomain(0, src_curves.curves_num());
- bke::CurvesGeometry &dst_curves = bke::CurvesGeometry::wrap(dst_curves_id->geometry);
- CurveComponent dst_component;
- dst_component.replace(dst_curves_id, GeometryOwnershipType::Editable);
- /* Directly copy curve attributes, since they stay the same (except for curve types). */
- CustomData_copy(&src_curves.curve_data,
- &dst_curves.curve_data,
- CD_MASK_ALL,
- CD_DUPLICATE,
- src_curves.curves_num());
-
- dst_curves.fill_curve_types(selection, dst_type);
-
- switch (dst_type) {
- case CURVE_TYPE_CATMULL_ROM:
- case CURVE_TYPE_POLY:
- /* Converting to Catmull Rom curves or poly curves should be handled
- * above by the optimization to avoid changing the point count. */
- BLI_assert_unreachable();
- break;
- case CURVE_TYPE_BEZIER:
- convert_to_bezier(src_component, src_curves, selection, dst_component, dst_curves);
- break;
- case CURVE_TYPE_NURBS:
- convert_to_nurbs(src_component, src_curves, selection, dst_component, dst_curves);
- break;
+ if (geometry::try_curves_conversion_in_place(
+ selection, dst_type, [&]() -> bke::CurvesGeometry & {
+ return bke::CurvesGeometry::wrap(geometry_set.get_curves_for_write()->geometry);
+ })) {
+ return;
}
+ bke::CurvesGeometry dst_curves = geometry::convert_curves(src_curves, selection, dst_type);
+
+ Curves *dst_curves_id = bke::curves_new_nomain(std::move(dst_curves));
+ bke::curves_copy_parameters(src_curves_id, *dst_curves_id);
geometry_set.replace_curves(dst_curves_id);
});
diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc
index 4d8745bf79e..bd44adb35a2 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc
@@ -1,10 +1,8 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
-#include "BLI_task.hh"
-#include "BLI_timeit.hh"
+#include "BKE_curves.hh"
-#include "BKE_attribute_math.hh"
-#include "BKE_spline.hh"
+#include "GEO_subdivide_curves.hh"
#include "UI_interface.h"
#include "UI_resources.h"
@@ -26,302 +24,6 @@ static void node_declare(NodeDeclarationBuilder &b)
b.add_output<decl::Geometry>(N_("Curve"));
}
-static Array<int> get_subdivided_offsets(const Spline &spline,
- const VArray<int> &cuts,
- const int spline_offset)
-{
- Array<int> offsets(spline.segments_num() + 1);
- int offset = 0;
- for (const int i : IndexRange(spline.segments_num())) {
- offsets[i] = offset;
- offset = offset + std::max(cuts[spline_offset + i], 0) + 1;
- }
- offsets.last() = offset;
- return offsets;
-}
-
-template<typename T>
-static void subdivide_attribute(Span<T> src,
- const Span<int> offsets,
- const bool is_cyclic,
- MutableSpan<T> dst)
-{
- const int src_num = src.size();
- threading::parallel_for(IndexRange(src_num - 1), 1024, [&](IndexRange range) {
- for (const int i : range) {
- const int cuts = offsets[i + 1] - offsets[i];
- dst[offsets[i]] = src[i];
- const float factor_delta = cuts == 0 ? 1.0f : 1.0f / cuts;
- for (const int cut : IndexRange(cuts)) {
- const float factor = cut * factor_delta;
- dst[offsets[i] + cut] = attribute_math::mix2(factor, src[i], src[i + 1]);
- }
- }
- });
-
- if (is_cyclic) {
- const int i = src_num - 1;
- const int cuts = offsets[i + 1] - offsets[i];
- dst[offsets[i]] = src.last();
- const float factor_delta = cuts == 0 ? 1.0f : 1.0f / cuts;
- for (const int cut : IndexRange(cuts)) {
- const float factor = cut * factor_delta;
- dst[offsets[i] + cut] = attribute_math::mix2(factor, src.last(), src.first());
- }
- }
- else {
- dst.last() = src.last();
- }
-}
-
-/**
- * In order to generate a Bezier spline with the same shape as the input spline, apply the
- * De Casteljau algorithm iteratively for the provided number of cuts, constantly updating the
- * previous result point's right handle and the left handle at the end of the segment.
- *
- * \note Non-vector segments in the result spline are given free handles. This could possibly be
- * improved with another pass that sets handles to aligned where possible, but currently that does
- * not provide much benefit for the increased complexity.
- */
-static void subdivide_bezier_segment(const BezierSpline &src,
- const int index,
- const int offset,
- const int result_num,
- Span<float3> src_positions,
- Span<float3> src_handles_left,
- Span<float3> src_handles_right,
- MutableSpan<float3> dst_positions,
- MutableSpan<float3> dst_handles_left,
- MutableSpan<float3> dst_handles_right,
- MutableSpan<int8_t> dst_type_left,
- MutableSpan<int8_t> dst_type_right)
-{
- const bool is_last_cyclic_segment = index == (src.size() - 1);
- const int next_index = is_last_cyclic_segment ? 0 : index + 1;
-
- /* The first point in the segment is always copied. */
- dst_positions[offset] = src_positions[index];
-
- if (src.segment_is_vector(index)) {
- if (is_last_cyclic_segment) {
- dst_type_left.first() = BEZIER_HANDLE_VECTOR;
- }
- dst_type_left.slice(offset + 1, result_num).fill(BEZIER_HANDLE_VECTOR);
- dst_type_right.slice(offset, result_num).fill(BEZIER_HANDLE_VECTOR);
-
- const float factor_delta = 1.0f / result_num;
- for (const int cut : IndexRange(result_num)) {
- const float factor = cut * factor_delta;
- dst_positions[offset + cut] = attribute_math::mix2(
- factor, src_positions[index], src_positions[next_index]);
- }
- }
- else {
- if (is_last_cyclic_segment) {
- dst_type_left.first() = BEZIER_HANDLE_FREE;
- }
- dst_type_left.slice(offset + 1, result_num).fill(BEZIER_HANDLE_FREE);
- dst_type_right.slice(offset, result_num).fill(BEZIER_HANDLE_FREE);
-
- const int i_segment_last = is_last_cyclic_segment ? 0 : offset + result_num;
-
- /* Create a Bezier segment to update iteratively for every subdivision
- * and references to the meaningful values for ease of use. */
- BezierSpline temp;
- temp.resize(2);
- float3 &segment_start = temp.positions().first();
- float3 &segment_end = temp.positions().last();
- float3 &handle_prev = temp.handle_positions_right().first();
- float3 &handle_next = temp.handle_positions_left().last();
- segment_start = src_positions[index];
- segment_end = src_positions[next_index];
- handle_prev = src_handles_right[index];
- handle_next = src_handles_left[next_index];
-
- for (const int cut : IndexRange(result_num - 1)) {
- const float parameter = 1.0f / (result_num - cut);
- const BezierSpline::InsertResult insert = temp.calculate_segment_insertion(0, 1, parameter);
-
- /* Copy relevant temporary data to the result. */
- dst_handles_right[offset + cut] = insert.handle_prev;
- dst_handles_left[offset + cut + 1] = insert.left_handle;
- dst_positions[offset + cut + 1] = insert.position;
-
- /* Update the segment to prepare it for the next subdivision. */
- segment_start = insert.position;
- handle_prev = insert.right_handle;
- handle_next = insert.handle_next;
- }
-
- /* Copy the handles for the last segment from the temporary spline. */
- dst_handles_right[offset + result_num - 1] = handle_prev;
- dst_handles_left[i_segment_last] = handle_next;
- }
-}
-
-static void subdivide_bezier_spline(const BezierSpline &src,
- const Span<int> offsets,
- BezierSpline &dst)
-{
- Span<float3> src_positions = src.positions();
- Span<float3> src_handles_left = src.handle_positions_left();
- Span<float3> src_handles_right = src.handle_positions_right();
- MutableSpan<float3> dst_positions = dst.positions();
- MutableSpan<float3> dst_handles_left = dst.handle_positions_left();
- MutableSpan<float3> dst_handles_right = dst.handle_positions_right();
- MutableSpan<int8_t> dst_type_left = dst.handle_types_left();
- MutableSpan<int8_t> dst_type_right = dst.handle_types_right();
-
- threading::parallel_for(IndexRange(src.size() - 1), 512, [&](IndexRange range) {
- for (const int i : range) {
- subdivide_bezier_segment(src,
- i,
- offsets[i],
- offsets[i + 1] - offsets[i],
- src_positions,
- src_handles_left,
- src_handles_right,
- dst_positions,
- dst_handles_left,
- dst_handles_right,
- dst_type_left,
- dst_type_right);
- }
- });
-
- if (src.is_cyclic()) {
- const int i_last = src.size() - 1;
- subdivide_bezier_segment(src,
- i_last,
- offsets[i_last],
- offsets.last() - offsets[i_last],
- src_positions,
- src_handles_left,
- src_handles_right,
- dst_positions,
- dst_handles_left,
- dst_handles_right,
- dst_type_left,
- dst_type_right);
- }
- else {
- dst_positions.last() = src_positions.last();
- dst_type_left.first() = src.handle_types_left().first();
- dst_type_right.last() = src.handle_types_right().last();
- dst_handles_left.first() = src_handles_left.first();
- dst_handles_right.last() = src_handles_right.last();
- }
-}
-
-static void subdivide_builtin_attributes(const Spline &src_spline,
- const Span<int> offsets,
- Spline &dst_spline)
-{
- const bool is_cyclic = src_spline.is_cyclic();
- subdivide_attribute<float>(src_spline.radii(), offsets, is_cyclic, dst_spline.radii());
- subdivide_attribute<float>(src_spline.tilts(), offsets, is_cyclic, dst_spline.tilts());
- switch (src_spline.type()) {
- case CURVE_TYPE_POLY: {
- const PolySpline &src = static_cast<const PolySpline &>(src_spline);
- PolySpline &dst = static_cast<PolySpline &>(dst_spline);
- subdivide_attribute<float3>(src.positions(), offsets, is_cyclic, dst.positions());
- break;
- }
- case CURVE_TYPE_BEZIER: {
- const BezierSpline &src = static_cast<const BezierSpline &>(src_spline);
- BezierSpline &dst = static_cast<BezierSpline &>(dst_spline);
- subdivide_bezier_spline(src, offsets, dst);
- dst.mark_cache_invalid();
- break;
- }
- case CURVE_TYPE_NURBS: {
- const NURBSpline &src = static_cast<const NURBSpline &>(src_spline);
- NURBSpline &dst = static_cast<NURBSpline &>(dst_spline);
- subdivide_attribute<float3>(src.positions(), offsets, is_cyclic, dst.positions());
- subdivide_attribute<float>(src.weights(), offsets, is_cyclic, dst.weights());
- break;
- }
- case CURVE_TYPE_CATMULL_ROM: {
- BLI_assert_unreachable();
- break;
- }
- }
-}
-
-static void subdivide_dynamic_attributes(const Spline &src_spline,
- const Span<int> offsets,
- Spline &dst_spline)
-{
- const bool is_cyclic = src_spline.is_cyclic();
- src_spline.attributes.foreach_attribute(
- [&](const bke::AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) {
- std::optional<GSpan> src = src_spline.attributes.get_for_read(attribute_id);
- BLI_assert(src);
-
- if (!dst_spline.attributes.create(attribute_id, meta_data.data_type)) {
- /* Since the source spline of the same type had the attribute, adding it should work. */
- BLI_assert_unreachable();
- }
-
- std::optional<GMutableSpan> dst = dst_spline.attributes.get_for_write(attribute_id);
- BLI_assert(dst);
-
- attribute_math::convert_to_static_type(dst->type(), [&](auto dummy) {
- using T = decltype(dummy);
- subdivide_attribute<T>(src->typed<T>(), offsets, is_cyclic, dst->typed<T>());
- });
- return true;
- },
- ATTR_DOMAIN_POINT);
-}
-
-static SplinePtr subdivide_spline(const Spline &spline,
- const VArray<int> &cuts,
- const int spline_offset)
-{
- if (spline.size() <= 1) {
- return spline.copy();
- }
-
- /* Since we expect to access each value many times, it should be worth it to make sure count
- * of cuts is a real span (especially considering the note below). Using the offset at each
- * point facilitates subdividing in parallel later. */
- Array<int> offsets = get_subdivided_offsets(spline, cuts, spline_offset);
- const int result_num = offsets.last() + int(!spline.is_cyclic());
- SplinePtr new_spline = spline.copy_only_settings();
- new_spline->resize(result_num);
- subdivide_builtin_attributes(spline, offsets, *new_spline);
- subdivide_dynamic_attributes(spline, offsets, *new_spline);
- return new_spline;
-}
-
-/**
- * \note Passing the virtual array for the entire spline is possibly quite inefficient here when
- * the attribute was on the point domain and stored separately for each spline already, and it
- * prevents some other optimizations like skipping splines with a single attribute value of < 1.
- * However, it allows the node to access builtin attribute easily, so it the makes most sense this
- * way until the attribute API is refactored.
- */
-static std::unique_ptr<CurveEval> subdivide_curve(const CurveEval &input_curve,
- const VArray<int> &cuts)
-{
- const Array<int> control_point_offsets = input_curve.control_point_offsets();
- const Span<SplinePtr> input_splines = input_curve.splines();
-
- std::unique_ptr<CurveEval> output_curve = std::make_unique<CurveEval>();
- output_curve->resize(input_splines.size());
- output_curve->attributes = input_curve.attributes;
- MutableSpan<SplinePtr> output_splines = output_curve->splines();
-
- threading::parallel_for(input_splines.index_range(), 128, [&](IndexRange range) {
- for (const int i : range) {
- output_splines[i] = subdivide_spline(*input_splines[i], cuts, control_point_offsets[i]);
- }
- });
-
- return output_curve;
-}
-
static void node_geo_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set = params.extract_input<GeometrySet>("Curve");
@@ -333,20 +35,24 @@ static void node_geo_exec(GeoNodeExecParams params)
}
const CurveComponent &component = *geometry_set.get_component_for_read<CurveComponent>();
- GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_POINT};
- const int domain_num = component.attribute_domain_num(ATTR_DOMAIN_POINT);
+ const Curves &src_curves_id = *component.get_for_read();
+ const bke::CurvesGeometry &src_curves = bke::CurvesGeometry::wrap(src_curves_id.geometry);
- fn::FieldEvaluator evaluator{field_context, domain_num};
+ GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_POINT};
+ fn::FieldEvaluator evaluator{field_context, src_curves.points_num()};
evaluator.add(cuts_field);
evaluator.evaluate();
- const VArray<int> &cuts = evaluator.get_evaluated<int>(0);
-
+ const VArray<int> cuts = evaluator.get_evaluated<int>(0);
if (cuts.is_single() && cuts.get_internal_single() < 1) {
return;
}
- std::unique_ptr<CurveEval> output_curve = subdivide_curve(
- *curves_to_curve_eval(*component.get_for_read()), cuts);
- geometry_set.replace_curves(curve_eval_to_curves(*output_curve));
+
+ bke::CurvesGeometry dst_curves = geometry::subdivide_curves(
+ src_curves, src_curves.curves_range(), cuts);
+
+ Curves *dst_curves_id = bke::curves_new_nomain(std::move(dst_curves));
+ bke::curves_copy_parameters(src_curves_id, *dst_curves_id);
+ geometry_set.replace_curves(dst_curves_id);
});
params.set_output("Curve", geometry_set);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc
index 903a5e7c1d7..bc319ce905a 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc
@@ -28,9 +28,10 @@ static void geometry_set_curve_to_mesh(GeometrySet &geometry_set,
const bool fill_caps)
{
const Curves &curves = *geometry_set.get_curves_for_read();
-
const Curves *profile_curves = profile_set.get_curves_for_read();
+ GeometryComponentEditData::remember_deformed_curve_positions_if_necessary(geometry_set);
+
if (profile_curves == nullptr) {
Mesh *mesh = bke::curve_to_wire_mesh(bke::CurvesGeometry::wrap(curves.geometry));
geometry_set.replace_mesh(mesh);
@@ -55,7 +56,7 @@ static void node_geo_exec(GeoNodeExecParams params)
has_curves = true;
geometry_set_curve_to_mesh(geometry_set, profile_set, fill_caps);
}
- geometry_set.keep_only({GEO_COMPONENT_TYPE_MESH, GEO_COMPONENT_TYPE_INSTANCES});
+ geometry_set.keep_only_during_modify({GEO_COMPONENT_TYPE_MESH});
});
params.set_output("Mesh", std::move(curve_set));
diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc
index 7d83b4b3ecb..fd75855bddb 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc
@@ -126,19 +126,18 @@ static GMutableSpan ensure_point_attribute(PointCloudComponent &points,
const AttributeIDRef &attribute_id,
const eCustomDataType data_type)
{
- points.attribute_try_create(attribute_id, ATTR_DOMAIN_POINT, data_type, AttributeInitDefault());
- WriteAttributeLookup attribute = points.attribute_try_get_for_write(attribute_id);
- BLI_assert(attribute);
- return attribute.varray.get_internal_span();
+ return points.attributes_for_write()
+ ->lookup_or_add_for_write(attribute_id, ATTR_DOMAIN_POINT, data_type)
+ .varray.get_internal_span();
}
template<typename T>
static MutableSpan<T> ensure_point_attribute(PointCloudComponent &points,
const AttributeIDRef &attribute_id)
{
- GMutableSpan attribute = ensure_point_attribute(
- points, attribute_id, bke::cpp_type_to_custom_data_type(CPPType::get<T>()));
- return attribute.typed<T>();
+ return points.attributes_for_write()
+ ->lookup_or_add_for_write<T>(attribute_id, ATTR_DOMAIN_POINT)
+ .varray.get_internal_span();
}
namespace {
@@ -317,9 +316,11 @@ static void node_geo_exec(GeoNodeExecParams params)
attribute_outputs.normal_id = StrongAnonymousAttributeID("Normal");
attribute_outputs.rotation_id = StrongAnonymousAttributeID("Rotation");
+ GeometryComponentEditData::remember_deformed_curve_positions_if_necessary(geometry_set);
+
geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
if (!geometry_set.has_curves()) {
- geometry_set.keep_only({GEO_COMPONENT_TYPE_INSTANCES});
+ geometry_set.remove_geometry_during_modify();
return;
}
const std::unique_ptr<CurveEval> curve = curves_to_curve_eval(
@@ -330,7 +331,7 @@ static void node_geo_exec(GeoNodeExecParams params)
const Array<int> offsets = calculate_spline_point_offsets(params, mode, *curve, splines);
const int total_num = offsets.last();
if (total_num == 0) {
- geometry_set.keep_only({GEO_COMPONENT_TYPE_INSTANCES});
+ geometry_set.remove_geometry_during_modify();
return;
}
@@ -356,7 +357,7 @@ static void node_geo_exec(GeoNodeExecParams params)
point_attributes.tangents, point_attributes.normals, point_attributes.rotations);
}
- geometry_set.keep_only({GEO_COMPONENT_TYPE_INSTANCES, GEO_COMPONENT_TYPE_POINT_CLOUD});
+ geometry_set.keep_only_during_modify({GEO_COMPONENT_TYPE_POINT_CLOUD});
});
params.set_output("Points", std::move(geometry_set));
diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc
index c993a3d305d..0932de237a9 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc
@@ -1,5 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
+#include "BKE_curves.hh"
#include "BKE_spline.hh"
#include "BLI_task.hh"
@@ -506,16 +507,17 @@ static void geometry_set_curve_trim(GeometrySet &geometry_set,
CurveComponent &component = geometry_set.get_component_for_write<CurveComponent>();
GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_CURVE};
- const int domain_num = component.attribute_domain_num(ATTR_DOMAIN_CURVE);
+ const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_CURVE);
- fn::FieldEvaluator evaluator{field_context, domain_num};
+ fn::FieldEvaluator evaluator{field_context, domain_size};
evaluator.add(start_field);
evaluator.add(end_field);
evaluator.evaluate();
- const blender::VArray<float> &starts = evaluator.get_evaluated<float>(0);
- const blender::VArray<float> &ends = evaluator.get_evaluated<float>(1);
+ const VArray<float> starts = evaluator.get_evaluated<float>(0);
+ const VArray<float> ends = evaluator.get_evaluated<float>(1);
- std::unique_ptr<CurveEval> curve = curves_to_curve_eval(*geometry_set.get_curves_for_read());
+ const Curves &src_curves_id = *geometry_set.get_curves_for_read();
+ std::unique_ptr<CurveEval> curve = curves_to_curve_eval(src_curves_id);
MutableSpan<SplinePtr> splines = curve->splines();
threading::parallel_for(splines.index_range(), 128, [&](IndexRange range) {
@@ -566,7 +568,9 @@ static void geometry_set_curve_trim(GeometrySet &geometry_set,
}
});
- geometry_set.replace_curves(curve_eval_to_curves(*curve));
+ Curves *dst_curves_id = curve_eval_to_curves(*curve);
+ bke::curves_copy_parameters(src_curves_id, *dst_curves_id);
+ geometry_set.replace_curves(dst_curves_id);
}
static void node_geo_exec(GeoNodeExecParams params)
@@ -575,6 +579,7 @@ static void node_geo_exec(GeoNodeExecParams params)
const GeometryNodeCurveSampleMode mode = (GeometryNodeCurveSampleMode)storage.mode;
GeometrySet geometry_set = params.extract_input<GeometrySet>("Curve");
+ GeometryComponentEditData::remember_deformed_curve_positions_if_necessary(geometry_set);
if (mode == GEO_NODE_CURVE_SAMPLE_FACTOR) {
Field<float> start_field = params.extract_input<Field<float>>("Start");
diff --git a/source/blender/nodes/geometry/nodes/node_geo_deform_curves_on_surface.cc b/source/blender/nodes/geometry/nodes/node_geo_deform_curves_on_surface.cc
new file mode 100644
index 00000000000..8b653296e12
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_deform_curves_on_surface.cc
@@ -0,0 +1,416 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include "BKE_attribute_math.hh"
+#include "BKE_curves.hh"
+#include "BKE_editmesh.h"
+#include "BKE_lib_id.h"
+#include "BKE_mesh.h"
+#include "BKE_mesh_runtime.h"
+#include "BKE_mesh_wrapper.h"
+#include "BKE_modifier.h"
+#include "BKE_type_conversions.hh"
+
+#include "BLI_float3x3.hh"
+#include "BLI_task.hh"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+
+#include "NOD_socket_search_link.hh"
+
+#include "GEO_reverse_uv_sampler.hh"
+
+#include "DEG_depsgraph_query.h"
+
+#include "node_geometry_util.hh"
+
+namespace blender::nodes::node_geo_deform_curves_on_surface_cc {
+
+using attribute_math::mix3;
+using bke::CurvesGeometry;
+using geometry::ReverseUVSampler;
+
+NODE_STORAGE_FUNCS(NodeGeometryCurveTrim)
+
+static void node_declare(NodeDeclarationBuilder &b)
+{
+ b.add_input<decl::Geometry>(N_("Curves")).supported_type(GEO_COMPONENT_TYPE_CURVE);
+ b.add_output<decl::Geometry>(N_("Curves"));
+}
+
+static void deform_curves(const CurvesGeometry &curves,
+ const Mesh &surface_mesh_old,
+ const Mesh &surface_mesh_new,
+ const Span<float2> curve_attachment_uvs,
+ const ReverseUVSampler &reverse_uv_sampler_old,
+ const ReverseUVSampler &reverse_uv_sampler_new,
+ const Span<float3> corner_normals_old,
+ const Span<float3> corner_normals_new,
+ const Span<float3> rest_positions,
+ const float4x4 &surface_to_curves,
+ MutableSpan<float3> r_positions,
+ MutableSpan<float3x3> r_rotations,
+ std::atomic<int> &r_invalid_uv_count)
+{
+ /* Find attachment points on old and new mesh. */
+ const int curves_num = curves.curves_num();
+ Array<ReverseUVSampler::Result> surface_samples_old(curves_num);
+ Array<ReverseUVSampler::Result> surface_samples_new(curves_num);
+ threading::parallel_invoke(
+ [&]() { reverse_uv_sampler_old.sample_many(curve_attachment_uvs, surface_samples_old); },
+ [&]() { reverse_uv_sampler_new.sample_many(curve_attachment_uvs, surface_samples_new); });
+
+ const float4x4 curves_to_surface = surface_to_curves.inverted();
+
+ threading::parallel_for(curves.curves_range(), 256, [&](const IndexRange range) {
+ for (const int curve_i : range) {
+ const ReverseUVSampler::Result &surface_sample_old = surface_samples_old[curve_i];
+ if (surface_sample_old.type != ReverseUVSampler::ResultType::Ok) {
+ r_invalid_uv_count++;
+ continue;
+ }
+ const ReverseUVSampler::Result &surface_sample_new = surface_samples_new[curve_i];
+ if (surface_sample_new.type != ReverseUVSampler::ResultType::Ok) {
+ r_invalid_uv_count++;
+ continue;
+ }
+
+ const MLoopTri &looptri_old = *surface_sample_old.looptri;
+ const MLoopTri &looptri_new = *surface_sample_new.looptri;
+ const float3 &bary_weights_old = surface_sample_old.bary_weights;
+ const float3 &bary_weights_new = surface_sample_new.bary_weights;
+
+ const int corner_0_old = looptri_old.tri[0];
+ const int corner_1_old = looptri_old.tri[1];
+ const int corner_2_old = looptri_old.tri[2];
+
+ const int corner_0_new = looptri_new.tri[0];
+ const int corner_1_new = looptri_new.tri[1];
+ const int corner_2_new = looptri_new.tri[2];
+
+ const int vert_0_old = surface_mesh_old.mloop[corner_0_old].v;
+ const int vert_1_old = surface_mesh_old.mloop[corner_1_old].v;
+ const int vert_2_old = surface_mesh_old.mloop[corner_2_old].v;
+
+ const int vert_0_new = surface_mesh_new.mloop[corner_0_new].v;
+ const int vert_1_new = surface_mesh_new.mloop[corner_1_new].v;
+ const int vert_2_new = surface_mesh_new.mloop[corner_2_new].v;
+
+ const float3 &normal_0_old = corner_normals_old[corner_0_old];
+ const float3 &normal_1_old = corner_normals_old[corner_1_old];
+ const float3 &normal_2_old = corner_normals_old[corner_2_old];
+ const float3 normal_old = math::normalize(
+ mix3(bary_weights_old, normal_0_old, normal_1_old, normal_2_old));
+
+ const float3 &normal_0_new = corner_normals_new[corner_0_new];
+ const float3 &normal_1_new = corner_normals_new[corner_1_new];
+ const float3 &normal_2_new = corner_normals_new[corner_2_new];
+ const float3 normal_new = math::normalize(
+ mix3(bary_weights_new, normal_0_new, normal_1_new, normal_2_new));
+
+ const float3 &pos_0_old = surface_mesh_old.mvert[vert_0_old].co;
+ const float3 &pos_1_old = surface_mesh_old.mvert[vert_1_old].co;
+ const float3 &pos_2_old = surface_mesh_old.mvert[vert_2_old].co;
+ const float3 pos_old = mix3(bary_weights_old, pos_0_old, pos_1_old, pos_2_old);
+
+ const float3 &pos_0_new = surface_mesh_new.mvert[vert_0_new].co;
+ const float3 &pos_1_new = surface_mesh_new.mvert[vert_1_new].co;
+ const float3 &pos_2_new = surface_mesh_new.mvert[vert_2_new].co;
+ const float3 pos_new = mix3(bary_weights_new, pos_0_new, pos_1_new, pos_2_new);
+
+ /* The translation is just the difference between the old and new position on the surface. */
+ const float3 translation = pos_new - pos_old;
+
+ const float3 &rest_pos_0 = rest_positions[vert_0_new];
+ const float3 &rest_pos_1 = rest_positions[vert_1_new];
+
+ /* The tangent reference direction is used to determine the rotation of the surface point
+ * around its normal axis. It's important that the old and new tangent reference are computed
+ * in a consistent way. If the surface has not been rotated, the old and new tangent
+ * reference have to have the same direction. For that reason, the old tangent reference is
+ * computed based on the rest position attribute instead of positions on the old mesh. This
+ * way the old and new tangent reference use the same topology.
+ *
+ * TODO: Figure out if this can be smoothly interpolated across the surface as well.
+ * Currently, this is a source of discontinuity in the deformation, because the vector
+ * changes instantly from one triangle to the next. */
+ const float3 tangent_reference_dir_old = rest_pos_1 - rest_pos_0;
+ const float3 tangent_reference_dir_new = pos_1_new - pos_0_new;
+
+ /* Compute first local tangent based on the (potentially smoothed) normal and the tangent
+ * reference. */
+ const float3 tangent_x_old = math::normalize(
+ math::cross(normal_old, tangent_reference_dir_old));
+ const float3 tangent_x_new = math::normalize(
+ math::cross(normal_new, tangent_reference_dir_new));
+
+ /* The second tangent defined by the normal and first tangent. */
+ const float3 tangent_y_old = math::normalize(math::cross(normal_old, tangent_x_old));
+ const float3 tangent_y_new = math::normalize(math::cross(normal_new, tangent_x_new));
+
+ /* Construct rotation matrix that encodes the orientation of the old surface position. */
+ float3x3 rotation_old;
+ copy_v3_v3(rotation_old.values[0], tangent_x_old);
+ copy_v3_v3(rotation_old.values[1], tangent_y_old);
+ copy_v3_v3(rotation_old.values[2], normal_old);
+
+ /* Construct rotation matrix that encodes the orientation of the new surface position. */
+ float3x3 rotation_new;
+ copy_v3_v3(rotation_new.values[0], tangent_x_new);
+ copy_v3_v3(rotation_new.values[1], tangent_y_new);
+ copy_v3_v3(rotation_new.values[2], normal_new);
+
+ /* Can use transpose instead of inverse because the matrix is orthonormal. In the case of
+ * zero-area triangles, the matrix would not be orthonormal, but in this case, none of this
+ * works anyway. */
+ const float3x3 rotation_old_inv = rotation_old.transposed();
+
+ /* Compute a rotation matrix that rotates points from the old to the new surface
+ * orientation. */
+ const float3x3 rotation = rotation_new * rotation_old_inv;
+ float4x4 rotation_4x4;
+ copy_m4_m3(rotation_4x4.values, rotation.values);
+
+ /* Construction transformation matrix for this surface position that includes rotation and
+ * translation. */
+ float4x4 surface_transform = float4x4::identity();
+ /* Subtract and add #pos_old, so that the rotation origin is the position on the surface. */
+ sub_v3_v3(surface_transform.values[3], pos_old);
+ mul_m4_m4_pre(surface_transform.values, rotation_4x4.values);
+ add_v3_v3(surface_transform.values[3], pos_old);
+ add_v3_v3(surface_transform.values[3], translation);
+
+ /* Change the basis of the transformation so to that it can be applied in the local space of
+ * the curves. */
+ const float4x4 curve_transform = surface_to_curves * surface_transform * curves_to_surface;
+
+ /* Actually transform all points. */
+ const IndexRange points = curves.points_for_curve(curve_i);
+ for (const int point_i : points) {
+ const float3 old_point_pos = r_positions[point_i];
+ const float3 new_point_pos = curve_transform * old_point_pos;
+ r_positions[point_i] = new_point_pos;
+ }
+
+ if (!r_rotations.is_empty()) {
+ for (const int point_i : points) {
+ r_rotations[point_i] = rotation * r_rotations[point_i];
+ }
+ }
+ }
+ });
+}
+
+static void node_geo_exec(GeoNodeExecParams params)
+{
+ GeometrySet curves_geometry = params.extract_input<GeometrySet>("Curves");
+
+ Mesh *surface_mesh_orig = nullptr;
+ bool free_suface_mesh_orig = false;
+ BLI_SCOPED_DEFER([&]() {
+ if (free_suface_mesh_orig) {
+ BKE_id_free(nullptr, surface_mesh_orig);
+ }
+ });
+
+ auto pass_through_input = [&]() { params.set_output("Curves", std::move(curves_geometry)); };
+
+ const Object *self_ob_eval = params.self_object();
+ if (self_ob_eval == nullptr || self_ob_eval->type != OB_CURVES) {
+ pass_through_input();
+ return;
+ }
+ const Curves *self_curves_eval = static_cast<const Curves *>(self_ob_eval->data);
+ if (self_curves_eval->surface_uv_map == nullptr || self_curves_eval->surface_uv_map[0] == '\0') {
+ pass_through_input();
+ const char *message = TIP_("Surface UV map not defined");
+ params.error_message_add(NodeWarningType::Error, message);
+ return;
+ }
+ /* Take surface information from self-object. */
+ Object *surface_ob_eval = self_curves_eval->surface;
+ const StringRefNull uv_map_name = self_curves_eval->surface_uv_map;
+ const StringRefNull rest_position_name = "rest_position";
+
+ if (!curves_geometry.has_curves()) {
+ pass_through_input();
+ return;
+ }
+ if (surface_ob_eval == nullptr || surface_ob_eval->type != OB_MESH) {
+ pass_through_input();
+ params.error_message_add(NodeWarningType::Error, "Curves not attached to a surface");
+ return;
+ }
+ Object *surface_ob_orig = DEG_get_original_object(surface_ob_eval);
+ Mesh &surface_object_data = *static_cast<Mesh *>(surface_ob_orig->data);
+
+ if (BMEditMesh *em = surface_object_data.edit_mesh) {
+ surface_mesh_orig = BKE_mesh_from_bmesh_for_eval_nomain(em->bm, NULL, &surface_object_data);
+ free_suface_mesh_orig = true;
+ }
+ else {
+ surface_mesh_orig = &surface_object_data;
+ }
+ Mesh *surface_mesh_eval = BKE_modifier_get_evaluated_mesh_from_evaluated_object(surface_ob_eval);
+ if (surface_mesh_eval == nullptr) {
+ pass_through_input();
+ params.error_message_add(NodeWarningType::Error, "Surface has no mesh");
+ return;
+ }
+
+ BKE_mesh_wrapper_ensure_mdata(surface_mesh_eval);
+
+ const AttributeAccessor mesh_attributes_eval = bke::mesh_attributes(*surface_mesh_eval);
+ const AttributeAccessor mesh_attributes_orig = bke::mesh_attributes(*surface_mesh_orig);
+
+ Curves &curves_id = *curves_geometry.get_curves_for_write();
+ CurvesGeometry &curves = CurvesGeometry::wrap(curves_id.geometry);
+
+ if (!mesh_attributes_eval.contains(uv_map_name)) {
+ pass_through_input();
+ char *message = BLI_sprintfN(TIP_("Evaluated surface missing UV map: %s"),
+ uv_map_name.c_str());
+ params.error_message_add(NodeWarningType::Error, message);
+ MEM_freeN(message);
+ return;
+ }
+ if (!mesh_attributes_orig.contains(uv_map_name)) {
+ pass_through_input();
+ char *message = BLI_sprintfN(TIP_("Original surface missing UV map: %s"), uv_map_name.c_str());
+ params.error_message_add(NodeWarningType::Error, message);
+ MEM_freeN(message);
+ return;
+ }
+ if (!mesh_attributes_eval.contains(rest_position_name)) {
+ pass_through_input();
+ params.error_message_add(NodeWarningType::Error,
+ TIP_("Evaluated surface missing attribute: rest_position"));
+ return;
+ }
+ if (curves.surface_uv_coords().is_empty() && curves.curves_num() > 0) {
+ pass_through_input();
+ params.error_message_add(NodeWarningType::Error,
+ TIP_("Curves are not attached to any UV map"));
+ return;
+ }
+ const VArraySpan<float2> uv_map_orig = mesh_attributes_orig.lookup<float2>(uv_map_name,
+ ATTR_DOMAIN_CORNER);
+ const VArraySpan<float2> uv_map_eval = mesh_attributes_eval.lookup<float2>(uv_map_name,
+ ATTR_DOMAIN_CORNER);
+ const VArraySpan<float3> rest_positions = mesh_attributes_eval.lookup<float3>(rest_position_name,
+ ATTR_DOMAIN_POINT);
+ const Span<float2> surface_uv_coords = curves.surface_uv_coords();
+
+ const Span<MLoopTri> looptris_orig{BKE_mesh_runtime_looptri_ensure(surface_mesh_orig),
+ BKE_mesh_runtime_looptri_len(surface_mesh_orig)};
+ const Span<MLoopTri> looptris_eval{BKE_mesh_runtime_looptri_ensure(surface_mesh_eval),
+ BKE_mesh_runtime_looptri_len(surface_mesh_eval)};
+ const ReverseUVSampler reverse_uv_sampler_orig{uv_map_orig, looptris_orig};
+ const ReverseUVSampler reverse_uv_sampler_eval{uv_map_eval, looptris_eval};
+
+ /* Retrieve face corner normals from each mesh. It's necessary to use face corner normals
+ * because face normals or vertex normals may lose information (custom normals, auto smooth) in
+ * some cases. It isn't yet possible to retrieve lazily calculated face corner normals from a
+ * const mesh, so they are calculated here every time. */
+ Array<float3> corner_normals_orig(surface_mesh_orig->totloop);
+ Array<float3> corner_normals_eval(surface_mesh_eval->totloop);
+ BKE_mesh_calc_normals_split_ex(
+ surface_mesh_orig, nullptr, reinterpret_cast<float(*)[3]>(corner_normals_orig.data()));
+ BKE_mesh_calc_normals_split_ex(
+ surface_mesh_eval, nullptr, reinterpret_cast<float(*)[3]>(corner_normals_eval.data()));
+
+ std::atomic<int> invalid_uv_count = 0;
+
+ const bke::CurvesSurfaceTransforms transforms{*self_ob_eval, surface_ob_eval};
+
+ bke::CurvesEditHints *edit_hints = curves_geometry.get_curve_edit_hints_for_write();
+ MutableSpan<float3> edit_hint_positions;
+ MutableSpan<float3x3> edit_hint_rotations;
+ if (edit_hints != nullptr) {
+ if (edit_hints->positions.has_value()) {
+ edit_hint_positions = *edit_hints->positions;
+ }
+ if (!edit_hints->deform_mats.has_value()) {
+ edit_hints->deform_mats.emplace(edit_hints->curves_id_orig.geometry.point_num,
+ float3x3::identity());
+ edit_hints->deform_mats->fill(float3x3::identity());
+ }
+ edit_hint_rotations = *edit_hints->deform_mats;
+ }
+
+ if (edit_hint_positions.is_empty()) {
+ deform_curves(curves,
+ *surface_mesh_orig,
+ *surface_mesh_eval,
+ surface_uv_coords,
+ reverse_uv_sampler_orig,
+ reverse_uv_sampler_eval,
+ corner_normals_orig,
+ corner_normals_eval,
+ rest_positions,
+ transforms.surface_to_curves,
+ curves.positions_for_write(),
+ edit_hint_rotations,
+ invalid_uv_count);
+ }
+ else {
+ /* First deform the actual curves in the input geometry. */
+ deform_curves(curves,
+ *surface_mesh_orig,
+ *surface_mesh_eval,
+ surface_uv_coords,
+ reverse_uv_sampler_orig,
+ reverse_uv_sampler_eval,
+ corner_normals_orig,
+ corner_normals_eval,
+ rest_positions,
+ transforms.surface_to_curves,
+ curves.positions_for_write(),
+ {},
+ invalid_uv_count);
+ /* Then also deform edit curve information for use in sculpt mode. */
+ const CurvesGeometry &curves_orig = CurvesGeometry::wrap(edit_hints->curves_id_orig.geometry);
+ deform_curves(curves_orig,
+ *surface_mesh_orig,
+ *surface_mesh_eval,
+ surface_uv_coords,
+ reverse_uv_sampler_orig,
+ reverse_uv_sampler_eval,
+ corner_normals_orig,
+ corner_normals_eval,
+ rest_positions,
+ transforms.surface_to_curves,
+ edit_hint_positions,
+ edit_hint_rotations,
+ invalid_uv_count);
+ }
+
+ curves.tag_positions_changed();
+
+ if (invalid_uv_count) {
+ char *message = BLI_sprintfN(TIP_("Invalid surface UVs on %d curves"),
+ invalid_uv_count.load());
+ params.error_message_add(NodeWarningType::Warning, message);
+ MEM_freeN(message);
+ }
+
+ params.set_output("Curves", curves_geometry);
+}
+
+} // namespace blender::nodes::node_geo_deform_curves_on_surface_cc
+
+void register_node_type_geo_deform_curves_on_surface()
+{
+ namespace file_ns = blender::nodes::node_geo_deform_curves_on_surface_cc;
+
+ static bNodeType ntype;
+ geo_node_type_base(
+ &ntype, GEO_NODE_DEFORM_CURVES_ON_SURFACE, "Deform Curves on Surface", NODE_CLASS_GEOMETRY);
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
+ ntype.declare = file_ns::node_declare;
+ node_type_size(&ntype, 170, 120, 700);
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc b/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc
index 352411dd8f5..b74b4e45199 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc
@@ -43,13 +43,13 @@ static void copy_data_based_on_map(Span<T> src, MutableSpan<T> dst, Span<int> in
* Copies the attributes with a domain in `domains` to `result_component`.
*/
static void copy_attributes(const Map<AttributeIDRef, AttributeKind> &attributes,
- const GeometryComponent &in_component,
- GeometryComponent &result_component,
+ const bke::AttributeAccessor src_attributes,
+ bke::MutableAttributeAccessor dst_attributes,
const Span<eAttrDomain> domains)
{
for (Map<AttributeIDRef, AttributeKind>::Item entry : attributes.items()) {
const AttributeIDRef attribute_id = entry.key;
- ReadAttributeLookup attribute = in_component.attribute_try_get_for_read(attribute_id);
+ GAttributeReader attribute = src_attributes.lookup(attribute_id);
if (!attribute) {
continue;
}
@@ -60,7 +60,7 @@ static void copy_attributes(const Map<AttributeIDRef, AttributeKind> &attributes
}
const eCustomDataType data_type = bke::cpp_type_to_custom_data_type(attribute.varray.type());
- OutputAttribute result_attribute = result_component.attribute_try_get_for_output_only(
+ GSpanAttributeWriter result_attribute = dst_attributes.lookup_or_add_for_write_only_span(
attribute_id, attribute.domain, data_type);
if (!result_attribute) {
@@ -69,11 +69,11 @@ static void copy_attributes(const Map<AttributeIDRef, AttributeKind> &attributes
attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
using T = decltype(dummy);
- VArray_Span<T> span{attribute.varray.typed<T>()};
- MutableSpan<T> out_span = result_attribute.as_span<T>();
+ VArraySpan<T> span{attribute.varray.typed<T>()};
+ MutableSpan<T> out_span = result_attribute.span.typed<T>();
out_span.copy_from(span);
});
- result_attribute.save();
+ result_attribute.finish();
}
}
@@ -82,14 +82,14 @@ static void copy_attributes(const Map<AttributeIDRef, AttributeKind> &attributes
* the mask to `result_component`.
*/
static void copy_attributes_based_on_mask(const Map<AttributeIDRef, AttributeKind> &attributes,
- const GeometryComponent &in_component,
- GeometryComponent &result_component,
+ const bke::AttributeAccessor src_attributes,
+ bke::MutableAttributeAccessor dst_attributes,
const eAttrDomain domain,
const IndexMask mask)
{
for (Map<AttributeIDRef, AttributeKind>::Item entry : attributes.items()) {
const AttributeIDRef attribute_id = entry.key;
- ReadAttributeLookup attribute = in_component.attribute_try_get_for_read(attribute_id);
+ GAttributeReader attribute = src_attributes.lookup(attribute_id);
if (!attribute) {
continue;
}
@@ -100,7 +100,7 @@ static void copy_attributes_based_on_mask(const Map<AttributeIDRef, AttributeKin
}
const eCustomDataType data_type = bke::cpp_type_to_custom_data_type(attribute.varray.type());
- OutputAttribute result_attribute = result_component.attribute_try_get_for_output_only(
+ GSpanAttributeWriter result_attribute = dst_attributes.lookup_or_add_for_write_only_span(
attribute_id, attribute.domain, data_type);
if (!result_attribute) {
@@ -109,23 +109,23 @@ static void copy_attributes_based_on_mask(const Map<AttributeIDRef, AttributeKin
attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
using T = decltype(dummy);
- VArray_Span<T> span{attribute.varray.typed<T>()};
- MutableSpan<T> out_span = result_attribute.as_span<T>();
+ VArraySpan<T> span{attribute.varray.typed<T>()};
+ MutableSpan<T> out_span = result_attribute.span.typed<T>();
copy_data_based_on_mask(span, out_span, mask);
});
- result_attribute.save();
+ result_attribute.finish();
}
}
static void copy_attributes_based_on_map(const Map<AttributeIDRef, AttributeKind> &attributes,
- const GeometryComponent &in_component,
- GeometryComponent &result_component,
+ const bke::AttributeAccessor src_attributes,
+ bke::MutableAttributeAccessor dst_attributes,
const eAttrDomain domain,
const Span<int> index_map)
{
for (Map<AttributeIDRef, AttributeKind>::Item entry : attributes.items()) {
const AttributeIDRef attribute_id = entry.key;
- ReadAttributeLookup attribute = in_component.attribute_try_get_for_read(attribute_id);
+ GAttributeReader attribute = src_attributes.lookup(attribute_id);
if (!attribute) {
continue;
}
@@ -136,7 +136,7 @@ static void copy_attributes_based_on_map(const Map<AttributeIDRef, AttributeKind
}
const eCustomDataType data_type = bke::cpp_type_to_custom_data_type(attribute.varray.type());
- OutputAttribute result_attribute = result_component.attribute_try_get_for_output_only(
+ GSpanAttributeWriter result_attribute = dst_attributes.lookup_or_add_for_write_only_span(
attribute_id, attribute.domain, data_type);
if (!result_attribute) {
@@ -145,17 +145,17 @@ static void copy_attributes_based_on_map(const Map<AttributeIDRef, AttributeKind
attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
using T = decltype(dummy);
- VArray_Span<T> span{attribute.varray.typed<T>()};
- MutableSpan<T> out_span = result_attribute.as_span<T>();
+ VArraySpan<T> span{attribute.varray.typed<T>()};
+ MutableSpan<T> out_span = result_attribute.span.typed<T>();
copy_data_based_on_map(span, out_span, index_map);
});
- result_attribute.save();
+ result_attribute.finish();
}
}
static void copy_face_corner_attributes(const Map<AttributeIDRef, AttributeKind> &attributes,
- const GeometryComponent &in_component,
- GeometryComponent &out_component,
+ const bke::AttributeAccessor src_attributes,
+ bke::MutableAttributeAccessor dst_attributes,
const int selected_loops_num,
const Span<int> selected_poly_indices,
const Mesh &mesh_in)
@@ -171,7 +171,7 @@ static void copy_face_corner_attributes(const Map<AttributeIDRef, AttributeKind>
}
}
copy_attributes_based_on_mask(
- attributes, in_component, out_component, ATTR_DOMAIN_CORNER, IndexMask(indices));
+ attributes, src_attributes, dst_attributes, ATTR_DOMAIN_CORNER, IndexMask(indices));
}
static void copy_masked_vertices_to_new_mesh(const Mesh &src_mesh,
@@ -319,7 +319,7 @@ static void delete_curves_selection(GeometrySet &geometry_set,
const CurveComponent &src_component = *geometry_set.get_component_for_read<CurveComponent>();
GeometryComponentFieldContext field_context{src_component, selection_domain};
- const int domain_num = src_component.attribute_domain_num(selection_domain);
+ const int domain_num = src_component.attribute_domain_size(selection_domain);
fn::FieldEvaluator evaluator{field_context, domain_num};
evaluator.set_selection(selection_field);
evaluator.evaluate();
@@ -351,7 +351,7 @@ static void separate_point_cloud_selection(GeometrySet &geometry_set,
*geometry_set.get_component_for_read<PointCloudComponent>();
GeometryComponentFieldContext field_context{src_points, ATTR_DOMAIN_POINT};
- fn::FieldEvaluator evaluator{field_context, src_points.attribute_domain_num(ATTR_DOMAIN_POINT)};
+ fn::FieldEvaluator evaluator{field_context, src_points.attribute_domain_size(ATTR_DOMAIN_POINT)};
evaluator.set_selection(selection_field);
evaluator.evaluate();
const IndexMask selection = evaluator.get_evaluated_selection_as_mask();
@@ -362,14 +362,15 @@ static void separate_point_cloud_selection(GeometrySet &geometry_set,
PointCloud *pointcloud = BKE_pointcloud_new_nomain(selection.size());
- PointCloudComponent dst_points;
- dst_points.replace(pointcloud, GeometryOwnershipType::Editable);
-
Map<AttributeIDRef, AttributeKind> attributes;
geometry_set.gather_attributes_for_propagation(
{GEO_COMPONENT_TYPE_POINT_CLOUD}, GEO_COMPONENT_TYPE_POINT_CLOUD, false, attributes);
- copy_attributes_based_on_mask(attributes, src_points, dst_points, ATTR_DOMAIN_POINT, selection);
+ copy_attributes_based_on_mask(attributes,
+ bke::pointcloud_attributes(*src_points.get_for_read()),
+ bke::pointcloud_attributes_for_write(*pointcloud),
+ ATTR_DOMAIN_POINT,
+ selection);
geometry_set.replace_pointcloud(pointcloud);
}
@@ -379,8 +380,7 @@ static void delete_selected_instances(GeometrySet &geometry_set,
InstancesComponent &instances = geometry_set.get_component_for_write<InstancesComponent>();
GeometryComponentFieldContext field_context{instances, ATTR_DOMAIN_INSTANCE};
- fn::FieldEvaluator evaluator{field_context,
- instances.attribute_domain_num(ATTR_DOMAIN_INSTANCE)};
+ fn::FieldEvaluator evaluator{field_context, instances.instances_num()};
evaluator.set_selection(selection_field);
evaluator.evaluate();
const IndexMask selection = evaluator.get_evaluated_selection_as_mask();
@@ -815,7 +815,7 @@ static void compute_selected_mesh_data_from_poly_selection(const Mesh &mesh,
* Keep the parts of the mesh that are in the selection.
*/
static void do_mesh_separation(GeometrySet &geometry_set,
- const MeshComponent &in_component,
+ const Mesh &mesh_in,
const Span<bool> selection,
const eAttrDomain domain,
const GeometryNodeDeleteGeometryMode mode)
@@ -826,9 +826,7 @@ static void do_mesh_separation(GeometrySet &geometry_set,
int selected_polys_num = 0;
int selected_loops_num = 0;
- const Mesh &mesh_in = *in_component.get_for_read();
Mesh *mesh_out;
- MeshComponent out_component;
Map<AttributeIDRef, AttributeKind> attributes;
geometry_set.gather_attributes_for_propagation(
@@ -890,7 +888,6 @@ static void do_mesh_separation(GeometrySet &geometry_set,
0,
selected_loops_num,
selected_polys_num);
- out_component.replace(mesh_out, GeometryOwnershipType::Editable);
/* Copy the selected parts of the mesh over to the new mesh. */
copy_masked_vertices_to_new_mesh(mesh_in, *mesh_out, vertex_map);
@@ -899,18 +896,24 @@ static void do_mesh_separation(GeometrySet &geometry_set,
mesh_in, *mesh_out, vertex_map, edge_map, selected_poly_indices, new_loop_starts);
/* Copy attributes. */
- copy_attributes_based_on_map(
- attributes, in_component, out_component, ATTR_DOMAIN_POINT, vertex_map);
- copy_attributes_based_on_map(
- attributes, in_component, out_component, ATTR_DOMAIN_EDGE, edge_map);
+ copy_attributes_based_on_map(attributes,
+ bke::mesh_attributes(mesh_in),
+ bke::mesh_attributes_for_write(*mesh_out),
+ ATTR_DOMAIN_POINT,
+ vertex_map);
+ copy_attributes_based_on_map(attributes,
+ bke::mesh_attributes(mesh_in),
+ bke::mesh_attributes_for_write(*mesh_out),
+ ATTR_DOMAIN_EDGE,
+ edge_map);
copy_attributes_based_on_mask(attributes,
- in_component,
- out_component,
+ bke::mesh_attributes(mesh_in),
+ bke::mesh_attributes_for_write(*mesh_out),
ATTR_DOMAIN_FACE,
IndexMask(Vector<int64_t>(selected_poly_indices.as_span())));
copy_face_corner_attributes(attributes,
- in_component,
- out_component,
+ bke::mesh_attributes(mesh_in),
+ bke::mesh_attributes_for_write(*mesh_out),
selected_loops_num,
selected_poly_indices,
mesh_in);
@@ -962,7 +965,6 @@ static void do_mesh_separation(GeometrySet &geometry_set,
0,
selected_loops_num,
selected_polys_num);
- out_component.replace(mesh_out, GeometryOwnershipType::Editable);
/* Copy the selected parts of the mesh over to the new mesh. */
memcpy(mesh_out->mvert, mesh_in.mvert, mesh_in.totvert * sizeof(MVert));
@@ -971,17 +973,23 @@ static void do_mesh_separation(GeometrySet &geometry_set,
mesh_in, *mesh_out, edge_map, selected_poly_indices, new_loop_starts);
/* Copy attributes. */
- copy_attributes(attributes, in_component, out_component, {ATTR_DOMAIN_POINT});
- copy_attributes_based_on_map(
- attributes, in_component, out_component, ATTR_DOMAIN_EDGE, edge_map);
+ copy_attributes(attributes,
+ bke::mesh_attributes(mesh_in),
+ bke::mesh_attributes_for_write(*mesh_out),
+ {ATTR_DOMAIN_POINT});
+ copy_attributes_based_on_map(attributes,
+ bke::mesh_attributes(mesh_in),
+ bke::mesh_attributes_for_write(*mesh_out),
+ ATTR_DOMAIN_EDGE,
+ edge_map);
copy_attributes_based_on_mask(attributes,
- in_component,
- out_component,
+ bke::mesh_attributes(mesh_in),
+ bke::mesh_attributes_for_write(*mesh_out),
ATTR_DOMAIN_FACE,
IndexMask(Vector<int64_t>(selected_poly_indices.as_span())));
copy_face_corner_attributes(attributes,
- in_component,
- out_component,
+ bke::mesh_attributes(mesh_in),
+ bke::mesh_attributes_for_write(*mesh_out),
selected_loops_num,
selected_poly_indices,
mesh_in);
@@ -1020,7 +1028,6 @@ static void do_mesh_separation(GeometrySet &geometry_set,
}
mesh_out = BKE_mesh_new_nomain_from_template(
&mesh_in, mesh_in.totvert, mesh_in.totedge, 0, selected_loops_num, selected_polys_num);
- out_component.replace(mesh_out, GeometryOwnershipType::Editable);
/* Copy the selected parts of the mesh over to the new mesh. */
memcpy(mesh_out->mvert, mesh_in.mvert, mesh_in.totvert * sizeof(MVert));
@@ -1028,16 +1035,18 @@ static void do_mesh_separation(GeometrySet &geometry_set,
copy_masked_polys_to_new_mesh(mesh_in, *mesh_out, selected_poly_indices, new_loop_starts);
/* Copy attributes. */
- copy_attributes(
- attributes, in_component, out_component, {ATTR_DOMAIN_POINT, ATTR_DOMAIN_EDGE});
+ copy_attributes(attributes,
+ bke::mesh_attributes(mesh_in),
+ bke::mesh_attributes_for_write(*mesh_out),
+ {ATTR_DOMAIN_POINT, ATTR_DOMAIN_EDGE});
copy_attributes_based_on_mask(attributes,
- in_component,
- out_component,
+ bke::mesh_attributes(mesh_in),
+ bke::mesh_attributes_for_write(*mesh_out),
ATTR_DOMAIN_FACE,
IndexMask(Vector<int64_t>(selected_poly_indices.as_span())));
copy_face_corner_attributes(attributes,
- in_component,
- out_component,
+ bke::mesh_attributes(mesh_in),
+ bke::mesh_attributes_for_write(*mesh_out),
selected_loops_num,
selected_poly_indices,
mesh_in);
@@ -1058,18 +1067,19 @@ static void separate_mesh_selection(GeometrySet &geometry_set,
GeometryComponentFieldContext field_context{src_component, selection_domain};
fn::FieldEvaluator evaluator{field_context,
- src_component.attribute_domain_num(selection_domain)};
+ src_component.attribute_domain_size(selection_domain)};
evaluator.add(selection_field);
evaluator.evaluate();
const VArray<bool> selection = evaluator.get_evaluated<bool>(0);
/* Check if there is anything to delete. */
- if (selection.is_single() && selection.get_internal_single()) {
+ if (selection.is_empty() || (selection.is_single() && selection.get_internal_single())) {
return;
}
- const VArray_Span<bool> selection_span{selection};
+ const VArraySpan<bool> selection_span{selection};
- do_mesh_separation(geometry_set, src_component, selection_span, selection_domain, mode);
+ do_mesh_separation(
+ geometry_set, *src_component.get_for_read(), selection_span, selection_domain, mode);
}
} // namespace blender::nodes::node_geo_delete_geometry_cc
diff --git a/source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc b/source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc
index f95601813a3..cfb9cbf7e24 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc
@@ -290,31 +290,32 @@ BLI_NOINLINE static void propagate_existing_attributes(
const Span<int> looptri_indices)
{
const Mesh &mesh = *mesh_component.get_for_read();
+ const AttributeAccessor mesh_attributes = *mesh_component.attributes();
+ MutableAttributeAccessor point_attributes = *point_component.attributes_for_write();
for (Map<AttributeIDRef, AttributeKind>::Item entry : attributes.items()) {
const AttributeIDRef attribute_id = entry.key;
const eCustomDataType output_data_type = entry.value.data_type;
- ReadAttributeLookup source_attribute = mesh_component.attribute_try_get_for_read(attribute_id);
+ GAttributeReader source_attribute = mesh_attributes.lookup(attribute_id);
if (!source_attribute) {
continue;
}
/* The output domain is always #ATTR_DOMAIN_POINT, since we are creating a point cloud. */
- OutputAttribute attribute_out = point_component.attribute_try_get_for_output_only(
+ GSpanAttributeWriter attribute_out = point_attributes.lookup_or_add_for_write_only_span(
attribute_id, ATTR_DOMAIN_POINT, output_data_type);
if (!attribute_out) {
continue;
}
- GMutableSpan out_span = attribute_out.as_span();
interpolate_attribute(mesh,
bary_coords,
looptri_indices,
source_attribute.domain,
source_attribute.varray,
- out_span);
- attribute_out.save();
+ attribute_out.span);
+ attribute_out.finish();
}
}
@@ -331,25 +332,21 @@ BLI_NOINLINE static void compute_attribute_outputs(const MeshComponent &mesh_com
const Span<int> looptri_indices,
const AttributeOutputs &attribute_outputs)
{
- OutputAttribute_Typed<int> id_attribute = point_component.attribute_try_get_for_output_only<int>(
- "id", ATTR_DOMAIN_POINT);
- MutableSpan<int> ids = id_attribute.as_span();
+ MutableAttributeAccessor pointcloud_attributes = *point_component.attributes_for_write();
- OutputAttribute_Typed<float3> normal_attribute;
- OutputAttribute_Typed<float3> rotation_attribute;
+ SpanAttributeWriter<int> ids = pointcloud_attributes.lookup_or_add_for_write_only_span<int>(
+ "id", ATTR_DOMAIN_POINT);
- MutableSpan<float3> normals;
- MutableSpan<float3> rotations;
+ SpanAttributeWriter<float3> normals;
+ SpanAttributeWriter<float3> rotations;
if (attribute_outputs.normal_id) {
- normal_attribute = point_component.attribute_try_get_for_output_only<float3>(
+ normals = pointcloud_attributes.lookup_or_add_for_write_only_span<float3>(
attribute_outputs.normal_id.get(), ATTR_DOMAIN_POINT);
- normals = normal_attribute.as_span();
}
if (attribute_outputs.rotation_id) {
- rotation_attribute = point_component.attribute_try_get_for_output_only<float3>(
+ rotations = pointcloud_attributes.lookup_or_add_for_write_only_span<float3>(
attribute_outputs.rotation_id.get(), ATTR_DOMAIN_POINT);
- rotations = rotation_attribute.as_span();
}
const Mesh &mesh = *mesh_component.get_for_read();
@@ -368,27 +365,27 @@ BLI_NOINLINE static void compute_attribute_outputs(const MeshComponent &mesh_com
const float3 v1_pos = float3(mesh.mvert[v1_index].co);
const float3 v2_pos = float3(mesh.mvert[v2_index].co);
- ids[i] = noise::hash(noise::hash_float(bary_coord), looptri_index);
+ ids.span[i] = noise::hash(noise::hash_float(bary_coord), looptri_index);
float3 normal;
- if (!normals.is_empty() || !rotations.is_empty()) {
+ if (!normals.span.is_empty() || !rotations.span.is_empty()) {
normal_tri_v3(normal, v0_pos, v1_pos, v2_pos);
}
- if (!normals.is_empty()) {
- normals[i] = normal;
+ if (!normals.span.is_empty()) {
+ normals.span[i] = normal;
}
- if (!rotations.is_empty()) {
- rotations[i] = normal_to_euler_rotation(normal);
+ if (!rotations.span.is_empty()) {
+ rotations.span[i] = normal_to_euler_rotation(normal);
}
}
- id_attribute.save();
+ ids.finish();
- if (normal_attribute) {
- normal_attribute.save();
+ if (normals) {
+ normals.finish();
}
- if (rotation_attribute) {
- rotation_attribute.save();
+ if (rotations) {
+ rotations.finish();
}
}
@@ -398,11 +395,11 @@ static Array<float> calc_full_density_factors_with_selection(const MeshComponent
{
const eAttrDomain attribute_domain = ATTR_DOMAIN_CORNER;
GeometryComponentFieldContext field_context{component, attribute_domain};
- const int domain_num = component.attribute_domain_num(attribute_domain);
+ const int domain_size = component.attribute_domain_size(attribute_domain);
- Array<float> densities(domain_num, 0.0f);
+ Array<float> densities(domain_size, 0.0f);
- fn::FieldEvaluator evaluator{field_context, domain_num};
+ fn::FieldEvaluator evaluator{field_context, domain_size};
evaluator.set_selection(selection_field);
evaluator.add_with_destination(density_field, densities.as_mutable_span());
evaluator.evaluate();
@@ -500,8 +497,17 @@ static void point_distribution_calculate(GeometrySet &geometry_set,
}
PointCloud *pointcloud = BKE_pointcloud_new_nomain(positions.size());
- memcpy(pointcloud->co, positions.data(), sizeof(float3) * positions.size());
- uninitialized_fill_n(pointcloud->radius, pointcloud->totpoint, 0.05f);
+ bke::MutableAttributeAccessor point_attributes = bke::pointcloud_attributes_for_write(
+ *pointcloud);
+ bke::SpanAttributeWriter<float3> point_positions =
+ point_attributes.lookup_or_add_for_write_only_span<float3>("position", ATTR_DOMAIN_POINT);
+ bke::SpanAttributeWriter<float> point_radii =
+ point_attributes.lookup_or_add_for_write_only_span<float>("radius", ATTR_DOMAIN_POINT);
+ point_positions.span.copy_from(positions);
+ point_radii.span.fill(0.05f);
+ point_positions.finish();
+ point_radii.finish();
+
geometry_set.replace_pointcloud(pointcloud);
PointCloudComponent &point_component =
@@ -544,7 +550,7 @@ static void node_geo_exec(GeoNodeExecParams params)
geometry_set, selection_field, method, seed, attribute_outputs, params);
/* Keep instances because the original geometry set may contain instances that are processed as
* well. */
- geometry_set.keep_only({GEO_COMPONENT_TYPE_POINT_CLOUD, GEO_COMPONENT_TYPE_INSTANCES});
+ geometry_set.keep_only_during_modify({GEO_COMPONENT_TYPE_POINT_CLOUD});
});
params.set_output("Points", std::move(geometry_set));
diff --git a/source/blender/nodes/geometry/nodes/node_geo_dual_mesh.cc b/source/blender/nodes/geometry/nodes/node_geo_dual_mesh.cc
index 52156b59c51..76eeee95239 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_dual_mesh.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_dual_mesh.cc
@@ -143,12 +143,12 @@ static void transfer_attributes(
const Span<int> new_to_old_edges_map,
const Span<int> new_to_old_face_corners_map,
const Span<std::pair<int, int>> boundary_vertex_to_relevant_face_map,
- const GeometryComponent &src_component,
- GeometryComponent &dst_component)
+ const AttributeAccessor src_attributes,
+ MutableAttributeAccessor dst_attributes)
{
for (Map<AttributeIDRef, AttributeKind>::Item entry : attributes.items()) {
const AttributeIDRef attribute_id = entry.key;
- ReadAttributeLookup src_attribute = src_component.attribute_try_get_for_read(attribute_id);
+ GAttributeReader src_attribute = src_attributes.lookup(attribute_id);
if (!src_attribute) {
continue;
}
@@ -166,7 +166,7 @@ static void transfer_attributes(
}
const eCustomDataType data_type = bke::cpp_type_to_custom_data_type(
src_attribute.varray.type());
- OutputAttribute dst_attribute = dst_component.attribute_try_get_for_output_only(
+ GSpanAttributeWriter dst_attribute = dst_attributes.lookup_or_add_for_write_only_span(
attribute_id, out_domain, data_type);
if (!dst_attribute) {
@@ -175,8 +175,8 @@ static void transfer_attributes(
attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
using T = decltype(dummy);
- VArray_Span<T> span{src_attribute.varray.typed<T>()};
- MutableSpan<T> dst_span = dst_attribute.as_span<T>();
+ VArraySpan<T> span{src_attribute.varray.typed<T>()};
+ MutableSpan<T> dst_span = dst_attribute.span.typed<T>();
if (src_attribute.domain == ATTR_DOMAIN_FACE) {
dst_span.take_front(span.size()).copy_from(span);
if (keep_boundaries) {
@@ -193,7 +193,7 @@ static void transfer_attributes(
copy_data_based_on_new_to_old_map(span, dst_span, new_to_old_face_corners_map);
}
});
- dst_attribute.save();
+ dst_attribute.finish();
}
}
@@ -872,16 +872,14 @@ static void calc_dual_mesh(GeometrySet &geometry_set,
}
Mesh *mesh_out = BKE_mesh_new_nomain(
vertex_positions.size(), new_edges.size(), 0, loops.size(), loop_lengths.size());
- MeshComponent out_component;
- out_component.replace(mesh_out, GeometryOwnershipType::Editable);
transfer_attributes(attributes,
vertex_types,
keep_boundaries,
new_to_old_edges_map,
new_to_old_face_corners_map,
boundary_vertex_to_relevant_face_map,
- in_component,
- out_component);
+ bke::mesh_attributes(mesh_in),
+ bke::mesh_attributes_for_write(*mesh_out));
int loop_start = 0;
for (const int i : IndexRange(mesh_out->totpoly)) {
diff --git a/source/blender/nodes/geometry/nodes/node_geo_duplicate_elements.cc b/source/blender/nodes/geometry/nodes/node_geo_duplicate_elements.cc
index 691f341b518..c6b0fb4c068 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_duplicate_elements.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_duplicate_elements.cc
@@ -61,13 +61,11 @@ struct IndexAttributes {
* \{ */
static Map<AttributeIDRef, AttributeKind> gather_attributes_without_id(
- const GeometrySet &geometry_set,
- const GeometryComponentType component_type,
- const bool include_instances)
+ const GeometrySet &geometry_set, const GeometryComponentType component_type)
{
Map<AttributeIDRef, AttributeKind> attributes;
geometry_set.gather_attributes_for_propagation(
- {component_type}, component_type, include_instances, attributes);
+ {component_type}, component_type, false, attributes);
attributes.remove("id");
return attributes;
};
@@ -143,23 +141,22 @@ static void threaded_id_offset_copy(const Span<int> offsets,
}
/** Create the copy indices for the duplication domain. */
-static void create_duplicate_index_attribute(GeometryComponent &component,
+static void create_duplicate_index_attribute(bke::MutableAttributeAccessor attributes,
const eAttrDomain output_domain,
const IndexMask selection,
const IndexAttributes &attribute_outputs,
const Span<int> offsets)
{
- OutputAttribute_Typed<int> copy_attribute = component.attribute_try_get_for_output_only<int>(
+ SpanAttributeWriter<int> duplicate_indices = attributes.lookup_or_add_for_write_only_span<int>(
attribute_outputs.duplicate_index.get(), output_domain);
- MutableSpan<int> duplicate_indices = copy_attribute.as_span();
for (const int i : IndexRange(selection.size())) {
const IndexRange range = range_for_offsets_index(offsets, i);
- MutableSpan<int> indices = duplicate_indices.slice(range);
+ MutableSpan<int> indices = duplicate_indices.span.slice(range);
for (const int i : indices.index_range()) {
indices[i] = i;
}
}
- copy_attribute.save();
+ duplicate_indices.finish();
}
/**
@@ -167,62 +164,57 @@ static void create_duplicate_index_attribute(GeometryComponent &component,
* and the duplicate number. This function is used for the point domain elements.
*/
static void copy_stable_id_point(const Span<int> offsets,
- const GeometryComponent &src_component,
- GeometryComponent &dst_component)
+ const bke::AttributeAccessor src_attributes,
+ bke::MutableAttributeAccessor dst_attributes)
{
- ReadAttributeLookup src_attribute = src_component.attribute_try_get_for_read("id");
+ GAttributeReader src_attribute = src_attributes.lookup("id");
if (!src_attribute) {
return;
}
- OutputAttribute dst_attribute = dst_component.attribute_try_get_for_output_only(
+ GSpanAttributeWriter dst_attribute = dst_attributes.lookup_or_add_for_write_only_span(
"id", ATTR_DOMAIN_POINT, CD_PROP_INT32);
if (!dst_attribute) {
return;
}
- VArray_Span<int> src{src_attribute.varray.typed<int>()};
- MutableSpan<int> dst = dst_attribute.as_span<int>();
+ VArraySpan<int> src{src_attribute.varray.typed<int>()};
+ MutableSpan<int> dst = dst_attribute.span.typed<int>();
threaded_id_offset_copy(offsets, src, dst);
- dst_attribute.save();
+ dst_attribute.finish();
}
-/* The attributes for the point (also instance) duplicated elements are stored sequentially
- * (1,1,1,2,2,2,3,3,3,etc) They can be copied by using a simple offset array. For each domain, if
- * elements are ordered differently a custom function is called to copy the attributes.
- */
-
-static void copy_point_attributes_without_id(GeometrySet &geometry_set,
- const GeometryComponentType component_type,
- const bool include_instances,
- const Span<int> offsets,
- const IndexMask selection,
- const GeometryComponent &src_component,
- GeometryComponent &dst_component)
+static void copy_attributes_without_id(GeometrySet &geometry_set,
+ const GeometryComponentType component_type,
+ const eAttrDomain domain,
+ const Span<int> offsets,
+ const IndexMask selection,
+ const bke::AttributeAccessor src_attributes,
+ bke::MutableAttributeAccessor dst_attributes)
{
- Map<AttributeIDRef, AttributeKind> attributes = gather_attributes_without_id(
- geometry_set, component_type, include_instances);
+ const Map<AttributeIDRef, AttributeKind> attributes = gather_attributes_without_id(
+ geometry_set, component_type);
for (const Map<AttributeIDRef, AttributeKind>::Item entry : attributes.items()) {
const AttributeIDRef attribute_id = entry.key;
- ReadAttributeLookup src_attribute = src_component.attribute_try_get_for_read(attribute_id);
- if (!src_attribute || src_attribute.domain != ATTR_DOMAIN_POINT) {
+ GAttributeReader src_attribute = src_attributes.lookup(attribute_id);
+ if (!src_attribute || src_attribute.domain != domain) {
continue;
}
eAttrDomain out_domain = src_attribute.domain;
const eCustomDataType data_type = bke::cpp_type_to_custom_data_type(
src_attribute.varray.type());
- OutputAttribute dst_attribute = dst_component.attribute_try_get_for_output_only(
+ GSpanAttributeWriter dst_attribute = dst_attributes.lookup_or_add_for_write_only_span(
attribute_id, out_domain, data_type);
if (!dst_attribute) {
continue;
}
attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
using T = decltype(dummy);
- VArray_Span<T> src = src_attribute.varray.typed<T>();
- MutableSpan<T> dst = dst_attribute.as_span<T>();
+ VArraySpan<T> src = src_attribute.varray.typed<T>();
+ MutableSpan<T> dst = dst_attribute.span.typed<T>();
threaded_slice_fill<T>(offsets, selection, src, dst);
});
- dst_attribute.save();
+ dst_attribute.finish();
}
}
@@ -237,19 +229,17 @@ static void copy_point_attributes_without_id(GeometrySet &geometry_set,
* copied with an offset fill, otherwise a mapping is used.
*/
static void copy_curve_attributes_without_id(const GeometrySet &geometry_set,
- const CurveComponent &src_component,
const bke::CurvesGeometry &src_curves,
const IndexMask selection,
const Span<int> curve_offsets,
- bke::CurvesGeometry &dst_curves,
- CurveComponent &dst_component)
+ bke::CurvesGeometry &dst_curves)
{
Map<AttributeIDRef, AttributeKind> attributes = gather_attributes_without_id(
- geometry_set, GEO_COMPONENT_TYPE_CURVE, false);
+ geometry_set, GEO_COMPONENT_TYPE_CURVE);
for (const Map<AttributeIDRef, AttributeKind>::Item entry : attributes.items()) {
const AttributeIDRef attribute_id = entry.key;
- ReadAttributeLookup src_attribute = src_component.attribute_try_get_for_read(attribute_id);
+ GAttributeReader src_attribute = src_curves.attributes().lookup(attribute_id);
if (!src_attribute) {
continue;
}
@@ -257,16 +247,17 @@ static void copy_curve_attributes_without_id(const GeometrySet &geometry_set,
eAttrDomain out_domain = src_attribute.domain;
const eCustomDataType data_type = bke::cpp_type_to_custom_data_type(
src_attribute.varray.type());
- OutputAttribute dst_attribute = dst_component.attribute_try_get_for_output_only(
- attribute_id, out_domain, data_type);
+ GSpanAttributeWriter dst_attribute =
+ dst_curves.attributes_for_write().lookup_or_add_for_write_only_span(
+ attribute_id, out_domain, data_type);
if (!dst_attribute) {
continue;
}
attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
using T = decltype(dummy);
- VArray_Span<T> src{src_attribute.varray.typed<T>()};
- MutableSpan<T> dst = dst_attribute.as_span<T>();
+ VArraySpan<T> src{src_attribute.varray.typed<T>()};
+ MutableSpan<T> dst = dst_attribute.span.typed<T>();
switch (out_domain) {
case ATTR_DOMAIN_CURVE:
@@ -287,7 +278,7 @@ static void copy_curve_attributes_without_id(const GeometrySet &geometry_set,
break;
}
});
- dst_attribute.save();
+ dst_attribute.finish();
}
}
@@ -300,22 +291,21 @@ static void copy_curve_attributes_without_id(const GeometrySet &geometry_set,
static void copy_stable_id_curves(const bke::CurvesGeometry &src_curves,
const IndexMask selection,
const Span<int> curve_offsets,
- const CurveComponent &src_component,
- bke::CurvesGeometry &dst_curves,
- CurveComponent &dst_component)
+ bke::CurvesGeometry &dst_curves)
{
- ReadAttributeLookup src_attribute = src_component.attribute_try_get_for_read("id");
+ GAttributeReader src_attribute = src_curves.attributes().lookup("id");
if (!src_attribute) {
return;
}
- OutputAttribute dst_attribute = dst_component.attribute_try_get_for_output_only(
- "id", ATTR_DOMAIN_POINT, CD_PROP_INT32);
+ GSpanAttributeWriter dst_attribute =
+ dst_curves.attributes_for_write().lookup_or_add_for_write_only_span(
+ "id", ATTR_DOMAIN_POINT, CD_PROP_INT32);
if (!dst_attribute) {
return;
}
- VArray_Span<int> src{src_attribute.varray.typed<int>()};
- MutableSpan<int> dst = dst_attribute.as_span<int>();
+ VArraySpan<int> src{src_attribute.varray.typed<int>()};
+ MutableSpan<int> dst = dst_attribute.span.typed<int>();
threading::parallel_for(selection.index_range(), 512, [&](IndexRange range) {
for (const int i_selection : range) {
@@ -329,7 +319,7 @@ static void copy_stable_id_curves(const bke::CurvesGeometry &src_curves,
}
}
});
- dst_attribute.save();
+ dst_attribute.finish();
}
static void duplicate_curves(GeometrySet &geometry_set,
@@ -338,10 +328,11 @@ static void duplicate_curves(GeometrySet &geometry_set,
const IndexAttributes &attribute_outputs)
{
if (!geometry_set.has_curves()) {
- geometry_set.keep_only({GEO_COMPONENT_TYPE_INSTANCES});
+ geometry_set.remove_geometry_during_modify();
return;
}
- geometry_set.keep_only({GEO_COMPONENT_TYPE_CURVE, GEO_COMPONENT_TYPE_INSTANCES});
+ geometry_set.keep_only_during_modify({GEO_COMPONENT_TYPE_CURVE});
+ GeometryComponentEditData::remember_deformed_curve_positions_if_necessary(geometry_set);
const CurveComponent &src_component = *geometry_set.get_component_for_read<CurveComponent>();
const Curves &curves_id = *src_component.get_for_read();
@@ -372,6 +363,7 @@ static void duplicate_curves(GeometrySet &geometry_set,
point_offsets.last() = dst_points_num;
Curves *new_curves_id = bke::curves_new_nomain(dst_points_num, dst_curves_num);
+ bke::curves_copy_parameters(curves_id, *new_curves_id);
bke::CurvesGeometry &new_curves = bke::CurvesGeometry::wrap(new_curves_id->geometry);
MutableSpan<int> all_dst_offsets = new_curves.offsets_for_write();
@@ -389,18 +381,16 @@ static void duplicate_curves(GeometrySet &geometry_set,
});
all_dst_offsets.last() = dst_points_num;
- CurveComponent dst_component;
- dst_component.replace(new_curves_id, GeometryOwnershipType::Editable);
+ copy_curve_attributes_without_id(geometry_set, curves, selection, curve_offsets, new_curves);
- copy_curve_attributes_without_id(
- geometry_set, src_component, curves, selection, curve_offsets, new_curves, dst_component);
-
- copy_stable_id_curves(
- curves, selection, curve_offsets, src_component, new_curves, dst_component);
+ copy_stable_id_curves(curves, selection, curve_offsets, new_curves);
if (attribute_outputs.duplicate_index) {
- create_duplicate_index_attribute(
- dst_component, ATTR_DOMAIN_CURVE, selection, attribute_outputs, curve_offsets);
+ create_duplicate_index_attribute(new_curves.attributes_for_write(),
+ ATTR_DOMAIN_CURVE,
+ selection,
+ attribute_outputs,
+ curve_offsets);
}
geometry_set.replace_curves(new_curves_id);
@@ -422,15 +412,15 @@ static void copy_face_attributes_without_id(GeometrySet &geometry_set,
const Span<int> loop_mapping,
const Span<int> offsets,
const IndexMask selection,
- const GeometryComponent &src_component,
- GeometryComponent &dst_component)
+ const bke::AttributeAccessor src_attributes,
+ bke::MutableAttributeAccessor dst_attributes)
{
Map<AttributeIDRef, AttributeKind> attributes = gather_attributes_without_id(
- geometry_set, GEO_COMPONENT_TYPE_MESH, false);
+ geometry_set, GEO_COMPONENT_TYPE_MESH);
for (const Map<AttributeIDRef, AttributeKind>::Item entry : attributes.items()) {
const AttributeIDRef attribute_id = entry.key;
- ReadAttributeLookup src_attribute = src_component.attribute_try_get_for_read(attribute_id);
+ GAttributeReader src_attribute = src_attributes.lookup(attribute_id);
if (!src_attribute) {
continue;
}
@@ -438,7 +428,7 @@ static void copy_face_attributes_without_id(GeometrySet &geometry_set,
eAttrDomain out_domain = src_attribute.domain;
const eCustomDataType data_type = bke::cpp_type_to_custom_data_type(
src_attribute.varray.type());
- OutputAttribute dst_attribute = dst_component.attribute_try_get_for_output_only(
+ GSpanAttributeWriter dst_attribute = dst_attributes.lookup_or_add_for_write_only_span(
attribute_id, out_domain, data_type);
if (!dst_attribute) {
continue;
@@ -446,8 +436,8 @@ static void copy_face_attributes_without_id(GeometrySet &geometry_set,
attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
using T = decltype(dummy);
- VArray_Span<T> src{src_attribute.varray.typed<T>()};
- MutableSpan<T> dst = dst_attribute.as_span<T>();
+ VArraySpan<T> src{src_attribute.varray.typed<T>()};
+ MutableSpan<T> dst = dst_attribute.span.typed<T>();
switch (out_domain) {
case ATTR_DOMAIN_FACE:
@@ -466,7 +456,7 @@ static void copy_face_attributes_without_id(GeometrySet &geometry_set,
break;
}
});
- dst_attribute.save();
+ dst_attribute.finish();
}
}
@@ -481,21 +471,21 @@ static void copy_stable_id_faces(const Mesh &mesh,
const IndexMask selection,
const Span<int> poly_offsets,
const Span<int> vert_mapping,
- const MeshComponent &src_component,
- MeshComponent &dst_component)
+ const bke::AttributeAccessor src_attributes,
+ bke::MutableAttributeAccessor dst_attributes)
{
- ReadAttributeLookup src_attribute = src_component.attribute_try_get_for_read("id");
+ GAttributeReader src_attribute = src_attributes.lookup("id");
if (!src_attribute) {
return;
}
- OutputAttribute dst_attribute = dst_component.attribute_try_get_for_output_only(
+ GSpanAttributeWriter dst_attribute = dst_attributes.lookup_or_add_for_write_only_span(
"id", ATTR_DOMAIN_POINT, CD_PROP_INT32);
if (!dst_attribute) {
return;
}
- VArray_Span<int> src{src_attribute.varray.typed<int>()};
- MutableSpan<int> dst = dst_attribute.as_span<int>();
+ VArraySpan<int> src{src_attribute.varray.typed<int>()};
+ MutableSpan<int> dst = dst_attribute.span.typed<int>();
Span<MPoly> polys(mesh.mpoly, mesh.totpoly);
int loop_index = 0;
@@ -518,7 +508,7 @@ static void copy_stable_id_faces(const Mesh &mesh,
}
}
- dst_attribute.save();
+ dst_attribute.finish();
}
static void duplicate_faces(GeometrySet &geometry_set,
@@ -527,10 +517,10 @@ static void duplicate_faces(GeometrySet &geometry_set,
const IndexAttributes &attribute_outputs)
{
if (!geometry_set.has_mesh()) {
- geometry_set.keep_only({GEO_COMPONENT_TYPE_INSTANCES});
+ geometry_set.remove_geometry_during_modify();
return;
}
- geometry_set.keep_only({GEO_COMPONENT_TYPE_MESH, GEO_COMPONENT_TYPE_INSTANCES});
+ geometry_set.keep_only_during_modify({GEO_COMPONENT_TYPE_MESH});
const MeshComponent &src_component = *geometry_set.get_component_for_read<MeshComponent>();
const Mesh &mesh = *src_component.get_for_read();
@@ -599,23 +589,28 @@ static void duplicate_faces(GeometrySet &geometry_set,
}
}
- MeshComponent dst_component;
- dst_component.replace(new_mesh, GeometryOwnershipType::Editable);
-
copy_face_attributes_without_id(geometry_set,
edge_mapping,
vert_mapping,
loop_mapping,
offsets,
selection,
- src_component,
- dst_component);
+ bke::mesh_attributes(mesh),
+ bke::mesh_attributes_for_write(*new_mesh));
- copy_stable_id_faces(mesh, selection, offsets, vert_mapping, src_component, dst_component);
+ copy_stable_id_faces(mesh,
+ selection,
+ offsets,
+ vert_mapping,
+ bke::mesh_attributes(mesh),
+ bke::mesh_attributes_for_write(*new_mesh));
if (attribute_outputs.duplicate_index) {
- create_duplicate_index_attribute(
- dst_component, ATTR_DOMAIN_FACE, selection, attribute_outputs, offsets);
+ create_duplicate_index_attribute(bke::mesh_attributes_for_write(*new_mesh),
+ ATTR_DOMAIN_FACE,
+ selection,
+ attribute_outputs,
+ offsets);
}
geometry_set.replace_mesh(new_mesh);
@@ -635,15 +630,15 @@ static void copy_edge_attributes_without_id(GeometrySet &geometry_set,
const Span<int> point_mapping,
const Span<int> offsets,
const IndexMask selection,
- const GeometryComponent &src_component,
- GeometryComponent &dst_component)
+ const bke::AttributeAccessor src_attributes,
+ bke::MutableAttributeAccessor dst_attributes)
{
Map<AttributeIDRef, AttributeKind> attributes = gather_attributes_without_id(
- geometry_set, GEO_COMPONENT_TYPE_MESH, false);
+ geometry_set, GEO_COMPONENT_TYPE_MESH);
for (const Map<AttributeIDRef, AttributeKind>::Item entry : attributes.items()) {
const AttributeIDRef attribute_id = entry.key;
- ReadAttributeLookup src_attribute = src_component.attribute_try_get_for_read(attribute_id);
+ GAttributeReader src_attribute = src_attributes.lookup(attribute_id);
if (!src_attribute) {
continue;
}
@@ -651,15 +646,15 @@ static void copy_edge_attributes_without_id(GeometrySet &geometry_set,
const eAttrDomain out_domain = src_attribute.domain;
const eCustomDataType data_type = bke::cpp_type_to_custom_data_type(
src_attribute.varray.type());
- OutputAttribute dst_attribute = dst_component.attribute_try_get_for_output_only(
+ GSpanAttributeWriter dst_attribute = dst_attributes.lookup_or_add_for_write_only_span(
attribute_id, out_domain, data_type);
if (!dst_attribute) {
continue;
}
attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
using T = decltype(dummy);
- VArray_Span<T> src{src_attribute.varray.typed<T>()};
- MutableSpan<T> dst = dst_attribute.as_span<T>();
+ VArraySpan<T> src{src_attribute.varray.typed<T>()};
+ MutableSpan<T> dst = dst_attribute.span.typed<T>();
switch (out_domain) {
case ATTR_DOMAIN_EDGE:
@@ -672,7 +667,7 @@ static void copy_edge_attributes_without_id(GeometrySet &geometry_set,
break;
}
});
- dst_attribute.save();
+ dst_attribute.finish();
}
}
@@ -683,14 +678,14 @@ static void copy_edge_attributes_without_id(GeometrySet &geometry_set,
static void copy_stable_id_edges(const Mesh &mesh,
const IndexMask selection,
const Span<int> edge_offsets,
- const MeshComponent &src_component,
- MeshComponent &dst_component)
+ const bke::AttributeAccessor src_attributes,
+ bke::MutableAttributeAccessor dst_attributes)
{
- ReadAttributeLookup src_attribute = src_component.attribute_try_get_for_read("id");
+ GAttributeReader src_attribute = src_attributes.lookup("id");
if (!src_attribute) {
return;
}
- OutputAttribute dst_attribute = dst_component.attribute_try_get_for_output_only(
+ GSpanAttributeWriter dst_attribute = dst_attributes.lookup_or_add_for_write_only_span(
"id", ATTR_DOMAIN_POINT, CD_PROP_INT32);
if (!dst_attribute) {
return;
@@ -698,8 +693,8 @@ static void copy_stable_id_edges(const Mesh &mesh,
Span<MEdge> edges(mesh.medge, mesh.totedge);
- VArray_Span<int> src{src_attribute.varray.typed<int>()};
- MutableSpan<int> dst = dst_attribute.as_span<int>();
+ VArraySpan<int> src{src_attribute.varray.typed<int>()};
+ MutableSpan<int> dst = dst_attribute.span.typed<int>();
threading::parallel_for(IndexRange(selection.size()), 1024, [&](IndexRange range) {
for (const int i_selection : range) {
const IndexRange edge_range = range_for_offsets_index(edge_offsets, i_selection);
@@ -717,7 +712,7 @@ static void copy_stable_id_edges(const Mesh &mesh,
}
}
});
- dst_attribute.save();
+ dst_attribute.finish();
}
static void duplicate_edges(GeometrySet &geometry_set,
@@ -726,7 +721,7 @@ static void duplicate_edges(GeometrySet &geometry_set,
const IndexAttributes &attribute_outputs)
{
if (!geometry_set.has_mesh()) {
- geometry_set.keep_only({GEO_COMPONENT_TYPE_INSTANCES});
+ geometry_set.remove_geometry_during_modify();
return;
};
const MeshComponent &src_component = *geometry_set.get_component_for_read<MeshComponent>();
@@ -775,17 +770,25 @@ static void duplicate_edges(GeometrySet &geometry_set,
}
});
- MeshComponent dst_component;
- dst_component.replace(new_mesh, GeometryOwnershipType::Editable);
-
- copy_edge_attributes_without_id(
- geometry_set, vert_orig_indices, edge_offsets, selection, src_component, dst_component);
+ copy_edge_attributes_without_id(geometry_set,
+ vert_orig_indices,
+ edge_offsets,
+ selection,
+ bke::mesh_attributes(mesh),
+ bke::mesh_attributes_for_write(*new_mesh));
- copy_stable_id_edges(mesh, selection, edge_offsets, src_component, dst_component);
+ copy_stable_id_edges(mesh,
+ selection,
+ edge_offsets,
+ bke::mesh_attributes(mesh),
+ bke::mesh_attributes_for_write(*new_mesh));
if (attribute_outputs.duplicate_index) {
- create_duplicate_index_attribute(
- dst_component, ATTR_DOMAIN_EDGE, selection, attribute_outputs, edge_offsets);
+ create_duplicate_index_attribute(bke::mesh_attributes_for_write(*new_mesh),
+ ATTR_DOMAIN_EDGE,
+ selection,
+ attribute_outputs,
+ edge_offsets);
}
geometry_set.replace_mesh(new_mesh);
@@ -829,6 +832,7 @@ static void duplicate_points_curve(GeometrySet &geometry_set,
});
Curves *new_curves_id = bke::curves_new_nomain(dst_num, dst_num);
+ bke::curves_copy_parameters(src_curves_id, *new_curves_id);
bke::CurvesGeometry &new_curves = bke::CurvesGeometry::wrap(new_curves_id->geometry);
MutableSpan<int> new_curve_offsets = new_curves.offsets_for_write();
for (const int i : new_curves.curves_range()) {
@@ -836,15 +840,12 @@ static void duplicate_points_curve(GeometrySet &geometry_set,
}
new_curve_offsets.last() = dst_num;
- CurveComponent dst_component;
- dst_component.replace(new_curves_id, GeometryOwnershipType::Editable);
-
Map<AttributeIDRef, AttributeKind> attributes = gather_attributes_without_id(
- geometry_set, GEO_COMPONENT_TYPE_CURVE, false);
+ geometry_set, GEO_COMPONENT_TYPE_CURVE);
for (const Map<AttributeIDRef, AttributeKind>::Item entry : attributes.items()) {
const AttributeIDRef attribute_id = entry.key;
- ReadAttributeLookup src_attribute = src_component.attribute_try_get_for_read(attribute_id);
+ GAttributeReader src_attribute = src_component.attributes()->lookup(attribute_id);
if (!src_attribute) {
continue;
}
@@ -852,16 +853,17 @@ static void duplicate_points_curve(GeometrySet &geometry_set,
eAttrDomain domain = src_attribute.domain;
const eCustomDataType data_type = bke::cpp_type_to_custom_data_type(
src_attribute.varray.type());
- OutputAttribute dst_attribute = dst_component.attribute_try_get_for_output_only(
- attribute_id, domain, data_type);
+ GSpanAttributeWriter dst_attribute =
+ new_curves.attributes_for_write().lookup_or_add_for_write_only_span(
+ attribute_id, domain, data_type);
if (!dst_attribute) {
continue;
}
attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
using T = decltype(dummy);
- VArray_Span<T> src{src_attribute.varray.typed<T>()};
- MutableSpan<T> dst = dst_attribute.as_span<T>();
+ VArraySpan<T> src{src_attribute.varray.typed<T>()};
+ MutableSpan<T> dst = dst_attribute.span.typed<T>();
switch (domain) {
case ATTR_DOMAIN_CURVE:
@@ -880,14 +882,17 @@ static void duplicate_points_curve(GeometrySet &geometry_set,
break;
}
});
- dst_attribute.save();
+ dst_attribute.finish();
}
- copy_stable_id_point(offsets, src_component, dst_component);
+ copy_stable_id_point(offsets, src_curves.attributes(), new_curves.attributes_for_write());
if (attribute_outputs.duplicate_index) {
- create_duplicate_index_attribute(
- dst_component, ATTR_DOMAIN_POINT, selection, attribute_outputs, offsets.as_span());
+ create_duplicate_index_attribute(new_curves.attributes_for_write(),
+ ATTR_DOMAIN_POINT,
+ selection,
+ attribute_outputs,
+ offsets.as_span());
}
geometry_set.replace_curves(new_curves_id);
@@ -923,21 +928,23 @@ static void duplicate_points_mesh(GeometrySet &geometry_set,
threaded_slice_fill(offsets.as_span(), selection, src_verts, dst_verts);
- MeshComponent dst_component;
- dst_component.replace(new_mesh, GeometryOwnershipType::Editable);
- copy_point_attributes_without_id(geometry_set,
- GEO_COMPONENT_TYPE_MESH,
- false,
- offsets,
- selection,
- src_component,
- dst_component);
+ copy_attributes_without_id(geometry_set,
+ GEO_COMPONENT_TYPE_MESH,
+ ATTR_DOMAIN_POINT,
+ offsets,
+ selection,
+ bke::mesh_attributes(mesh),
+ bke::mesh_attributes_for_write(*new_mesh));
- copy_stable_id_point(offsets, src_component, dst_component);
+ copy_stable_id_point(
+ offsets, bke::mesh_attributes(mesh), bke::mesh_attributes_for_write(*new_mesh));
if (attribute_outputs.duplicate_index) {
- create_duplicate_index_attribute(
- dst_component, ATTR_DOMAIN_POINT, selection, attribute_outputs, offsets.as_span());
+ create_duplicate_index_attribute(bke::mesh_attributes_for_write(*new_mesh),
+ ATTR_DOMAIN_POINT,
+ selection,
+ attribute_outputs,
+ offsets.as_span());
}
geometry_set.replace_mesh(new_mesh);
@@ -956,7 +963,7 @@ static void duplicate_points_pointcloud(GeometrySet &geometry_set,
{
const PointCloudComponent &src_points =
*geometry_set.get_component_for_read<PointCloudComponent>();
- const int point_num = src_points.attribute_domain_num(ATTR_DOMAIN_POINT);
+ const int point_num = src_points.attribute_domain_size(ATTR_DOMAIN_POINT);
GeometryComponentFieldContext field_context{src_points, ATTR_DOMAIN_POINT};
FieldEvaluator evaluator{field_context, point_num};
@@ -969,22 +976,24 @@ static void duplicate_points_pointcloud(GeometrySet &geometry_set,
Array<int> offsets = accumulate_counts_to_offsets(selection, counts);
PointCloud *pointcloud = BKE_pointcloud_new_nomain(offsets.last());
- PointCloudComponent dst_component;
- dst_component.replace(pointcloud, GeometryOwnershipType::Editable);
- copy_point_attributes_without_id(geometry_set,
- GEO_COMPONENT_TYPE_POINT_CLOUD,
- false,
- offsets,
- selection,
- src_points,
- dst_component);
+ copy_attributes_without_id(geometry_set,
+ GEO_COMPONENT_TYPE_POINT_CLOUD,
+ ATTR_DOMAIN_POINT,
+ offsets,
+ selection,
+ *src_points.attributes(),
+ bke::pointcloud_attributes_for_write(*pointcloud));
- copy_stable_id_point(offsets, src_points, dst_component);
+ copy_stable_id_point(
+ offsets, *src_points.attributes(), bke::pointcloud_attributes_for_write(*pointcloud));
if (attribute_outputs.duplicate_index) {
- create_duplicate_index_attribute(
- dst_component, ATTR_DOMAIN_POINT, selection, attribute_outputs, offsets);
+ create_duplicate_index_attribute(bke::pointcloud_attributes_for_write(*pointcloud),
+ ATTR_DOMAIN_POINT,
+ selection,
+ attribute_outputs,
+ offsets);
}
geometry_set.replace_pointcloud(pointcloud);
}
@@ -1024,7 +1033,7 @@ static void duplicate_points(GeometrySet &geometry_set,
}
}
component_types.append(GEO_COMPONENT_TYPE_INSTANCES);
- geometry_set.keep_only(component_types);
+ geometry_set.keep_only_during_modify(component_types);
}
/** \} */
@@ -1076,17 +1085,20 @@ static void duplicate_instances(GeometrySet &geometry_set,
dst_instances.instance_reference_handles().slice(range).fill(new_handle);
}
- copy_point_attributes_without_id(geometry_set,
- GEO_COMPONENT_TYPE_INSTANCES,
- true,
- offsets,
- selection,
- src_instances,
- dst_instances);
+ copy_attributes_without_id(geometry_set,
+ GEO_COMPONENT_TYPE_INSTANCES,
+ ATTR_DOMAIN_INSTANCE,
+ offsets,
+ selection,
+ *src_instances.attributes(),
+ *dst_instances.attributes_for_write());
if (attribute_outputs.duplicate_index) {
- create_duplicate_index_attribute(
- dst_instances, ATTR_DOMAIN_INSTANCE, selection, attribute_outputs, offsets);
+ create_duplicate_index_attribute(*dst_instances.attributes_for_write(),
+ ATTR_DOMAIN_INSTANCE,
+ selection,
+ attribute_outputs,
+ offsets);
}
geometry_set = std::move(dst_geometry);
diff --git a/source/blender/nodes/geometry/nodes/node_geo_edge_split.cc b/source/blender/nodes/geometry/nodes/node_geo_edge_split.cc
index 94fbec66bfe..84acab47661 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_edge_split.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_edge_split.cc
@@ -57,8 +57,8 @@ static void node_geo_exec(GeoNodeExecParams params)
const MeshComponent &mesh_component = *geometry_set.get_component_for_read<MeshComponent>();
GeometryComponentFieldContext field_context{mesh_component, ATTR_DOMAIN_EDGE};
- const int domain_num = mesh_component.attribute_domain_num(ATTR_DOMAIN_EDGE);
- fn::FieldEvaluator selection_evaluator{field_context, domain_num};
+ const int domain_size = mesh_component.attribute_domain_size(ATTR_DOMAIN_EDGE);
+ fn::FieldEvaluator selection_evaluator{field_context, domain_size};
selection_evaluator.add(selection_field);
selection_evaluator.evaluate();
const IndexMask selection = selection_evaluator.get_evaluated_as_mask(0);
diff --git a/source/blender/nodes/geometry/nodes/node_geo_extrude_mesh.cc b/source/blender/nodes/geometry/nodes/node_geo_extrude_mesh.cc
index 3eca92e37a3..acf85e74353 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_extrude_mesh.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_extrude_mesh.cc
@@ -66,21 +66,21 @@ static void save_selection_as_attribute(MeshComponent &component,
const eAttrDomain domain,
const IndexMask selection)
{
- BLI_assert(!component.attribute_exists(id));
+ BLI_assert(!component.attributes()->contains(id));
- OutputAttribute_Typed<bool> attribute = component.attribute_try_get_for_output_only<bool>(
- id, domain);
+ SpanAttributeWriter<bool> attribute =
+ component.attributes_for_write()->lookup_or_add_for_write_span<bool>(id, domain);
/* Rely on the new attribute being zeroed by default. */
- BLI_assert(!attribute.as_span().as_span().contains(true));
+ BLI_assert(!attribute.span.as_span().contains(true));
if (selection.is_range()) {
- attribute.as_span().slice(selection.as_range()).fill(true);
+ attribute.span.slice(selection.as_range()).fill(true);
}
else {
- attribute.as_span().fill_indices(selection, true);
+ attribute.span.fill_indices(selection, true);
}
- attribute.save();
+ attribute.finish();
}
static MutableSpan<MVert> mesh_verts(Mesh &mesh)
@@ -164,11 +164,10 @@ static CustomData &get_customdata(Mesh &mesh, const eAttrDomain domain)
static MutableSpan<int> get_orig_index_layer(Mesh &mesh, const eAttrDomain domain)
{
- MeshComponent component;
- component.replace(&mesh, GeometryOwnershipType::ReadOnly);
+ const bke::AttributeAccessor attributes = bke::mesh_attributes(mesh);
CustomData &custom_data = get_customdata(mesh, domain);
if (int *orig_indices = static_cast<int *>(CustomData_get_layer(&custom_data, CD_ORIGINDEX))) {
- return {orig_indices, component.attribute_domain_num(domain)};
+ return {orig_indices, attributes.domain_size(domain)};
}
return {};
}
@@ -280,16 +279,18 @@ static void extrude_mesh_vertices(MeshComponent &component,
new_edges[i_selection] = new_loose_edge(selection[i_selection], new_vert_range[i_selection]);
}
- component.attribute_foreach([&](const AttributeIDRef &id, const AttributeMetaData meta_data) {
+ MutableAttributeAccessor attributes = *component.attributes_for_write();
+
+ attributes.for_all([&](const AttributeIDRef &id, const AttributeMetaData meta_data) {
if (!ELEM(meta_data.domain, ATTR_DOMAIN_POINT, ATTR_DOMAIN_EDGE)) {
return true;
}
- OutputAttribute attribute = component.attribute_try_get_for_output(
+ GSpanAttributeWriter attribute = attributes.lookup_or_add_for_write_span(
id, meta_data.domain, meta_data.data_type);
attribute_math::convert_to_static_type(meta_data.data_type, [&](auto dummy) {
using T = decltype(dummy);
- MutableSpan<T> data = attribute.as_span().typed<T>();
- switch (attribute.domain()) {
+ MutableSpan<T> data = attribute.span.typed<T>();
+ switch (attribute.domain) {
case ATTR_DOMAIN_POINT: {
/* New vertices copy the attribute values from their source vertex. */
copy_with_mask(data.slice(new_vert_range), data.as_span(), selection);
@@ -307,7 +308,7 @@ static void extrude_mesh_vertices(MeshComponent &component,
}
});
- attribute.save();
+ attribute.finish();
return true;
});
@@ -424,7 +425,7 @@ static void extrude_mesh_edges(MeshComponent &component,
edge_evaluator.add(offset_field);
edge_evaluator.evaluate();
const IndexMask edge_selection = edge_evaluator.get_evaluated_selection_as_mask();
- const VArray<float3> &edge_offsets = edge_evaluator.get_evaluated<float3>(0);
+ const VArray<float3> edge_offsets = edge_evaluator.get_evaluated<float3>(0);
if (edge_selection.is_empty()) {
return;
}
@@ -524,8 +525,10 @@ static void extrude_mesh_edges(MeshComponent &component,
const Array<Vector<int>> new_vert_to_duplicate_edge_map = create_vert_to_edge_map(
new_vert_range.size(), duplicate_edges, orig_vert_size);
- component.attribute_foreach([&](const AttributeIDRef &id, const AttributeMetaData meta_data) {
- OutputAttribute attribute = component.attribute_try_get_for_output(
+ MutableAttributeAccessor attributes = *component.attributes_for_write();
+
+ attributes.for_all([&](const AttributeIDRef &id, const AttributeMetaData meta_data) {
+ GSpanAttributeWriter attribute = attributes.lookup_or_add_for_write_span(
id, meta_data.domain, meta_data.data_type);
if (!attribute) {
return true; /* Impossible to write the "normal" attribute. */
@@ -533,8 +536,8 @@ static void extrude_mesh_edges(MeshComponent &component,
attribute_math::convert_to_static_type(meta_data.data_type, [&](auto dummy) {
using T = decltype(dummy);
- MutableSpan<T> data = attribute.as_span().typed<T>();
- switch (attribute.domain()) {
+ MutableSpan<T> data = attribute.span.typed<T>();
+ switch (attribute.domain) {
case ATTR_DOMAIN_POINT: {
/* New vertices copy the attribute values from their source vertex. */
copy_with_indices(data.slice(new_vert_range), data.as_span(), new_vert_indices);
@@ -626,7 +629,7 @@ static void extrude_mesh_edges(MeshComponent &component,
}
});
- attribute.save();
+ attribute.finish();
return true;
});
@@ -686,7 +689,7 @@ static void extrude_mesh_face_regions(MeshComponent &component,
poly_evaluator.add(offset_field);
poly_evaluator.evaluate();
const IndexMask poly_selection = poly_evaluator.get_evaluated_selection_as_mask();
- const VArray<float3> &poly_offsets = poly_evaluator.get_evaluated<float3>(0);
+ const VArray<float3> poly_offsets = poly_evaluator.get_evaluated<float3>(0);
if (poly_selection.is_empty()) {
return;
}
@@ -902,8 +905,10 @@ static void extrude_mesh_face_regions(MeshComponent &component,
const Array<Vector<int>> new_vert_to_duplicate_edge_map = create_vert_to_edge_map(
new_vert_range.size(), boundary_edges, orig_vert_size);
- component.attribute_foreach([&](const AttributeIDRef &id, const AttributeMetaData meta_data) {
- OutputAttribute attribute = component.attribute_try_get_for_output(
+ MutableAttributeAccessor attributes = *component.attributes_for_write();
+
+ attributes.for_all([&](const AttributeIDRef &id, const AttributeMetaData meta_data) {
+ GSpanAttributeWriter attribute = attributes.lookup_or_add_for_write_span(
id, meta_data.domain, meta_data.data_type);
if (!attribute) {
return true; /* Impossible to write the "normal" attribute. */
@@ -911,8 +916,8 @@ static void extrude_mesh_face_regions(MeshComponent &component,
attribute_math::convert_to_static_type(meta_data.data_type, [&](auto dummy) {
using T = decltype(dummy);
- MutableSpan<T> data = attribute.as_span().typed<T>();
- switch (attribute.domain()) {
+ MutableSpan<T> data = attribute.span.typed<T>();
+ switch (attribute.domain) {
case ATTR_DOMAIN_POINT: {
/* New vertices copy the attributes from their original vertices. */
copy_with_indices(data.slice(new_vert_range), data.as_span(), new_vert_indices);
@@ -991,7 +996,7 @@ static void extrude_mesh_face_regions(MeshComponent &component,
}
});
- attribute.save();
+ attribute.finish();
return true;
});
@@ -1154,8 +1159,10 @@ static void extrude_individual_mesh_faces(MeshComponent &component,
}
});
- component.attribute_foreach([&](const AttributeIDRef &id, const AttributeMetaData meta_data) {
- OutputAttribute attribute = component.attribute_try_get_for_output(
+ MutableAttributeAccessor attributes = *component.attributes_for_write();
+
+ attributes.for_all([&](const AttributeIDRef &id, const AttributeMetaData meta_data) {
+ GSpanAttributeWriter attribute = attributes.lookup_or_add_for_write_span(
id, meta_data.domain, meta_data.data_type);
if (!attribute) {
return true; /* Impossible to write the "normal" attribute. */
@@ -1163,8 +1170,8 @@ static void extrude_individual_mesh_faces(MeshComponent &component,
attribute_math::convert_to_static_type(meta_data.data_type, [&](auto dummy) {
using T = decltype(dummy);
- MutableSpan<T> data = attribute.as_span().typed<T>();
- switch (attribute.domain()) {
+ MutableSpan<T> data = attribute.span.typed<T>();
+ switch (attribute.domain) {
case ATTR_DOMAIN_POINT: {
/* New vertices copy the attributes from their original vertices. */
MutableSpan<T> new_data = data.slice(new_vert_range);
@@ -1267,7 +1274,7 @@ static void extrude_individual_mesh_faces(MeshComponent &component,
}
});
- attribute.save();
+ attribute.finish();
return true;
});
diff --git a/source/blender/nodes/geometry/nodes/node_geo_field_at_index.cc b/source/blender/nodes/geometry/nodes/node_geo_field_at_index.cc
index 58281df43d3..64861e529bc 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_field_at_index.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_field_at_index.cc
@@ -91,7 +91,7 @@ class FieldAtIndex final : public GeometryFieldInput {
{
const GeometryComponentFieldContext value_field_context{component, value_field_domain_};
FieldEvaluator value_evaluator{value_field_context,
- component.attribute_domain_num(value_field_domain_)};
+ component.attribute_domain_size(value_field_domain_)};
value_evaluator.add(value_field_);
value_evaluator.evaluate();
const GVArray &values = value_evaluator.get_evaluated(0);
@@ -100,7 +100,7 @@ class FieldAtIndex final : public GeometryFieldInput {
FieldEvaluator index_evaluator{index_field_context, &mask};
index_evaluator.add(index_field_);
index_evaluator.evaluate();
- const VArray<int> &indices = index_evaluator.get_evaluated<int>(0);
+ const VArray<int> indices = index_evaluator.get_evaluated<int>(0);
GVArray output_array;
attribute_math::convert_to_static_type(*type_, [&](auto dummy) {
diff --git a/source/blender/nodes/geometry/nodes/node_geo_field_on_domain.cc b/source/blender/nodes/geometry/nodes/node_geo_field_on_domain.cc
index e6906f4fb21..59e243db4a2 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_field_on_domain.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_field_on_domain.cc
@@ -85,12 +85,13 @@ class FieldOnDomain final : public GeometryFieldInput {
IndexMask /* mask */) const final
{
const GeometryComponentFieldContext context{component, src_domain_};
- FieldEvaluator value_evaluator{context, component.attribute_domain_num(src_domain_)};
- value_evaluator.add(src_field_);
+ const int64_t src_domain_size = component.attribute_domain_size(src_domain_);
+ GArray values(src_field_.cpp_type(), src_domain_size);
+ FieldEvaluator value_evaluator{context, src_domain_size};
+ value_evaluator.add_with_destination(src_field_, values.as_mutable_span());
value_evaluator.evaluate();
- const GVArray &values = value_evaluator.get_evaluated(0);
-
- return component.attribute_try_adapt_domain(values, src_domain_, domain);
+ return component.attributes()->adapt_domain(
+ GVArray::ForGArray(std::move(values)), src_domain_, domain);
}
};
diff --git a/source/blender/nodes/geometry/nodes/node_geo_flip_faces.cc b/source/blender/nodes/geometry/nodes/node_geo_flip_faces.cc
index 0484017cf3b..15b2822805a 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_flip_faces.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_flip_faces.cc
@@ -22,11 +22,11 @@ static void node_declare(NodeDeclarationBuilder &b)
static void mesh_flip_faces(MeshComponent &component, const Field<bool> &selection_field)
{
GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_FACE};
- const int domain_num = component.attribute_domain_num(ATTR_DOMAIN_FACE);
- if (domain_num == 0) {
+ const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_FACE);
+ if (domain_size == 0) {
return;
}
- fn::FieldEvaluator evaluator{field_context, domain_num};
+ fn::FieldEvaluator evaluator{field_context, domain_size};
evaluator.add(selection_field);
evaluator.evaluate();
const IndexMask selection = evaluator.get_evaluated_as_mask(0);
@@ -49,20 +49,21 @@ static void mesh_flip_faces(MeshComponent &component, const Field<bool> &selecti
}
}
- component.attribute_foreach(
+ MutableAttributeAccessor attributes = *component.attributes_for_write();
+ attributes.for_all(
[&](const bke::AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) {
if (meta_data.domain == ATTR_DOMAIN_CORNER) {
- OutputAttribute attribute = component.attribute_try_get_for_output(
- attribute_id, ATTR_DOMAIN_CORNER, meta_data.data_type, nullptr);
+ GSpanAttributeWriter attribute = attributes.lookup_or_add_for_write_span(
+ attribute_id, ATTR_DOMAIN_CORNER, meta_data.data_type);
attribute_math::convert_to_static_type(meta_data.data_type, [&](auto dummy) {
using T = decltype(dummy);
- MutableSpan<T> dst_span = attribute.as_span<T>();
+ MutableSpan<T> dst_span = attribute.span.typed<T>();
for (const int j : selection.index_range()) {
const MPoly &poly = polys[selection[j]];
dst_span.slice(poly.loopstart + 1, poly.totloop - 1).reverse();
}
});
- attribute.save();
+ attribute.finish();
}
return true;
});
diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_curve_handles.cc b/source/blender/nodes/geometry/nodes/node_geo_input_curve_handles.cc
index da249278867..bc1b9e940a1 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_input_curve_handles.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_input_curve_handles.cc
@@ -37,13 +37,15 @@ class HandlePositionFieldInput final : public GeometryFieldInput {
fn::FieldEvaluator evaluator(field_context, &mask);
evaluator.add(relative_);
evaluator.evaluate();
- const VArray<bool> &relative = evaluator.get_evaluated<bool>(0);
+ const VArray<bool> relative = evaluator.get_evaluated<bool>(0);
- VArray<float3> positions = component.attribute_get_for_read<float3>(
+ const AttributeAccessor attributes = *component.attributes();
+
+ VArray<float3> positions = attributes.lookup_or_default<float3>(
"position", ATTR_DOMAIN_POINT, {0, 0, 0});
StringRef side = left_ ? "handle_left" : "handle_right";
- VArray<float3> handles = component.attribute_get_for_read<float3>(
+ VArray<float3> handles = attributes.lookup_or_default<float3>(
side, ATTR_DOMAIN_POINT, {0, 0, 0});
if (relative.is_single()) {
@@ -52,10 +54,10 @@ class HandlePositionFieldInput final : public GeometryFieldInput {
for (const int i : positions.index_range()) {
output[i] = handles[i] - positions[i];
}
- return component.attribute_try_adapt_domain<float3>(
+ return attributes.adapt_domain<float3>(
VArray<float3>::ForContainer(std::move(output)), ATTR_DOMAIN_POINT, domain);
}
- return component.attribute_try_adapt_domain<float3>(handles, ATTR_DOMAIN_POINT, domain);
+ return attributes.adapt_domain<float3>(handles, ATTR_DOMAIN_POINT, domain);
}
Array<float3> output(positions.size());
@@ -67,7 +69,7 @@ class HandlePositionFieldInput final : public GeometryFieldInput {
output[i] = handles[i];
}
}
- return component.attribute_try_adapt_domain<float3>(
+ return component.attributes()->adapt_domain<float3>(
VArray<float3>::ForContainer(std::move(output)), ATTR_DOMAIN_POINT, domain);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_mesh_edge_angle.cc b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_edge_angle.cc
index f27e0305c7d..b009aaa5291 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_input_mesh_edge_angle.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_edge_angle.cc
@@ -91,7 +91,7 @@ class AngleFieldInput final : public GeometryFieldInput {
};
VArray<float> angles = VArray<float>::ForFunc(mesh->totedge, angle_fn);
- return component.attribute_try_adapt_domain<float>(
+ return component.attributes()->adapt_domain<float>(
std::move(angles), ATTR_DOMAIN_EDGE, domain);
}
@@ -166,7 +166,7 @@ class SignedAngleFieldInput final : public GeometryFieldInput {
};
VArray<float> angles = VArray<float>::ForFunc(mesh->totedge, angle_fn);
- return component.attribute_try_adapt_domain<float>(
+ return component.attributes()->adapt_domain<float>(
std::move(angles), ATTR_DOMAIN_EDGE, domain);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_mesh_edge_neighbors.cc b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_edge_neighbors.cc
index cbc2ebc3e68..50d6998bb27 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_input_mesh_edge_neighbors.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_edge_neighbors.cc
@@ -40,7 +40,7 @@ class EdgeNeighborCountFieldInput final : public GeometryFieldInput {
face_count[mesh->mloop[i].e]++;
}
- return mesh_component.attribute_try_adapt_domain<int>(
+ return mesh_component.attributes()->adapt_domain<int>(
VArray<int>::ForContainer(std::move(face_count)), ATTR_DOMAIN_EDGE, domain);
}
return {};
diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_mesh_edge_vertices.cc b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_edge_vertices.cc
index 6201ad26bfb..83e511f45c2 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_input_mesh_edge_vertices.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_edge_vertices.cc
@@ -93,14 +93,14 @@ static VArray<float3> construct_edge_positions_gvarray(const MeshComponent &comp
}
if (vertex == VERTEX_ONE) {
- return component.attribute_try_adapt_domain<float3>(
+ return component.attributes()->adapt_domain<float3>(
VArray<float3>::ForFunc(
mesh->totedge,
[mesh](const int i) { return float3(mesh->mvert[mesh->medge[i].v1].co); }),
ATTR_DOMAIN_EDGE,
domain);
}
- return component.attribute_try_adapt_domain<float3>(
+ return component.attributes()->adapt_domain<float3>(
VArray<float3>::ForFunc(
mesh->totedge,
[mesh](const int i) { return float3(mesh->mvert[mesh->medge[i].v2].co); }),
diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_mesh_face_area.cc b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_face_area.cc
index 7a0e3e37a65..4d21bf9443a 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_input_mesh_face_area.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_face_area.cc
@@ -29,7 +29,7 @@ static VArray<float> construct_face_area_gvarray(const MeshComponent &component,
return BKE_mesh_calc_poly_area(mp, &mesh->mloop[mp->loopstart], mesh->mvert);
};
- return component.attribute_try_adapt_domain<float>(
+ return component.attributes()->adapt_domain<float>(
VArray<float>::ForFunc(mesh->totpoly, area_fn), ATTR_DOMAIN_FACE, domain);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_mesh_face_is_planar.cc b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_face_is_planar.cc
index 532c3dc81e5..6b04ff08d9e 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_input_mesh_face_is_planar.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_face_is_planar.cc
@@ -51,7 +51,7 @@ class PlanarFieldInput final : public GeometryFieldInput {
fn::FieldEvaluator evaluator{context, mesh->totpoly};
evaluator.add(threshold_);
evaluator.evaluate();
- const VArray<float> &thresholds = evaluator.get_evaluated<float>(0);
+ const VArray<float> thresholds = evaluator.get_evaluated<float>(0);
Span<float3> poly_normals{(float3 *)BKE_mesh_poly_normals_ensure(mesh), mesh->totpoly};
@@ -80,7 +80,7 @@ class PlanarFieldInput final : public GeometryFieldInput {
return max - min < thresholds[i_poly] / 2.0f;
};
- return component.attribute_try_adapt_domain<bool>(
+ return component.attributes()->adapt_domain<bool>(
VArray<bool>::ForFunc(mesh->totpoly, planar_fn), ATTR_DOMAIN_FACE, domain);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_mesh_face_neighbors.cc b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_face_neighbors.cc
index 67a21cb06f0..a225ce61b14 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_input_mesh_face_neighbors.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_face_neighbors.cc
@@ -40,7 +40,7 @@ static VArray<int> construct_neighbor_count_gvarray(const MeshComponent &compone
}
}
- return component.attribute_try_adapt_domain<int>(
+ return component.attributes()->adapt_domain<int>(
VArray<int>::ForContainer(std::move(poly_count)), ATTR_DOMAIN_FACE, domain);
}
@@ -83,7 +83,7 @@ static VArray<int> construct_vertex_count_gvarray(const MeshComponent &component
return {};
}
- return component.attribute_try_adapt_domain<int>(
+ return component.attributes()->adapt_domain<int>(
VArray<int>::ForFunc(mesh->totpoly,
[mesh](const int i) -> float { return mesh->mpoly[i].totloop; }),
ATTR_DOMAIN_FACE,
diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_mesh_island.cc b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_island.cc
index bd57924d685..2c7eef5665f 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_input_mesh_island.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_island.cc
@@ -54,7 +54,7 @@ class IslandFieldInput final : public GeometryFieldInput {
output[i] = ordered_roots.index_of_or_add(root);
}
- return mesh_component.attribute_try_adapt_domain<int>(
+ return mesh_component.attributes()->adapt_domain<int>(
VArray<int>::ForContainer(std::move(output)), ATTR_DOMAIN_POINT, domain);
}
@@ -101,7 +101,8 @@ class IslandCountFieldInput final : public GeometryFieldInput {
island_list.add(root);
}
- return VArray<int>::ForSingle(island_list.size(), mesh_component.attribute_domain_num(domain));
+ return VArray<int>::ForSingle(island_list.size(),
+ mesh_component.attribute_domain_size(domain));
}
uint64_t hash() const override
diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_spline_length.cc b/source/blender/nodes/geometry/nodes/node_geo_input_spline_length.cc
index def82eefca5..267ba44cc00 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_input_spline_length.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_input_spline_length.cc
@@ -32,7 +32,7 @@ static VArray<int> construct_curve_point_count_gvarray(const CurveComponent &com
}
if (domain == ATTR_DOMAIN_POINT) {
VArray<int> count = VArray<int>::ForFunc(curves.curves_num(), count_fn);
- return component.attribute_try_adapt_domain<int>(
+ return component.attributes()->adapt_domain<int>(
std::move(count), ATTR_DOMAIN_CURVE, ATTR_DOMAIN_POINT);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_tangent.cc b/source/blender/nodes/geometry/nodes/node_geo_input_tangent.cc
index f5831941094..a2aab5464aa 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_input_tangent.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_input_tangent.cc
@@ -75,7 +75,7 @@ static VArray<float3> construct_curve_tangent_gvarray(const CurveComponent &comp
const VArray<int8_t> types = curves.curve_types();
if (curves.is_single_type(CURVE_TYPE_POLY)) {
- return component.attribute_try_adapt_domain<float3>(
+ return component.attributes()->adapt_domain<float3>(
VArray<float3>::ForSpan(curves.evaluated_tangents()), ATTR_DOMAIN_POINT, domain);
}
@@ -86,7 +86,7 @@ static VArray<float3> construct_curve_tangent_gvarray(const CurveComponent &comp
}
if (domain == ATTR_DOMAIN_CURVE) {
- return component.attribute_try_adapt_domain<float3>(
+ return component.attributes()->adapt_domain<float3>(
VArray<float3>::ForContainer(std::move(tangents)), ATTR_DOMAIN_POINT, ATTR_DOMAIN_CURVE);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_instance_on_points.cc b/source/blender/nodes/geometry/nodes/node_geo_instance_on_points.cc
index 21ef8765e43..119d895fead 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_instance_on_points.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_instance_on_points.cc
@@ -50,7 +50,7 @@ static void add_instances_from_component(
const Map<AttributeIDRef, AttributeKind> &attributes_to_propagate)
{
const eAttrDomain domain = ATTR_DOMAIN_POINT;
- const int domain_num = src_component.attribute_domain_num(domain);
+ const int domain_num = src_component.attribute_domain_size(domain);
VArray<bool> pick_instance;
VArray<int> indices;
@@ -82,7 +82,7 @@ static void add_instances_from_component(
MutableSpan<float4x4> dst_transforms = dst_component.instance_transforms().slice(start_len,
select_len);
- VArray<float3> positions = src_component.attribute_get_for_read<float3>(
+ VArray<float3> positions = src_component.attributes()->lookup_or_default<float3>(
"position", domain, {0, 0, 0});
const InstancesComponent *src_instances = instance.get_component_for_read<InstancesComponent>();
@@ -154,12 +154,12 @@ static void add_instances_from_component(
}
}
- bke::CustomDataAttributes &instance_attributes = dst_component.attributes();
+ bke::CustomDataAttributes &instance_attributes = dst_component.instance_attributes();
for (const auto item : attributes_to_propagate.items()) {
const AttributeIDRef &attribute_id = item.key;
const AttributeKind attribute_kind = item.value;
- const GVArray src_attribute = src_component.attribute_get_for_read(
+ const GVArray src_attribute = src_component.attributes()->lookup_or_default(
attribute_id, ATTR_DOMAIN_POINT, attribute_kind.data_type);
BLI_assert(src_attribute);
std::optional<GMutableSpan> dst_attribute_opt = instance_attributes.get_for_write(
@@ -213,7 +213,7 @@ static void node_geo_exec(GeoNodeExecParams params)
}
}
- geometry_set.keep_only({GEO_COMPONENT_TYPE_INSTANCES});
+ geometry_set.remove_geometry_during_modify();
});
/* Unused references may have been added above. Remove those now so that other nodes don't
diff --git a/source/blender/nodes/geometry/nodes/node_geo_instances_to_points.cc b/source/blender/nodes/geometry/nodes/node_geo_instances_to_points.cc
index 2126a5cc329..5e0789e557b 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_instances_to_points.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_instances_to_points.cc
@@ -22,16 +22,6 @@ static void node_declare(NodeDeclarationBuilder &b)
b.add_output<decl::Geometry>(N_("Points"));
}
-template<typename T>
-static void copy_attribute_to_points(const VArray<T> &src,
- const IndexMask mask,
- MutableSpan<T> dst)
-{
- for (const int i : mask.index_range()) {
- dst[i] = src[mask[i]];
- }
-}
-
static void convert_instances_to_points(GeometrySet &geometry_set,
Field<float3> position_field,
Field<float> radius_field,
@@ -40,9 +30,9 @@ static void convert_instances_to_points(GeometrySet &geometry_set,
const InstancesComponent &instances = *geometry_set.get_component_for_read<InstancesComponent>();
GeometryComponentFieldContext field_context{instances, ATTR_DOMAIN_INSTANCE};
- const int domain_num = instances.attribute_domain_num(ATTR_DOMAIN_INSTANCE);
+ const int domain_size = instances.instances_num();
- fn::FieldEvaluator evaluator{field_context, domain_num};
+ fn::FieldEvaluator evaluator{field_context, domain_size};
evaluator.set_selection(std::move(selection_field));
evaluator.add(std::move(position_field));
evaluator.add(std::move(radius_field));
@@ -51,16 +41,24 @@ static void convert_instances_to_points(GeometrySet &geometry_set,
if (selection.is_empty()) {
return;
}
+ const VArray<float3> &positions = evaluator.get_evaluated<float3>(0);
+ const VArray<float> radii = evaluator.get_evaluated<float>(1);
PointCloud *pointcloud = BKE_pointcloud_new_nomain(selection.size());
geometry_set.replace_pointcloud(pointcloud);
- PointCloudComponent &points = geometry_set.get_component_for_write<PointCloudComponent>();
+ bke::MutableAttributeAccessor point_attributes = bke::pointcloud_attributes_for_write(
+ *pointcloud);
- const VArray<float3> &positions = evaluator.get_evaluated<float3>(0);
- copy_attribute_to_points(positions, selection, {(float3 *)pointcloud->co, pointcloud->totpoint});
- const VArray<float> &radii = evaluator.get_evaluated<float>(1);
- copy_attribute_to_points(radii, selection, {pointcloud->radius, pointcloud->totpoint});
+ bke::SpanAttributeWriter<float3> point_positions =
+ point_attributes.lookup_or_add_for_write_only_span<float3>("position", ATTR_DOMAIN_POINT);
+ bke::SpanAttributeWriter<float> point_radii =
+ point_attributes.lookup_or_add_for_write_only_span<float>("radius", ATTR_DOMAIN_POINT);
+
+ positions.materialize_compressed_to_uninitialized(selection, point_positions.span);
+ radii.materialize_compressed_to_uninitialized(selection, point_radii.span);
+ point_positions.finish();
+ point_radii.finish();
Map<AttributeIDRef, AttributeKind> attributes_to_propagate;
geometry_set.gather_attributes_for_propagation({GEO_COMPONENT_TYPE_INSTANCES},
@@ -75,18 +73,15 @@ static void convert_instances_to_points(GeometrySet &geometry_set,
const AttributeIDRef &attribute_id = item.key;
const AttributeKind attribute_kind = item.value;
- const GVArray src = instances.attribute_get_for_read(
+ const GVArray src = instances.attributes()->lookup_or_default(
attribute_id, ATTR_DOMAIN_INSTANCE, attribute_kind.data_type);
BLI_assert(src);
- OutputAttribute dst = points.attribute_try_get_for_output_only(
+ GSpanAttributeWriter dst = point_attributes.lookup_or_add_for_write_only_span(
attribute_id, ATTR_DOMAIN_POINT, attribute_kind.data_type);
BLI_assert(dst);
- attribute_math::convert_to_static_type(attribute_kind.data_type, [&](auto dummy) {
- using T = decltype(dummy);
- copy_attribute_to_points(src.typed<T>(), selection, dst.as_span().typed<T>());
- });
- dst.save();
+ src.materialize_compressed_to_uninitialized(selection, dst.span.data());
+ dst.finish();
}
}
@@ -99,7 +94,7 @@ static void node_geo_exec(GeoNodeExecParams params)
params.extract_input<Field<float3>>("Position"),
params.extract_input<Field<float>>("Radius"),
params.extract_input<Field<bool>>("Selection"));
- geometry_set.keep_only({GEO_COMPONENT_TYPE_POINT_CLOUD});
+ geometry_set.keep_only({GEO_COMPONENT_TYPE_POINT_CLOUD, GEO_COMPONENT_TYPE_EDIT});
params.set_output("Points", std::move(geometry_set));
}
else {
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 e7aa209a099..1bdc0e58164 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc
@@ -24,7 +24,7 @@ static Map<AttributeIDRef, AttributeMetaData> get_final_attribute_info(
Map<AttributeIDRef, AttributeMetaData> info;
for (const GeometryComponent *component : components) {
- component->attribute_foreach(
+ component->attributes()->for_all(
[&](const bke::AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) {
if (attribute_id.is_named() && ignored_attributes.contains(attribute_id.name())) {
return true;
@@ -56,14 +56,14 @@ static void fill_new_attribute(Span<const GeometryComponent *> src_components,
int offset = 0;
for (const GeometryComponent *component : src_components) {
- const int domain_num = component->attribute_domain_num(domain);
+ const int domain_num = component->attribute_domain_size(domain);
if (domain_num == 0) {
continue;
}
- GVArray read_attribute = component->attribute_get_for_read(
+ GVArray read_attribute = component->attributes()->lookup_or_default(
attribute_id, domain, data_type, nullptr);
- GVArray_GSpan src_span{read_attribute};
+ GVArraySpan src_span{read_attribute};
const void *src_buffer = src_span.data();
void *dst_buffer = dst_span[offset];
cpp_type->copy_assign_n(src_buffer, dst_buffer, domain_num);
@@ -83,15 +83,15 @@ static void join_attributes(Span<const GeometryComponent *> src_components,
const AttributeIDRef attribute_id = item.key;
const AttributeMetaData &meta_data = item.value;
- OutputAttribute write_attribute = result.attribute_try_get_for_output_only(
- attribute_id, meta_data.domain, meta_data.data_type);
+ GSpanAttributeWriter write_attribute =
+ result.attributes_for_write()->lookup_or_add_for_write_only_span(
+ attribute_id, meta_data.domain, meta_data.data_type);
if (!write_attribute) {
continue;
}
- GMutableSpan dst_span = write_attribute.as_span();
fill_new_attribute(
- src_components, attribute_id, meta_data.data_type, meta_data.domain, dst_span);
- write_attribute.save();
+ src_components, attribute_id, meta_data.data_type, meta_data.domain, write_attribute.span);
+ write_attribute.finish();
}
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_material_selection.cc b/source/blender/nodes/geometry/nodes/node_geo_material_selection.cc
index 5875606da97..ca613ae009b 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_material_selection.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_material_selection.cc
@@ -71,7 +71,7 @@ class MaterialSelectionFieldInput final : public GeometryFieldInput {
Array<bool> selection(mesh->totpoly);
select_mesh_by_material(*mesh, material_, IndexMask(mesh->totpoly), selection);
- return mesh_component.attribute_try_adapt_domain<bool>(
+ return mesh_component.attributes()->adapt_domain<bool>(
VArray<bool>::ForContainer(std::move(selection)), ATTR_DOMAIN_FACE, domain);
return nullptr;
diff --git a/source/blender/nodes/geometry/nodes/node_geo_merge_by_distance.cc b/source/blender/nodes/geometry/nodes/node_geo_merge_by_distance.cc
index 1def4089115..a4fb79bef7a 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_merge_by_distance.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_merge_by_distance.cc
@@ -39,7 +39,7 @@ static PointCloud *pointcloud_merge_by_distance(const PointCloudComponent &src_p
const float merge_distance,
const Field<bool> &selection_field)
{
- const int src_num = src_points.attribute_domain_num(ATTR_DOMAIN_POINT);
+ const int src_num = src_points.attribute_domain_size(ATTR_DOMAIN_POINT);
GeometryComponentFieldContext context{src_points, ATTR_DOMAIN_POINT};
FieldEvaluator evaluator{context, src_num};
evaluator.add(selection_field);
@@ -50,14 +50,14 @@ static PointCloud *pointcloud_merge_by_distance(const PointCloudComponent &src_p
return nullptr;
}
- return geometry::point_merge_by_distance(src_points, merge_distance, selection);
+ return geometry::point_merge_by_distance(*src_points.get_for_read(), merge_distance, selection);
}
static std::optional<Mesh *> mesh_merge_by_distance_connected(const MeshComponent &mesh_component,
const float merge_distance,
const Field<bool> &selection_field)
{
- const int src_num = mesh_component.attribute_domain_num(ATTR_DOMAIN_POINT);
+ const int src_num = mesh_component.attribute_domain_size(ATTR_DOMAIN_POINT);
Array<bool> selection(src_num);
GeometryComponentFieldContext context{mesh_component, ATTR_DOMAIN_POINT};
FieldEvaluator evaluator{context, src_num};
@@ -72,7 +72,7 @@ static std::optional<Mesh *> mesh_merge_by_distance_all(const MeshComponent &mes
const float merge_distance,
const Field<bool> &selection_field)
{
- const int src_num = mesh_component.attribute_domain_num(ATTR_DOMAIN_POINT);
+ const int src_num = mesh_component.attribute_domain_size(ATTR_DOMAIN_POINT);
GeometryComponentFieldContext context{mesh_component, ATTR_DOMAIN_POINT};
FieldEvaluator evaluator{context, src_num};
evaluator.add(selection_field);
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 b882d4bdf09..cb79ef93de9 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
@@ -480,53 +480,49 @@ static void calculate_selection_outputs(Mesh *mesh,
const ConeConfig &config,
ConeAttributeOutputs &attribute_outputs)
{
- MeshComponent mesh_component;
- mesh_component.replace(mesh, GeometryOwnershipType::Editable);
+ MutableAttributeAccessor attributes = bke::mesh_attributes_for_write(*mesh);
/* Populate "Top" selection output. */
if (attribute_outputs.top_id) {
const bool face = !config.top_is_point && config.fill_type != GEO_NODE_MESH_CIRCLE_FILL_NONE;
- OutputAttribute_Typed<bool> attribute = mesh_component.attribute_try_get_for_output_only<bool>(
+ SpanAttributeWriter<bool> selection = attributes.lookup_or_add_for_write_only_span<bool>(
attribute_outputs.top_id.get(), face ? ATTR_DOMAIN_FACE : ATTR_DOMAIN_POINT);
- MutableSpan<bool> selection = attribute.as_span();
if (config.top_is_point) {
- selection[config.first_vert] = true;
+ selection.span[config.first_vert] = true;
}
else {
- selection.slice(0, face ? config.top_faces_len : config.circle_segments).fill(true);
+ selection.span.slice(0, face ? config.top_faces_len : config.circle_segments).fill(true);
}
- attribute.save();
+ selection.finish();
}
/* Populate "Bottom" selection output. */
if (attribute_outputs.bottom_id) {
const bool face = !config.bottom_is_point &&
config.fill_type != GEO_NODE_MESH_CIRCLE_FILL_NONE;
- OutputAttribute_Typed<bool> attribute = mesh_component.attribute_try_get_for_output_only<bool>(
+ SpanAttributeWriter<bool> selection = attributes.lookup_or_add_for_write_only_span<bool>(
attribute_outputs.bottom_id.get(), face ? ATTR_DOMAIN_FACE : ATTR_DOMAIN_POINT);
- MutableSpan<bool> selection = attribute.as_span();
if (config.bottom_is_point) {
- selection[config.last_vert] = true;
+ selection.span[config.last_vert] = true;
}
else if (face) {
- selection.slice(config.bottom_faces_start, config.bottom_faces_len).fill(true);
+ selection.span.slice(config.bottom_faces_start, config.bottom_faces_len).fill(true);
}
else {
- selection.slice(config.last_ring_verts_start + 1, config.circle_segments).fill(true);
+ selection.span.slice(config.last_ring_verts_start + 1, config.circle_segments).fill(true);
}
- attribute.save();
+ selection.finish();
}
/* Populate "Side" selection output. */
if (attribute_outputs.side_id) {
- OutputAttribute_Typed<bool> attribute = mesh_component.attribute_try_get_for_output_only<bool>(
+ SpanAttributeWriter<bool> selection = attributes.lookup_or_add_for_write_only_span<bool>(
attribute_outputs.side_id.get(), ATTR_DOMAIN_FACE);
- MutableSpan<bool> selection = attribute.as_span();
- selection.slice(config.side_faces_start, config.side_faces_len).fill(true);
- attribute.save();
+ selection.span.slice(config.side_faces_start, config.side_faces_len).fill(true);
+ selection.finish();
}
}
@@ -540,11 +536,11 @@ static void calculate_selection_outputs(Mesh *mesh,
*/
static void calculate_cone_uvs(Mesh *mesh, const ConeConfig &config)
{
- MeshComponent mesh_component;
- mesh_component.replace(mesh, GeometryOwnershipType::Editable);
- 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();
+ MutableAttributeAccessor attributes = bke::mesh_attributes_for_write(*mesh);
+
+ SpanAttributeWriter<float2> uv_attribute = attributes.lookup_or_add_for_write_only_span<float2>(
+ "uv_map", ATTR_DOMAIN_CORNER);
+ MutableSpan<float2> uvs = uv_attribute.span;
Array<float2> circle(config.circle_segments);
float angle = 0.0f;
@@ -654,7 +650,7 @@ static void calculate_cone_uvs(Mesh *mesh, const ConeConfig &config)
}
}
- uv_attribute.save();
+ uv_attribute.finish();
}
static Mesh *create_vertex_mesh()
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 523dbd5dac2..9baf0b3171e 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
@@ -18,23 +18,22 @@ namespace blender::nodes {
static void calculate_uvs(
Mesh *mesh, Span<MVert> verts, Span<MLoop> loops, const float size_x, const float size_y)
{
- MeshComponent mesh_component;
- mesh_component.replace(mesh, GeometryOwnershipType::Editable);
- 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();
+ MutableAttributeAccessor attributes = bke::mesh_attributes_for_write(*mesh);
+
+ SpanAttributeWriter<float2> uv_attribute = attributes.lookup_or_add_for_write_only_span<float2>(
+ "uv_map", ATTR_DOMAIN_CORNER);
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;
threading::parallel_for(loops.index_range(), 1024, [&](IndexRange range) {
for (const int i : range) {
const float3 &co = verts[loops[i].v].co;
- uvs[i].x = (co.x + size_x * 0.5f) * dx;
- uvs[i].y = (co.y + size_y * 0.5f) * dy;
+ uv_attribute.span[i].x = (co.x + size_x * 0.5f) * dx;
+ uv_attribute.span[i].y = (co.y + size_y * 0.5f) * dy;
}
});
- uv_attribute.save();
+ uv_attribute.finish();
}
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 4e0e5c7c912..a46bb40a3eb 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
@@ -1,5 +1,7 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
+#include "BLI_task.hh"
+
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
@@ -61,15 +63,26 @@ static int sphere_face_total(const int segments, const int rings)
* Also calculate vertex normals here, since the calculation is trivial, and it allows avoiding the
* calculation later, if it's necessary. The vertex normals are just the normalized positions.
*/
-static void calculate_sphere_vertex_data(MutableSpan<MVert> verts,
- MutableSpan<float3> vert_normals,
- const float radius,
- const int segments,
- const int rings)
+BLI_NOINLINE static void calculate_sphere_vertex_data(MutableSpan<MVert> verts,
+ MutableSpan<float3> vert_normals,
+ const float radius,
+ const int segments,
+ const int rings)
{
const float delta_theta = M_PI / rings;
const float delta_phi = (2.0f * M_PI) / segments;
+ Array<float, 64> segment_cosines(segments + 1);
+ for (const int segment : IndexRange(1, segments)) {
+ const float phi = segment * delta_phi;
+ segment_cosines[segment] = std::cos(phi);
+ }
+ Array<float, 64> segment_sines(segments + 1);
+ for (const int segment : IndexRange(1, segments)) {
+ const float phi = segment * delta_phi;
+ segment_sines[segment] = std::sin(phi);
+ }
+
copy_v3_v3(verts[0].co, float3(0.0f, 0.0f, radius));
vert_normals.first() = float3(0.0f, 0.0f, 1.0f);
@@ -79,9 +92,8 @@ static void calculate_sphere_vertex_data(MutableSpan<MVert> verts,
const float sin_theta = std::sin(theta);
const float z = std::cos(theta);
for (const int segment : IndexRange(1, segments)) {
- const float phi = segment * delta_phi;
- const float x = sin_theta * std::cos(phi);
- const float y = sin_theta * std::sin(phi);
+ const float x = sin_theta * segment_cosines[segment];
+ const float y = sin_theta * segment_sines[segment];
copy_v3_v3(verts[vert_index].co, float3(x, y, z) * radius);
vert_normals[vert_index] = float3(x, y, z);
vert_index++;
@@ -92,9 +104,9 @@ static void calculate_sphere_vertex_data(MutableSpan<MVert> verts,
vert_normals.last() = float3(0.0f, 0.0f, -1.0f);
}
-static void calculate_sphere_edge_indices(MutableSpan<MEdge> edges,
- const int segments,
- const int rings)
+BLI_NOINLINE static void calculate_sphere_edge_indices(MutableSpan<MEdge> edges,
+ const int segments,
+ const int rings)
{
int edge_index = 0;
@@ -142,20 +154,46 @@ static void calculate_sphere_edge_indices(MutableSpan<MEdge> edges,
}
}
-static void calculate_sphere_faces(MutableSpan<MLoop> loops,
- MutableSpan<MPoly> polys,
- const int segments,
- const int rings)
+BLI_NOINLINE static void calculate_sphere_faces(MutableSpan<MPoly> polys, const int segments)
{
int loop_index = 0;
- int poly_index = 0;
/* Add the triangles connected to the top vertex. */
- const int first_vert_ring_index_start = 1;
- for (const int segment : IndexRange(segments)) {
- MPoly &poly = polys[poly_index++];
+ for (MPoly &poly : polys.take_front(segments)) {
+ poly.loopstart = loop_index;
+ poly.totloop = 3;
+ loop_index += 3;
+ }
+
+ /* Add the middle quads. */
+ for (MPoly &poly : polys.drop_front(segments).drop_back(segments)) {
+ poly.loopstart = loop_index;
+ poly.totloop = 4;
+ loop_index += 4;
+ }
+
+ /* Add the triangles connected to the bottom vertex. */
+ for (MPoly &poly : polys.take_back(segments)) {
poly.loopstart = loop_index;
poly.totloop = 3;
+ loop_index += 3;
+ }
+}
+
+BLI_NOINLINE static void calculate_sphere_corners(MutableSpan<MLoop> loops,
+ const int segments,
+ const int rings)
+{
+ int loop_index = 0;
+ auto segment_next_or_first = [&](const int segment) {
+ return segment == segments - 1 ? 0 : segment + 1;
+ };
+
+ /* Add the triangles connected to the top vertex. */
+ const int first_vert_ring_index_start = 1;
+ for (const int segment : IndexRange(segments)) {
+ const int segment_next = segment_next_or_first(segment);
+
MLoop &loop_a = loops[loop_index++];
loop_a.v = 0;
loop_a.e = segment;
@@ -163,8 +201,8 @@ static void calculate_sphere_faces(MutableSpan<MLoop> loops,
loop_b.v = first_vert_ring_index_start + segment;
loop_b.e = segments + segment;
MLoop &loop_c = loops[loop_index++];
- loop_c.v = first_vert_ring_index_start + (segment + 1) % segments;
- loop_c.e = (segment + 1) % segments;
+ loop_c.v = first_vert_ring_index_start + segment_next;
+ loop_c.e = segment_next;
}
int ring_vert_index_start = 1;
@@ -175,9 +213,7 @@ static void calculate_sphere_faces(MutableSpan<MLoop> loops,
const int ring_vertical_edge_index_start = ring_edge_index_start + segments;
for (const int segment : IndexRange(segments)) {
- MPoly &poly = polys[poly_index++];
- poly.loopstart = loop_index;
- poly.totloop = 4;
+ const int segment_next = segment_next_or_first(segment);
MLoop &loop_a = loops[loop_index++];
loop_a.v = ring_vert_index_start + segment;
@@ -186,10 +222,10 @@ static void calculate_sphere_faces(MutableSpan<MLoop> loops,
loop_b.v = next_ring_vert_index_start + segment;
loop_b.e = next_ring_edge_index_start + segment;
MLoop &loop_c = loops[loop_index++];
- loop_c.v = next_ring_vert_index_start + (segment + 1) % segments;
- loop_c.e = ring_vertical_edge_index_start + (segment + 1) % segments;
+ loop_c.v = next_ring_vert_index_start + segment_next;
+ loop_c.e = ring_vertical_edge_index_start + segment_next;
MLoop &loop_d = loops[loop_index++];
- loop_d.v = ring_vert_index_start + (segment + 1) % segments;
+ loop_d.v = ring_vert_index_start + segment_next;
loop_d.e = ring_edge_index_start + segment;
}
ring_vert_index_start += segments;
@@ -202,15 +238,13 @@ static void calculate_sphere_faces(MutableSpan<MLoop> loops,
const int last_vert_index = sphere_vert_total(segments, rings) - 1;
const int last_vert_ring_start = last_vert_index - segments;
for (const int segment : IndexRange(segments)) {
- MPoly &poly = polys[poly_index++];
- poly.loopstart = loop_index;
- poly.totloop = 3;
+ const int segment_next = segment_next_or_first(segment);
MLoop &loop_a = loops[loop_index++];
loop_a.v = last_vert_index;
- loop_a.e = bottom_edge_fan_start + (segment + 1) % segments;
+ loop_a.e = bottom_edge_fan_start + segment_next;
MLoop &loop_b = loops[loop_index++];
- loop_b.v = last_vert_ring_start + (segment + 1) % segments;
+ loop_b.v = last_vert_ring_start + segment_next;
loop_b.e = last_edge_ring_start + segment;
MLoop &loop_c = loops[loop_index++];
loop_c.v = last_vert_ring_start + segment;
@@ -218,43 +252,45 @@ static void calculate_sphere_faces(MutableSpan<MLoop> loops,
}
}
-static void calculate_sphere_uvs(Mesh *mesh, const float segments, const float rings)
+BLI_NOINLINE static void calculate_sphere_uvs(Mesh *mesh, const float segments, const float rings)
{
- MeshComponent mesh_component;
- mesh_component.replace(mesh, GeometryOwnershipType::Editable);
- 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();
+ MutableAttributeAccessor attributes = bke::mesh_attributes_for_write(*mesh);
+
+ SpanAttributeWriter<float2> uv_attribute = attributes.lookup_or_add_for_write_only_span<float2>(
+ "uv_map", ATTR_DOMAIN_CORNER);
+ MutableSpan<float2> uvs = uv_attribute.span;
int loop_index = 0;
const float dy = 1.0f / rings;
+ const float segments_inv = 1.0f / segments;
+
for (const int i_segment : IndexRange(segments)) {
const float segment = static_cast<float>(i_segment);
- uvs[loop_index++] = float2((segment + 0.5f) / segments, 0.0f);
- uvs[loop_index++] = float2(segment / segments, dy);
- uvs[loop_index++] = float2((segment + 1.0f) / segments, dy);
+ uvs[loop_index++] = float2((segment + 0.5f) * segments_inv, 0.0f);
+ uvs[loop_index++] = float2(segment * segments_inv, dy);
+ uvs[loop_index++] = float2((segment + 1.0f) * segments_inv, dy);
}
for (const int i_ring : IndexRange(1, rings - 2)) {
const float ring = static_cast<float>(i_ring);
for (const int i_segment : IndexRange(segments)) {
const float segment = static_cast<float>(i_segment);
- uvs[loop_index++] = float2(segment / segments, ring / rings);
- uvs[loop_index++] = float2(segment / segments, (ring + 1.0f) / rings);
- uvs[loop_index++] = float2((segment + 1.0f) / segments, (ring + 1.0f) / rings);
- uvs[loop_index++] = float2((segment + 1.0f) / segments, ring / rings);
+ uvs[loop_index++] = float2(segment * segments_inv, ring / rings);
+ uvs[loop_index++] = float2(segment * segments_inv, (ring + 1.0f) / rings);
+ uvs[loop_index++] = float2((segment + 1.0f) * segments_inv, (ring + 1.0f) / rings);
+ uvs[loop_index++] = float2((segment + 1.0f) * segments_inv, ring / rings);
}
}
for (const int i_segment : IndexRange(segments)) {
const float segment = static_cast<float>(i_segment);
- uvs[loop_index++] = float2((segment + 0.5f) / segments, 1.0f);
- uvs[loop_index++] = float2((segment + 1.0f) / segments, 1.0f - dy);
- uvs[loop_index++] = float2(segment / segments, 1.0f - dy);
+ uvs[loop_index++] = float2((segment + 0.5f) * segments_inv, 1.0f);
+ uvs[loop_index++] = float2((segment + 1.0f) * segments_inv, 1.0f - dy);
+ uvs[loop_index++] = float2(segment * segments_inv, 1.0f - dy);
}
- uv_attribute.save();
+ uv_attribute.finish();
}
static Mesh *create_uv_sphere_mesh(const float radius, const int segments, const int rings)
@@ -270,15 +306,16 @@ static Mesh *create_uv_sphere_mesh(const float radius, const int segments, const
MutableSpan<MEdge> edges{mesh->medge, mesh->totedge};
MutableSpan<MPoly> polys{mesh->mpoly, mesh->totpoly};
- MutableSpan vert_normals{(float3 *)BKE_mesh_vertex_normals_for_write(mesh), mesh->totvert};
- calculate_sphere_vertex_data(verts, vert_normals, radius, segments, rings);
- BKE_mesh_vertex_normals_clear_dirty(mesh);
-
- calculate_sphere_edge_indices(edges, segments, rings);
-
- calculate_sphere_faces(loops, polys, segments, rings);
-
- calculate_sphere_uvs(mesh, segments, rings);
+ threading::parallel_invoke(
+ [&]() {
+ MutableSpan vert_normals{(float3 *)BKE_mesh_vertex_normals_for_write(mesh), mesh->totvert};
+ calculate_sphere_vertex_data(verts, vert_normals, radius, segments, rings);
+ BKE_mesh_vertex_normals_clear_dirty(mesh);
+ },
+ [&]() { calculate_sphere_edge_indices(edges, segments, rings); },
+ [&]() { calculate_sphere_faces(polys, segments); },
+ [&]() { calculate_sphere_corners(loops, segments, rings); },
+ [&]() { calculate_sphere_uvs(mesh, segments, rings); });
return mesh;
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc
index ec6acf55dd8..40169def51e 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc
@@ -18,24 +18,26 @@ static void node_geo_exec(GeoNodeExecParams params)
GeometrySet geometry_set = params.extract_input<GeometrySet>("Mesh");
geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
- if (!geometry_set.has_mesh()) {
- geometry_set.keep_only({GEO_COMPONENT_TYPE_INSTANCES});
+ const Mesh *mesh = geometry_set.get_mesh_for_read();
+ if (mesh == nullptr) {
+ geometry_set.remove_geometry_during_modify();
return;
}
const MeshComponent &component = *geometry_set.get_component_for_read<MeshComponent>();
GeometryComponentFieldContext context{component, ATTR_DOMAIN_EDGE};
- fn::FieldEvaluator evaluator{context, component.attribute_domain_num(ATTR_DOMAIN_EDGE)};
+ fn::FieldEvaluator evaluator{context, component.attribute_domain_size(ATTR_DOMAIN_EDGE)};
evaluator.add(params.get_input<Field<bool>>("Selection"));
evaluator.evaluate();
const IndexMask selection = evaluator.get_evaluated_as_mask(0);
if (selection.size() == 0) {
- geometry_set.keep_only({GEO_COMPONENT_TYPE_INSTANCES});
+ geometry_set.remove_geometry_during_modify();
return;
}
- geometry_set.replace_curves(geometry::mesh_to_curve_convert(component, selection));
- geometry_set.keep_only({GEO_COMPONENT_TYPE_CURVE, GEO_COMPONENT_TYPE_INSTANCES});
+ bke::CurvesGeometry curves = geometry::mesh_to_curve_convert(*mesh, selection);
+ geometry_set.replace_curves(bke::curves_new_nomain(std::move(curves)));
+ geometry_set.keep_only_during_modify({GEO_COMPONENT_TYPE_CURVE});
});
params.set_output("Curve", std::move(geometry_set));
diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_to_points.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_to_points.cc
index 7463eb01471..d3d1312be6d 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_mesh_to_points.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_to_points.cc
@@ -62,13 +62,13 @@ static void geometry_set_mesh_to_points(GeometrySet &geometry_set,
{
const MeshComponent *mesh_component = geometry_set.get_component_for_read<MeshComponent>();
if (mesh_component == nullptr) {
- geometry_set.keep_only({GEO_COMPONENT_TYPE_INSTANCES});
+ geometry_set.remove_geometry_during_modify();
return;
}
GeometryComponentFieldContext field_context{*mesh_component, domain};
- const int domain_num = mesh_component->attribute_domain_num(domain);
+ const int domain_num = mesh_component->attribute_domain_size(domain);
if (domain_num == 0) {
- geometry_set.keep_only({GEO_COMPONENT_TYPE_INSTANCES});
+ geometry_set.remove_geometry_during_modify();
return;
}
fn::FieldEvaluator evaluator{field_context, domain_num};
@@ -83,20 +83,20 @@ static void geometry_set_mesh_to_points(GeometrySet &geometry_set,
PointCloud *pointcloud = BKE_pointcloud_new_nomain(selection.size());
geometry_set.replace_pointcloud(pointcloud);
- PointCloudComponent &point_component =
- geometry_set.get_component_for_write<PointCloudComponent>();
+ MutableAttributeAccessor pointcloud_attributes = bke::pointcloud_attributes_for_write(
+ *pointcloud);
- OutputAttribute position = point_component.attribute_try_get_for_output_only(
+ GSpanAttributeWriter position = pointcloud_attributes.lookup_or_add_for_write_only_span(
"position", ATTR_DOMAIN_POINT, CD_PROP_FLOAT3);
materialize_compressed_to_uninitialized_threaded(
- evaluator.get_evaluated(0), selection, position.as_span());
- position.save();
+ evaluator.get_evaluated(0), selection, position.span);
+ position.finish();
- OutputAttribute radius = point_component.attribute_try_get_for_output_only(
+ GSpanAttributeWriter radius = pointcloud_attributes.lookup_or_add_for_write_only_span(
"radius", ATTR_DOMAIN_POINT, CD_PROP_FLOAT);
materialize_compressed_to_uninitialized_threaded(
- evaluator.get_evaluated(1), selection, radius.as_span());
- radius.save();
+ evaluator.get_evaluated(1), selection, radius.span);
+ radius.finish();
Map<AttributeIDRef, AttributeKind> attributes;
geometry_set.gather_attributes_for_propagation(
@@ -106,16 +106,16 @@ static void geometry_set_mesh_to_points(GeometrySet &geometry_set,
for (Map<AttributeIDRef, AttributeKind>::Item entry : attributes.items()) {
const AttributeIDRef attribute_id = entry.key;
const eCustomDataType data_type = entry.value.data_type;
- GVArray src = mesh_component->attribute_get_for_read(attribute_id, domain, data_type);
- OutputAttribute dst = point_component.attribute_try_get_for_output_only(
+ GVArray src = mesh_component->attributes()->lookup_or_default(attribute_id, domain, data_type);
+ GSpanAttributeWriter dst = pointcloud_attributes.lookup_or_add_for_write_only_span(
attribute_id, ATTR_DOMAIN_POINT, data_type);
if (dst && src) {
- materialize_compressed_to_uninitialized_threaded(src, selection, dst.as_span());
- dst.save();
+ materialize_compressed_to_uninitialized_threaded(src, selection, dst.span);
+ dst.finish();
}
}
- geometry_set.keep_only({GEO_COMPONENT_TYPE_POINT_CLOUD, GEO_COMPONENT_TYPE_INSTANCES});
+ geometry_set.keep_only_during_modify({GEO_COMPONENT_TYPE_POINT_CLOUD});
}
static void node_geo_exec(GeoNodeExecParams params)
diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_to_volume.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_to_volume.cc
index 5890e070b2f..92814a8bc5e 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_mesh_to_volume.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_to_volume.cc
@@ -154,7 +154,7 @@ static void node_geo_exec(GeoNodeExecParams params)
if (geometry_set.has_mesh()) {
Volume *volume = create_volume_from_mesh(*geometry_set.get_mesh_for_read(), params);
geometry_set.replace_volume(volume);
- geometry_set.keep_only({GEO_COMPONENT_TYPE_VOLUME, GEO_COMPONENT_TYPE_INSTANCES});
+ geometry_set.keep_only_during_modify({GEO_COMPONENT_TYPE_VOLUME});
}
});
params.set_output("Volume", std::move(geometry_set));
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 f4d91d7496b..0b2159364f1 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_object_info.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_object_info.cc
@@ -2,6 +2,8 @@
#include "BLI_math_matrix.h"
+#include "BKE_geometry_set_instances.hh"
+
#include "UI_interface.h"
#include "UI_resources.h"
diff --git a/source/blender/nodes/geometry/nodes/node_geo_points.cc b/source/blender/nodes/geometry/nodes/node_geo_points.cc
index ced41c6c85c..dd32e6714f4 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_points.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_points.cc
@@ -62,7 +62,6 @@ static void node_geo_exec(GeoNodeExecParams params)
{
const int count = params.extract_input<int>("Count");
if (count <= 0) {
- params.error_message_add(NodeWarningType::Warning, TIP_("Point count should be at least 1"));
params.set_default_remaining_outputs();
return;
}
@@ -73,19 +72,20 @@ static void node_geo_exec(GeoNodeExecParams params)
PointCloud *new_point_cloud = BKE_pointcloud_new_nomain(count);
GeometrySet geometry_set = GeometrySet::create_with_pointcloud(new_point_cloud);
PointCloudComponent &points = geometry_set.get_component_for_write<PointCloudComponent>();
- OutputAttribute_Typed<float3> output_position = points.attribute_try_get_for_output_only<float3>(
+ MutableAttributeAccessor attributes = *points.attributes_for_write();
+ AttributeWriter<float3> output_position = attributes.lookup_or_add_for_write<float3>(
"position", ATTR_DOMAIN_POINT);
- OutputAttribute_Typed<float> output_radii = points.attribute_try_get_for_output_only<float>(
+ AttributeWriter<float> output_radii = attributes.lookup_or_add_for_write<float>(
"radius", ATTR_DOMAIN_POINT);
PointsFieldContext context{count};
fn::FieldEvaluator evaluator{context, count};
- evaluator.add_with_destination(position_field, output_position.as_span());
- evaluator.add_with_destination(radius_field, output_radii.as_span());
+ evaluator.add_with_destination(position_field, output_position.varray);
+ evaluator.add_with_destination(radius_field, output_radii.varray);
evaluator.evaluate();
- output_position.save();
- output_radii.save();
+ output_position.finish();
+ output_radii.finish();
params.set_output("Geometry", std::move(geometry_set));
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_points_to_vertices.cc b/source/blender/nodes/geometry/nodes/node_geo_points_to_vertices.cc
index 00b3d167755..ed7ef9b7c71 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_points_to_vertices.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_points_to_vertices.cc
@@ -18,14 +18,6 @@ static void node_declare(NodeDeclarationBuilder &b)
b.add_output<decl::Geometry>(N_("Mesh"));
}
-template<typename T>
-static void copy_attribute_to_vertices(const Span<T> src, const IndexMask mask, MutableSpan<T> dst)
-{
- for (const int i : mask.index_range()) {
- dst[i] = src[mask[i]];
- }
-}
-
/* One improvement would be to move the attribute arrays directly to the mesh when possible. */
static void geometry_set_points_to_vertices(GeometrySet &geometry_set,
Field<bool> &selection_field)
@@ -33,14 +25,14 @@ static void geometry_set_points_to_vertices(GeometrySet &geometry_set,
const PointCloudComponent *point_component =
geometry_set.get_component_for_read<PointCloudComponent>();
if (point_component == nullptr) {
- geometry_set.keep_only({GEO_COMPONENT_TYPE_INSTANCES});
+ geometry_set.remove_geometry_during_modify();
return;
}
GeometryComponentFieldContext field_context{*point_component, ATTR_DOMAIN_POINT};
- const int domain_num = point_component->attribute_domain_num(ATTR_DOMAIN_POINT);
+ const int domain_num = point_component->attribute_domain_size(ATTR_DOMAIN_POINT);
if (domain_num == 0) {
- geometry_set.keep_only({GEO_COMPONENT_TYPE_INSTANCES});
+ geometry_set.remove_geometry_during_modify();
return;
}
@@ -60,22 +52,18 @@ static void geometry_set_points_to_vertices(GeometrySet &geometry_set,
for (Map<AttributeIDRef, AttributeKind>::Item entry : attributes.items()) {
const AttributeIDRef attribute_id = entry.key;
const eCustomDataType data_type = entry.value.data_type;
- GVArray src = point_component->attribute_get_for_read(
- attribute_id, ATTR_DOMAIN_POINT, data_type);
- OutputAttribute dst = mesh_component.attribute_try_get_for_output_only(
+ GVArray src = point_component->attributes()->lookup_or_default(
attribute_id, ATTR_DOMAIN_POINT, data_type);
+ GSpanAttributeWriter dst =
+ mesh_component.attributes_for_write()->lookup_or_add_for_write_only_span(
+ attribute_id, ATTR_DOMAIN_POINT, data_type);
if (dst && src) {
- attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
- using T = decltype(dummy);
- VArray<T> src_typed = src.typed<T>();
- VArray_Span<T> src_typed_span{src_typed};
- copy_attribute_to_vertices(src_typed_span, selection, dst.as_span().typed<T>());
- });
- dst.save();
+ src.materialize_compressed_to_uninitialized(selection, dst.span.data());
+ dst.finish();
}
}
- geometry_set.keep_only({GEO_COMPONENT_TYPE_MESH, GEO_COMPONENT_TYPE_INSTANCES});
+ geometry_set.keep_only_during_modify({GEO_COMPONENT_TYPE_MESH});
}
static void node_geo_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 42cee4c0efe..4a3048e5f4a 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
@@ -163,12 +163,15 @@ static void gather_point_data_from_component(GeoNodeExecParams &params,
Vector<float3> &r_positions,
Vector<float> &r_radii)
{
- VArray<float3> positions = component.attribute_get_for_read<float3>(
+ if (component.is_empty()) {
+ return;
+ }
+ VArray<float3> positions = component.attributes()->lookup_or_default<float3>(
"position", ATTR_DOMAIN_POINT, {0, 0, 0});
Field<float> radius_field = params.get_input<Field<float>>("Radius");
GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_POINT};
- const int domain_num = component.attribute_domain_num(ATTR_DOMAIN_POINT);
+ const int domain_num = component.attribute_domain_size(ATTR_DOMAIN_POINT);
r_positions.resize(r_positions.size() + domain_num);
positions.materialize(r_positions.as_mutable_span().take_back(domain_num));
@@ -221,7 +224,7 @@ static void initialize_volume_component_from_points(GeoNodeExecParams &params,
new_grid->transform().postScale(voxel_size);
BKE_volume_grid_add_vdb(*volume, "density", std::move(new_grid));
- r_geometry_set.keep_only({GEO_COMPONENT_TYPE_VOLUME, GEO_COMPONENT_TYPE_INSTANCES});
+ r_geometry_set.keep_only_during_modify({GEO_COMPONENT_TYPE_VOLUME});
r_geometry_set.replace_volume(volume);
}
#endif
diff --git a/source/blender/nodes/geometry/nodes/node_geo_raycast.cc b/source/blender/nodes/geometry/nodes/node_geo_raycast.cc
index a92cee2d066..f81748da587 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_raycast.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_raycast.cc
@@ -312,8 +312,8 @@ class RaycastFunction : public fn::MultiFunction {
}
const MeshComponent &mesh_component = *target_.get_component_for_read<MeshComponent>();
target_context_.emplace(GeometryComponentFieldContext{mesh_component, domain_});
- const int domain_num = mesh_component.attribute_domain_num(domain_);
- target_evaluator_ = std::make_unique<FieldEvaluator>(*target_context_, domain_num);
+ const int domain_size = mesh_component.attribute_domain_size(domain_);
+ target_evaluator_ = std::make_unique<FieldEvaluator>(*target_context_, domain_size);
target_evaluator_->add(std::move(src_field));
target_evaluator_->evaluate();
target_data_ = &target_evaluator_->get_evaluated(0);
diff --git a/source/blender/nodes/geometry/nodes/node_geo_remove_attribute.cc b/source/blender/nodes/geometry/nodes/node_geo_remove_attribute.cc
index da42b8c5ee0..ee279ba58f9 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_remove_attribute.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_remove_attribute.cc
@@ -39,7 +39,7 @@ static void node_geo_exec(GeoNodeExecParams params)
/* First check if the attribute exists before getting write access,
* to avoid potentially expensive unnecessary copies. */
const GeometryComponent &read_only_component = *geometry_set.get_component_for_read(type);
- if (read_only_component.attribute_exists(name)) {
+ if (read_only_component.attributes()->contains(name)) {
attribute_exists = true;
}
else {
@@ -47,7 +47,7 @@ static void node_geo_exec(GeoNodeExecParams params)
}
GeometryComponent &component = geometry_set.get_component_for_write(type);
- if (!component.attribute_try_delete(name)) {
+ if (!component.attributes_for_write()->remove(name)) {
cannot_delete = true;
}
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_rotate_instances.cc b/source/blender/nodes/geometry/nodes/node_geo_rotate_instances.cc
index 59e203afd08..d414bb1fa1d 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_rotate_instances.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_rotate_instances.cc
@@ -29,9 +29,9 @@ static void rotate_instances(GeoNodeExecParams &params, InstancesComponent &inst
evaluator.evaluate();
const IndexMask selection = evaluator.get_evaluated_selection_as_mask();
- const VArray<float3> &rotations = evaluator.get_evaluated<float3>(0);
- const VArray<float3> &pivots = evaluator.get_evaluated<float3>(1);
- const VArray<bool> &local_spaces = evaluator.get_evaluated<bool>(2);
+ const VArray<float3> rotations = evaluator.get_evaluated<float3>(0);
+ const VArray<float3> pivots = evaluator.get_evaluated<float3>(1);
+ const VArray<bool> local_spaces = evaluator.get_evaluated<bool>(2);
MutableSpan<float4x4> instance_transforms = instances_component.instance_transforms();
diff --git a/source/blender/nodes/geometry/nodes/node_geo_scale_instances.cc b/source/blender/nodes/geometry/nodes/node_geo_scale_instances.cc
index d4716a6b6f0..7156feb37d7 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_scale_instances.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_scale_instances.cc
@@ -31,9 +31,9 @@ static void scale_instances(GeoNodeExecParams &params, InstancesComponent &insta
evaluator.evaluate();
const IndexMask selection = evaluator.get_evaluated_selection_as_mask();
- const VArray<float3> &scales = evaluator.get_evaluated<float3>(0);
- const VArray<float3> &pivots = evaluator.get_evaluated<float3>(1);
- const VArray<bool> &local_spaces = evaluator.get_evaluated<bool>(2);
+ const VArray<float3> scales = evaluator.get_evaluated<float3>(0);
+ const VArray<float3> pivots = evaluator.get_evaluated<float3>(1);
+ const VArray<bool> local_spaces = evaluator.get_evaluated<bool>(2);
MutableSpan<float4x4> instance_transforms = instances_component.instance_transforms();
diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_curve_handles.cc b/source/blender/nodes/geometry/nodes/node_geo_set_curve_handles.cc
index d2082924fa7..fc3cb7006bb 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_set_curve_handles.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_set_curve_handles.cc
@@ -75,19 +75,19 @@ static void set_position_in_component(CurveComponent &component,
const Field<float3> &offset_field)
{
GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_POINT};
- const int domain_num = component.attribute_domain_num(ATTR_DOMAIN_POINT);
- if (domain_num == 0) {
+ const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_POINT);
+ if (domain_size == 0) {
return;
}
- fn::FieldEvaluator evaluator{field_context, domain_num};
+ fn::FieldEvaluator evaluator{field_context, domain_size};
evaluator.set_selection(selection_field);
evaluator.add(position_field);
evaluator.add(offset_field);
evaluator.evaluate();
const IndexMask selection = evaluator.get_evaluated_selection_as_mask();
- const VArray<float3> &new_positions = evaluator.get_evaluated<float3>(0);
- const VArray<float3> &new_offsets = evaluator.get_evaluated<float3>(1);
+ const VArray<float3> new_positions = evaluator.get_evaluated<float3>(0);
+ const VArray<float3> new_offsets = evaluator.get_evaluated<float3>(1);
Curves &curves_id = *component.get_for_write();
bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id.geometry);
@@ -146,8 +146,8 @@ static void node_geo_exec(GeoNodeExecParams params)
}
has_curves = true;
const CurveComponent &component = *geometry_set.get_component_for_read<CurveComponent>();
- if (!component.attribute_exists("handle_left") ||
- !component.attribute_exists("handle_right")) {
+ const AttributeAccessor attributes = *component.attributes();
+ if (!attributes.contains("handle_left") || !attributes.contains("handle_right")) {
return;
}
has_bezier = true;
diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_curve_radius.cc b/source/blender/nodes/geometry/nodes/node_geo_set_curve_radius.cc
index 4c84093bfcb..e4fae95b5a5 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_set_curve_radius.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_set_curve_radius.cc
@@ -10,7 +10,7 @@ static void node_declare(NodeDeclarationBuilder &b)
b.add_input<decl::Bool>(N_("Selection")).default_value(true).hide_value().supports_field();
b.add_input<decl::Float>(N_("Radius"))
.min(0.0f)
- .default_value(1.0f)
+ .default_value(0.005f)
.supports_field()
.subtype(PROP_DISTANCE);
b.add_output<decl::Geometry>(N_("Curve"));
@@ -20,21 +20,22 @@ static void set_radius_in_component(GeometryComponent &component,
const Field<bool> &selection_field,
const Field<float> &radius_field)
{
- GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_POINT};
- const int domain_num = component.attribute_domain_num(ATTR_DOMAIN_POINT);
- if (domain_num == 0) {
+ const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_POINT);
+ if (domain_size == 0) {
return;
}
+ MutableAttributeAccessor attributes = *component.attributes_for_write();
+ GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_POINT};
- OutputAttribute_Typed<float> radii = component.attribute_try_get_for_output_only<float>(
- "radius", ATTR_DOMAIN_POINT);
+ AttributeWriter<float> radii = attributes.lookup_or_add_for_write<float>("radius",
+ ATTR_DOMAIN_POINT);
- fn::FieldEvaluator evaluator{field_context, domain_num};
+ fn::FieldEvaluator evaluator{field_context, domain_size};
evaluator.set_selection(selection_field);
- evaluator.add_with_destination(radius_field, radii.varray());
+ evaluator.add_with_destination(radius_field, radii.varray);
evaluator.evaluate();
- radii.save();
+ radii.finish();
}
static void node_geo_exec(GeoNodeExecParams params)
diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_curve_tilt.cc b/source/blender/nodes/geometry/nodes/node_geo_set_curve_tilt.cc
index 8b1e5935a61..2211ac62727 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_set_curve_tilt.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_set_curve_tilt.cc
@@ -16,21 +16,23 @@ static void set_tilt_in_component(GeometryComponent &component,
const Field<bool> &selection_field,
const Field<float> &tilt_field)
{
- GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_POINT};
- const int domain_num = component.attribute_domain_num(ATTR_DOMAIN_POINT);
- if (domain_num == 0) {
+ const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_POINT);
+ if (domain_size == 0) {
return;
}
+ MutableAttributeAccessor attributes = *component.attributes_for_write();
+
+ GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_POINT};
- OutputAttribute_Typed<float> tilts = component.attribute_try_get_for_output_only<float>(
- "tilt", ATTR_DOMAIN_POINT);
+ AttributeWriter<float> tilts = attributes.lookup_or_add_for_write<float>("tilt",
+ ATTR_DOMAIN_POINT);
- fn::FieldEvaluator evaluator{field_context, domain_num};
+ fn::FieldEvaluator evaluator{field_context, domain_size};
evaluator.set_selection(selection_field);
- evaluator.add_with_destination(tilt_field, tilts.varray());
+ evaluator.add_with_destination(tilt_field, tilts.varray);
evaluator.evaluate();
- tilts.save();
+ tilts.finish();
}
static void node_geo_exec(GeoNodeExecParams params)
diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_id.cc b/source/blender/nodes/geometry/nodes/node_geo_set_id.cc
index 87d48daddea..fbb2ecbb799 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_set_id.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_set_id.cc
@@ -19,34 +19,34 @@ static void set_id_in_component(GeometryComponent &component,
const eAttrDomain domain = (component.type() == GEO_COMPONENT_TYPE_INSTANCES) ?
ATTR_DOMAIN_INSTANCE :
ATTR_DOMAIN_POINT;
- GeometryComponentFieldContext field_context{component, domain};
- const int domain_num = component.attribute_domain_num(domain);
- if (domain_num == 0) {
+ const int domain_size = component.attribute_domain_size(domain);
+ if (domain_size == 0) {
return;
}
+ MutableAttributeAccessor attributes = *component.attributes_for_write();
+ GeometryComponentFieldContext field_context{component, domain};
- fn::FieldEvaluator evaluator{field_context, domain_num};
+ fn::FieldEvaluator evaluator{field_context, domain_size};
evaluator.set_selection(selection_field);
/* Since adding the ID attribute can change the result of the field evaluation (the random value
* node uses the index if the ID is unavailable), make sure that it isn't added before evaluating
* the field. However, as an optimization, use a faster code path when it already exists. */
- if (component.attribute_exists("id")) {
- OutputAttribute_Typed<int> id_attribute = component.attribute_try_get_for_output_only<int>(
- "id", domain);
- evaluator.add_with_destination(id_field, id_attribute.varray());
+ if (attributes.contains("id")) {
+ AttributeWriter<int> id_attribute = attributes.lookup_or_add_for_write<int>("id", domain);
+ evaluator.add_with_destination(id_field, id_attribute.varray);
evaluator.evaluate();
- id_attribute.save();
+ id_attribute.finish();
}
else {
evaluator.add(id_field);
evaluator.evaluate();
const IndexMask selection = evaluator.get_evaluated_selection_as_mask();
- const VArray<int> &result_ids = evaluator.get_evaluated<int>(0);
- OutputAttribute_Typed<int> id_attribute = component.attribute_try_get_for_output_only<int>(
- "id", domain);
- result_ids.materialize(selection, id_attribute.as_span());
- id_attribute.save();
+ const VArray<int> result_ids = evaluator.get_evaluated<int>(0);
+ SpanAttributeWriter<int> id_attribute = attributes.lookup_or_add_for_write_span<int>("id",
+ domain);
+ result_ids.materialize(selection, id_attribute.span);
+ id_attribute.finish();
}
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_material_index.cc b/source/blender/nodes/geometry/nodes/node_geo_set_material_index.cc
index 58613dae832..0dc89bb7ef4 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_set_material_index.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_set_material_index.cc
@@ -16,20 +16,21 @@ static void set_material_index_in_component(GeometryComponent &component,
const Field<bool> &selection_field,
const Field<int> &index_field)
{
- GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_FACE};
- const int domain_num = component.attribute_domain_num(ATTR_DOMAIN_FACE);
- if (domain_num == 0) {
+ const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_FACE);
+ if (domain_size == 0) {
return;
}
+ MutableAttributeAccessor attributes = *component.attributes_for_write();
+ GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_FACE};
- OutputAttribute_Typed<int> indices = component.attribute_try_get_for_output_only<int>(
- "material_index", ATTR_DOMAIN_FACE);
+ AttributeWriter<int> indices = attributes.lookup_or_add_for_write<int>("material_index",
+ ATTR_DOMAIN_FACE);
- fn::FieldEvaluator evaluator{field_context, domain_num};
+ fn::FieldEvaluator evaluator{field_context, domain_size};
evaluator.set_selection(selection_field);
- evaluator.add_with_destination(index_field, indices.varray());
+ evaluator.add_with_destination(index_field, indices.varray);
evaluator.evaluate();
- indices.save();
+ indices.finish();
}
static void node_geo_exec(GeoNodeExecParams params)
diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_point_radius.cc b/source/blender/nodes/geometry/nodes/node_geo_set_point_radius.cc
index 571bead9743..da7977a4fb4 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_set_point_radius.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_set_point_radius.cc
@@ -20,21 +20,22 @@ static void set_radius_in_component(GeometryComponent &component,
const Field<bool> &selection_field,
const Field<float> &radius_field)
{
- GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_POINT};
- const int domain_num = component.attribute_domain_num(ATTR_DOMAIN_POINT);
- if (domain_num == 0) {
+ const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_POINT);
+ if (domain_size == 0) {
return;
}
+ MutableAttributeAccessor attributes = *component.attributes_for_write();
+ GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_POINT};
- OutputAttribute_Typed<float> radii = component.attribute_try_get_for_output_only<float>(
- "radius", ATTR_DOMAIN_POINT);
+ AttributeWriter<float> radii = attributes.lookup_or_add_for_write<float>("radius",
+ ATTR_DOMAIN_POINT);
- fn::FieldEvaluator evaluator{field_context, domain_num};
+ fn::FieldEvaluator evaluator{field_context, domain_size};
evaluator.set_selection(selection_field);
- evaluator.add_with_destination(radius_field, radii.varray());
+ evaluator.add_with_destination(radius_field, radii.varray);
evaluator.evaluate();
- radii.save();
+ radii.finish();
}
static void node_geo_exec(GeoNodeExecParams params)
diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_position.cc b/source/blender/nodes/geometry/nodes/node_geo_set_position.cc
index e9ed87e552f..880252de4fa 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_set_position.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_set_position.cc
@@ -25,12 +25,10 @@ static void node_declare(NodeDeclarationBuilder &b)
static void set_computed_position_and_offset(GeometryComponent &component,
const VArray<float3> &in_positions,
const VArray<float3> &in_offsets,
- const eAttrDomain domain,
const IndexMask selection)
{
-
- OutputAttribute_Typed<float3> positions = component.attribute_try_get_for_output<float3>(
- "position", domain, {0, 0, 0});
+ MutableAttributeAccessor attributes = *component.attributes_for_write();
+ AttributeWriter<float3> positions = attributes.lookup_for_write<float3>("position");
const int grain_size = 10000;
@@ -38,7 +36,7 @@ static void set_computed_position_and_offset(GeometryComponent &component,
case GEO_COMPONENT_TYPE_MESH: {
Mesh *mesh = static_cast<MeshComponent &>(component).get_for_write();
MutableSpan<MVert> mverts{mesh->mvert, mesh->totvert};
- if (in_positions.is_same(positions.varray())) {
+ if (in_positions.is_same(positions.varray)) {
devirtualize_varray(in_offsets, [&](const auto in_offsets) {
threading::parallel_for(
selection.index_range(), grain_size, [&](const IndexRange range) {
@@ -67,18 +65,13 @@ static void set_computed_position_and_offset(GeometryComponent &component,
CurveComponent &curve_component = static_cast<CurveComponent &>(component);
Curves &curves_id = *curve_component.get_for_write();
bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id.geometry);
- if (component.attribute_exists("handle_right") &&
- component.attribute_exists("handle_left")) {
- OutputAttribute_Typed<float3> handle_right_attribute =
- component.attribute_try_get_for_output<float3>(
- "handle_right", ATTR_DOMAIN_POINT, {0, 0, 0});
- OutputAttribute_Typed<float3> handle_left_attribute =
- component.attribute_try_get_for_output<float3>(
- "handle_left", ATTR_DOMAIN_POINT, {0, 0, 0});
- MutableSpan<float3> handle_right = handle_right_attribute.as_span();
- MutableSpan<float3> handle_left = handle_left_attribute.as_span();
-
- MutableSpan<float3> out_positions_span = positions.as_span();
+ if (attributes.contains("handle_right") && attributes.contains("handle_left")) {
+ SpanAttributeWriter<float3> handle_right_attribute =
+ attributes.lookup_or_add_for_write_span<float3>("handle_right", ATTR_DOMAIN_POINT);
+ SpanAttributeWriter<float3> handle_left_attribute =
+ attributes.lookup_or_add_for_write_span<float3>("handle_left", ATTR_DOMAIN_POINT);
+
+ MutableVArraySpan<float3> out_positions_span = positions.varray;
devirtualize_varray2(
in_positions, in_offsets, [&](const auto in_positions, const auto in_offsets) {
threading::parallel_for(
@@ -86,15 +79,16 @@ static void set_computed_position_and_offset(GeometryComponent &component,
for (const int i : selection.slice(range)) {
const float3 new_position = in_positions[i] + in_offsets[i];
const float3 delta = new_position - out_positions_span[i];
- handle_right[i] += delta;
- handle_left[i] += delta;
+ handle_right_attribute.span[i] += delta;
+ handle_left_attribute.span[i] += delta;
out_positions_span[i] = new_position;
}
});
});
- handle_right_attribute.save();
- handle_left_attribute.save();
+ out_positions_span.save();
+ handle_right_attribute.finish();
+ handle_left_attribute.finish();
/* Automatic Bezier handles must be recalculated based on the new positions. */
curves.calculate_bezier_auto_handles();
@@ -105,8 +99,8 @@ static void set_computed_position_and_offset(GeometryComponent &component,
}
}
default: {
- MutableSpan<float3> out_positions_span = positions.as_span();
- if (in_positions.is_same(positions.varray())) {
+ MutableVArraySpan<float3> out_positions_span = positions.varray;
+ if (in_positions.is_same(positions.varray)) {
devirtualize_varray(in_offsets, [&](const auto in_offsets) {
threading::parallel_for(
selection.index_range(), grain_size, [&](const IndexRange range) {
@@ -127,11 +121,12 @@ static void set_computed_position_and_offset(GeometryComponent &component,
});
});
}
+ out_positions_span.save();
break;
}
}
- positions.save();
+ positions.finish();
}
static void set_position_in_component(GeometryComponent &component,
@@ -142,21 +137,22 @@ static void set_position_in_component(GeometryComponent &component,
eAttrDomain domain = component.type() == GEO_COMPONENT_TYPE_INSTANCES ? ATTR_DOMAIN_INSTANCE :
ATTR_DOMAIN_POINT;
GeometryComponentFieldContext field_context{component, domain};
- const int domain_num = component.attribute_domain_num(domain);
- if (domain_num == 0) {
+ const int domain_size = component.attribute_domain_size(domain);
+ if (domain_size == 0) {
return;
}
- fn::FieldEvaluator evaluator{field_context, domain_num};
+ fn::FieldEvaluator evaluator{field_context, domain_size};
evaluator.set_selection(selection_field);
evaluator.add(position_field);
evaluator.add(offset_field);
evaluator.evaluate();
const IndexMask selection = evaluator.get_evaluated_selection_as_mask();
- const VArray<float3> &positions_input = evaluator.get_evaluated<float3>(0);
- const VArray<float3> &offsets_input = evaluator.get_evaluated<float3>(1);
- set_computed_position_and_offset(component, positions_input, offsets_input, domain, selection);
+
+ const VArray<float3> positions_input = evaluator.get_evaluated<float3>(0);
+ const VArray<float3> offsets_input = evaluator.get_evaluated<float3>(1);
+ set_computed_position_and_offset(component, positions_input, offsets_input, selection);
}
static void node_geo_exec(GeoNodeExecParams params)
diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_shade_smooth.cc b/source/blender/nodes/geometry/nodes/node_geo_set_shade_smooth.cc
index b98fbd0a0fe..e0cf0f98d58 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_set_shade_smooth.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_set_shade_smooth.cc
@@ -16,21 +16,23 @@ static void set_smooth_in_component(GeometryComponent &component,
const Field<bool> &selection_field,
const Field<bool> &shade_field)
{
- GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_FACE};
- const int domain_num = component.attribute_domain_num(ATTR_DOMAIN_FACE);
- if (domain_num == 0) {
+ const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_FACE);
+ if (domain_size == 0) {
return;
}
+ MutableAttributeAccessor attributes = *component.attributes_for_write();
+
+ GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_FACE};
- OutputAttribute_Typed<bool> shades = component.attribute_try_get_for_output_only<bool>(
- "shade_smooth", ATTR_DOMAIN_FACE);
+ AttributeWriter<bool> shades = attributes.lookup_or_add_for_write<bool>("shade_smooth",
+ ATTR_DOMAIN_FACE);
- fn::FieldEvaluator evaluator{field_context, domain_num};
+ fn::FieldEvaluator evaluator{field_context, domain_size};
evaluator.set_selection(selection_field);
- evaluator.add_with_destination(shade_field, shades.varray());
+ evaluator.add_with_destination(shade_field, shades.varray);
evaluator.evaluate();
- shades.save();
+ shades.finish();
}
static void node_geo_exec(GeoNodeExecParams params)
diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_spline_cyclic.cc b/source/blender/nodes/geometry/nodes/node_geo_set_spline_cyclic.cc
index 976857883f0..a35d8d66558 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_set_spline_cyclic.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_set_spline_cyclic.cc
@@ -16,21 +16,23 @@ static void set_cyclic_in_component(GeometryComponent &component,
const Field<bool> &selection_field,
const Field<bool> &cyclic_field)
{
- GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_CURVE};
- const int domain_num = component.attribute_domain_num(ATTR_DOMAIN_CURVE);
- if (domain_num == 0) {
+ const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_CURVE);
+ if (domain_size == 0) {
return;
}
+ MutableAttributeAccessor attributes = *component.attributes_for_write();
+
+ GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_CURVE};
- OutputAttribute_Typed<bool> cyclics = component.attribute_try_get_for_output_only<bool>(
- "cyclic", ATTR_DOMAIN_CURVE);
+ AttributeWriter<bool> cyclics = attributes.lookup_or_add_for_write<bool>("cyclic",
+ ATTR_DOMAIN_CURVE);
- fn::FieldEvaluator evaluator{field_context, domain_num};
+ fn::FieldEvaluator evaluator{field_context, domain_size};
evaluator.set_selection(selection_field);
- evaluator.add_with_destination(cyclic_field, cyclics.varray());
+ evaluator.add_with_destination(cyclic_field, cyclics.varray);
evaluator.evaluate();
- cyclics.save();
+ cyclics.finish();
}
static void node_geo_exec(GeoNodeExecParams params)
diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_spline_resolution.cc b/source/blender/nodes/geometry/nodes/node_geo_set_spline_resolution.cc
index 8b665376c01..fcebc1116d7 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_set_spline_resolution.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_set_spline_resolution.cc
@@ -16,21 +16,23 @@ static void set_resolution_in_component(GeometryComponent &component,
const Field<bool> &selection_field,
const Field<int> &resolution_field)
{
- GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_CURVE};
- const int domain_num = component.attribute_domain_num(ATTR_DOMAIN_CURVE);
- if (domain_num == 0) {
+ const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_CURVE);
+ if (domain_size == 0) {
return;
}
+ MutableAttributeAccessor attributes = *component.attributes_for_write();
+
+ GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_CURVE};
- OutputAttribute_Typed<int> resolutions = component.attribute_try_get_for_output_only<int>(
- "resolution", ATTR_DOMAIN_CURVE);
+ AttributeWriter<int> resolutions = attributes.lookup_or_add_for_write<int>("resolution",
+ ATTR_DOMAIN_CURVE);
- fn::FieldEvaluator evaluator{field_context, domain_num};
+ fn::FieldEvaluator evaluator{field_context, domain_size};
evaluator.set_selection(selection_field);
- evaluator.add_with_destination(resolution_field, resolutions.varray());
+ evaluator.add_with_destination(resolution_field, resolutions.varray);
evaluator.evaluate();
- resolutions.save();
+ resolutions.finish();
}
static void node_geo_exec(GeoNodeExecParams params)
diff --git a/source/blender/nodes/geometry/nodes/node_geo_store_named_attribute.cc b/source/blender/nodes/geometry/nodes/node_geo_store_named_attribute.cc
index abac6d8d6b3..1d3beb8be96 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_store_named_attribute.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_store_named_attribute.cc
@@ -1,8 +1,12 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
+#include <atomic>
+
#include "UI_interface.h"
#include "UI_resources.h"
+#include "RNA_enum_types.h"
+
#include "NOD_socket_search_link.hh"
#include "BKE_type_conversions.hh"
@@ -85,43 +89,49 @@ static void node_gather_link_searches(GatherLinkSearchOpParams &params)
static void try_capture_field_on_geometry(GeometryComponent &component,
const StringRef name,
const eAttrDomain domain,
- const GField &field)
+ const GField &field,
+ std::atomic<bool> &r_failure)
{
+ MutableAttributeAccessor attributes = *component.attributes_for_write();
+ const int domain_size = attributes.domain_size(domain);
+ if (domain_size == 0) {
+ return;
+ }
+
GeometryComponentFieldContext field_context{component, domain};
- const int domain_num = component.attribute_domain_num(domain);
- const IndexMask mask{IndexMask(domain_num)};
+ const IndexMask mask{IndexMask(domain_size)};
const CPPType &type = field.cpp_type();
const eCustomDataType data_type = bke::cpp_type_to_custom_data_type(type);
/* Could avoid allocating a new buffer if:
- * - We are writing to an attribute that exists already.
+ * - We are writing to an attribute that exists already with the correct domain and type.
* - The field does not depend on that attribute (we can't easily check for that yet). */
- void *buffer = MEM_mallocN(type.size() * domain_num, __func__);
+ void *buffer = MEM_mallocN(type.size() * domain_size, __func__);
fn::FieldEvaluator evaluator{field_context, &mask};
- evaluator.add_with_destination(field, GMutableSpan{type, buffer, domain_num});
+ evaluator.add_with_destination(field, GMutableSpan{type, buffer, domain_size});
evaluator.evaluate();
- component.attribute_try_delete(name);
- if (component.attribute_exists(name)) {
- WriteAttributeLookup write_attribute = component.attribute_try_get_for_write(name);
- if (write_attribute && write_attribute.domain == domain &&
- write_attribute.varray.type() == type) {
- write_attribute.varray.set_all(buffer);
- write_attribute.tag_modified_fn();
- }
- else {
- /* Cannot change type of built-in attribute. */
- }
- type.destruct_n(buffer, domain_num);
- MEM_freeN(buffer);
- }
- else {
- if (!component.attribute_try_create(name, domain, data_type, AttributeInitMove{buffer})) {
+ if (GAttributeWriter attribute = attributes.lookup_for_write(name)) {
+ if (attribute.domain == domain && attribute.varray.type() == type) {
+ attribute.varray.set_all(buffer);
+ attribute.finish();
+ type.destruct_n(buffer, domain_size);
MEM_freeN(buffer);
+ return;
}
}
+ attributes.remove(name);
+ if (attributes.add(name, domain, data_type, bke::AttributeInitMove{buffer})) {
+ return;
+ }
+
+ /* If the name corresponds to a builtin attribute, removing the attribute might fail if
+ * it's required, and adding the attribute might fail if the domain or type is incorrect. */
+ type.destruct_n(buffer, domain_size);
+ MEM_freeN(buffer);
+ r_failure = true;
}
static void node_geo_exec(GeoNodeExecParams params)
@@ -172,12 +182,14 @@ static void node_geo_exec(GeoNodeExecParams params)
break;
}
+ std::atomic<bool> failure = false;
+
/* Run on the instances component separately to only affect the top level of instances. */
if (domain == ATTR_DOMAIN_INSTANCE) {
if (geometry_set.has_instances()) {
GeometryComponent &component = geometry_set.get_component_for_write(
GEO_COMPONENT_TYPE_INSTANCES);
- try_capture_field_on_geometry(component, name, domain, field);
+ try_capture_field_on_geometry(component, name, domain, field, failure);
}
}
else {
@@ -186,12 +198,26 @@ static void node_geo_exec(GeoNodeExecParams params)
{GEO_COMPONENT_TYPE_MESH, GEO_COMPONENT_TYPE_POINT_CLOUD, GEO_COMPONENT_TYPE_CURVE}) {
if (geometry_set.has(type)) {
GeometryComponent &component = geometry_set.get_component_for_write(type);
- try_capture_field_on_geometry(component, name, domain, field);
+ try_capture_field_on_geometry(component, name, domain, field, failure);
}
}
});
}
+ if (failure) {
+ const char *domain_name = nullptr;
+ RNA_enum_name_from_value(rna_enum_attribute_domain_items, domain, &domain_name);
+ const char *type_name = nullptr;
+ RNA_enum_name_from_value(rna_enum_attribute_type_items, data_type, &type_name);
+ char *message = BLI_sprintfN(
+ TIP_("Failed to write to attribute \"%s\" with domain \"%s\" and type \"%s\""),
+ name.c_str(),
+ TIP_(domain_name),
+ TIP_(type_name));
+ params.error_message_add(NodeWarningType::Warning, message);
+ MEM_freeN(message);
+ }
+
params.set_output("Geometry", std::move(geometry_set));
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_string_to_curves.cc b/source/blender/nodes/geometry/nodes/node_geo_string_to_curves.cc
index efcc8809c9c..afd7db6604d 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_string_to_curves.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_string_to_curves.cc
@@ -157,12 +157,18 @@ struct TextLayout {
float final_font_size;
};
-static TextLayout get_text_layout(GeoNodeExecParams &params)
+static std::optional<TextLayout> get_text_layout(GeoNodeExecParams &params)
{
+ VFont *vfont = reinterpret_cast<VFont *>(params.node().id);
+ if (!vfont) {
+ params.error_message_add(NodeWarningType::Error, TIP_("Font not specified"));
+ return std::nullopt;
+ }
+
TextLayout layout;
layout.text = params.extract_input<std::string>("String");
if (layout.text.empty()) {
- return {};
+ return std::nullopt;
}
const NodeGeometryStringToCurves &storage = node_storage(params.node());
@@ -181,7 +187,6 @@ static TextLayout get_text_layout(GeoNodeExecParams &params)
const float textbox_h = overflow == GEO_NODE_STRING_TO_CURVES_MODE_OVERFLOW ?
0.0f :
params.extract_input<float>("Text Box Height");
- VFont *vfont = (VFont *)params.node().id;
Curve cu = dna::shallow_zero_initialize();
cu.type = OB_FONT;
@@ -330,13 +335,14 @@ static void create_attributes(GeoNodeExecParams &params,
const TextLayout &layout,
InstancesComponent &instances)
{
+ MutableAttributeAccessor attributes = *instances.attributes_for_write();
+
if (params.output_is_required("Line")) {
StrongAnonymousAttributeID line_id = StrongAnonymousAttributeID("Line");
- OutputAttribute_Typed<int> line_attribute = instances.attribute_try_get_for_output_only<int>(
+ SpanAttributeWriter<int> line_attribute = attributes.lookup_or_add_for_write_only_span<int>(
line_id.get(), ATTR_DOMAIN_INSTANCE);
- MutableSpan<int> lines = line_attribute.as_span();
- lines.copy_from(layout.line_numbers);
- line_attribute.save();
+ line_attribute.span.copy_from(layout.line_numbers);
+ line_attribute.finish();
params.set_output("Line",
AnonymousAttributeFieldInput::Create<int>(std::move(line_id),
params.attribute_producer_name()));
@@ -344,15 +350,14 @@ static void create_attributes(GeoNodeExecParams &params,
if (params.output_is_required("Pivot Point")) {
StrongAnonymousAttributeID pivot_id = StrongAnonymousAttributeID("Pivot");
- OutputAttribute_Typed<float3> pivot_attribute =
- instances.attribute_try_get_for_output_only<float3>(pivot_id.get(), ATTR_DOMAIN_INSTANCE);
- MutableSpan<float3> pivots = pivot_attribute.as_span();
+ SpanAttributeWriter<float3> pivot_attribute =
+ attributes.lookup_or_add_for_write_only_span<float3>(pivot_id.get(), ATTR_DOMAIN_INSTANCE);
for (const int i : layout.char_codes.index_range()) {
- pivots[i] = layout.pivot_points.lookup(layout.char_codes[i]);
+ pivot_attribute.span[i] = layout.pivot_points.lookup(layout.char_codes[i]);
}
- pivot_attribute.save();
+ pivot_attribute.finish();
params.set_output("Pivot Point",
AnonymousAttributeFieldInput::Create<float3>(
std::move(pivot_id), params.attribute_producer_name()));
@@ -361,15 +366,19 @@ static void create_attributes(GeoNodeExecParams &params,
static void node_geo_exec(GeoNodeExecParams params)
{
- TextLayout layout = get_text_layout(params);
+ std::optional<TextLayout> layout = get_text_layout(params);
+ if (!layout) {
+ params.set_default_remaining_outputs();
+ return;
+ }
const NodeGeometryStringToCurves &storage =
*(const NodeGeometryStringToCurves *)params.node().storage;
if (storage.overflow == GEO_NODE_STRING_TO_CURVES_MODE_TRUNCATE) {
- params.set_output("Remainder", std::move(layout.truncated_text));
+ params.set_output("Remainder", std::move(layout->truncated_text));
}
- if (layout.positions.size() == 0) {
+ if (layout->positions.size() == 0) {
params.set_output("Curve Instances", GeometrySet());
params.set_default_remaining_outputs();
return;
@@ -378,9 +387,9 @@ static void node_geo_exec(GeoNodeExecParams params)
/* Create and add instances. */
GeometrySet geometry_set_out;
InstancesComponent &instances = geometry_set_out.get_component_for_write<InstancesComponent>();
- Map<int, int> char_handles = create_curve_instances(params, layout, instances);
- add_instances_from_handles(instances, char_handles, layout);
- create_attributes(params, layout, instances);
+ Map<int, int> char_handles = create_curve_instances(params, *layout, instances);
+ add_instances_from_handles(instances, char_handles, *layout);
+ create_attributes(params, *layout, instances);
params.set_output("Curve Instances", std::move(geometry_set_out));
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc b/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc
index 9eda5bb34ff..eda6a51d412 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc
@@ -6,6 +6,7 @@
#include "DNA_meshdata_types.h"
#include "DNA_modifier_types.h"
+#include "BKE_attribute.hh"
#include "BKE_mesh.h"
#include "BKE_subdiv.h"
#include "BKE_subdiv_mesh.h"
@@ -79,10 +80,11 @@ static void write_vertex_creases(Mesh &mesh, const VArray<float> &crease_varray)
static void write_edge_creases(MeshComponent &mesh, const VArray<float> &crease_varray)
{
- OutputAttribute_Typed<float> attribute = mesh.attribute_try_get_for_output_only<float>(
- "crease", ATTR_DOMAIN_EDGE);
- materialize_and_clamp_creases(crease_varray, attribute.as_span());
- attribute.save();
+ bke::SpanAttributeWriter<float> attribute =
+ mesh.attributes_for_write()->lookup_or_add_for_write_only_span<float>("crease",
+ ATTR_DOMAIN_EDGE);
+ materialize_and_clamp_creases(crease_varray, attribute.span);
+ attribute.finish();
}
static bool varray_is_nonzero(const VArray<float> &varray)
@@ -118,8 +120,8 @@ static void node_geo_exec(GeoNodeExecParams params)
}
const MeshComponent &mesh_component = *geometry_set.get_component_for_read<MeshComponent>();
- const int verts_num = mesh_component.attribute_domain_num(ATTR_DOMAIN_POINT);
- const int edges_num = mesh_component.attribute_domain_num(ATTR_DOMAIN_EDGE);
+ const int verts_num = mesh_component.attribute_domain_size(ATTR_DOMAIN_POINT);
+ const int edges_num = mesh_component.attribute_domain_size(ATTR_DOMAIN_EDGE);
if (verts_num == 0 || edges_num == 0) {
return;
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_transfer_attribute.cc b/source/blender/nodes/geometry/nodes/node_geo_transfer_attribute.cc
index 0af6c76feaf..cd75822f665 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_transfer_attribute.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_transfer_attribute.cc
@@ -365,7 +365,7 @@ static bool component_is_available(const GeometrySet &geometry,
if (component.is_empty()) {
return false;
}
- return component.attribute_domain_num(domain) != 0;
+ return component.attribute_domain_size(domain) != 0;
}
/**
@@ -433,7 +433,7 @@ class NearestInterpolatedTransferFunction : public fn::MultiFunction {
{
const MeshComponent &mesh_component = *source_.get_component_for_read<MeshComponent>();
source_context_.emplace(GeometryComponentFieldContext{mesh_component, domain_});
- const int domain_num = mesh_component.attribute_domain_num(domain_);
+ const int domain_num = mesh_component.attribute_domain_size(domain_);
source_evaluator_ = std::make_unique<FieldEvaluator>(*source_context_, domain_num);
source_evaluator_->add(src_field_);
source_evaluator_->evaluate();
@@ -578,7 +578,7 @@ class NearestTransferFunction : public fn::MultiFunction {
{
if (use_mesh_) {
const MeshComponent &mesh = *source_.get_component_for_read<MeshComponent>();
- const int domain_num = mesh.attribute_domain_num(domain_);
+ const int domain_num = mesh.attribute_domain_size(domain_);
mesh_context_.emplace(GeometryComponentFieldContext(mesh, domain_));
mesh_evaluator_ = std::make_unique<FieldEvaluator>(*mesh_context_, domain_num);
mesh_evaluator_->add(src_field_);
@@ -588,7 +588,7 @@ class NearestTransferFunction : public fn::MultiFunction {
if (use_points_) {
const PointCloudComponent &points = *source_.get_component_for_read<PointCloudComponent>();
- const int domain_num = points.attribute_domain_num(domain_);
+ const int domain_num = points.attribute_domain_size(domain_);
point_context_.emplace(GeometryComponentFieldContext(points, domain_));
point_evaluator_ = std::make_unique<FieldEvaluator>(*point_context_, domain_num);
point_evaluator_->add(src_field_);
@@ -658,7 +658,7 @@ class IndexTransferFunction : public fn::MultiFunction {
if (component == nullptr) {
return;
}
- const int domain_num = component->attribute_domain_num(domain_);
+ const int domain_num = component->attribute_domain_size(domain_);
geometry_context_.emplace(GeometryComponentFieldContext(*component, domain_));
evaluator_ = std::make_unique<FieldEvaluator>(*geometry_context_, domain_num);
evaluator_->add(src_field_);
diff --git a/source/blender/nodes/geometry/nodes/node_geo_transform.cc b/source/blender/nodes/geometry/nodes/node_geo_transform.cc
index e95db205920..945d5fbdcac 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_transform.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_transform.cc
@@ -47,21 +47,24 @@ static void transform_mesh(Mesh &mesh, const float4x4 &transform)
static void translate_pointcloud(PointCloud &pointcloud, const float3 translation)
{
- CustomData_duplicate_referenced_layer(&pointcloud.pdata, CD_PROP_FLOAT3, pointcloud.totpoint);
- BKE_pointcloud_update_customdata_pointers(&pointcloud);
- for (const int i : IndexRange(pointcloud.totpoint)) {
- add_v3_v3(pointcloud.co[i], translation);
+ MutableAttributeAccessor attributes = bke::pointcloud_attributes_for_write(pointcloud);
+ SpanAttributeWriter position = attributes.lookup_or_add_for_write_span<float3>(
+ "position", ATTR_DOMAIN_POINT);
+ for (const int i : position.span.index_range()) {
+ position.span[i] += translation;
}
+ position.finish();
}
static void transform_pointcloud(PointCloud &pointcloud, const float4x4 &transform)
{
- CustomData_duplicate_referenced_layer(&pointcloud.pdata, CD_PROP_FLOAT3, pointcloud.totpoint);
- BKE_pointcloud_update_customdata_pointers(&pointcloud);
- for (const int i : IndexRange(pointcloud.totpoint)) {
- float3 &co = *(float3 *)pointcloud.co[i];
- co = transform * co;
+ MutableAttributeAccessor attributes = bke::pointcloud_attributes_for_write(pointcloud);
+ SpanAttributeWriter position = attributes.lookup_or_add_for_write_span<float3>(
+ "position", ATTR_DOMAIN_POINT);
+ for (const int i : position.span.index_range()) {
+ position.span[i] = transform * position.span[i];
}
+ position.finish();
}
static void translate_instances(InstancesComponent &instances, const float3 translation)
@@ -120,6 +123,34 @@ static void translate_volume(Volume &volume, const float3 translation, const Dep
transform_volume(volume, float4x4::from_location(translation), depsgraph);
}
+static void transform_curve_edit_hints(bke::CurvesEditHints &edit_hints, const float4x4 &transform)
+{
+ if (edit_hints.positions.has_value()) {
+ for (float3 &pos : *edit_hints.positions) {
+ pos = transform * pos;
+ }
+ }
+ float3x3 deform_mat;
+ copy_m3_m4(deform_mat.values, transform.values);
+ if (edit_hints.deform_mats.has_value()) {
+ for (float3x3 &mat : *edit_hints.deform_mats) {
+ mat = deform_mat * mat;
+ }
+ }
+ else {
+ edit_hints.deform_mats.emplace(edit_hints.curves_id_orig.geometry.point_num, deform_mat);
+ }
+}
+
+static void translate_curve_edit_hints(bke::CurvesEditHints &edit_hints, const float3 &translation)
+{
+ if (edit_hints.positions.has_value()) {
+ for (float3 &pos : *edit_hints.positions) {
+ pos += translation;
+ }
+ }
+}
+
static void translate_geometry_set(GeometrySet &geometry,
const float3 translation,
const Depsgraph &depsgraph)
@@ -139,6 +170,9 @@ static void translate_geometry_set(GeometrySet &geometry,
if (geometry.has_instances()) {
translate_instances(geometry.get_component_for_write<InstancesComponent>(), translation);
}
+ if (bke::CurvesEditHints *curve_edit_hints = geometry.get_curve_edit_hints_for_write()) {
+ translate_curve_edit_hints(*curve_edit_hints, translation);
+ }
}
void transform_geometry_set(GeometrySet &geometry,
@@ -160,6 +194,9 @@ void transform_geometry_set(GeometrySet &geometry,
if (geometry.has_instances()) {
transform_instances(geometry.get_component_for_write<InstancesComponent>(), transform);
}
+ if (bke::CurvesEditHints *curve_edit_hints = geometry.get_curve_edit_hints_for_write()) {
+ transform_curve_edit_hints(*curve_edit_hints, transform);
+ }
}
void transform_mesh(Mesh &mesh,
diff --git a/source/blender/nodes/geometry/nodes/node_geo_translate_instances.cc b/source/blender/nodes/geometry/nodes/node_geo_translate_instances.cc
index 258c1ac3fba..ae538072e65 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_translate_instances.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_translate_instances.cc
@@ -26,8 +26,8 @@ static void translate_instances(GeoNodeExecParams &params, InstancesComponent &i
evaluator.evaluate();
const IndexMask selection = evaluator.get_evaluated_selection_as_mask();
- const VArray<float3> &translations = evaluator.get_evaluated<float3>(0);
- const VArray<bool> &local_spaces = evaluator.get_evaluated<bool>(1);
+ const VArray<float3> translations = evaluator.get_evaluated<float3>(0);
+ const VArray<bool> local_spaces = evaluator.get_evaluated<bool>(1);
MutableSpan<float4x4> instance_transforms = instances_component.instance_transforms();
diff --git a/source/blender/nodes/geometry/nodes/node_geo_triangulate.cc b/source/blender/nodes/geometry/nodes/node_geo_triangulate.cc
index e47dc22da04..992470e8279 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_triangulate.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_triangulate.cc
@@ -83,9 +83,9 @@ static void node_geo_exec(GeoNodeExecParams params)
GeometryComponent &component = geometry_set.get_component_for_write<MeshComponent>();
const Mesh &mesh_in = *geometry_set.get_mesh_for_read();
- const int domain_num = component.attribute_domain_num(ATTR_DOMAIN_FACE);
+ const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_FACE);
GeometryComponentFieldContext context{component, ATTR_DOMAIN_FACE};
- FieldEvaluator evaluator{context, domain_num};
+ FieldEvaluator evaluator{context, domain_size};
evaluator.add(selection_field);
evaluator.evaluate();
const IndexMask selection = evaluator.get_evaluated_as_mask(0);
diff --git a/source/blender/nodes/geometry/nodes/node_geo_uv_pack_islands.cc b/source/blender/nodes/geometry/nodes/node_geo_uv_pack_islands.cc
index feff6efc3f8..17413e64f7d 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_uv_pack_islands.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_uv_pack_islands.cc
@@ -40,7 +40,7 @@ static VArray<float3> construct_uv_gvarray(const MeshComponent &component,
return {};
}
- const int face_num = component.attribute_domain_num(ATTR_DOMAIN_FACE);
+ const int face_num = component.attribute_domain_size(ATTR_DOMAIN_FACE);
GeometryComponentFieldContext face_context{component, ATTR_DOMAIN_FACE};
FieldEvaluator face_evaluator{face_context, face_num};
face_evaluator.add(selection_field);
@@ -50,7 +50,7 @@ static VArray<float3> construct_uv_gvarray(const MeshComponent &component,
return {};
}
- const int corner_num = component.attribute_domain_num(ATTR_DOMAIN_CORNER);
+ const int corner_num = component.attribute_domain_size(ATTR_DOMAIN_CORNER);
GeometryComponentFieldContext corner_context{component, ATTR_DOMAIN_CORNER};
FieldEvaluator evaluator{corner_context, corner_num};
Array<float3> uv(corner_num);
@@ -88,7 +88,7 @@ static VArray<float3> construct_uv_gvarray(const MeshComponent &component,
GEO_uv_parametrizer_flush(handle);
GEO_uv_parametrizer_delete(handle);
- return component.attribute_try_adapt_domain<float3>(
+ return component.attributes()->adapt_domain<float3>(
VArray<float3>::ForContainer(std::move(uv)), ATTR_DOMAIN_CORNER, domain);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_uv_unwrap.cc b/source/blender/nodes/geometry/nodes/node_geo_uv_unwrap.cc
index bf960c5c809..03657f3e016 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_uv_unwrap.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_uv_unwrap.cc
@@ -65,7 +65,7 @@ static VArray<float3> construct_uv_gvarray(const MeshComponent &component,
return {};
}
- const int face_num = component.attribute_domain_num(ATTR_DOMAIN_FACE);
+ const int face_num = component.attribute_domain_size(ATTR_DOMAIN_FACE);
GeometryComponentFieldContext face_context{component, ATTR_DOMAIN_FACE};
FieldEvaluator face_evaluator{face_context, face_num};
face_evaluator.add(selection_field);
@@ -75,14 +75,14 @@ static VArray<float3> construct_uv_gvarray(const MeshComponent &component,
return {};
}
- const int edge_num = component.attribute_domain_num(ATTR_DOMAIN_EDGE);
+ const int edge_num = component.attribute_domain_size(ATTR_DOMAIN_EDGE);
GeometryComponentFieldContext edge_context{component, ATTR_DOMAIN_EDGE};
FieldEvaluator edge_evaluator{edge_context, edge_num};
edge_evaluator.add(seam_field);
edge_evaluator.evaluate();
const IndexMask seam = edge_evaluator.get_evaluated_as_mask(0);
- Array<float3> uv(mesh->totloop);
+ Array<float3> uv(mesh->totloop, float3(0));
ParamHandle *handle = GEO_uv_parametrizer_construct_begin();
for (const int mp_index : selection) {
@@ -121,12 +121,12 @@ static VArray<float3> construct_uv_gvarray(const MeshComponent &component,
GEO_uv_parametrizer_lscm_begin(handle, false, method == GEO_NODE_UV_UNWRAP_METHOD_ANGLE_BASED);
GEO_uv_parametrizer_lscm_solve(handle, nullptr, nullptr);
GEO_uv_parametrizer_lscm_end(handle);
- GEO_uv_parametrizer_average(handle, true);
+ GEO_uv_parametrizer_average(handle, true, false, false);
GEO_uv_parametrizer_pack(handle, margin, true, true);
GEO_uv_parametrizer_flush(handle);
GEO_uv_parametrizer_delete(handle);
- return component.attribute_try_adapt_domain<float3>(
+ return component.attributes()->adapt_domain<float3>(
VArray<float3>::ForContainer(std::move(uv)), ATTR_DOMAIN_CORNER, domain);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_volume_to_mesh.cc b/source/blender/nodes/geometry/nodes/node_geo_volume_to_mesh.cc
index e1d1c67b8c8..91429560ac8 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_volume_to_mesh.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_volume_to_mesh.cc
@@ -193,7 +193,7 @@ static void node_geo_exec(GeoNodeExecParams params)
geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
Mesh *mesh = create_mesh_from_volume(geometry_set, params);
geometry_set.replace_mesh(mesh);
- geometry_set.keep_only({GEO_COMPONENT_TYPE_MESH, GEO_COMPONENT_TYPE_INSTANCES});
+ geometry_set.keep_only_during_modify({GEO_COMPONENT_TYPE_MESH});
});
#else
params.error_message_add(NodeWarningType::Error,
diff --git a/source/blender/nodes/intern/geometry_nodes_eval_log.cc b/source/blender/nodes/intern/geometry_nodes_eval_log.cc
index 85dfdf03b82..55930dcb1ee 100644
--- a/source/blender/nodes/intern/geometry_nodes_eval_log.cc
+++ b/source/blender/nodes/intern/geometry_nodes_eval_log.cc
@@ -2,6 +2,7 @@
#include "NOD_geometry_nodes_eval_log.hh"
+#include "BKE_curves.hh"
#include "BKE_geometry_set_instances.hh"
#include "DNA_modifier_types.h"
@@ -228,7 +229,7 @@ GeometryValueLog::GeometryValueLog(const GeometrySet &geometry_set, bool log_ful
all_component_types,
true,
[&](const bke::AttributeIDRef &attribute_id,
- const AttributeMetaData &meta_data,
+ const bke::AttributeMetaData &meta_data,
const GeometryComponent &UNUSED(component)) {
if (attribute_id.is_named() && names.add(attribute_id.name())) {
this->attributes_.append({attribute_id.name(), meta_data.domain, meta_data.data_type});
@@ -241,21 +242,21 @@ GeometryValueLog::GeometryValueLog(const GeometrySet &geometry_set, bool log_ful
case GEO_COMPONENT_TYPE_MESH: {
const MeshComponent &mesh_component = *(const MeshComponent *)component;
MeshInfo &info = this->mesh_info.emplace();
- info.verts_num = mesh_component.attribute_domain_num(ATTR_DOMAIN_POINT);
- info.edges_num = mesh_component.attribute_domain_num(ATTR_DOMAIN_EDGE);
- info.faces_num = mesh_component.attribute_domain_num(ATTR_DOMAIN_FACE);
+ info.verts_num = mesh_component.attribute_domain_size(ATTR_DOMAIN_POINT);
+ info.edges_num = mesh_component.attribute_domain_size(ATTR_DOMAIN_EDGE);
+ info.faces_num = mesh_component.attribute_domain_size(ATTR_DOMAIN_FACE);
break;
}
case GEO_COMPONENT_TYPE_CURVE: {
const CurveComponent &curve_component = *(const CurveComponent *)component;
CurveInfo &info = this->curve_info.emplace();
- info.splines_num = curve_component.attribute_domain_num(ATTR_DOMAIN_CURVE);
+ info.splines_num = curve_component.attribute_domain_size(ATTR_DOMAIN_CURVE);
break;
}
case GEO_COMPONENT_TYPE_POINT_CLOUD: {
const PointCloudComponent &pointcloud_component = *(const PointCloudComponent *)component;
PointCloudInfo &info = this->pointcloud_info.emplace();
- info.points_num = pointcloud_component.attribute_domain_num(ATTR_DOMAIN_POINT);
+ info.points_num = pointcloud_component.attribute_domain_size(ATTR_DOMAIN_POINT);
break;
}
case GEO_COMPONENT_TYPE_INSTANCES: {
@@ -264,6 +265,17 @@ GeometryValueLog::GeometryValueLog(const GeometrySet &geometry_set, bool log_ful
info.instances_num = instances_component.instances_num();
break;
}
+ case GEO_COMPONENT_TYPE_EDIT: {
+ const GeometryComponentEditData &edit_component = *(
+ const GeometryComponentEditData *)component;
+ if (const bke::CurvesEditHints *curve_edit_hints =
+ edit_component.curves_edit_hints_.get()) {
+ EditDataInfo &info = this->edit_data_info.emplace();
+ info.has_deform_matrices = curve_edit_hints->deform_mats.has_value();
+ info.has_deformed_positions = curve_edit_hints->positions.has_value();
+ }
+ break;
+ }
case GEO_COMPONENT_TYPE_VOLUME: {
break;
}
diff --git a/source/blender/nodes/intern/node_common.cc b/source/blender/nodes/intern/node_common.cc
index b7c5f9570e4..6402ec3f3d6 100644
--- a/source/blender/nodes/intern/node_common.cc
+++ b/source/blender/nodes/intern/node_common.cc
@@ -37,6 +37,7 @@ using blender::MultiValueMap;
using blender::Set;
using blender::Stack;
using blender::StringRef;
+using blender::Vector;
/* -------------------------------------------------------------------- */
/** \name Node Group
@@ -160,6 +161,7 @@ static void group_verify_socket_list(bNodeTree &node_tree,
const bool ensure_extend_socket_exists)
{
ListBase old_sockets = verify_lb;
+ Vector<bNodeSocket *> ordered_old_sockets = old_sockets;
BLI_listbase_clear(&verify_lb);
LISTBASE_FOREACH (const bNodeSocket *, interface_socket, &interface_sockets) {
@@ -193,6 +195,19 @@ static void group_verify_socket_list(bNodeTree &node_tree,
LISTBASE_FOREACH_MUTABLE (bNodeSocket *, unused_socket, &old_sockets) {
nodeRemoveSocket(&node_tree, &node, unused_socket);
}
+
+ {
+ /* Check if new sockets match the old sockets. */
+ int index;
+ LISTBASE_FOREACH_INDEX (bNodeSocket *, new_socket, &verify_lb, index) {
+ if (index < ordered_old_sockets.size()) {
+ if (ordered_old_sockets[index] != new_socket) {
+ BKE_ntree_update_tag_interface(&node_tree);
+ break;
+ }
+ }
+ }
+ }
}
void node_group_update(struct bNodeTree *ntree, struct bNode *node)
diff --git a/source/blender/nodes/intern/node_geometry_exec.cc b/source/blender/nodes/intern/node_geometry_exec.cc
index 56e9c9f0496..c6ebc22c43c 100644
--- a/source/blender/nodes/intern/node_geometry_exec.cc
+++ b/source/blender/nodes/intern/node_geometry_exec.cc
@@ -4,6 +4,7 @@
#include "DEG_depsgraph_query.h"
+#include "BKE_curves.hh"
#include "BKE_type_conversions.hh"
#include "NOD_geometry_exec.hh"
@@ -94,11 +95,27 @@ void GeoNodeExecParams::check_input_geometry_set(StringRef identifier,
message += TIP_("Curve");
break;
}
+ case GEO_COMPONENT_TYPE_EDIT: {
+ continue;
+ }
}
this->error_message_add(NodeWarningType::Info, std::move(message));
}
}
+void GeoNodeExecParams::check_output_geometry_set(const GeometrySet &geometry_set) const
+{
+ UNUSED_VARS_NDEBUG(geometry_set);
+#ifdef DEBUG
+ if (const bke::CurvesEditHints *curve_edit_hints =
+ geometry_set.get_curve_edit_hints_for_read()) {
+ /* If this is not valid, it's likely that the number of stored deformed points does not match
+ * the number of points in the original data. */
+ BLI_assert(curve_edit_hints->is_valid());
+ }
+#endif
+}
+
const bNodeSocket *GeoNodeExecParams::find_available_socket(const StringRef name) const
{
for (const InputSocketRef *socket : provider_->dnode->inputs()) {
diff --git a/source/blender/nodes/intern/node_tree_ref.cc b/source/blender/nodes/intern/node_tree_ref.cc
index 64a8690a869..05e7fe33776 100644
--- a/source/blender/nodes/intern/node_tree_ref.cc
+++ b/source/blender/nodes/intern/node_tree_ref.cc
@@ -522,6 +522,7 @@ static void toposort_from_start_node(const NodeTreeRef::ToposortDirection direct
/* Do a depth-first search to sort nodes topologically. */
Stack<Item, 64> nodes_to_check;
nodes_to_check.push({&start_node});
+ node_states[start_node.id()].is_in_stack = true;
while (!nodes_to_check.is_empty()) {
Item &item = nodes_to_check.peek();
const NodeRef &node = *item.node;
diff --git a/source/blender/nodes/shader/nodes/node_shader_bsdf_hair_principled.cc b/source/blender/nodes/shader/nodes/node_shader_bsdf_hair_principled.cc
index 6495dcfffba..a0579372a15 100644
--- a/source/blender/nodes/shader/nodes/node_shader_bsdf_hair_principled.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_bsdf_hair_principled.cc
@@ -42,8 +42,7 @@ static void node_declare(NodeDeclarationBuilder &b)
.min(0.0f)
.max(1.0f)
.subtype(PROP_FACTOR);
- b.add_input<decl::Float>(N_("IOR")).default_value(1.55f).min(0.0f).max(1000.0f).subtype(
- PROP_FACTOR);
+ b.add_input<decl::Float>(N_("IOR")).default_value(1.55f).min(0.0f).max(1000.0f);
b.add_input<decl::Float>(N_("Offset"))
.default_value(2.0f * ((float)M_PI) / 180.0f)
.min(-M_PI_2)