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:
-rw-r--r--release/scripts/startup/nodeitems_builtins.py28
-rw-r--r--source/blender/blenkernel/BKE_attribute_access.hh10
-rw-r--r--source/blender/blenkernel/BKE_customdata.h8
-rw-r--r--source/blender/blenkernel/BKE_field.hh553
-rw-r--r--source/blender/blenkernel/BKE_geometry_set.hh91
-rw-r--r--source/blender/blenkernel/BKE_node.h15
-rw-r--r--source/blender/blenkernel/intern/attribute_access.cc443
-rw-r--r--source/blender/blenkernel/intern/attribute_access_intern.hh42
-rw-r--r--source/blender/blenkernel/intern/curve_eval.cc6
-rw-r--r--source/blender/blenkernel/intern/customdata.c81
-rw-r--r--source/blender/blenkernel/intern/geometry_component_curve.cc159
-rw-r--r--source/blender/blenkernel/intern/geometry_component_mesh.cc201
-rw-r--r--source/blender/blenkernel/intern/geometry_set.cc1
-rw-r--r--source/blender/blenkernel/intern/node.cc15
-rw-r--r--source/blender/blenkernel/intern/spline_base.cc2
-rw-r--r--source/blender/blenlib/BLI_hash.hh14
-rw-r--r--source/blender/blenlib/BLI_optional_ptr.hh88
-rw-r--r--source/blender/blenlib/BLI_span.hh4
-rw-r--r--source/blender/blenlib/BLI_user_counter.hh12
-rw-r--r--source/blender/blenlib/BLI_virtual_array.hh2
-rw-r--r--source/blender/blenlib/tests/BLI_map_test.cc12
-rw-r--r--source/blender/bmesh/intern/bmesh_mesh.c137
-rw-r--r--source/blender/bmesh/intern/bmesh_mesh.h13
-rw-r--r--source/blender/bmesh/intern/bmesh_opdefines.c7
-rw-r--r--source/blender/bmesh/operators/bmo_extrude.c1
-rw-r--r--source/blender/bmesh/operators/bmo_inset.c54
-rw-r--r--source/blender/editors/object/object_intern.h1
-rw-r--r--source/blender/editors/object/object_modifier.c52
-rw-r--r--source/blender/editors/object/object_ops.c1
-rw-r--r--source/blender/editors/space_node/node_draw.cc171
-rw-r--r--source/blender/editors/space_node/node_geometry_attribute_search.cc53
-rw-r--r--source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc3
-rw-r--r--source/blender/functions/FN_generic_virtual_array.hh126
-rw-r--r--source/blender/functions/intern/generic_virtual_array.cc10
-rw-r--r--source/blender/makesdna/DNA_customdata_types.h10
-rw-r--r--source/blender/makesdna/DNA_node_types.h42
-rw-r--r--source/blender/makesrna/RNA_enum_items.h1
-rw-r--r--source/blender/makesrna/intern/rna_attribute.c8
-rw-r--r--source/blender/makesrna/intern/rna_nodetree.c129
-rw-r--r--source/blender/modifiers/intern/MOD_nodes.cc138
-rw-r--r--source/blender/modifiers/intern/MOD_nodes_evaluator.cc127
-rw-r--r--source/blender/nodes/CMakeLists.txt16
-rw-r--r--source/blender/nodes/NOD_function.h1
-rw-r--r--source/blender/nodes/NOD_geometry.h14
-rw-r--r--source/blender/nodes/NOD_geometry_exec.hh59
-rw-r--r--source/blender/nodes/NOD_geometry_nodes_eval_log.hh1
-rw-r--r--source/blender/nodes/NOD_static_types.h24
-rw-r--r--source/blender/nodes/function/nodes/node_fn_align_rotation_to_vector.cc191
-rw-r--r--source/blender/nodes/geometry/node_geometry_util.cc223
-rw-r--r--source/blender/nodes/geometry/node_geometry_util.hh20
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_align_rotation_to_vector.cc242
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute.cc119
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_extract.cc182
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_fill.cc46
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_freeze.cc204
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_proximity.cc118
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_length.cc2
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_parameter.cc42
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_select_by_handle_type.cc10
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_set_handles.cc13
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_spline_type.cc17
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc48
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc129
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_evaluate_curve.cc178
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_extrude.cc206
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_extrude_and_move.cc291
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_index.cc45
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_is_viewport.cc3
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_material_assign.cc18
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc19
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_normal.cc45
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_object_info.cc6
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_point_distribute.cc225
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_point_instance.cc81
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_point_separate.cc25
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_point_translate.cc47
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_points_to_volume.cc15
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_position.cc45
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_raycast.cc222
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_sample_mesh_surface.cc193
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_select_by_material.cc27
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_set_position.cc109
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_switch.cc53
-rw-r--r--source/blender/nodes/intern/geometry_nodes_eval_log.cc23
-rw-r--r--source/blender/nodes/intern/node_socket.cc63
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_tex_noise.c103
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_tex_noise.cc224
87 files changed, 5783 insertions, 1075 deletions
diff --git a/release/scripts/startup/nodeitems_builtins.py b/release/scripts/startup/nodeitems_builtins.py
index 6f9b222571c..cf9b66cf35b 100644
--- a/release/scripts/startup/nodeitems_builtins.py
+++ b/release/scripts/startup/nodeitems_builtins.py
@@ -475,24 +475,15 @@ texture_node_categories = [
geometry_node_categories = [
# Geometry Nodes
GeometryNodeCategory("GEO_ATTRIBUTE", "Attribute", items=[
- NodeItem("GeometryNodeAttributeRandomize"),
- NodeItem("GeometryNodeAttributeMath"),
- NodeItem("GeometryNodeAttributeClamp"),
- NodeItem("GeometryNodeAttributeCompare"),
- NodeItem("GeometryNodeAttributeConvert"),
NodeItem("GeometryNodeAttributeCurveMap"),
NodeItem("GeometryNodeAttributeFill"),
NodeItem("GeometryNodeAttributeMix"),
- NodeItem("GeometryNodeAttributeProximity"),
- NodeItem("GeometryNodeAttributeColorRamp"),
- NodeItem("GeometryNodeAttributeVectorMath"),
- NodeItem("GeometryNodeAttributeVectorRotate"),
NodeItem("GeometryNodeAttributeSampleTexture"),
- NodeItem("GeometryNodeAttributeCombineXYZ"),
- NodeItem("GeometryNodeAttributeSeparateXYZ"),
NodeItem("GeometryNodeAttributeRemove"),
NodeItem("GeometryNodeAttributeMapRange"),
NodeItem("GeometryNodeAttributeTransfer"),
+ NodeItem("GeometryNodeAttributeExtract"),
+ NodeItem("GeometryNodeAttributeFreeze"),
]),
GeometryNodeCategory("GEO_COLOR", "Color", items=[
NodeItem("ShaderNodeMixRGB"),
@@ -514,6 +505,7 @@ geometry_node_categories = [
NodeItem("GeometryNodeCurveSplineType"),
NodeItem("GeometryNodeCurveSetHandles"),
NodeItem("GeometryNodeCurveSelectHandles"),
+ NodeItem("GeometryNodeEvaluateCurve"),
]),
GeometryNodeCategory("GEO_PRIMITIVES_CURVE", "Curve Primitives", items=[
NodeItem("GeometryNodeCurvePrimitiveLine"),
@@ -532,6 +524,8 @@ geometry_node_categories = [
NodeItem("GeometryNodeJoinGeometry"),
NodeItem("GeometryNodeSeparateComponents"),
NodeItem("GeometryNodeRaycast"),
+ NodeItem("GeometryNodeAttributeProximity"),
+ NodeItem("GeometryNodeSetPosition"),
]),
GeometryNodeCategory("GEO_INPUT", "Input", items=[
NodeItem("GeometryNodeObjectInfo"),
@@ -542,6 +536,11 @@ geometry_node_categories = [
NodeItem("FunctionNodeInputVector"),
NodeItem("GeometryNodeInputMaterial"),
NodeItem("GeometryNodeIsViewport"),
+ NodeItem("GeometryNodeAttribute"),
+ NodeItem("GeometryNodeIndex"),
+ NodeItem("GeometryNodeNormal"),
+ NodeItem("GeometryNodePosition"),
+ NodeItem("GeometryNodeCurveParameter"),
]),
GeometryNodeCategory("GEO_MATERIAL", "Material", items=[
NodeItem("GeometryNodeMaterialAssign"),
@@ -554,6 +553,9 @@ geometry_node_categories = [
NodeItem("GeometryNodeEdgeSplit"),
NodeItem("GeometryNodeSubdivisionSurface"),
NodeItem("GeometryNodeMeshSubdivide"),
+ NodeItem("GeometryNodeExtrude"),
+ NodeItem("GeometryNodeExtrudeAndMove"),
+ NodeItem("GeometryNodeSampleMeshSurface"),
]),
GeometryNodeCategory("GEO_PRIMITIVES_MESH", "Mesh Primitives", items=[
NodeItem("GeometryNodeMeshCircle"),
@@ -565,7 +567,6 @@ geometry_node_categories = [
NodeItem("GeometryNodeMeshLine"),
NodeItem("GeometryNodeMeshUVSphere"),
]),
-
GeometryNodeCategory("GEO_POINT", "Point", items=[
NodeItem("GeometryNodePointDistribute"),
NodeItem("GeometryNodePointInstance"),
@@ -573,7 +574,6 @@ geometry_node_categories = [
NodeItem("GeometryNodePointScale"),
NodeItem("GeometryNodePointTranslate"),
NodeItem("GeometryNodeRotatePoints"),
- NodeItem("GeometryNodeAlignRotationToVector"),
]),
GeometryNodeCategory("GEO_UTILITIES", "Utilities", items=[
NodeItem("ShaderNodeMapRange"),
@@ -583,6 +583,7 @@ geometry_node_categories = [
NodeItem("FunctionNodeFloatCompare"),
NodeItem("FunctionNodeFloatToInt"),
NodeItem("GeometryNodeSwitch"),
+ NodeItem("ShaderNodeTexNoise"),
]),
GeometryNodeCategory("GEO_VECTOR", "Vector", items=[
NodeItem("ShaderNodeVectorCurve"),
@@ -590,6 +591,7 @@ geometry_node_categories = [
NodeItem("ShaderNodeCombineXYZ"),
NodeItem("ShaderNodeVectorMath"),
NodeItem("ShaderNodeVectorRotate"),
+ NodeItem("FunctionNodeAlignRotationToVector"),
]),
GeometryNodeCategory("GEO_OUTPUT", "Output", items=[
NodeItem("GeometryNodeViewer"),
diff --git a/source/blender/blenkernel/BKE_attribute_access.hh b/source/blender/blenkernel/BKE_attribute_access.hh
index c3f7dbd4bd9..97cac30129e 100644
--- a/source/blender/blenkernel/BKE_attribute_access.hh
+++ b/source/blender/blenkernel/BKE_attribute_access.hh
@@ -37,10 +37,12 @@
struct AttributeMetaData {
AttributeDomain domain;
CustomDataType data_type;
+ const AnonymousCustomDataLayerID *anonymous_layer_id = nullptr;
constexpr friend bool operator==(AttributeMetaData a, AttributeMetaData b)
{
- return (a.domain == b.domain) && (a.data_type == b.data_type);
+ return (a.domain == b.domain) && (a.data_type == b.data_type) &&
+ (a.anonymous_layer_id == b.anonymous_layer_id);
}
};
@@ -354,6 +356,12 @@ class CustomDataAttributes {
bool create_by_move(const blender::StringRef name, const CustomDataType data_type, void *buffer);
bool remove(const blender::StringRef name);
+ bool create_anonymous(const AnonymousCustomDataLayerID &id, const CustomDataType data_type);
+ std::optional<blender::fn::GSpan> get_anonymous_for_read(
+ const AnonymousCustomDataLayerID &id) const;
+ std::optional<blender::fn::GMutableSpan> get_anonymous_for_write(
+ const AnonymousCustomDataLayerID &id);
+
bool foreach_attribute(const AttributeForeachCallback callback,
const AttributeDomain domain) const;
};
diff --git a/source/blender/blenkernel/BKE_customdata.h b/source/blender/blenkernel/BKE_customdata.h
index 7a44553c565..b2a0e7fc7e8 100644
--- a/source/blender/blenkernel/BKE_customdata.h
+++ b/source/blender/blenkernel/BKE_customdata.h
@@ -482,6 +482,14 @@ void CustomData_external_reload(struct CustomData *data,
CustomDataMask mask,
int totelem);
+/* Anonymous layers. */
+struct AnonymousCustomDataLayerID *CustomData_anonymous_id_new(const char *debug_name);
+void CustomData_anonymous_id_strong_decrement(const struct AnonymousCustomDataLayerID *layer_id);
+void CustomData_anonymous_id_strong_increment(const struct AnonymousCustomDataLayerID *layer_id);
+void CustomData_anonymous_id_weak_decrement(const struct AnonymousCustomDataLayerID *layer_id);
+void CustomData_anonymous_id_weak_increment(const struct AnonymousCustomDataLayerID *layer_id);
+bool CustomData_layer_is_unused_anonymous(const struct CustomDataLayer *layer);
+
/* Mesh-to-mesh transfer data. */
struct CustomDataTransferLayerMap;
diff --git a/source/blender/blenkernel/BKE_field.hh b/source/blender/blenkernel/BKE_field.hh
new file mode 100644
index 00000000000..801060cc070
--- /dev/null
+++ b/source/blender/blenkernel/BKE_field.hh
@@ -0,0 +1,553 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#pragma once
+
+/** \file
+ * \ingroup bke
+ */
+
+#include <atomic>
+
+#include "BLI_function_ref.hh"
+#include "BLI_map.hh"
+#include "BLI_optional_ptr.hh"
+#include "BLI_user_counter.hh"
+#include "BLI_vector.hh"
+#include "BLI_virtual_array.hh"
+
+#include "FN_cpp_type.hh"
+#include "FN_cpp_type_make.hh"
+#include "FN_multi_function.hh"
+
+#include "BKE_customdata.h"
+
+namespace blender::bke {
+
+using fn::CPPType;
+using fn::GMutableSpan;
+using fn::GVArray;
+using fn::GVArrayPtr;
+using fn::MultiFunction;
+
+class FieldInputKey {
+ public:
+ virtual ~FieldInputKey() = default;
+ virtual uint64_t hash() const = 0;
+ virtual const CPPType &type() const = 0;
+
+ friend bool operator==(const FieldInputKey &a, const FieldInputKey &b)
+ {
+ return a.is_same_as(b);
+ }
+
+ private:
+ virtual bool is_same_as(const FieldInputKey &other) const
+ {
+ UNUSED_VARS(other);
+ return false;
+ }
+};
+
+class FieldInputValue {
+ public:
+ virtual ~FieldInputValue() = default;
+};
+
+class IndexFieldInputKey : public FieldInputKey {
+ public:
+ uint64_t hash() const override
+ {
+ /* Arbitrary number. */
+ return 78582029;
+ }
+
+ const CPPType &type() const override
+ {
+ return CPPType::get<int>();
+ }
+
+ private:
+ bool is_same_as(const FieldInputKey &other) const override
+ {
+ return dynamic_cast<const IndexFieldInputKey *>(&other) != nullptr;
+ }
+};
+
+class CurveParameterFieldInputKey : public FieldInputKey {
+ public:
+ uint64_t hash() const override
+ {
+ /* Arbitrary number. */
+ return 928347504059;
+ }
+
+ const CPPType &type() const override
+ {
+ return CPPType::get<float>();
+ }
+
+ private:
+ bool is_same_as(const FieldInputKey &other) const override
+ {
+ return dynamic_cast<const CurveParameterFieldInputKey *>(&other) != nullptr;
+ }
+};
+
+class AnonymousAttributeFieldInputKey : public FieldInputKey {
+ private:
+ AnonymousCustomDataLayerID *layer_id_;
+ const CPPType &type_;
+
+ public:
+ AnonymousAttributeFieldInputKey(AnonymousCustomDataLayerID &layer_id, const CPPType &type)
+ : layer_id_(&layer_id), type_(type)
+ {
+ CustomData_anonymous_id_strong_increment(layer_id_);
+ }
+
+ ~AnonymousAttributeFieldInputKey()
+ {
+ CustomData_anonymous_id_strong_decrement(layer_id_);
+ }
+
+ const CPPType &type() const override
+ {
+ return type_;
+ }
+
+ uint64_t hash() const override
+ {
+ return get_default_hash(layer_id_);
+ }
+
+ const AnonymousCustomDataLayerID &layer_id() const
+ {
+ return *layer_id_;
+ }
+
+ private:
+ bool is_same_as(const FieldInputKey &other) const override
+ {
+ if (const AnonymousAttributeFieldInputKey *other_typed =
+ dynamic_cast<const AnonymousAttributeFieldInputKey *>(&other)) {
+ return layer_id_ == other_typed->layer_id_ && type_ == other_typed->type_;
+ }
+ return false;
+ }
+};
+
+class PersistentAttributeFieldInputKey : public FieldInputKey {
+ private:
+ std::string name_;
+ const CPPType *type_;
+
+ public:
+ PersistentAttributeFieldInputKey(std::string name, const CPPType &type)
+ : name_(std::move(name)), type_(&type)
+ {
+ }
+
+ uint64_t hash() const override
+ {
+ return get_default_hash_2(name_, type_);
+ }
+
+ const CPPType &type() const override
+ {
+ return *type_;
+ }
+
+ StringRefNull name() const
+ {
+ return name_;
+ }
+
+ private:
+ bool is_same_as(const FieldInputKey &other) const override
+ {
+ if (const PersistentAttributeFieldInputKey *other_typed =
+ dynamic_cast<const PersistentAttributeFieldInputKey *>(&other)) {
+ return other_typed->type_ == type_ && other_typed->name_ == name_;
+ }
+ return false;
+ }
+};
+
+class GVArrayFieldInputValue : public FieldInputValue {
+ private:
+ optional_ptr<GVArray> varray_;
+
+ public:
+ GVArrayFieldInputValue(optional_ptr<GVArray> varray) : varray_(std::move(varray))
+ {
+ }
+
+ const GVArray &varray() const
+ {
+ return *varray_;
+ }
+};
+
+class FieldInputs {
+ private:
+ using InputMap = Map<std::reference_wrapper<const FieldInputKey>, const FieldInputValue *>;
+ InputMap inputs_;
+
+ friend class Field;
+
+ public:
+ InputMap::KeyIterator begin() const
+ {
+ return inputs_.keys().begin();
+ }
+
+ InputMap::KeyIterator end() const
+ {
+ return inputs_.keys().end();
+ }
+
+ int tot_inputs() const
+ {
+ return inputs_.size();
+ }
+
+ void set_input(const FieldInputKey &key, const FieldInputValue &value)
+ {
+ *inputs_.lookup_ptr(key) = &value;
+ }
+
+ const FieldInputValue *get(const FieldInputKey &key) const
+ {
+ return inputs_.lookup_default(key, nullptr);
+ }
+
+ template<typename ValueT> const ValueT *get(const FieldInputKey &key) const
+ {
+ return dynamic_cast<const ValueT *>(this->get(key));
+ }
+};
+
+class FieldOutput {
+ private:
+ optional_ptr<const GVArray> varray_;
+
+ public:
+ FieldOutput(optional_ptr<const GVArray> varray) : varray_(std::move(varray))
+ {
+ }
+
+ const GVArray &varray_ref() const
+ {
+ return *varray_;
+ }
+};
+
+class Field {
+ private:
+ mutable std::atomic<int> users_ = 1;
+
+ public:
+ virtual ~Field() = default;
+
+ FieldInputs prepare_inputs() const
+ {
+ FieldInputs inputs;
+ this->foreach_input_key([&](const FieldInputKey &key) { inputs.inputs_.add(key, nullptr); });
+ return inputs;
+ }
+
+ virtual void foreach_input_key(FunctionRef<void(const FieldInputKey &key)> callback) const
+ {
+ UNUSED_VARS(callback);
+ }
+
+ virtual const CPPType &output_type() const = 0;
+
+ virtual FieldOutput evaluate(IndexMask mask, const FieldInputs &inputs) const = 0;
+
+ void user_add() const
+ {
+ users_.fetch_add(1);
+ }
+
+ void user_remove() const
+ {
+ const int new_users = users_.fetch_sub(1) - 1;
+ if (new_users == 0) {
+ delete this;
+ }
+ }
+};
+
+using FieldPtr = UserCounter<Field>;
+
+template<typename T> class ConstantField : public Field {
+ private:
+ T value_;
+
+ public:
+ ConstantField(T value) : value_(std::move(value))
+ {
+ }
+
+ const CPPType &output_type() const override
+ {
+ return CPPType::get<T>();
+ }
+
+ FieldOutput evaluate(IndexMask mask, const FieldInputs &UNUSED(inputs)) const
+ {
+ return optional_ptr<const GVArray>{std::make_unique<fn::GVArray_For_SingleValue>(
+ CPPType::get<T>(), mask.min_array_size(), &value_)};
+ }
+};
+
+template<typename KeyT> class GVArrayInputField : public Field {
+ private:
+ KeyT key_;
+
+ public:
+ template<typename... Args> GVArrayInputField(Args &&...args) : key_(std::forward<Args>(args)...)
+ {
+ }
+
+ void foreach_input_key(FunctionRef<void(const FieldInputKey &key)> callback) const override
+ {
+ callback(key_);
+ }
+
+ const CPPType &output_type() const override
+ {
+ return key_.type();
+ }
+
+ FieldOutput evaluate(IndexMask mask, const FieldInputs &inputs) const override
+ {
+ const GVArrayFieldInputValue *input = inputs.get<GVArrayFieldInputValue>(key_);
+ if (input == nullptr) {
+ return FieldOutput{
+ optional_ptr<const GVArray>{std::make_unique<fn::GVArray_For_SingleValueRef>(
+ key_.type(), mask.min_array_size(), key_.type().default_value())}};
+ }
+ return FieldOutput{optional_ptr<const GVArray>{input->varray()}};
+ }
+};
+
+class MultiFunctionField : public Field {
+ private:
+ Vector<FieldPtr> input_fields_;
+ optional_ptr<const MultiFunction> fn_;
+ const int output_param_index_;
+
+ public:
+ MultiFunctionField(Vector<FieldPtr> input_fields,
+ optional_ptr<const MultiFunction> fn,
+ const int output_param_index)
+ : input_fields_(std::move(input_fields)),
+ fn_(std::move(fn)),
+ output_param_index_(output_param_index)
+ {
+ }
+
+ const CPPType &output_type() const override
+ {
+ return fn_->param_type(output_param_index_).data_type().single_type();
+ }
+
+ void foreach_input_key(FunctionRef<void(const FieldInputKey &key)> callback) const override
+ {
+ for (const FieldPtr &field : input_fields_) {
+ field->foreach_input_key(callback);
+ }
+ }
+
+ FieldOutput evaluate(IndexMask mask, const FieldInputs &inputs) const final
+ {
+ fn::MFParamsBuilder params{*fn_, mask.min_array_size()};
+ fn::MFContextBuilder context;
+
+ ResourceScope &scope = params.resource_scope();
+
+ Vector<GMutableSpan> outputs;
+ int output_span_index = -1;
+
+ int input_index = 0;
+ for (const int param_index : fn_->param_indices()) {
+ fn::MFParamType param_type = fn_->param_type(param_index);
+ switch (param_type.category()) {
+ case fn::MFParamType::SingleInput: {
+ const Field &field = *input_fields_[input_index];
+ FieldOutput &output = scope.add_value(field.evaluate(mask, inputs), __func__);
+ params.add_readonly_single_input(output.varray_ref());
+ input_index++;
+ break;
+ }
+ case fn::MFParamType::SingleOutput: {
+ const CPPType &type = param_type.data_type().single_type();
+ void *buffer = MEM_mallocN_aligned(
+ mask.min_array_size() * type.size(), type.alignment(), __func__);
+ GMutableSpan span{type, buffer, mask.min_array_size()};
+ outputs.append(span);
+ params.add_uninitialized_single_output(span);
+ if (param_index == output_param_index_) {
+ output_span_index = outputs.size() - 1;
+ }
+ break;
+ }
+ case fn::MFParamType::SingleMutable:
+ case fn::MFParamType::VectorInput:
+ case fn::MFParamType::VectorMutable:
+ case fn::MFParamType::VectorOutput:
+ BLI_assert_unreachable();
+ break;
+ }
+ }
+
+ fn_->call(mask, params, context);
+
+ GMutableSpan output_span = outputs[output_span_index];
+ outputs.remove(output_span_index);
+
+ for (GMutableSpan span : outputs) {
+ span.type().destruct_indices(span.data(), mask);
+ MEM_freeN(span.data());
+ }
+
+ std::unique_ptr<GVArray> out_array = std::make_unique<fn::GVArray_For_OwnedGSpan>(output_span,
+ mask);
+ return FieldOutput{optional_ptr<const GVArray>{std::move(out_array)}};
+ }
+};
+
+class PersistentAttributeField : public GVArrayInputField<PersistentAttributeFieldInputKey> {
+ public:
+ PersistentAttributeField(std::string name, const CPPType &type)
+ : GVArrayInputField<PersistentAttributeFieldInputKey>(std::move(name), type)
+ {
+ }
+};
+
+class AnonymousAttributeField : public GVArrayInputField<AnonymousAttributeFieldInputKey> {
+ public:
+ AnonymousAttributeField(AnonymousCustomDataLayerID &layer_id, const CPPType &type)
+ : GVArrayInputField<AnonymousAttributeFieldInputKey>(layer_id, type)
+ {
+ }
+};
+
+class IndexField : public GVArrayInputField<IndexFieldInputKey> {
+};
+class CurveParameterField : public GVArrayInputField<CurveParameterFieldInputKey> {
+};
+
+class FieldRefBase {
+ protected:
+ FieldPtr field_;
+
+ public:
+ const FieldPtr &field() const
+ {
+ return field_;
+ }
+};
+
+template<typename T> class FieldRef : public FieldRefBase {
+
+ public:
+ FieldRef()
+ {
+ field_ = new ConstantField<T>(T());
+ }
+
+ FieldRef(FieldPtr field)
+ {
+ field_ = std::move(field);
+ }
+
+ const Field *operator->() const
+ {
+ return &*field_;
+ }
+
+ uint64_t hash() const
+ {
+ return get_default_hash(&*field_);
+ }
+
+ friend bool operator==(const FieldRef &a, const FieldRef &b)
+ {
+ return &*a.field_ == &*b.field_;
+ }
+
+ friend std::ostream &operator<<(std::ostream &stream, const FieldRef &a)
+ {
+ stream << &*a.field_;
+ return stream;
+ }
+};
+
+template<typename T> struct FieldRefCPPTypeParam {
+};
+
+class FieldRefCPPType : public CPPType {
+ private:
+ const CPPType &field_type_;
+ FieldPtr (*get_field_)(const void *field_ref);
+ void (*construct_)(void *dst, FieldPtr field);
+
+ public:
+ template<typename T>
+ FieldRefCPPType(FieldRefCPPTypeParam<FieldRef<T>> /* unused */, StringRef debug_name)
+ : CPPType(fn::CPPTypeParam<FieldRef<T>, CPPTypeFlags::BasicType>(), debug_name),
+ field_type_(CPPType::get<T>())
+ {
+ get_field_ = [](const void *field_ref) {
+ return ((const blender::bke::FieldRef<T> *)field_ref)->field();
+ };
+ construct_ = [](void *dst, blender::bke::FieldPtr field) {
+ new (dst) blender::bke::FieldRef<T>(std::move(field));
+ };
+ }
+
+ const CPPType &field_type() const
+ {
+ return field_type_;
+ };
+
+ FieldPtr get_field(const void *field_ref) const
+ {
+ return get_field_(field_ref);
+ }
+
+ void construct(void *dst, FieldPtr field) const
+ {
+ construct_(dst, std::move(field));
+ }
+};
+
+} // namespace blender::bke
+
+#define MAKE_FIELD_REF_CPP_TYPE(DEBUG_NAME, FIELD_TYPE) \
+ template<> \
+ const blender::fn::CPPType & \
+ blender::fn::CPPType::get_impl<blender::bke::FieldRef<FIELD_TYPE>>() \
+ { \
+ static blender::bke::FieldRefCPPType cpp_type{ \
+ blender::bke::FieldRefCPPTypeParam<blender::bke::FieldRef<FIELD_TYPE>>(), \
+ STRINGIFY(DEBUG_NAME)}; \
+ return cpp_type; \
+ }
diff --git a/source/blender/blenkernel/BKE_geometry_set.hh b/source/blender/blenkernel/BKE_geometry_set.hh
index 42e9ce82278..55a239f6a5c 100644
--- a/source/blender/blenkernel/BKE_geometry_set.hh
+++ b/source/blender/blenkernel/BKE_geometry_set.hh
@@ -128,6 +128,28 @@ class GeometryComponent {
const CustomDataType data_type,
const AttributeInit &initializer);
+ bool attribute_try_create_anonymous(const AnonymousCustomDataLayerID &layer_id,
+ const AttributeDomain domain,
+ const CustomDataType data_type,
+ const AttributeInit &initializer);
+
+ blender::bke::ReadAttributeLookup attribute_try_get_anonymous_for_read(
+ const AnonymousCustomDataLayerID &layer_id) const;
+
+ blender::fn::GVArrayPtr attribute_try_get_anonymous_for_read(
+ const AnonymousCustomDataLayerID &id,
+ const AttributeDomain domain,
+ const CustomDataType data_type) const;
+
+ blender::fn::GVArrayPtr attribute_try_get_anonymous_for_read(
+ const AnonymousCustomDataLayerID &id,
+ const AttributeDomain domain,
+ const CustomDataType data_type,
+ const void *default_value) const;
+
+ blender::bke::WriteAttributeLookup attribute_try_get_anonymous_for_write(
+ const AnonymousCustomDataLayerID &layer_id);
+
/* Try to create the builtin attribute with the given name. No data type or domain has to be
* provided, because those are fixed for builtin attributes. */
bool attribute_try_create_builtin(const blender::StringRef attribute_name,
@@ -139,8 +161,8 @@ class GeometryComponent {
virtual bool is_empty() const;
/* Get a virtual array to read the data of an attribute on the given domain and data type.
- * Returns null when the attribute does not exist or cannot be converted to the requested domain
- * and data type. */
+ * Returns null when the attribute does not exist or cannot be converted to the requested
+ * domain and data type. */
std::unique_ptr<blender::fn::GVArray> attribute_try_get_for_read(
const blender::StringRef attribute_name,
const AttributeDomain domain,
@@ -181,14 +203,14 @@ class GeometryComponent {
}
/**
- * Returns an "output attribute", which is essentially a mutable virtual array with some commonly
- * used convince features. The returned output attribute might be empty if requested attribute
- * cannot exist on the geometry.
+ * Returns an "output attribute", which is essentially a mutable virtual array with some
+ * commonly used convince features. The returned output attribute might be empty if requested
+ * attribute cannot exist on the geometry.
*
* The included convenience features are:
* - Implicit type conversion when writing to builtin attributes.
- * - If the attribute name exists already, but has a different type/domain, a temporary attribute
- * is created that will overwrite the existing attribute in the end.
+ * - If the attribute name exists already, but has a different type/domain, a temporary
+ * attribute is created that will overwrite the existing attribute in the end.
*/
blender::bke::OutputAttribute attribute_try_get_for_output(
const blender::StringRef attribute_name,
@@ -197,8 +219,8 @@ class GeometryComponent {
const void *default_value = nullptr);
/* Same as attribute_try_get_for_output, but should be used when the original values in the
- * attributes are not read, i.e. the attribute is used only for output. Since values are not read
- * from this attribute, no default value is necessary. */
+ * attributes are not read, i.e. the attribute is used only for output. Since values are not
+ * read from this attribute, no default value is necessary. */
blender::bke::OutputAttribute attribute_try_get_for_output_only(
const blender::StringRef attribute_name,
const AttributeDomain domain,
@@ -224,6 +246,35 @@ class GeometryComponent {
return this->attribute_try_get_for_output_only(attribute_name, domain, data_type);
}
+ blender::bke::OutputAttribute attribute_try_get_anonymous_for_output(
+ const AnonymousCustomDataLayerID &id,
+ const AttributeDomain domain,
+ const CustomDataType data_type,
+ const void *default_value = nullptr);
+
+ blender::bke::OutputAttribute attribute_try_get_anonymous_for_output_only(
+ const AnonymousCustomDataLayerID &id,
+ const AttributeDomain domain,
+ const CustomDataType data_type);
+
+ template<typename T>
+ blender::bke::OutputAttribute_Typed<T> attribute_try_get_anonymous_for_output(
+ const AnonymousCustomDataLayerID &id, const AttributeDomain domain, const T default_value)
+ {
+ const blender::fn::CPPType &cpp_type = blender::fn::CPPType::get<T>();
+ const CustomDataType data_type = blender::bke::cpp_type_to_custom_data_type(cpp_type);
+ return this->attribute_try_get_anonymous_for_output(id, domain, data_type, &default_value);
+ }
+
+ template<typename T>
+ blender::bke::OutputAttribute_Typed<T> attribute_try_get_anonymous_for_output_only(
+ const AnonymousCustomDataLayerID &id, const AttributeDomain domain)
+ {
+ const blender::fn::CPPType &cpp_type = blender::fn::CPPType::get<T>();
+ const CustomDataType data_type = blender::bke::cpp_type_to_custom_data_type(cpp_type);
+ return this->attribute_try_get_anonymous_for_output_only(id, domain, data_type);
+ }
+
private:
virtual const blender::bke::ComponentAttributeProviders *get_attribute_providers() const;
};
@@ -232,12 +283,12 @@ template<typename T>
inline constexpr bool is_geometry_component_v = std::is_base_of_v<GeometryComponent, T>;
/**
- * A geometry set contains zero or more geometry components. There is at most one component of each
- * type. Individual components might be shared between multiple geometries. Shared components are
- * copied automatically when write access is requested.
+ * A geometry set contains zero or more geometry components. There is at most one component of
+ * each type. Individual components might be shared between multiple geometries. Shared
+ * components are copied automatically when write access is requested.
*
- * Copying a geometry set is a relatively cheap operation, because it does not copy the referenced
- * geometry components.
+ * Copying a geometry set is a relatively cheap operation, because it does not copy the
+ * referenced geometry components.
*/
struct GeometrySet {
private:
@@ -345,6 +396,10 @@ class MeshComponent : public GeometryComponent {
const AttributeDomain from_domain,
const AttributeDomain to_domain) const final;
+ blender::VArrayPtr<bool> adapt_selection(blender::VArrayPtr<bool> selection,
+ AttributeDomain from_domain,
+ AttributeDomain to_domain) const;
+
bool is_empty() const final;
bool owns_direct_data() const override;
@@ -441,8 +496,8 @@ class InstanceReference {
enum class Type {
/**
* An empty instance. This allows an `InstanceReference` to be default constructed without
- * being in an invalid state. There might also be other use cases that we haven't explored much
- * yet (such as changing the instance later on, and "disabling" some instances).
+ * being in an invalid state. There might also be other use cases that we haven't explored
+ * much yet (such as changing the instance later on, and "disabling" some instances).
*/
None,
Object,
@@ -513,8 +568,8 @@ class InstancesComponent : public GeometryComponent {
blender::Vector<int> instance_ids_;
/* These almost unique ids are generated based on `ids_`, which might not contain unique ids at
- * all. They are *almost* unique, because under certain very unlikely circumstances, they are not
- * unique. Code using these ids should not crash when they are not unique but can generally
+ * all. They are *almost* unique, because under certain very unlikely circumstances, they are
+ * not unique. Code using these ids should not crash when they are not unique but can generally
* expect them to be unique. */
mutable std::mutex almost_unique_ids_mutex_;
mutable blender::Array<int> almost_unique_ids_;
diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h
index 06528cd213c..c7864e8909d 100644
--- a/source/blender/blenkernel/BKE_node.h
+++ b/source/blender/blenkernel/BKE_node.h
@@ -1415,7 +1415,7 @@ int ntreeTexExecTree(struct bNodeTree *ntree,
#define GEO_NODE_ATTRIBUTE_COMPARE 1015
#define GEO_NODE_POINT_ROTATE 1016
#define GEO_NODE_ATTRIBUTE_VECTOR_MATH 1017
-#define GEO_NODE_ALIGN_ROTATION_TO_VECTOR 1018
+// #define GEO_NODE_ALIGN_ROTATION_TO_VECTOR 1018
#define GEO_NODE_POINT_TRANSLATE 1019
#define GEO_NODE_POINT_SCALE 1020
#define GEO_NODE_ATTRIBUTE_SAMPLE_TEXTURE 1021
@@ -1472,6 +1472,18 @@ int ntreeTexExecTree(struct bNodeTree *ntree,
#define GEO_NODE_CURVE_SET_HANDLES 1072
#define GEO_NODE_CURVE_SPLINE_TYPE 1073
#define GEO_NODE_CURVE_SELECT_HANDLES 1074
+#define GEO_NODE_ATTRIBUTE 1075
+#define GEO_NODE_INDEX 1076
+#define GEO_NODE_EXTRUDE 1077
+#define GEO_NODE_ATTRIBUTE_FREEZE 1078
+#define GEO_NODE_ATTRIBUTE_EXTRACT 1079
+#define GEO_NODE_NORMAL 1080
+#define GEO_NODE_CURVE_PARAMETER 1081
+#define GEO_NODE_EXTRUDE_AND_MOVE 1082
+#define GEO_NODE_POSITION 1083
+#define GEO_NODE_SET_POSITION 1084
+#define GEO_NODE_SAMPLE_MESH_SURFACE 1085
+#define GEO_NODE_EVALUATE_CURVE 1086
/** \} */
@@ -1485,6 +1497,7 @@ int ntreeTexExecTree(struct bNodeTree *ntree,
#define FN_NODE_INPUT_VECTOR 1207
#define FN_NODE_INPUT_STRING 1208
#define FN_NODE_FLOAT_TO_INT 1209
+#define FN_NODE_ALIGN_ROTATION_TO_VECTOR 1210
/** \} */
diff --git a/source/blender/blenkernel/intern/attribute_access.cc b/source/blender/blenkernel/intern/attribute_access.cc
index aa0af294bc3..bb83d7bced4 100644
--- a/source/blender/blenkernel/intern/attribute_access.cc
+++ b/source/blender/blenkernel/intern/attribute_access.cc
@@ -346,27 +346,33 @@ ReadAttributeLookup CustomDataAttributeProvider::try_get_for_read(
if (layer.name != attribute_name) {
continue;
}
- const CustomDataType data_type = (CustomDataType)layer.type;
- switch (data_type) {
- case CD_PROP_FLOAT:
- return this->layer_to_read_attribute<float>(layer, domain_size);
- case CD_PROP_FLOAT2:
- return this->layer_to_read_attribute<float2>(layer, domain_size);
- case CD_PROP_FLOAT3:
- return this->layer_to_read_attribute<float3>(layer, domain_size);
- case CD_PROP_INT32:
- return this->layer_to_read_attribute<int>(layer, domain_size);
- case CD_PROP_COLOR:
- return this->layer_to_read_attribute<ColorGeometry4f>(layer, domain_size);
- case CD_PROP_BOOL:
- return this->layer_to_read_attribute<bool>(layer, domain_size);
- default:
- break;
- }
+ return this->layer_to_read_attribute(layer, domain_size);
}
return {};
}
+ReadAttributeLookup CustomDataAttributeProvider::layer_to_read_attribute(
+ const CustomDataLayer &layer, const int domain_size) const
+{
+ const CustomDataType data_type = (CustomDataType)layer.type;
+ switch (data_type) {
+ case CD_PROP_FLOAT:
+ return this->layer_to_read_attribute<float>(layer, domain_size);
+ case CD_PROP_FLOAT2:
+ return this->layer_to_read_attribute<float2>(layer, domain_size);
+ case CD_PROP_FLOAT3:
+ return this->layer_to_read_attribute<float3>(layer, domain_size);
+ case CD_PROP_INT32:
+ return this->layer_to_read_attribute<int>(layer, domain_size);
+ case CD_PROP_COLOR:
+ return this->layer_to_read_attribute<ColorGeometry4f>(layer, domain_size);
+ case CD_PROP_BOOL:
+ return this->layer_to_read_attribute<bool>(layer, domain_size);
+ default:
+ return {};
+ }
+}
+
WriteAttributeLookup CustomDataAttributeProvider::try_get_for_write(
GeometryComponent &component, const StringRef attribute_name) const
{
@@ -380,27 +386,33 @@ WriteAttributeLookup CustomDataAttributeProvider::try_get_for_write(
continue;
}
CustomData_duplicate_referenced_layer_named(custom_data, layer.type, layer.name, domain_size);
- const CustomDataType data_type = (CustomDataType)layer.type;
- switch (data_type) {
- case CD_PROP_FLOAT:
- return this->layer_to_write_attribute<float>(layer, domain_size);
- case CD_PROP_FLOAT2:
- return this->layer_to_write_attribute<float2>(layer, domain_size);
- case CD_PROP_FLOAT3:
- return this->layer_to_write_attribute<float3>(layer, domain_size);
- case CD_PROP_INT32:
- return this->layer_to_write_attribute<int>(layer, domain_size);
- case CD_PROP_COLOR:
- return this->layer_to_write_attribute<ColorGeometry4f>(layer, domain_size);
- case CD_PROP_BOOL:
- return this->layer_to_write_attribute<bool>(layer, domain_size);
- default:
- break;
- }
+ return this->layer_to_write_attribute(layer, domain_size);
}
return {};
}
+WriteAttributeLookup CustomDataAttributeProvider::layer_to_write_attribute(
+ CustomDataLayer &layer, const int domain_size) const
+{
+ const CustomDataType data_type = (CustomDataType)layer.type;
+ switch (data_type) {
+ case CD_PROP_FLOAT:
+ return this->layer_to_write_attribute<float>(layer, domain_size);
+ case CD_PROP_FLOAT2:
+ return this->layer_to_write_attribute<float2>(layer, domain_size);
+ case CD_PROP_FLOAT3:
+ return this->layer_to_write_attribute<float3>(layer, domain_size);
+ case CD_PROP_INT32:
+ return this->layer_to_write_attribute<int>(layer, domain_size);
+ case CD_PROP_COLOR:
+ return this->layer_to_write_attribute<ColorGeometry4f>(layer, domain_size);
+ case CD_PROP_BOOL:
+ return this->layer_to_write_attribute<bool>(layer, domain_size);
+ default:
+ return {};
+ }
+}
+
bool CustomDataAttributeProvider::try_delete(GeometryComponent &component,
const StringRef attribute_name) const
{
@@ -487,6 +499,84 @@ bool CustomDataAttributeProvider::try_create(GeometryComponent &component,
return true;
}
+static std::string get_anonymous_attribute_name()
+{
+ static std::atomic<int> index = 0;
+ const int next_index = index.fetch_add(1);
+ return "anonymous_attribute_" + std::to_string(next_index);
+}
+
+bool CustomDataAttributeProvider::try_create_anonymous(GeometryComponent &component,
+ const AnonymousCustomDataLayerID &layer_id,
+ const AttributeDomain domain,
+ const CustomDataType data_type,
+ const AttributeInit &initializer) const
+{
+ if (domain_ != domain) {
+ return false;
+ }
+ if (!this->type_is_supported(data_type)) {
+ return false;
+ }
+ CustomData *custom_data = custom_data_access_.get_custom_data(component);
+ if (custom_data == nullptr) {
+ return false;
+ }
+ for (const CustomDataLayer &layer : Span(custom_data->layers, custom_data->totlayer)) {
+ if (layer.anonymous_id == &layer_id) {
+ /* Don't create two layers with the same id. */
+ return false;
+ }
+ }
+
+ const int domain_size = component.attribute_domain_size(domain_);
+ const std::string attribute_name = get_anonymous_attribute_name();
+ add_named_custom_data_layer_from_attribute_init(
+ attribute_name, *custom_data, data_type, domain_size, initializer);
+ const int layer_index = CustomData_get_named_layer_index(
+ custom_data, data_type, attribute_name.c_str());
+ CustomDataLayer *layer = &custom_data->layers[layer_index];
+ layer->flag |= CD_FLAG_ANONYMOUS;
+ layer->anonymous_id = &layer_id;
+ CustomData_anonymous_id_weak_increment(&layer_id);
+ return true;
+}
+
+ReadAttributeLookup CustomDataAttributeProvider::try_get_anonymous_for_read(
+ const GeometryComponent &component, const AnonymousCustomDataLayerID &layer_id) const
+{
+ const CustomData *custom_data = custom_data_access_.get_const_custom_data(component);
+ if (custom_data == nullptr) {
+ return {};
+ }
+ const int domain_size = component.attribute_domain_size(domain_);
+ for (const CustomDataLayer &layer : Span(custom_data->layers, custom_data->totlayer)) {
+ if (layer.anonymous_id != &layer_id) {
+ continue;
+ }
+ return this->layer_to_read_attribute(layer, domain_size);
+ }
+ return {};
+}
+
+WriteAttributeLookup CustomDataAttributeProvider::try_get_anonymous_for_write(
+ GeometryComponent &component, const AnonymousCustomDataLayerID &layer_id) const
+{
+ CustomData *custom_data = custom_data_access_.get_custom_data(component);
+ if (custom_data == nullptr) {
+ return {};
+ }
+ const int domain_size = component.attribute_domain_size(domain_);
+ for (CustomDataLayer &layer : MutableSpan(custom_data->layers, custom_data->totlayer)) {
+ if (layer.anonymous_id != &layer_id) {
+ continue;
+ }
+ CustomData_duplicate_referenced_layer_named(custom_data, layer.type, layer.name, domain_size);
+ return this->layer_to_write_attribute(layer, domain_size);
+ }
+ return {};
+}
+
bool CustomDataAttributeProvider::foreach_attribute(const GeometryComponent &component,
const AttributeForeachCallback callback) const
{
@@ -497,7 +587,7 @@ bool CustomDataAttributeProvider::foreach_attribute(const GeometryComponent &com
for (const CustomDataLayer &layer : Span(custom_data->layers, custom_data->totlayer)) {
const CustomDataType data_type = (CustomDataType)layer.type;
if (this->type_is_supported(data_type)) {
- AttributeMetaData meta_data{domain_, data_type};
+ AttributeMetaData meta_data{domain_, data_type, layer.anonymous_id};
if (!callback(layer.name, meta_data)) {
return false;
}
@@ -698,6 +788,56 @@ bool CustomDataAttributes::create_by_move(const blender::StringRef name,
return result != nullptr;
}
+bool CustomDataAttributes::create_anonymous(const AnonymousCustomDataLayerID &id,
+ const CustomDataType data_type)
+{
+ for (const CustomDataLayer &layer : Span(data.layers, data.totlayer)) {
+ if (layer.anonymous_id == &id) {
+ /* Don't create two layers with the same id. */
+ return false;
+ }
+ }
+
+ const std::string name = get_anonymous_attribute_name();
+ if (!this->create(name, data_type)) {
+ return false;
+ }
+
+ const int layer_index = CustomData_get_named_layer_index(&data, data_type, name.c_str());
+ CustomDataLayer &layer = data.layers[layer_index];
+ layer.flag |= CD_FLAG_ANONYMOUS;
+ layer.anonymous_id = &id;
+ CustomData_anonymous_id_weak_increment(&id);
+
+ return true;
+}
+
+std::optional<GSpan> CustomDataAttributes::get_anonymous_for_read(
+ const AnonymousCustomDataLayerID &id) const
+{
+ for (const CustomDataLayer &layer : Span(data.layers, data.totlayer)) {
+ if (layer.anonymous_id == &id) {
+ const CPPType *cpp_type = custom_data_type_to_cpp_type((CustomDataType)layer.type);
+ BLI_assert(cpp_type != nullptr);
+ return GSpan(*cpp_type, layer.data, size_);
+ }
+ }
+ return {};
+}
+
+std::optional<GMutableSpan> CustomDataAttributes::get_anonymous_for_write(
+ const AnonymousCustomDataLayerID &id)
+{
+ for (CustomDataLayer &layer : MutableSpan(data.layers, data.totlayer)) {
+ if (layer.anonymous_id == &id) {
+ const CPPType *cpp_type = custom_data_type_to_cpp_type((CustomDataType)layer.type);
+ BLI_assert(cpp_type != nullptr);
+ return GMutableSpan(*cpp_type, layer.data, size_);
+ }
+ }
+ return {};
+}
+
bool CustomDataAttributes::remove(const blender::StringRef name)
{
bool result = false;
@@ -721,7 +861,7 @@ bool CustomDataAttributes::foreach_attribute(const AttributeForeachCallback call
const AttributeDomain domain) const
{
for (const CustomDataLayer &layer : Span(data.layers, data.totlayer)) {
- AttributeMetaData meta_data{domain, (CustomDataType)layer.type};
+ AttributeMetaData meta_data{domain, (CustomDataType)layer.type, layer.anonymous_id};
if (!callback(layer.name, meta_data)) {
return false;
}
@@ -735,6 +875,14 @@ bool CustomDataAttributes::foreach_attribute(const AttributeForeachCallback call
/** \name Geometry Component
* \{ */
+static std::unique_ptr<blender::fn::GVArray> try_adapt_data_type(
+ std::unique_ptr<blender::fn::GVArray> varray, const blender::fn::CPPType &to_type)
+{
+ const blender::nodes::DataTypeConversions &conversions =
+ blender::nodes::get_implicit_type_conversions();
+ return conversions.try_convert(std::move(varray), to_type);
+}
+
const blender::bke::ComponentAttributeProviders *GeometryComponent::get_attribute_providers() const
{
return nullptr;
@@ -875,6 +1023,102 @@ bool GeometryComponent::attribute_try_create(const StringRef attribute_name,
return false;
}
+bool GeometryComponent::attribute_try_create_anonymous(const AnonymousCustomDataLayerID &layer_id,
+ const AttributeDomain domain,
+ const CustomDataType data_type,
+ const AttributeInit &initializer)
+{
+ using namespace blender::bke;
+ const ComponentAttributeProviders *providers = this->get_attribute_providers();
+ for (const DynamicAttributesProvider *dynamic_provider :
+ providers->dynamic_attribute_providers()) {
+ if (dynamic_provider->try_create_anonymous(*this, layer_id, domain, data_type, initializer)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+blender::bke::ReadAttributeLookup GeometryComponent::attribute_try_get_anonymous_for_read(
+ const AnonymousCustomDataLayerID &layer_id) const
+{
+ using namespace blender::bke;
+ const ComponentAttributeProviders *providers = this->get_attribute_providers();
+ for (const DynamicAttributesProvider *dynamic_provider :
+ providers->dynamic_attribute_providers()) {
+ ReadAttributeLookup attribute = dynamic_provider->try_get_anonymous_for_read(*this, layer_id);
+ if (attribute) {
+ return attribute;
+ }
+ }
+ return {};
+}
+
+blender::fn::GVArrayPtr GeometryComponent::attribute_try_get_anonymous_for_read(
+ const AnonymousCustomDataLayerID &id,
+ const AttributeDomain domain,
+ const CustomDataType data_type) const
+{
+ blender::bke::ReadAttributeLookup attribute = this->attribute_try_get_anonymous_for_read(id);
+ if (!attribute) {
+ return {};
+ }
+
+ std::unique_ptr<blender::fn::GVArray> varray = std::move(attribute.varray);
+ if (domain != ATTR_DOMAIN_AUTO && attribute.domain != domain) {
+ varray = this->attribute_try_adapt_domain(std::move(varray), attribute.domain, domain);
+ if (!varray) {
+ return {};
+ }
+ }
+
+ const blender::fn::CPPType *cpp_type = blender::bke::custom_data_type_to_cpp_type(data_type);
+ BLI_assert(cpp_type != nullptr);
+ if (varray->type() != *cpp_type) {
+ varray = try_adapt_data_type(std::move(varray), *cpp_type);
+ if (!varray) {
+ return {};
+ }
+ }
+
+ return varray;
+}
+
+blender::fn::GVArrayPtr GeometryComponent::attribute_try_get_anonymous_for_read(
+ const AnonymousCustomDataLayerID &id,
+ const AttributeDomain domain,
+ const CustomDataType data_type,
+ const void *default_value) const
+{
+ blender::fn::GVArrayPtr varray = this->attribute_try_get_anonymous_for_read(
+ id, domain, data_type);
+ if (varray) {
+ return varray;
+ }
+ const blender::fn::CPPType *type = blender::bke::custom_data_type_to_cpp_type(data_type);
+ if (default_value == nullptr) {
+ default_value = type->default_value();
+ }
+ const int domain_size = this->attribute_domain_size(domain);
+ return std::make_unique<blender::fn::GVArray_For_SingleValue>(*type, domain_size, default_value);
+}
+
+blender::bke::WriteAttributeLookup GeometryComponent::attribute_try_get_anonymous_for_write(
+ const AnonymousCustomDataLayerID &layer_id)
+{
+ using namespace blender::bke;
+ const ComponentAttributeProviders *providers = this->get_attribute_providers();
+ for (const DynamicAttributesProvider *dynamic_providers :
+ providers->dynamic_attribute_providers()) {
+ WriteAttributeLookup attribute = dynamic_providers->try_get_anonymous_for_write(*this,
+ layer_id);
+ if (attribute) {
+ return attribute;
+ }
+ }
+ return {};
+}
+
bool GeometryComponent::attribute_try_create_builtin(const blender::StringRef attribute_name,
const AttributeInit &initializer)
{
@@ -968,14 +1212,6 @@ std::optional<AttributeMetaData> GeometryComponent::attribute_get_meta_data(
return result;
}
-static std::unique_ptr<blender::fn::GVArray> try_adapt_data_type(
- std::unique_ptr<blender::fn::GVArray> varray, const blender::fn::CPPType &to_type)
-{
- const blender::nodes::DataTypeConversions &conversions =
- blender::nodes::get_implicit_type_conversions();
- return conversions.try_convert(std::move(varray), to_type);
-}
-
std::unique_ptr<blender::fn::GVArray> GeometryComponent::attribute_try_get_for_read(
const StringRef attribute_name,
const AttributeDomain domain,
@@ -1227,3 +1463,122 @@ blender::bke::OutputAttribute GeometryComponent::attribute_try_get_for_output_on
{
return create_output_attribute(*this, attribute_name, domain, data_type, true, nullptr);
}
+
+class GVMutableAttribute_For_AnonymousOutputAttribute
+ : public blender::fn::GVMutableArray_For_GMutableSpan {
+ public:
+ GeometryComponent *component;
+ const AnonymousCustomDataLayerID &final_id;
+
+ GVMutableAttribute_For_AnonymousOutputAttribute(GMutableSpan data,
+ GeometryComponent &component,
+ const AnonymousCustomDataLayerID &final_id)
+
+ : blender::fn::GVMutableArray_For_GMutableSpan(data),
+ component(&component),
+ final_id(final_id)
+ {
+ }
+
+ ~GVMutableAttribute_For_AnonymousOutputAttribute() override
+ {
+ type_->destruct_n(data_, size_);
+ MEM_freeN(data_);
+ }
+};
+
+static void save_output_anonymous_attribute(blender::bke::OutputAttribute &output_attribute)
+{
+ using namespace blender;
+ using namespace blender::fn;
+ using namespace blender::bke;
+
+ GVMutableAttribute_For_AnonymousOutputAttribute &varray =
+ dynamic_cast<GVMutableAttribute_For_AnonymousOutputAttribute &>(output_attribute.varray());
+
+ GeometryComponent &component = *varray.component;
+ WriteAttributeLookup write_attribute = component.attribute_try_get_anonymous_for_write(
+ varray.final_id);
+ BUFFER_FOR_CPP_TYPE_VALUE(varray.type(), buffer);
+ for (const int i : IndexRange(varray.size())) {
+ varray.get(i, buffer);
+ write_attribute.varray->set_by_relocate(i, buffer);
+ }
+}
+
+static blender::bke::OutputAttribute create_output_attribute_anonymous(
+ GeometryComponent &component,
+ const AnonymousCustomDataLayerID &id,
+ const AttributeDomain domain,
+ const CustomDataType data_type,
+ const bool ignore_old_values,
+ const void *default_value)
+{
+ using namespace blender;
+ using namespace blender::fn;
+ using namespace blender::bke;
+
+ const CPPType *cpp_type = custom_data_type_to_cpp_type(data_type);
+ BLI_assert(cpp_type != nullptr);
+
+ const int domain_size = component.attribute_domain_size(domain);
+
+ WriteAttributeLookup attribute = component.attribute_try_get_anonymous_for_write(id);
+ if (!attribute) {
+ if (default_value) {
+ const GVArray_For_SingleValueRef default_varray{*cpp_type, domain_size, default_value};
+ component.attribute_try_create_anonymous(
+ id, domain, data_type, AttributeInitVArray(&default_varray));
+ }
+ else {
+ component.attribute_try_create_anonymous(id, domain, data_type, AttributeInitDefault());
+ }
+
+ attribute = component.attribute_try_get_anonymous_for_write(id);
+ if (!attribute) {
+ /* Can't create the attribute. */
+ return {};
+ }
+ }
+ if (attribute.domain == domain && attribute.varray->type() == *cpp_type) {
+ /* Existing generic attribute matches exactly. */
+ return OutputAttribute(std::move(attribute.varray), domain, {}, ignore_old_values);
+ }
+
+ /* Allocate a new array that lives next to the existing attribute. It will overwrite the existing
+ * attribute after processing is done. */
+ void *data = MEM_mallocN_aligned(
+ cpp_type->size() * domain_size, cpp_type->alignment(), __func__);
+ if (ignore_old_values) {
+ /* This does nothing for trivially constructible types, but is necessary for correctness. */
+ cpp_type->default_construct_n(data, domain);
+ }
+ else {
+ BLI_assert_unreachable();
+ /* Fill the temporary array with values from the existing attribute. */
+ GVArrayPtr old_varray = component.attribute_try_get_anonymous_for_read(
+ id, domain, data_type, default_value);
+ old_varray->materialize_to_uninitialized(IndexRange(domain_size), data);
+ }
+ GVMutableArrayPtr varray = std::make_unique<GVMutableAttribute_For_AnonymousOutputAttribute>(
+ GMutableSpan{*cpp_type, data, domain_size}, component, id);
+
+ return OutputAttribute(std::move(varray), domain, save_output_anonymous_attribute, true);
+}
+
+blender::bke::OutputAttribute GeometryComponent::attribute_try_get_anonymous_for_output(
+ const AnonymousCustomDataLayerID &id,
+ const AttributeDomain domain,
+ const CustomDataType data_type,
+ const void *default_value)
+{
+ return create_output_attribute_anonymous(*this, id, domain, data_type, false, default_value);
+}
+
+blender::bke::OutputAttribute GeometryComponent::attribute_try_get_anonymous_for_output_only(
+ const AnonymousCustomDataLayerID &id,
+ const AttributeDomain domain,
+ const CustomDataType data_type)
+{
+ return create_output_attribute_anonymous(*this, id, domain, data_type, true, nullptr);
+}
diff --git a/source/blender/blenkernel/intern/attribute_access_intern.hh b/source/blender/blenkernel/intern/attribute_access_intern.hh
index b3a795faa30..e58d320a33d 100644
--- a/source/blender/blenkernel/intern/attribute_access_intern.hh
+++ b/source/blender/blenkernel/intern/attribute_access_intern.hh
@@ -130,6 +130,30 @@ class DynamicAttributesProvider {
return false;
};
+ /** Returns the id of the new anonymous or null if no new attribute was created. */
+ virtual bool try_create_anonymous(GeometryComponent &UNUSED(component),
+ const AnonymousCustomDataLayerID &UNUSED(layer_id),
+ const AttributeDomain UNUSED(domain),
+ const CustomDataType UNUSED(data_type),
+ const AttributeInit &UNUSED(initializer)) const
+ {
+ return false;
+ }
+
+ virtual ReadAttributeLookup try_get_anonymous_for_read(
+ const GeometryComponent &UNUSED(component),
+ const AnonymousCustomDataLayerID &UNUSED(layer_id)) const
+ {
+ return {};
+ }
+
+ virtual WriteAttributeLookup try_get_anonymous_for_write(
+ GeometryComponent &UNUSED(component),
+ const AnonymousCustomDataLayerID &UNUSED(layer_id)) const
+ {
+ return {};
+ }
+
virtual bool foreach_attribute(const GeometryComponent &component,
const AttributeForeachCallback callback) const = 0;
virtual void foreach_domain(const FunctionRef<void(AttributeDomain)> callback) const = 0;
@@ -167,6 +191,18 @@ class CustomDataAttributeProvider final : public DynamicAttributesProvider {
const CustomDataType data_type,
const AttributeInit &initializer) const final;
+ bool try_create_anonymous(GeometryComponent &component,
+ const AnonymousCustomDataLayerID &layer_id,
+ const AttributeDomain domain,
+ const CustomDataType data_type,
+ const AttributeInit &initializer) const final;
+
+ ReadAttributeLookup try_get_anonymous_for_read(
+ const GeometryComponent &component, const AnonymousCustomDataLayerID &layer_id) const final;
+
+ WriteAttributeLookup try_get_anonymous_for_write(
+ GeometryComponent &component, const AnonymousCustomDataLayerID &layer_id) const final;
+
bool foreach_attribute(const GeometryComponent &component,
const AttributeForeachCallback callback) const final;
@@ -176,6 +212,9 @@ class CustomDataAttributeProvider final : public DynamicAttributesProvider {
}
private:
+ ReadAttributeLookup layer_to_read_attribute(const CustomDataLayer &layer,
+ const int domain_size) const;
+
template<typename T>
ReadAttributeLookup layer_to_read_attribute(const CustomDataLayer &layer,
const int domain_size) const
@@ -185,6 +224,9 @@ class CustomDataAttributeProvider final : public DynamicAttributesProvider {
domain_};
}
+ WriteAttributeLookup layer_to_write_attribute(CustomDataLayer &layer,
+ const int domain_size) const;
+
template<typename T>
WriteAttributeLookup layer_to_write_attribute(CustomDataLayer &layer,
const int domain_size) const
diff --git a/source/blender/blenkernel/intern/curve_eval.cc b/source/blender/blenkernel/intern/curve_eval.cc
index 5c18f6f3807..61b6cfdae2e 100644
--- a/source/blender/blenkernel/intern/curve_eval.cc
+++ b/source/blender/blenkernel/intern/curve_eval.cc
@@ -339,7 +339,11 @@ void CurveEval::assert_valid_point_attributes() const
name,
[&](AttributeMetaData *map_data) {
/* All unique attribute names should be added on the first spline. */
- BLI_assert(spline == splines_.first());
+ /* TODO(Hans/Jacques): This check seems very bad, anonymous attributes with have
+ * different names on different splines. */
+ if (meta_data.anonymous_layer_id == nullptr) {
+ BLI_assert(spline == splines_.first());
+ }
*map_data = meta_data;
},
[&](AttributeMetaData *map_data) {
diff --git a/source/blender/blenkernel/intern/customdata.c b/source/blender/blenkernel/intern/customdata.c
index 1a3200a9b6c..fdce73e737b 100644
--- a/source/blender/blenkernel/intern/customdata.c
+++ b/source/blender/blenkernel/intern/customdata.c
@@ -57,6 +57,8 @@
#include "BLO_read_write.h"
+#include "atomic_ops.h"
+
#include "bmesh.h"
#include "CLG_log.h"
@@ -2127,6 +2129,9 @@ bool CustomData_merge(const struct CustomData *source,
if (flag & CD_FLAG_NOCOPY) {
continue;
}
+ if (CustomData_layer_is_unused_anonymous(layer)) {
+ continue;
+ }
if (!(mask & CD_TYPE_AS_MASK(type))) {
continue;
}
@@ -2164,8 +2169,13 @@ bool CustomData_merge(const struct CustomData *source,
newlayer->active_rnd = lastrender;
newlayer->active_clone = lastclone;
newlayer->active_mask = lastmask;
- newlayer->flag |= flag & (CD_FLAG_EXTERNAL | CD_FLAG_IN_MEMORY);
+ newlayer->flag |= flag & (CD_FLAG_EXTERNAL | CD_FLAG_IN_MEMORY | CD_FLAG_ANONYMOUS);
changed = true;
+
+ if (layer->flag & CD_FLAG_ANONYMOUS) {
+ CustomData_anonymous_id_weak_increment(layer->anonymous_id);
+ newlayer->anonymous_id = layer->anonymous_id;
+ }
}
}
@@ -2206,6 +2216,10 @@ static void customData_free_layer__internal(CustomDataLayer *layer, int totelem)
{
const LayerTypeInfo *typeInfo;
+ if (layer->flag & CD_FLAG_ANONYMOUS) {
+ CustomData_anonymous_id_weak_decrement(layer->anonymous_id);
+ layer->anonymous_id = NULL;
+ }
if (!(layer->flag & CD_FLAG_NOFREE) && layer->data) {
typeInfo = layerType_getInfo(layer->type);
@@ -4244,7 +4258,8 @@ void CustomData_blend_write_prepare(CustomData *data,
for (i = 0, j = 0; i < totlayer; i++) {
CustomDataLayer *layer = &data->layers[i];
- if (layer->flag & CD_FLAG_NOCOPY) { /* Layers with this flag set are not written to file. */
+ /* Layers with this flag set are not written to file. */
+ if (layer->flag & (CD_FLAG_NOCOPY | CD_FLAG_ANONYMOUS)) {
data->totlayer--;
// CLOG_WARN(&LOG, "skipping layer %p (%s)", layer, layer->name);
}
@@ -5031,6 +5046,68 @@ void CustomData_data_transfer(const MeshPairRemap *me_remap,
MEM_SAFE_FREE(tmp_data_src);
}
+AnonymousCustomDataLayerID *CustomData_anonymous_id_new(const char *debug_name)
+{
+ AnonymousCustomDataLayerID *layer_id = MEM_callocN(sizeof(AnonymousCustomDataLayerID), __func__);
+ layer_id->debug_name = BLI_strdup(debug_name);
+ return layer_id;
+}
+
+static void CustomData_anonymous_id_free(AnonymousCustomDataLayerID *layer_id)
+{
+ BLI_assert(layer_id->strong_references == 0);
+ BLI_assert(layer_id->tot_references == 0);
+ MEM_freeN(layer_id->debug_name);
+ MEM_freeN(layer_id);
+}
+
+void CustomData_anonymous_id_strong_decrement(const AnonymousCustomDataLayerID *layer_id)
+{
+ AnonymousCustomDataLayerID *mutable_layer_id = (AnonymousCustomDataLayerID *)layer_id;
+ int strong_references = atomic_sub_and_fetch_int32(&mutable_layer_id->strong_references, 1);
+ BLI_assert(strong_references >= 0);
+ UNUSED_VARS_NDEBUG(strong_references);
+
+ int tot_references = atomic_sub_and_fetch_int32(&mutable_layer_id->tot_references, 1);
+ BLI_assert(tot_references >= 0);
+ if (tot_references == 0) {
+ CustomData_anonymous_id_free(mutable_layer_id);
+ }
+}
+
+void CustomData_anonymous_id_strong_increment(const AnonymousCustomDataLayerID *layer_id)
+{
+ AnonymousCustomDataLayerID *mutable_layer_id = (AnonymousCustomDataLayerID *)layer_id;
+ atomic_add_and_fetch_int32(&mutable_layer_id->tot_references, 1);
+ atomic_add_and_fetch_int32(&mutable_layer_id->strong_references, 1);
+}
+
+void CustomData_anonymous_id_weak_decrement(const AnonymousCustomDataLayerID *layer_id)
+{
+ AnonymousCustomDataLayerID *mutable_layer_id = (AnonymousCustomDataLayerID *)layer_id;
+ int tot_references = atomic_sub_and_fetch_int32(&mutable_layer_id->tot_references, 1);
+ BLI_assert(tot_references >= 0);
+ if (tot_references == 0) {
+ CustomData_anonymous_id_free(mutable_layer_id);
+ }
+}
+
+void CustomData_anonymous_id_weak_increment(const AnonymousCustomDataLayerID *layer_id)
+{
+ AnonymousCustomDataLayerID *mutable_layer_id = (AnonymousCustomDataLayerID *)layer_id;
+ atomic_add_and_fetch_int32(&mutable_layer_id->tot_references, 1);
+}
+
+bool CustomData_layer_is_unused_anonymous(const CustomDataLayer *layer)
+{
+ if (layer->flag & CD_FLAG_ANONYMOUS) {
+ if (layer->anonymous_id->strong_references == 0) {
+ return true;
+ }
+ }
+ return false;
+}
+
static void write_mdisps(BlendWriter *writer, int count, MDisps *mdlist, int external)
{
if (mdlist) {
diff --git a/source/blender/blenkernel/intern/geometry_component_curve.cc b/source/blender/blenkernel/intern/geometry_component_curve.cc
index 0b6ba966974..471bb765706 100644
--- a/source/blender/blenkernel/intern/geometry_component_curve.cc
+++ b/source/blender/blenkernel/intern/geometry_component_curve.cc
@@ -1094,6 +1094,165 @@ class DynamicPointAttributeProvider final : public DynamicAttributesProvider {
return true;
}
+ bool try_create_anonymous(GeometryComponent &component,
+ const AnonymousCustomDataLayerID &layer_id,
+ const AttributeDomain domain,
+ const CustomDataType data_type,
+ const AttributeInit &initializer) const
+ {
+ BLI_assert(this->type_is_supported(data_type));
+ if (domain != ATTR_DOMAIN_POINT) {
+ return false;
+ }
+
+ CurveEval *curve = get_curve_from_component_for_write(component);
+ if (curve == nullptr || curve->splines().size() == 0) {
+ return false;
+ }
+
+ MutableSpan<SplinePtr> splines = curve->splines();
+
+ /* Otherwise just create a custom data layer on each of the splines. */
+ for (const int i : splines.index_range()) {
+ if (!splines[i]->attributes.create_anonymous(layer_id, data_type)) {
+ /* If attribute creation fails on one of the splines, we cannot leave the custom data
+ * layers in the previous splines around, so delete them before returning. However,
+ * this is not an expected case. */
+ BLI_assert_unreachable();
+ return false;
+ }
+ }
+
+ /* With a default initializer type, we can keep the values at their initial values. */
+ if (initializer.type == AttributeInit::Type::Default) {
+ return true;
+ }
+
+ WriteAttributeLookup write_attribute = this->try_get_anonymous_for_write(component, layer_id);
+ /* We just created the attribute, it should exist. */
+ BLI_assert(write_attribute);
+
+ const int total_size = curve->control_point_offsets().last();
+ GVArrayPtr source_varray = varray_from_initializer(initializer, data_type, total_size);
+ /* TODO: When we can call a variant of #set_all with a virtual array argument,
+ * this theoretically unnecessary materialize step could be removed. */
+ GVArray_GSpan source_varray_span{*source_varray};
+ write_attribute.varray->set_all(source_varray_span.data());
+
+ if (initializer.type == AttributeInit::Type::MoveArray) {
+ MEM_freeN(static_cast<const AttributeInitMove &>(initializer).data);
+ }
+
+ curve->assert_valid_point_attributes();
+
+ return true;
+ }
+
+ ReadAttributeLookup try_get_anonymous_for_read(const GeometryComponent &component,
+ const AnonymousCustomDataLayerID &layer_id) const
+ {
+ const CurveEval *curve = get_curve_from_component_for_read(component);
+ if (curve == nullptr || curve->splines().size() == 0) {
+ return {};
+ }
+
+ Span<SplinePtr> splines = curve->splines();
+ Vector<GSpan> spans; /* GSpan has no default constructor. */
+ spans.reserve(splines.size());
+ std::optional<GSpan> first_span = splines[0]->attributes.get_anonymous_for_read(layer_id);
+ if (!first_span) {
+ return {};
+ }
+ spans.append(*first_span);
+ for (const int i : IndexRange(1, splines.size() - 1)) {
+ std::optional<GSpan> span = splines[i]->attributes.get_anonymous_for_read(layer_id);
+ if (!span) {
+ /* All splines should have the same set of data layers. It would be possible to recover
+ * here and return partial data instead, but that would add a lot of complexity for a
+ * situation we don't even expect to encounter. */
+ BLI_assert_unreachable();
+ return {};
+ }
+ if (span->type() != spans.last().type()) {
+ /* Data layer types on separate splines do not match. */
+ BLI_assert_unreachable();
+ return {};
+ }
+ spans.append(*span);
+ }
+
+ /* First check for the simpler situation when we can return a simpler span virtual array. */
+ if (spans.size() == 1) {
+ return {std::make_unique<GVArray_For_GSpan>(spans.first()), ATTR_DOMAIN_POINT};
+ }
+
+ ReadAttributeLookup attribute = {};
+ Array<int> offsets = curve->control_point_offsets();
+ attribute_math::convert_to_static_type(spans[0].type(), [&](auto dummy) {
+ using T = decltype(dummy);
+ Array<Span<T>> data(splines.size());
+ for (const int i : splines.index_range()) {
+ data[i] = spans[i].typed<T>();
+ BLI_assert(data[i].data() != nullptr);
+ }
+ attribute = {point_data_gvarray(data, offsets), ATTR_DOMAIN_POINT};
+ });
+ return attribute;
+ }
+
+ WriteAttributeLookup try_get_anonymous_for_write(
+ GeometryComponent &component, const AnonymousCustomDataLayerID &layer_id) const
+ {
+ CurveEval *curve = get_curve_from_component_for_write(component);
+ if (curve == nullptr || curve->splines().size() == 0) {
+ return {};
+ }
+
+ MutableSpan<SplinePtr> splines = curve->splines();
+ Vector<GMutableSpan> spans; /* GMutableSpan has no default constructor. */
+ spans.reserve(splines.size());
+ std::optional<GMutableSpan> first_span = splines[0]->attributes.get_anonymous_for_write(
+ layer_id);
+ if (!first_span) {
+ return {};
+ }
+ spans.append(*first_span);
+ for (const int i : IndexRange(1, splines.size() - 1)) {
+ std::optional<GMutableSpan> span = splines[i]->attributes.get_anonymous_for_write(layer_id);
+ if (!span) {
+ /* All splines should have the same set of data layers. It would be possible to recover
+ * here and return partial data instead, but that would add a lot of complexity for a
+ * situation we don't even expect to encounter. */
+ BLI_assert_unreachable();
+ return {};
+ }
+ if (span->type() != spans.last().type()) {
+ /* Data layer types on separate splines do not match. */
+ BLI_assert_unreachable();
+ return {};
+ }
+ spans.append(*span);
+ }
+
+ /* First check for the simpler situation when we can return a simpler span virtual array. */
+ if (spans.size() == 1) {
+ return {std::make_unique<GVMutableArray_For_GMutableSpan>(spans.first()), ATTR_DOMAIN_POINT};
+ }
+
+ WriteAttributeLookup attribute = {};
+ Array<int> offsets = curve->control_point_offsets();
+ attribute_math::convert_to_static_type(spans[0].type(), [&](auto dummy) {
+ using T = decltype(dummy);
+ Array<MutableSpan<T>> data(splines.size());
+ for (const int i : splines.index_range()) {
+ data[i] = spans[i].typed<T>();
+ BLI_assert(data[i].data() != nullptr);
+ }
+ attribute = {point_data_gvarray(data, offsets), ATTR_DOMAIN_POINT};
+ });
+ return attribute;
+ }
+
bool foreach_attribute(const GeometryComponent &component,
const AttributeForeachCallback callback) const final
{
diff --git a/source/blender/blenkernel/intern/geometry_component_mesh.cc b/source/blender/blenkernel/intern/geometry_component_mesh.cc
index ef93a3f9b3f..d694cdc634d 100644
--- a/source/blender/blenkernel/intern/geometry_component_mesh.cc
+++ b/source/blender/blenkernel/intern/geometry_component_mesh.cc
@@ -651,6 +651,207 @@ blender::fn::GVArrayPtr MeshComponent::attribute_try_adapt_domain(
return {};
}
+namespace blender::bke::adapt_selection_domain {
+static VArrayPtr<bool> varray_from_array(Array<bool> array)
+{
+ return std::make_unique<VArray_For_ArrayContainer<Array<bool>>>(std::move(array));
+}
+
+static VArrayPtr<bool> adapt_selection_point_to_face(const Mesh &mesh, VArrayPtr<bool> selection)
+{
+ Array<bool> new_selection(mesh.totpoly);
+ for (const int poly_index : IndexRange(mesh.totpoly)) {
+ const MPoly &poly = mesh.mpoly[poly_index];
+ bool poly_is_selected = true;
+ for (const int loop_index : IndexRange(poly.loopstart, poly.totloop)) {
+ const MLoop &loop = mesh.mloop[loop_index];
+ if (!selection->get(loop.v)) {
+ poly_is_selected = false;
+ break;
+ }
+ }
+ new_selection[poly_index] = poly_is_selected;
+ }
+ return varray_from_array(std::move(new_selection));
+}
+
+static VArrayPtr<bool> adapt_selection_point_to_corner(const Mesh &mesh, VArrayPtr<bool> selection)
+{
+ Array<bool> new_selection(mesh.totloop);
+ for (const int loop_index : IndexRange(mesh.totloop)) {
+ const MLoop &loop = mesh.mloop[loop_index];
+ new_selection[loop_index] = selection->get(loop.v);
+ }
+ return varray_from_array(std::move(new_selection));
+}
+
+static VArrayPtr<bool> adapt_selection_point_to_edge(const Mesh &mesh, VArrayPtr<bool> selection)
+{
+ Array<bool> new_selection(mesh.totedge);
+ for (const int edge_index : IndexRange(mesh.totedge)) {
+ const MEdge &edge = mesh.medge[edge_index];
+ const bool edge_is_selected = selection->get(edge.v1) && selection->get(edge.v2);
+ new_selection[edge_index] = edge_is_selected;
+ }
+ return varray_from_array(std::move(new_selection));
+}
+
+static VArrayPtr<bool> adapt_selection_edge_to_point(const Mesh &mesh, VArrayPtr<bool> selection)
+{
+ Array<bool> new_selection(mesh.totvert, false);
+ for (const int edge_index : IndexRange(mesh.totedge)) {
+ if (selection->get(edge_index)) {
+ const MEdge &edge = mesh.medge[edge_index];
+ new_selection[edge.v1] = true;
+ new_selection[edge.v2] = true;
+ }
+ }
+ return varray_from_array(std::move(new_selection));
+}
+
+static VArrayPtr<bool> adapt_selection_edge_to_face(const Mesh &mesh, VArrayPtr<bool> selection)
+{
+ Array<bool> new_selection(mesh.totpoly);
+ for (const int poly_index : IndexRange(mesh.totpoly)) {
+ const MPoly &poly = mesh.mpoly[poly_index];
+ bool poly_is_selected = true;
+ for (const int loop_index : IndexRange(poly.loopstart, poly.totloop)) {
+ const MLoop &loop = mesh.mloop[loop_index];
+ if (!selection->get(loop.e)) {
+ poly_is_selected = false;
+ break;
+ }
+ }
+ new_selection[poly_index] = poly_is_selected;
+ }
+ return varray_from_array(std::move(new_selection));
+}
+
+static VArrayPtr<bool> adapt_selection_face_to_point(const Mesh &mesh, VArrayPtr<bool> selection)
+{
+ Array<bool> new_selection(mesh.totvert, false);
+ for (const int poly_index : IndexRange(mesh.totpoly)) {
+ const MPoly &poly = mesh.mpoly[poly_index];
+ if (selection->get(poly_index)) {
+ for (const int loop_index : IndexRange(poly.loopstart, poly.totloop)) {
+ const MLoop &loop = mesh.mloop[loop_index];
+ BLI_assert(loop.v < mesh.totvert);
+ new_selection[loop.v] = true;
+ }
+ }
+ }
+ return varray_from_array(std::move(new_selection));
+}
+
+static VArrayPtr<bool> adapt_selection_face_to_edge(const Mesh &mesh, VArrayPtr<bool> selection)
+{
+ Array<bool> new_selection(mesh.totedge, false);
+ for (const int poly_index : IndexRange(mesh.totpoly)) {
+ const MPoly &poly = mesh.mpoly[poly_index];
+ if (selection->get(poly_index)) {
+ for (const int loop_index : IndexRange(poly.loopstart, poly.totloop)) {
+ const MLoop &loop = mesh.mloop[loop_index];
+ new_selection[loop.e] = true;
+ }
+ }
+ }
+ return varray_from_array(std::move(new_selection));
+}
+
+static VArrayPtr<bool> adapt_selection_face_to_corner(const Mesh &mesh, VArrayPtr<bool> selection)
+{
+ Array<bool> new_selection(mesh.totloop);
+ for (const int poly_index : IndexRange(mesh.totpoly)) {
+ const MPoly &poly = mesh.mpoly[poly_index];
+ const bool is_selected = selection->get(poly_index);
+ for (const int loop_index : IndexRange(poly.loopstart, poly.totloop)) {
+ new_selection[loop_index] = is_selected;
+ }
+ }
+ return varray_from_array(std::move(new_selection));
+}
+
+} // namespace blender::bke::adapt_selection_domain
+
+blender::VArrayPtr<bool> MeshComponent::adapt_selection(blender::VArrayPtr<bool> selection,
+ const AttributeDomain from_domain,
+ const AttributeDomain to_domain) const
+{
+ using namespace blender::bke::adapt_selection_domain;
+
+ const int from_domain_size = this->attribute_domain_size(from_domain);
+ BLI_assert(selection->size() == from_domain_size);
+
+ if (from_domain == to_domain) {
+ return selection;
+ }
+ if (from_domain_size == 0) {
+ return selection;
+ }
+ if (selection->is_single()) {
+ return selection;
+ }
+
+ switch (from_domain) {
+ case ATTR_DOMAIN_CORNER: {
+ switch (to_domain) {
+ case ATTR_DOMAIN_POINT:
+ break;
+ case ATTR_DOMAIN_FACE:
+ break;
+ case ATTR_DOMAIN_EDGE:
+ break;
+ default:
+ break;
+ }
+ break;
+ }
+ case ATTR_DOMAIN_POINT: {
+ switch (to_domain) {
+ case ATTR_DOMAIN_CORNER:
+ return adapt_selection_point_to_corner(*mesh_, std::move(selection));
+ case ATTR_DOMAIN_FACE:
+ return adapt_selection_point_to_face(*mesh_, std::move(selection));
+ case ATTR_DOMAIN_EDGE:
+ return adapt_selection_point_to_edge(*mesh_, std::move(selection));
+ default:
+ break;
+ }
+ break;
+ }
+ case ATTR_DOMAIN_FACE: {
+ switch (to_domain) {
+ case ATTR_DOMAIN_POINT:
+ return adapt_selection_face_to_point(*mesh_, std::move(selection));
+ case ATTR_DOMAIN_CORNER:
+ return adapt_selection_face_to_corner(*mesh_, std::move(selection));
+ case ATTR_DOMAIN_EDGE:
+ return adapt_selection_face_to_edge(*mesh_, std::move(selection));
+ default:
+ break;
+ }
+ break;
+ }
+ case ATTR_DOMAIN_EDGE: {
+ switch (to_domain) {
+ case ATTR_DOMAIN_CORNER:
+ break;
+ case ATTR_DOMAIN_POINT:
+ return adapt_selection_edge_to_point(*mesh_, std::move(selection));
+ case ATTR_DOMAIN_FACE:
+ return adapt_selection_edge_to_face(*mesh_, std::move(selection));
+ default:
+ break;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+
+ return {};
+}
+
static Mesh *get_mesh_from_component_for_write(GeometryComponent &component)
{
BLI_assert(component.type() == GEO_COMPONENT_TYPE_MESH);
diff --git a/source/blender/blenkernel/intern/geometry_set.cc b/source/blender/blenkernel/intern/geometry_set.cc
index 07b4e715ea9..682a3ca0409 100644
--- a/source/blender/blenkernel/intern/geometry_set.cc
+++ b/source/blender/blenkernel/intern/geometry_set.cc
@@ -18,6 +18,7 @@
#include "BKE_attribute.h"
#include "BKE_attribute_access.hh"
+#include "BKE_field.hh"
#include "BKE_geometry_set.hh"
#include "BKE_lib_id.h"
#include "BKE_mesh.h"
diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc
index 2a0e05a2616..151448e3038 100644
--- a/source/blender/blenkernel/intern/node.cc
+++ b/source/blender/blenkernel/intern/node.cc
@@ -5115,13 +5115,16 @@ static void registerGeometryNodes()
{
register_node_type_geo_group();
- register_node_type_geo_align_rotation_to_vector();
+ register_node_type_geo_extrude();
+
+ register_node_type_geo_attribute();
register_node_type_geo_attribute_clamp();
register_node_type_geo_attribute_color_ramp();
register_node_type_geo_attribute_combine_xyz();
register_node_type_geo_attribute_compare();
register_node_type_geo_attribute_convert();
register_node_type_geo_attribute_curve_map();
+ register_node_type_geo_attribute_extract();
register_node_type_geo_attribute_fill();
register_node_type_geo_attribute_map_range();
register_node_type_geo_attribute_math();
@@ -5139,6 +5142,7 @@ static void registerGeometryNodes()
register_node_type_geo_convex_hull();
register_node_type_geo_curve_endpoints();
register_node_type_geo_curve_length();
+ register_node_type_geo_curve_parameter();
register_node_type_geo_curve_primitive_bezier_segment();
register_node_type_geo_curve_primitive_circle();
register_node_type_geo_curve_primitive_line();
@@ -5156,6 +5160,9 @@ static void registerGeometryNodes()
register_node_type_geo_curve_trim();
register_node_type_geo_delete_geometry();
register_node_type_geo_edge_split();
+ register_node_type_geo_evaluate_curve();
+ register_node_type_geo_extrude_and_move();
+ register_node_type_geo_index();
register_node_type_geo_input_material();
register_node_type_geo_is_viewport();
register_node_type_geo_join_geometry();
@@ -5171,6 +5178,7 @@ static void registerGeometryNodes()
register_node_type_geo_mesh_primitive_uv_sphere();
register_node_type_geo_mesh_subdivide();
register_node_type_geo_mesh_to_curve();
+ register_node_type_geo_normal();
register_node_type_geo_object_info();
register_node_type_geo_point_distribute();
register_node_type_geo_point_instance();
@@ -5179,17 +5187,21 @@ static void registerGeometryNodes()
register_node_type_geo_point_separate();
register_node_type_geo_point_translate();
register_node_type_geo_points_to_volume();
+ register_node_type_geo_position();
register_node_type_geo_raycast();
+ register_node_type_geo_sample_mesh_surface();
register_node_type_geo_sample_texture();
register_node_type_geo_select_by_handle_type();
register_node_type_geo_select_by_material();
register_node_type_geo_separate_components();
+ register_node_type_geo_set_position();
register_node_type_geo_subdivision_surface();
register_node_type_geo_switch();
register_node_type_geo_transform();
register_node_type_geo_triangulate();
register_node_type_geo_viewer();
register_node_type_geo_volume_to_mesh();
+ register_node_type_geo_attribute_freeze();
}
static void registerFunctionNodes()
@@ -5199,6 +5211,7 @@ static void registerFunctionNodes()
register_node_type_fn_float_to_int();
register_node_type_fn_input_string();
register_node_type_fn_input_vector();
+ register_node_type_fn_align_rotation_to_vector();
register_node_type_fn_random_float();
}
diff --git a/source/blender/blenkernel/intern/spline_base.cc b/source/blender/blenkernel/intern/spline_base.cc
index dda7abea0fc..732fabc6582 100644
--- a/source/blender/blenkernel/intern/spline_base.cc
+++ b/source/blender/blenkernel/intern/spline_base.cc
@@ -123,7 +123,7 @@ int Spline::evaluated_edges_size() const
float Spline::length() const
{
Span<float> lengths = this->evaluated_lengths();
- return (lengths.size() == 0) ? 0 : this->evaluated_lengths().last();
+ return lengths.is_empty() ? 0.0f : this->evaluated_lengths().last();
}
int Spline::segments_size() const
diff --git a/source/blender/blenlib/BLI_hash.hh b/source/blender/blenlib/BLI_hash.hh
index fbed321534c..11ff7d040aa 100644
--- a/source/blender/blenlib/BLI_hash.hh
+++ b/source/blender/blenlib/BLI_hash.hh
@@ -250,6 +250,20 @@ template<typename T> struct DefaultHash<std::unique_ptr<T>> {
}
};
+template<typename T> struct DefaultHash<std::shared_ptr<T>> {
+ uint64_t operator()(const std::shared_ptr<T> &value) const
+ {
+ return get_default_hash(value.get());
+ }
+};
+
+template<typename T> struct DefaultHash<std::reference_wrapper<T>> {
+ uint64_t operator()(const std::reference_wrapper<T> &value) const
+ {
+ return get_default_hash(value.get());
+ }
+};
+
template<typename T1, typename T2> struct DefaultHash<std::pair<T1, T2>> {
uint64_t operator()(const std::pair<T1, T2> &value) const
{
diff --git a/source/blender/blenlib/BLI_optional_ptr.hh b/source/blender/blenlib/BLI_optional_ptr.hh
new file mode 100644
index 00000000000..2488262b3cb
--- /dev/null
+++ b/source/blender/blenlib/BLI_optional_ptr.hh
@@ -0,0 +1,88 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#pragma once
+
+/** \file
+ * \ingroup bli
+ */
+
+#include <memory>
+
+#include "BLI_utildefines.h"
+
+namespace blender {
+
+template<typename T, typename OwnedTPtr = std::unique_ptr<T>> class optional_ptr {
+ private:
+ OwnedTPtr owned_ptr_;
+ T *ptr_ = nullptr;
+
+ public:
+ optional_ptr() = default;
+
+ optional_ptr(T &ptr) : ptr_(&ptr)
+ {
+ }
+
+ optional_ptr(OwnedTPtr owned_ptr) : owned_ptr_(std::move(owned_ptr)), ptr_(&*owned_ptr_)
+ {
+ }
+
+ bool is_owned() const
+ {
+ return static_cast<bool>(owned_ptr_);
+ }
+
+ OwnedTPtr extract_owned()
+ {
+ OwnedTPtr ptr = std::move(owned_ptr_);
+ owned_ptr_ = OwnedTPtr{nullptr};
+ ptr_ = nullptr;
+ return ptr;
+ }
+
+ T *operator->()
+ {
+ BLI_assert(ptr_ != nullptr);
+ return ptr_;
+ }
+
+ const T *operator->() const
+ {
+ BLI_assert(ptr_ != nullptr);
+ return ptr_;
+ }
+
+ T &operator*()
+ {
+ BLI_assert(ptr_ != nullptr);
+ return *ptr_;
+ }
+
+ const T &operator*() const
+ {
+ BLI_assert(ptr_ != nullptr);
+ return *ptr_;
+ }
+
+ operator bool() const
+ {
+ return ptr_ != nullptr;
+ }
+};
+
+} // namespace blender
diff --git a/source/blender/blenlib/BLI_span.hh b/source/blender/blenlib/BLI_span.hh
index e04295b0e51..387ada56e98 100644
--- a/source/blender/blenlib/BLI_span.hh
+++ b/source/blender/blenlib/BLI_span.hh
@@ -478,8 +478,8 @@ template<typename T> class MutableSpan {
using size_type = int64_t;
protected:
- T *data_;
- int64_t size_;
+ T *data_ = nullptr;
+ int64_t size_ = 0;
public:
constexpr MutableSpan() = default;
diff --git a/source/blender/blenlib/BLI_user_counter.hh b/source/blender/blenlib/BLI_user_counter.hh
index 3e6d5af4c3f..8cebadeac4c 100644
--- a/source/blender/blenlib/BLI_user_counter.hh
+++ b/source/blender/blenlib/BLI_user_counter.hh
@@ -84,12 +84,24 @@ template<typename T> class UserCounter {
return data_;
}
+ const T *operator->() const
+ {
+ BLI_assert(data_ != nullptr);
+ return data_;
+ }
+
T &operator*()
{
BLI_assert(data_ != nullptr);
return *data_;
}
+ const T &operator*() const
+ {
+ BLI_assert(data_ != nullptr);
+ return *data_;
+ }
+
operator bool() const
{
return data_ != nullptr;
diff --git a/source/blender/blenlib/BLI_virtual_array.hh b/source/blender/blenlib/BLI_virtual_array.hh
index 1c02bce8411..71c6db7e56e 100644
--- a/source/blender/blenlib/BLI_virtual_array.hh
+++ b/source/blender/blenlib/BLI_virtual_array.hh
@@ -255,7 +255,7 @@ template<typename T> class VMutableArray : public VArray<T> {
}
};
-template<typename T> using VArrayPtr = std::unique_ptr<VArray<T>>;
+template<typename T> using VArrayPtr = std::unique_ptr<const VArray<T>>;
template<typename T> using VMutableArrayPtr = std::unique_ptr<VMutableArray<T>>;
/**
diff --git a/source/blender/blenlib/tests/BLI_map_test.cc b/source/blender/blenlib/tests/BLI_map_test.cc
index 679a10e9ce0..7c6eaac0c55 100644
--- a/source/blender/blenlib/tests/BLI_map_test.cc
+++ b/source/blender/blenlib/tests/BLI_map_test.cc
@@ -653,6 +653,18 @@ TEST(map, LookupKey)
EXPECT_EQ(map.lookup_key_ptr("a"), map.lookup_key_ptr_as("a"));
}
+TEST(map, ReferenceWrapperKey)
+{
+ Map<std::reference_wrapper<int>, int> map;
+ int a = 2;
+ int b = 5;
+ map.add(a, 10);
+ map.add(a, 20);
+ map.add(b, 20);
+ EXPECT_EQ(map.lookup(a), 10);
+ EXPECT_EQ(map.lookup(b), 20);
+}
+
/**
* Set this to 1 to activate the benchmark. It is disabled by default, because it prints a lot.
*/
diff --git a/source/blender/bmesh/intern/bmesh_mesh.c b/source/blender/bmesh/intern/bmesh_mesh.c
index b2958a9e744..53cf597466a 100644
--- a/source/blender/bmesh/intern/bmesh_mesh.c
+++ b/source/blender/bmesh/intern/bmesh_mesh.c
@@ -1446,4 +1446,141 @@ void BM_mesh_vert_coords_apply_with_mat4(BMesh *bm,
}
}
+/**
+ * Use to select bmesh vertex data based on an array of bool.
+ */
+void BM_select_vertices(BMesh *bm, const bool *mask)
+{
+ BMIter iter;
+ BMVert *v;
+ int i = 0;
+ BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) {
+ if (mask[i]) {
+ BM_elem_flag_set(v, BM_ELEM_SELECT, true);
+ }
+ else {
+ BM_elem_flag_set(v, BM_ELEM_SELECT, false);
+ }
+ i++;
+ }
+}
+
+/**
+ * Use to select bmesh edge data based on an array of bool.
+ */
+void BM_select_edges(BMesh *bm, const bool *mask)
+{
+ BMIter iter;
+ BMEdge *e;
+ int i = 0;
+ BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) {
+ if (mask[i]) {
+ BM_elem_flag_set(e, BM_ELEM_SELECT, true);
+ }
+ else {
+ BM_elem_flag_set(e, BM_ELEM_SELECT, false);
+ }
+ i++;
+ }
+}
+
+/**
+ * Use to select bmesh face data based on an array of bool.
+ */
+void BM_select_faces(BMesh *bm, const bool *mask)
+{
+ BMIter iter;
+ BMFace *f;
+ int i = 0;
+ BM_ITER_MESH_INDEX (f, &iter, bm, BM_FACES_OF_MESH, i) {
+ BM_elem_flag_set(f, BM_ELEM_SELECT, mask[i]);
+ }
+}
+
+void BM_tag_vertices(BMesh *bm, const bool *mask)
+{
+ BMIter iter;
+ BMVert *v;
+ int i = 0;
+ BM_ITER_MESH_INDEX (v, &iter, bm, BM_VERTS_OF_MESH, i) {
+ BM_elem_flag_set(v, BM_ELEM_TAG, mask[i]);
+ }
+}
+
+/**
+ * Use to temporary tag bmesh edge data based on an array of bool.
+ */
+void BM_tag_edges(BMesh *bm, const bool *mask)
+{
+ BMIter iter;
+ BMEdge *e;
+ int i = 0;
+ BM_ITER_MESH_INDEX (e, &iter, bm, BM_EDGES_OF_MESH, i) {
+ BM_elem_flag_set(e, BM_ELEM_TAG, mask[i]);
+ }
+}
+
+/**
+ * Use to temporary tag bmesh face data based on an array of bool.
+ */
+void BM_tag_faces(BMesh *bm, const bool *mask)
+{
+ BMIter iter;
+ BMFace *f;
+ int i = 0;
+ BM_ITER_MESH_INDEX (f, &iter, bm, BM_FACES_OF_MESH, i) {
+ BM_elem_flag_set(f, BM_ELEM_TAG, mask[i]);
+ }
+}
+
+void BM_get_tagged_faces(BMesh *bm, bool *selection)
+{
+ BMIter iter;
+ BMFace *f;
+ int i = 0;
+ BM_ITER_MESH_INDEX (f, &iter, bm, BM_FACES_OF_MESH, i) {
+ selection[i] = BM_elem_flag_test(f, BM_ELEM_TAG);
+ }
+}
+
+void BM_tag_new_faces(BMesh *bm, BMOperator *b_mesh_operator)
+{
+ BMOIter iter;
+ BMFace *f;
+ BM_mesh_elem_hflag_disable_all(bm, BM_FACE, BM_ELEM_TAG, false);
+ BMO_ITER (f, &iter, b_mesh_operator->slots_out, "faces.out", BM_FACE) {
+ BM_elem_flag_enable(f, BM_ELEM_TAG);
+ }
+}
+
+void BM_get_selected_faces(BMesh *bm, bool *selection)
+{
+ BMIter iter;
+ BMFace *f;
+ int i = 0;
+ BM_ITER_MESH_INDEX (f, &iter, bm, BM_FACES_OF_MESH, i) {
+ selection[i] = BM_elem_flag_test(f, BM_ELEM_SELECT);
+ }
+}
+
+void BM_get_selected_edges(BMesh *bm, bool *selection)
+{
+ BMIter iter;
+ BMEdge *e;
+ int i = 0;
+ BM_ITER_MESH_INDEX (e, &iter, bm, BM_EDGES_OF_MESH, i) {
+ selection[i] = BM_elem_flag_test(e, BM_ELEM_SELECT);
+ }
+}
+
+void BM_get_selected_vertices(BMesh *bm, bool *selection)
+{
+ BMIter iter;
+ BMVert *v;
+ int i = 0;
+ BM_ITER_MESH_INDEX (v, &iter, bm, BM_VERTS_OF_MESH, i) {
+ selection[i] = BM_elem_flag_test(v, BM_ELEM_SELECT);
+ }
+}
+
/** \} */
diff --git a/source/blender/bmesh/intern/bmesh_mesh.h b/source/blender/bmesh/intern/bmesh_mesh.h
index bd0504b038a..2e9e3402903 100644
--- a/source/blender/bmesh/intern/bmesh_mesh.h
+++ b/source/blender/bmesh/intern/bmesh_mesh.h
@@ -134,3 +134,16 @@ void BM_mesh_vert_coords_apply(BMesh *bm, const float (*vert_coords)[3]);
void BM_mesh_vert_coords_apply_with_mat4(BMesh *bm,
const float (*vert_coords)[3],
const float mat[4][4]);
+
+void BM_select_vertices(BMesh *bm, const bool *mask);
+void BM_select_edges(BMesh *bm, const bool *mask);
+void BM_select_faces(BMesh *bm, const bool *mask);
+void BM_tag_vertices(BMesh *bm, const bool *mask);
+void BM_tag_edges(BMesh *bm, const bool *mask);
+void BM_tag_faces(BMesh *bm, const bool *mask);
+
+void BM_get_tagged_faces(BMesh *bm, bool *selection);
+void BM_get_selected_faces(BMesh *bm, bool *selection);
+void BM_get_selected_edges(BMesh *bm, bool *selection);
+void BM_get_selected_vertices(BMesh *bm, bool *selection);
+void BM_tag_new_faces(BMesh *bm, BMOperator *b_mesh_operator); \ No newline at end of file
diff --git a/source/blender/bmesh/intern/bmesh_opdefines.c b/source/blender/bmesh/intern/bmesh_opdefines.c
index b63a09a97a6..fc3f592cb79 100644
--- a/source/blender/bmesh/intern/bmesh_opdefines.c
+++ b/source/blender/bmesh/intern/bmesh_opdefines.c
@@ -1898,6 +1898,9 @@ static BMOpDefine bmo_inset_individual_def = {
{{"faces", BMO_OP_SLOT_ELEMENT_BUF, {BM_FACE}}, /* input faces */
{"thickness", BMO_OP_SLOT_FLT}, /* thickness */
{"depth", BMO_OP_SLOT_FLT}, /* depth */
+ {"thickness_array", BMO_OP_SLOT_PTR}, /* thickness */
+ {"depth_array", BMO_OP_SLOT_PTR}, /* depth */
+ {"use_attributes", BMO_OP_SLOT_BOOL}, /* Use spans for thickness and depth */
{"use_even_offset", BMO_OP_SLOT_BOOL}, /* scale the offset to give more even thickness */
{"use_interpolate", BMO_OP_SLOT_BOOL}, /* blend face data across the inset */
{"use_relative_offset", BMO_OP_SLOT_BOOL}, /* scale the offset by surrounding geometry */
@@ -1929,6 +1932,9 @@ static BMOpDefine bmo_inset_region_def = {
{"use_edge_rail", BMO_OP_SLOT_BOOL}, /* inset the region along existing edges */
{"thickness", BMO_OP_SLOT_FLT}, /* thickness */
{"depth", BMO_OP_SLOT_FLT}, /* depth */
+ {"thickness_array", BMO_OP_SLOT_PTR}, /* thickness */
+ {"depth_array", BMO_OP_SLOT_PTR}, /* depth */
+ {"use_attributes", BMO_OP_SLOT_BOOL}, /* Use spans for thickness and depth */
{"use_outset", BMO_OP_SLOT_BOOL}, /* outset rather than inset */
{{'\0'}},
},
@@ -1940,7 +1946,6 @@ static BMOpDefine bmo_inset_region_def = {
(BMO_OPTYPE_FLAG_NORMALS_CALC |
BMO_OPTYPE_FLAG_SELECT_FLUSH),
};
-
/*
* Edge-loop Offset.
*
diff --git a/source/blender/bmesh/operators/bmo_extrude.c b/source/blender/bmesh/operators/bmo_extrude.c
index 0cedc2324f2..5e50f8fa495 100644
--- a/source/blender/bmesh/operators/bmo_extrude.c
+++ b/source/blender/bmesh/operators/bmo_extrude.c
@@ -471,6 +471,7 @@ void bmo_extrude_face_region_exec(BMesh *bm, BMOperator *op)
for (e = BMO_iter_new(&siter, dupeop.slots_out, "boundary_map.out", 0); e;
e = BMO_iter_step(&siter)) {
BMVert *f_verts[4];
+
#ifdef USE_EDGE_REGION_FLAGS
BMEdge *f_edges[4];
#endif
diff --git a/source/blender/bmesh/operators/bmo_inset.c b/source/blender/bmesh/operators/bmo_inset.c
index 4833290aa0b..7db1fdde84d 100644
--- a/source/blender/bmesh/operators/bmo_inset.c
+++ b/source/blender/bmesh/operators/bmo_inset.c
@@ -419,6 +419,9 @@ void bmo_inset_individual_exec(BMesh *bm, BMOperator *op)
BMOIter oiter;
MemArena *interp_arena = NULL;
+ const bool use_attributes = BMO_slot_bool_get(op->slots_in, "use_attributes");
+ const float *thickness_array = BMO_slot_ptr_get(op->slots_in, "thickness_array");
+ const float *depth_array = BMO_slot_ptr_get(op->slots_in, "depth_array");
const float thickness = BMO_slot_float_get(op->slots_in, "thickness");
const float depth = BMO_slot_float_get(op->slots_in, "depth");
const bool use_even_offset = BMO_slot_bool_get(op->slots_in, "use_even_offset");
@@ -433,19 +436,37 @@ void bmo_inset_individual_exec(BMesh *bm, BMOperator *op)
if (use_interpolate) {
interp_arena = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, __func__);
}
+ int i = 0;
+ if (use_attributes) {
+ BMO_ITER_INDEX (f, &oiter, op->slots_in, "faces", BM_FACE, i) {
+ bmo_face_inset_individual(bm,
+ f,
+ interp_arena,
+ thickness_array[i],
+ depth_array[i],
+ use_even_offset,
+ use_relative_offset,
+ use_interpolate);
- BMO_ITER (f, &oiter, op->slots_in, "faces", BM_FACE) {
- bmo_face_inset_individual(bm,
- f,
- interp_arena,
- thickness,
- depth,
- use_even_offset,
- use_relative_offset,
- use_interpolate);
+ if (use_interpolate) {
+ BLI_memarena_clear(interp_arena);
+ }
+ }
+ }
+ else {
+ BMO_ITER (f, &oiter, op->slots_in, "faces", BM_FACE) {
+ bmo_face_inset_individual(bm,
+ f,
+ interp_arena,
+ thickness,
+ depth,
+ use_even_offset,
+ use_relative_offset,
+ use_interpolate);
- if (use_interpolate) {
- BLI_memarena_clear(interp_arena);
+ if (use_interpolate) {
+ BLI_memarena_clear(interp_arena);
+ }
}
}
@@ -677,12 +698,16 @@ void bmo_inset_region_exec(BMesh *bm, BMOperator *op)
const bool use_outset = BMO_slot_bool_get(op->slots_in, "use_outset");
const bool use_boundary = BMO_slot_bool_get(op->slots_in, "use_boundary") &&
(use_outset == false);
+
const bool use_even_offset = BMO_slot_bool_get(op->slots_in, "use_even_offset");
const bool use_even_boundary = use_even_offset; /* could make own option */
const bool use_relative_offset = BMO_slot_bool_get(op->slots_in, "use_relative_offset");
const bool use_edge_rail = BMO_slot_bool_get(op->slots_in, "use_edge_rail");
const bool use_interpolate = BMO_slot_bool_get(op->slots_in, "use_interpolate");
const float thickness = BMO_slot_float_get(op->slots_in, "thickness");
+ const bool use_attributes = BMO_slot_bool_get(op->slots_in, "use_attributes");
+ const float *thickness_array = BMO_slot_ptr_get(op->slots_in, "thickness_array");
+ // const float *depth_array = BMO_slot_ptr_get(op->slots_in, "depth_array");
const float depth = BMO_slot_float_get(op->slots_in, "depth");
#ifdef USE_LOOP_CUSTOMDATA_MERGE
const bool has_math_ldata = (use_interpolate && CustomData_has_math(&bm->ldata));
@@ -1096,7 +1121,12 @@ void bmo_inset_region_exec(BMesh *bm, BMOperator *op)
}
/* apply the offset */
- madd_v3_v3fl(v_split->co, tvec, thickness);
+ if (use_attributes) {
+ madd_v3_v3fl(v_split->co, tvec, thickness_array[v_split->head.index]);
+ }
+ else {
+ madd_v3_v3fl(v_split->co, tvec, thickness);
+ }
}
/* this saves expensive/slow glue check for common cases */
diff --git a/source/blender/editors/object/object_intern.h b/source/blender/editors/object/object_intern.h
index 10e016738d0..47ece926da8 100644
--- a/source/blender/editors/object/object_intern.h
+++ b/source/blender/editors/object/object_intern.h
@@ -191,6 +191,7 @@ void OBJECT_OT_skin_radii_equalize(struct wmOperatorType *ot);
void OBJECT_OT_skin_armature_create(struct wmOperatorType *ot);
void OBJECT_OT_laplaciandeform_bind(struct wmOperatorType *ot);
void OBJECT_OT_surfacedeform_bind(struct wmOperatorType *ot);
+void OBJECT_OT_geometry_nodes_modifier_value_or_attribute_toggle(struct wmOperatorType *ot);
/* object_gpencil_modifiers.c */
void OBJECT_OT_gpencil_modifier_add(struct wmOperatorType *ot);
diff --git a/source/blender/editors/object/object_modifier.c b/source/blender/editors/object/object_modifier.c
index e9142742d15..6b728a1e66e 100644
--- a/source/blender/editors/object/object_modifier.c
+++ b/source/blender/editors/object/object_modifier.c
@@ -3242,3 +3242,55 @@ void OBJECT_OT_surfacedeform_bind(wmOperatorType *ot)
}
/** \} */
+
+/* ------------------------------------------------------------------- */
+/** \name Toggle Value or Attribute Operator
+ * \{ */
+
+static int geometry_nodes_modifier_value_or_attribute_toggle_exec(bContext *C, wmOperator *op)
+{
+ Object *ob = ED_object_active_context(C);
+
+ char modifier_name[MAX_NAME];
+ RNA_string_get(op->ptr, "modifier_name", modifier_name);
+
+ NodesModifierData *nmd = (NodesModifierData *)BKE_modifiers_findby_name(ob, modifier_name);
+
+ if (nmd == NULL) {
+ return OPERATOR_CANCELLED;
+ }
+
+ char prop_path[MAX_NAME];
+ RNA_string_get(op->ptr, "prop_path", prop_path);
+
+ PointerRNA mod_ptr;
+ RNA_pointer_create(&ob->id, &RNA_Modifier, nmd, &mod_ptr);
+
+ const int old_value = RNA_int_get(&mod_ptr, prop_path);
+ const int new_value = !old_value;
+ RNA_int_set(&mod_ptr, prop_path, new_value);
+
+ DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
+ WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob);
+ return OPERATOR_FINISHED;
+}
+
+void OBJECT_OT_geometry_nodes_modifier_value_or_attribute_toggle(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Toggle Geometry Nodes Modifier Value or Attribute Toggle";
+ ot->description = "Toggle between attribute and value";
+ ot->idname = "OBJECT_OT_geometry_nodes_modifier_value_or_attribute_toggle";
+
+ /* api callbacks */
+ ot->exec = geometry_nodes_modifier_value_or_attribute_toggle_exec;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL;
+
+ RNA_def_string(ot->srna, "prop_path", NULL, 0, "Prop Path", "Prop path");
+ RNA_def_string(
+ ot->srna, "modifier_name", NULL, MAX_NAME, "Modifier", "Name of the modifier to edit");
+}
+
+/** \} */
diff --git a/source/blender/editors/object/object_ops.c b/source/blender/editors/object/object_ops.c
index c1928cf7f8a..b7924239491 100644
--- a/source/blender/editors/object/object_ops.c
+++ b/source/blender/editors/object/object_ops.c
@@ -145,6 +145,7 @@ void ED_operatortypes_object(void)
WM_operatortype_append(OBJECT_OT_skin_loose_mark_clear);
WM_operatortype_append(OBJECT_OT_skin_radii_equalize);
WM_operatortype_append(OBJECT_OT_skin_armature_create);
+ WM_operatortype_append(OBJECT_OT_geometry_nodes_modifier_value_or_attribute_toggle);
/* grease pencil modifiers */
WM_operatortype_append(OBJECT_OT_gpencil_modifier_add);
diff --git a/source/blender/editors/space_node/node_draw.cc b/source/blender/editors/space_node/node_draw.cc
index 3b1a55f55ab..cca61148c85 100644
--- a/source/blender/editors/space_node/node_draw.cc
+++ b/source/blender/editors/space_node/node_draw.cc
@@ -45,6 +45,7 @@
#include "BLT_translation.h"
#include "BKE_context.h"
+#include "BKE_field.hh"
#include "BKE_idtype.h"
#include "BKE_lib_id.h"
#include "BKE_main.h"
@@ -767,14 +768,14 @@ static void node_socket_draw(const bNodeSocket *sock,
immVertex2f(pos_id, locx, locy);
}
-static void node_socket_draw_multi_input(const float color[4],
- const float color_outline[4],
- const float width,
- const float height,
- const int locx,
- const int locy)
+static void node_socket_draw_rounded_rectangle(const float color[4],
+ const float color_outline[4],
+ const float width,
+ const float height,
+ const float locx,
+ const float locy,
+ const float outline_width)
{
- const float outline_width = 1.0f;
/* UI_draw_roundbox draws the outline on the outer side, so compensate for the outline width. */
const rctf rect = {
locx - width + outline_width * 0.5f,
@@ -788,6 +789,40 @@ static void node_socket_draw_multi_input(const float color[4],
&rect, color, nullptr, 1.0f, color_outline, outline_width, width - outline_width * 0.5f);
}
+static bool use_special_non_field_socket_drawing(const bNodeTree *node_tree,
+ const bNode *node,
+ const bNodeSocket *socket)
+{
+ if (node_tree->type != NTREE_GEOMETRY) {
+ return false;
+ }
+ if (ELEM(socket->type,
+ SOCK_MATERIAL,
+ SOCK_GEOMETRY,
+ SOCK_TEXTURE,
+ SOCK_COLLECTION,
+ SOCK_OBJECT,
+ SOCK_IMAGE,
+ SOCK_STRING)) {
+ return false;
+ }
+ if (socket->flag & SOCK_FIELD) {
+ return false;
+ }
+ if (socket->in_out == SOCK_OUT) {
+ return false;
+ }
+ if (node->typeinfo->expand_in_mf_network) {
+ /* Wow, that's hacky. Don't use vertical bar for function nodes. */
+ return false;
+ }
+ if (ELEM(node->type, NODE_REROUTE, NODE_GROUP, NODE_GROUP_OUTPUT)) {
+ return false;
+ }
+
+ return true;
+}
+
static const float virtual_node_socket_outline_color[4] = {0.5, 0.5, 0.5, 1.0};
static void node_socket_outline_color_get(const bool selected,
@@ -842,31 +877,68 @@ static void create_inspection_string_for_generic_value(const geo_log::GenericVal
};
const GPointer value = value_log.value();
- if (value.is_type<int>()) {
- ss << *value.get<int>() << TIP_(" (Integer)");
- }
- else if (value.is_type<float>()) {
- ss << *value.get<float>() << TIP_(" (Float)");
- }
- else if (value.is_type<blender::float3>()) {
- ss << *value.get<blender::float3>() << TIP_(" (Vector)");
- }
- else if (value.is_type<bool>()) {
- ss << (*value.get<bool>() ? TIP_("True") : TIP_("False")) << TIP_(" (Boolean)");
+ const CPPType &type = *value.type();
+ if (const blender::bke::FieldRefCPPType *field_ref_type =
+ dynamic_cast<const blender::bke::FieldRefCPPType *>(&type)) {
+ const CPPType &base_type = field_ref_type->field_type();
+ blender::bke::FieldPtr field = field_ref_type->get_field(value.get());
+ blender::bke::FieldInputs field_inputs = field->prepare_inputs();
+ const int tot_inputs = field_inputs.tot_inputs();
+ if (tot_inputs > 0) {
+ ss << "Field Inputs:\n";
+ int i = 0;
+ for (const blender::bke::FieldInputKey &key : field_inputs) {
+ ss << "\u2022 ";
+ if (const blender::bke::PersistentAttributeFieldInputKey *persistent_attribute_key =
+ dynamic_cast<const blender::bke::PersistentAttributeFieldInputKey *>(&key)) {
+ ss << "Persistent attribute: '" << persistent_attribute_key->name() << "'";
+ }
+ else if (const blender::bke::AnonymousAttributeFieldInputKey *anonymous_attribute_key =
+ dynamic_cast<const blender::bke::AnonymousAttributeFieldInputKey *>(&key)) {
+ ss << "Anonymous attribute: '" << anonymous_attribute_key->layer_id().debug_name << "'";
+ }
+ else if (dynamic_cast<const blender::bke::IndexFieldInputKey *>(&key)) {
+ ss << "Index";
+ }
+ if (++i < tot_inputs) {
+ ss << ".\n";
+ }
+ }
+ }
+ else {
+ blender::bke::FieldOutput field_output = field->evaluate({0}, field_inputs);
+ BUFFER_FOR_CPP_TYPE_VALUE(base_type, buffer);
+ field_output.varray_ref().get(0, buffer);
+ if (base_type.is<int>()) {
+ ss << *(int *)buffer << TIP_(" (Integer)");
+ }
+ else if (base_type.is<float>()) {
+ ss << *(float *)buffer << TIP_(" (Float)");
+ }
+ else if (base_type.is<blender::float3>()) {
+ ss << *(blender::float3 *)buffer << TIP_(" (Vector)");
+ }
+ else if (base_type.is<bool>()) {
+ ss << (*(bool *)buffer ? TIP_("True") : TIP_("False")) << TIP_(" (Boolean)");
+ }
+ else if (base_type.is<blender::ColorGeometry4f>()) {
+ ss << *(blender::ColorGeometry4f *)buffer << TIP_(" (Color)");
+ }
+ }
}
- else if (value.is_type<std::string>()) {
+ else if (type.is<std::string>()) {
ss << *value.get<std::string>() << TIP_(" (String)");
}
- else if (value.is_type<Object *>()) {
+ else if (type.is<Object *>()) {
id_to_inspection_string((ID *)*value.get<Object *>());
}
- else if (value.is_type<Material *>()) {
+ else if (type.is<Material *>()) {
id_to_inspection_string((ID *)*value.get<Material *>());
}
- else if (value.is_type<Tex *>()) {
+ else if (type.is<Tex *>()) {
id_to_inspection_string((ID *)*value.get<Tex *>());
}
- else if (value.is_type<Collection *>()) {
+ else if (type.is<Collection *>()) {
id_to_inspection_string((ID *)*value.get<Collection *>());
}
}
@@ -988,24 +1060,27 @@ static void node_socket_draw_nested(const bContext *C,
node_socket_color_get((bContext *)C, ntree, node_ptr, sock, color);
node_socket_outline_color_get(selected, sock->type, outline_color);
- node_socket_draw(sock,
- color,
- outline_color,
- size,
- sock->locx,
- sock->locy,
- pos_id,
- col_id,
- shape_id,
- size_id,
- outline_col_id);
+ bNode *node = (bNode *)node_ptr->data;
+
+ if (!use_special_non_field_socket_drawing(ntree, node, sock)) {
+ node_socket_draw(sock,
+ color,
+ outline_color,
+ size,
+ sock->locx,
+ sock->locy,
+ pos_id,
+ col_id,
+ shape_id,
+ size_id,
+ outline_col_id);
+ }
if (ntree->type != NTREE_GEOMETRY) {
/* Only geometry nodes has socket value tooltips currently. */
return;
}
- bNode *node = (bNode *)node_ptr->data;
uiBlock *block = node->block;
/* Ideally sockets themselves should be buttons, but they aren't currently. So add an invisible
@@ -1365,20 +1440,28 @@ void node_draw_sockets(const View2D *v2d,
if (nodeSocketIsHidden(socket)) {
continue;
}
- if (!(socket->flag & SOCK_MULTI_INPUT)) {
- continue;
- }
-
- const bool is_node_hidden = (node->flag & NODE_HIDDEN);
- const float width = NODE_SOCKSIZE;
- float height = is_node_hidden ? width : node_socket_calculate_height(socket) - width;
-
float color[4];
float outline_color[4];
node_socket_color_get((bContext *)C, ntree, &node_ptr, socket, color);
node_socket_outline_color_get(selected, socket->type, outline_color);
- node_socket_draw_multi_input(color, outline_color, width, height, socket->locx, socket->locy);
+ if (socket->flag & SOCK_MULTI_INPUT) {
+ const bool is_node_hidden = (node->flag & NODE_HIDDEN);
+ const float width = NODE_SOCKSIZE;
+ float height = is_node_hidden ? width : node_socket_calculate_height(socket) - width;
+
+ node_socket_draw_rounded_rectangle(
+ color, outline_color, width, height, socket->locx, socket->locy, 1.0f);
+ }
+ else if (use_special_non_field_socket_drawing(ntree, node, socket)) {
+ node_socket_draw_rounded_rectangle(color,
+ outline_color,
+ NODE_SOCKSIZE * 0.6f,
+ NODE_SOCKSIZE * 1.3,
+ socket->locx - 0.8f,
+ socket->locy,
+ 0.75f);
+ }
}
}
diff --git a/source/blender/editors/space_node/node_geometry_attribute_search.cc b/source/blender/editors/space_node/node_geometry_attribute_search.cc
index a6901c21862..8cd2d8d4cb8 100644
--- a/source/blender/editors/space_node/node_geometry_attribute_search.cc
+++ b/source/blender/editors/space_node/node_geometry_attribute_search.cc
@@ -99,12 +99,23 @@ static void attribute_search_update_fn(
AttributeSearchData *data = static_cast<AttributeSearchData *>(arg);
SpaceNode *snode = CTX_wm_space_node(C);
- const geo_log::NodeLog *node_log = geo_log::ModifierLog::find_node_by_node_editor_context(
- *snode, *data->node);
- if (node_log == nullptr) {
- return;
+ blender::Vector<const GeometryAttributeInfo *> infos;
+ if (data->node->type == GEO_NODE_ATTRIBUTE) {
+ const geo_log::ModifierLog *modifier_log =
+ geo_log::ModifierLog::find_root_by_node_editor_context(*snode);
+ if (modifier_log == nullptr) {
+ return;
+ }
+ infos = modifier_log->lookup_available_attributes();
+ }
+ else {
+ const geo_log::NodeLog *node_log = geo_log::ModifierLog::find_node_by_node_editor_context(
+ *snode, *data->node);
+ if (node_log == nullptr) {
+ return;
+ }
+ infos = node_log->lookup_available_attributes();
}
- blender::Vector<const GeometryAttributeInfo *> infos = node_log->lookup_available_attributes();
GeometryAttributeInfo &dummy_info = get_dummy_item_info();
@@ -165,6 +176,38 @@ static void attribute_search_exec_fn(bContext *C, void *data_v, void *item_v)
bNodeSocketValueString *value = static_cast<bNodeSocketValueString *>(socket.default_value);
BLI_strncpy(value->value, item->name.c_str(), MAX_NAME);
+ if (data->node->type == GEO_NODE_ATTRIBUTE) {
+ NodeGeometryAttribute *storage = (NodeGeometryAttribute *)data->node->storage;
+ switch (item->data_type) {
+ case CD_PROP_FLOAT: {
+ storage->output_type = SOCK_FLOAT;
+ break;
+ }
+ case CD_PROP_INT32: {
+ storage->output_type = SOCK_INT;
+ break;
+ }
+ case CD_PROP_FLOAT2:
+ case CD_PROP_FLOAT3: {
+ storage->output_type = SOCK_VECTOR;
+ break;
+ }
+ case CD_PROP_BOOL: {
+ storage->output_type = SOCK_BOOLEAN;
+ break;
+ }
+ case CD_PROP_COLOR: {
+ storage->output_type = SOCK_RGBA;
+ break;
+ }
+ default:
+ BLI_assert_unreachable();
+ break;
+ }
+ snode_update(CTX_wm_space_node(C), (bNode *)data->node);
+ ntreeUpdateTree(CTX_data_main(C), (bNodeTree *)data->tree);
+ }
+
ED_undo_push(C, "Assign Attribute Name");
}
diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc b/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc
index e38c70afd0f..eaec43f0208 100644
--- a/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc
+++ b/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc
@@ -49,6 +49,9 @@ void GeometryDataSource::foreach_default_column_ids(
if (meta_data.domain != domain_) {
return true;
}
+ if (meta_data.anonymous_layer_id != nullptr) {
+ return true;
+ }
SpreadsheetColumnID column_id;
column_id.name = (char *)name.c_str();
fn(column_id);
diff --git a/source/blender/functions/FN_generic_virtual_array.hh b/source/blender/functions/FN_generic_virtual_array.hh
index f429243e2de..d58fb263d7e 100644
--- a/source/blender/functions/FN_generic_virtual_array.hh
+++ b/source/blender/functions/FN_generic_virtual_array.hh
@@ -25,6 +25,7 @@
#include <optional>
+#include "BLI_optional_ptr.hh"
#include "BLI_virtual_array.hh"
#include "FN_generic_span.hh"
@@ -260,6 +261,23 @@ class GVArray_For_GSpan : public GVArray {
GSpan get_internal_span_impl() const override;
};
+class GVArray_For_OwnedGSpan : public GVArray_For_GSpan {
+ private:
+ IndexMask indices_to_destruct_;
+
+ public:
+ GVArray_For_OwnedGSpan(const GSpan span) : GVArray_For_OwnedGSpan(span, IndexMask(span.size()))
+ {
+ }
+
+ GVArray_For_OwnedGSpan(const GSpan span, IndexMask indices_to_destruct)
+ : GVArray_For_GSpan(span), indices_to_destruct_(indices_to_destruct)
+ {
+ }
+
+ ~GVArray_For_OwnedGSpan();
+};
+
class GVArray_For_Empty : public GVArray {
public:
GVArray_For_Empty(const CPPType &type) : GVArray(type, 0)
@@ -339,11 +357,21 @@ class GVArray_For_SingleValue : public GVArray_For_SingleValueRef {
/* Used to convert a typed virtual array into a generic one. */
template<typename T> class GVArray_For_VArray : public GVArray {
protected:
- const VArray<T> *varray_ = nullptr;
+ optional_ptr<const VArray<T>> varray_;
public:
GVArray_For_VArray(const VArray<T> &varray)
- : GVArray(CPPType::get<T>(), varray.size()), varray_(&varray)
+ : GVArray(CPPType::get<T>(), varray.size()), varray_(varray)
+ {
+ }
+
+ GVArray_For_VArray(std::unique_ptr<const VArray<T>> varray)
+ : GVArray_For_VArray(optional_ptr<const VArray<T>>(std::move(varray)))
+ {
+ }
+
+ GVArray_For_VArray(optional_ptr<const VArray<T>> varray)
+ : GVArray(CPPType::get<T>(), varray->size()), varray_(std::move(varray))
{
}
@@ -394,21 +422,31 @@ template<typename T> class GVArray_For_VArray : public GVArray {
const void *try_get_internal_varray_impl() const override
{
- return varray_;
+ return &*varray_;
}
};
/* Used to convert any generic virtual array into a typed one. */
template<typename T> class VArray_For_GVArray : public VArray<T> {
protected:
- const GVArray *varray_ = nullptr;
+ optional_ptr<const GVArray> varray_;
public:
- VArray_For_GVArray(const GVArray &varray) : VArray<T>(varray.size()), varray_(&varray)
+ VArray_For_GVArray(const GVArray &varray) : VArray<T>(varray.size()), varray_(varray)
{
BLI_assert(varray_->type().template is<T>());
}
+ VArray_For_GVArray(optional_ptr<const GVArray> varray)
+ : VArray<T>(varray->size()), varray_(std::move(varray))
+ {
+ }
+
+ VArray_For_GVArray(GVArrayPtr varray)
+ : VArray_For_GVArray(optional_ptr<const GVArray>(std::move(varray)))
+ {
+ }
+
protected:
VArray_For_GVArray(const int64_t size) : VArray<T>(size)
{
@@ -447,15 +485,20 @@ template<typename T> class VArray_For_GVArray : public VArray<T> {
/* Used to convert an generic mutable virtual array into a typed one. */
template<typename T> class VMutableArray_For_GVMutableArray : public VMutableArray<T> {
protected:
- GVMutableArray *varray_ = nullptr;
+ optional_ptr<GVMutableArray> varray_;
public:
VMutableArray_For_GVMutableArray(GVMutableArray &varray)
- : VMutableArray<T>(varray.size()), varray_(&varray)
+ : VMutableArray<T>(varray.size()), varray_(varray)
{
BLI_assert(varray.type().template is<T>());
}
+ VMutableArray_For_GVMutableArray(optional_ptr<GVMutableArray> varray)
+ : varray_(std::move(varray))
+ {
+ }
+
VMutableArray_For_GVMutableArray(const int64_t size) : VMutableArray<T>(size)
{
}
@@ -480,7 +523,7 @@ template<typename T> class VMutableArray_For_GVMutableArray : public VMutableArr
Span<T> get_internal_span_impl() const override
{
- return varray_->get_internal_span().template typed<T>();
+ return static_cast<const GVArray &>(*varray_).get_internal_span().template typed<T>();
}
bool is_single_impl() const override
@@ -499,7 +542,7 @@ template<typename T> class VMutableArray_For_GVMutableArray : public VMutableArr
/* Used to convert any typed virtual mutable array into a generic one. */
template<typename T> class GVMutableArray_For_VMutableArray : public GVMutableArray {
protected:
- VMutableArray<T> *varray_ = nullptr;
+ optional_ptr<VMutableArray<T>> varray_;
public:
GVMutableArray_For_VMutableArray(VMutableArray<T> &varray)
@@ -507,6 +550,11 @@ template<typename T> class GVMutableArray_For_VMutableArray : public GVMutableAr
{
}
+ GVMutableArray_For_VMutableArray(optional_ptr<VMutableArray<T>> varray)
+ : varray_(std::move(varray))
+ {
+ }
+
protected:
GVMutableArray_For_VMutableArray(const int64_t size) : GVMutableArray(CPPType::get<T>(), size)
{
@@ -529,7 +577,7 @@ template<typename T> class GVMutableArray_For_VMutableArray : public GVMutableAr
GSpan get_internal_span_impl() const override
{
- Span<T> span = varray_->get_internal_span();
+ Span<T> span = static_cast<const VArray<T> &>(*varray_).get_internal_span();
return span;
}
@@ -579,12 +627,12 @@ template<typename T> class GVMutableArray_For_VMutableArray : public GVMutableAr
const void *try_get_internal_varray_impl() const override
{
- return (const VArray<T> *)varray_;
+ return static_cast<const VArray<T> *>(&*varray_);
}
void *try_get_internal_mutable_varray_impl() override
{
- return varray_;
+ return &*varray_;
}
};
@@ -629,56 +677,6 @@ template<typename T> class GVArray_Span : public Span<T> {
}
};
-template<typename T> class GVArray_For_OwnedVArray : public GVArray_For_VArray<T> {
- private:
- VArrayPtr<T> owned_varray_;
-
- public:
- /* Takes ownership of varray and passes a reference to the base class. */
- GVArray_For_OwnedVArray(VArrayPtr<T> varray)
- : GVArray_For_VArray<T>(*varray), owned_varray_(std::move(varray))
- {
- }
-};
-
-template<typename T> class VArray_For_OwnedGVArray : public VArray_For_GVArray<T> {
- private:
- GVArrayPtr owned_varray_;
-
- public:
- /* Takes ownership of varray and passes a reference to the base class. */
- VArray_For_OwnedGVArray(GVArrayPtr varray)
- : VArray_For_GVArray<T>(*varray), owned_varray_(std::move(varray))
- {
- }
-};
-
-template<typename T>
-class GVMutableArray_For_OwnedVMutableArray : public GVMutableArray_For_VMutableArray<T> {
- private:
- VMutableArrayPtr<T> owned_varray_;
-
- public:
- /* Takes ownership of varray and passes a reference to the base class. */
- GVMutableArray_For_OwnedVMutableArray(VMutableArrayPtr<T> varray)
- : GVMutableArray_For_VMutableArray<T>(*varray), owned_varray_(std::move(varray))
- {
- }
-};
-
-template<typename T>
-class VMutableArray_For_OwnedGVMutableArray : public VMutableArray_For_GVMutableArray<T> {
- private:
- GVMutableArrayPtr owned_varray_;
-
- public:
- /* Takes ownership of varray and passes a reference to the base class. */
- VMutableArray_For_OwnedGVMutableArray(GVMutableArrayPtr varray)
- : VMutableArray_For_GVMutableArray<T>(*varray), owned_varray_(std::move(varray))
- {
- }
-};
-
/* Utility to embed a typed virtual array into a generic one. This avoids one allocation and give
* the compiler more opportunity to optimize the generic virtual array. */
template<typename T, typename VArrayT>
@@ -691,7 +689,7 @@ class GVArray_For_EmbeddedVArray : public GVArray_For_VArray<T> {
GVArray_For_EmbeddedVArray(const int64_t size, Args &&...args)
: GVArray_For_VArray<T>(size), embedded_varray_(std::forward<Args>(args)...)
{
- this->varray_ = &embedded_varray_;
+ this->varray_ = embedded_varray_;
}
};
@@ -706,7 +704,7 @@ class GVMutableArray_For_EmbeddedVMutableArray : public GVMutableArray_For_VMuta
GVMutableArray_For_EmbeddedVMutableArray(const int64_t size, Args &&...args)
: GVMutableArray_For_VMutableArray<T>(size), embedded_varray_(std::forward<Args>(args)...)
{
- this->varray_ = &embedded_varray_;
+ this->varray_ = embedded_varray_;
}
};
diff --git a/source/blender/functions/intern/generic_virtual_array.cc b/source/blender/functions/intern/generic_virtual_array.cc
index bd033a429de..8f2023ae8db 100644
--- a/source/blender/functions/intern/generic_virtual_array.cc
+++ b/source/blender/functions/intern/generic_virtual_array.cc
@@ -216,6 +216,16 @@ GSpan GVArray_For_GSpan::get_internal_span_impl() const
}
/* --------------------------------------------------------------------
+ * GVArray_For_OwnedGSpan.
+ */
+
+GVArray_For_OwnedGSpan::~GVArray_For_OwnedGSpan()
+{
+ type_->destruct_indices((void *)data_, indices_to_destruct_);
+ MEM_freeN((void *)data_);
+}
+
+/* --------------------------------------------------------------------
* GVMutableArray_For_GMutableSpan.
*/
diff --git a/source/blender/makesdna/DNA_customdata_types.h b/source/blender/makesdna/DNA_customdata_types.h
index e5282409329..3fb3eab06f1 100644
--- a/source/blender/makesdna/DNA_customdata_types.h
+++ b/source/blender/makesdna/DNA_customdata_types.h
@@ -31,6 +31,12 @@
extern "C" {
#endif
+typedef struct AnonymousCustomDataLayerID {
+ int strong_references;
+ int tot_references;
+ char *debug_name;
+} AnonymousCustomDataLayerID;
+
/** Descriptor and storage for a custom data layer. */
typedef struct CustomDataLayer {
/** Type of data in layer. */
@@ -53,6 +59,7 @@ typedef struct CustomDataLayer {
char name[64];
/** Layer data. */
void *data;
+ const struct AnonymousCustomDataLayerID *anonymous_id;
} CustomDataLayer;
#define MAX_CUSTOMDATA_LAYER_NAME 64
@@ -242,6 +249,9 @@ enum {
CD_FLAG_EXTERNAL = (1 << 3),
/* Indicates external data is read into memory */
CD_FLAG_IN_MEMORY = (1 << 4),
+ /* An anonymous custom data layer can be deleted when its identifier is not referenced
+ * anymore. */
+ CD_FLAG_ANONYMOUS = (1 << 5),
};
/* Limits */
diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h
index fd794ed1b21..aa3322541a2 100644
--- a/source/blender/makesdna/DNA_node_types.h
+++ b/source/blender/makesdna/DNA_node_types.h
@@ -178,6 +178,7 @@ typedef enum eNodeSocketDisplayShape {
SOCK_DISPLAY_SHAPE_CIRCLE_DOT = 3,
SOCK_DISPLAY_SHAPE_SQUARE_DOT = 4,
SOCK_DISPLAY_SHAPE_DIAMOND_DOT = 5,
+ SOCK_DISPLAY_SHAPE_TALL_RECTANGLE = 6,
} eNodeSocketDisplayShape;
/* Socket side (input/output). */
@@ -214,6 +215,11 @@ typedef enum eNodeSocketFlag {
* type is obvious and the name takes up too much space.
*/
SOCK_HIDE_LABEL = (1 << 12),
+ /**
+ * For geometry nodes, the result is not a single value, but evaluated as a callback and
+ * potentially many different values.
+ */
+ SOCK_FIELD = (1 << 13),
} eNodeSocketFlag;
/* TODO: Limit data in bNode to what we want to see saved. */
@@ -1252,16 +1258,12 @@ typedef struct NodeGeometryRotatePoints {
char _pad[3];
} NodeGeometryRotatePoints;
-typedef struct NodeGeometryAlignRotationToVector {
+typedef struct FunctionNodeAlignRotationToVector {
/* GeometryNodeAlignRotationToVectorAxis */
uint8_t axis;
/* GeometryNodeAlignRotationToVectorPivotAxis */
uint8_t pivot_axis;
-
- /* GeometryNodeAttributeInputMode */
- uint8_t input_type_factor;
- uint8_t input_type_vector;
-} NodeGeometryAlignRotationToVector;
+} FunctionNodeAlignRotationToVector;
typedef struct NodeGeometryPointScale {
/* GeometryNodeAttributeInputMode */
@@ -1406,11 +1408,6 @@ typedef struct NodeGeometryCurveResample {
uint8_t mode;
} NodeGeometryCurveResample;
-typedef struct NodeGeometryCurveSubdivide {
- /* GeometryNodeAttributeInputMode (integer or attribute). */
- uint8_t cuts_type;
-} NodeGeometryCurveSubdivide;
-
typedef struct NodeGeometryCurveTrim {
/* GeometryNodeCurveInterpolateMode. */
uint8_t mode;
@@ -1431,12 +1428,27 @@ typedef struct NodeGeometryAttributeTransfer {
typedef struct NodeGeometryRaycast {
/* GeometryNodeRaycastMapMode. */
uint8_t mapping;
-
- uint8_t input_type_ray_direction;
- uint8_t input_type_ray_length;
- char _pad[1];
} NodeGeometryRaycast;
+typedef struct NodeGeometryAttributeFreeze {
+ /* CustomDataType. */
+ int8_t data_type;
+ /* AttributeDomain. */
+ int8_t domain;
+} NodeGeometryAttributeFreeze;
+
+typedef struct NodeGeometryAttribute {
+ /* eNodeSocketDatatype. */
+ int8_t output_type;
+} NodeGeometryAttribute;
+
+typedef struct NodeGeometryAttributeExtract {
+ /* CustomDataType. */
+ int8_t data_type;
+ /* Boolean that indicates whether the persistent attribute should be removed. */
+ uint8_t delete_persistent;
+} NodeGeometryAttributeExtract;
+
/* script node mode */
#define NODE_SCRIPT_INTERNAL 0
#define NODE_SCRIPT_EXTERNAL 1
diff --git a/source/blender/makesrna/RNA_enum_items.h b/source/blender/makesrna/RNA_enum_items.h
index c8f44262020..768cf50c945 100644
--- a/source/blender/makesrna/RNA_enum_items.h
+++ b/source/blender/makesrna/RNA_enum_items.h
@@ -209,6 +209,7 @@ DEF_ENUM(rna_enum_attribute_type_items)
DEF_ENUM(rna_enum_attribute_type_with_auto_items)
DEF_ENUM(rna_enum_attribute_domain_items)
DEF_ENUM(rna_enum_attribute_domain_with_auto_items)
+DEF_ENUM(rna_enum_attribute_domain_no_face_corner_items)
DEF_ENUM(rna_enum_collection_color_items)
diff --git a/source/blender/makesrna/intern/rna_attribute.c b/source/blender/makesrna/intern/rna_attribute.c
index b4ea70c33ab..6607bc594ce 100644
--- a/source/blender/makesrna/intern/rna_attribute.c
+++ b/source/blender/makesrna/intern/rna_attribute.c
@@ -75,6 +75,14 @@ const EnumPropertyItem rna_enum_attribute_domain_items[] = {
{0, NULL, 0, NULL, NULL},
};
+const EnumPropertyItem rna_enum_attribute_domain_no_face_corner_items[] = {
+ {ATTR_DOMAIN_POINT, "POINT", 0, "Point", "Attribute on point"},
+ {ATTR_DOMAIN_EDGE, "EDGE", 0, "Edge", "Attribute on mesh edge"},
+ {ATTR_DOMAIN_FACE, "FACE", 0, "Face", "Attribute on mesh faces"},
+ {ATTR_DOMAIN_CURVE, "CURVE", 0, "Spline", "Attribute on spline"},
+ {0, NULL, 0, NULL, NULL},
+};
+
const EnumPropertyItem rna_enum_attribute_domain_with_auto_items[] = {
{ATTR_DOMAIN_AUTO, "AUTO", 0, "Auto", ""},
{ATTR_DOMAIN_POINT, "POINT", 0, "Point", "Attribute on point"},
diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c
index d8ab7c7a61b..ea780260be2 100644
--- a/source/blender/makesrna/intern/rna_nodetree.c
+++ b/source/blender/makesrna/intern/rna_nodetree.c
@@ -555,11 +555,13 @@ static const EnumPropertyItem rna_node_geometry_attribute_input_type_items_float
ITEM_FLOAT,
{0, NULL, 0, NULL, NULL},
};
+# if 0
static const EnumPropertyItem rna_node_geometry_attribute_input_type_items_int[] = {
ITEM_ATTRIBUTE,
ITEM_INTEGER,
{0, NULL, 0, NULL, NULL},
};
+# endif
static const EnumPropertyItem rna_node_geometry_attribute_input_type_items_no_boolean[] = {
ITEM_ATTRIBUTE,
ITEM_FLOAT,
@@ -2068,6 +2070,20 @@ static const EnumPropertyItem *rna_GeometryNodeSwitch_type_itemf(bContext *UNUSE
return itemf_function_check(node_socket_data_type_items, switch_type_supported);
}
+static bool attribute_type_supported(const EnumPropertyItem *item)
+{
+ return ELEM(item->value, SOCK_FLOAT, SOCK_INT, SOCK_BOOLEAN, SOCK_VECTOR, SOCK_RGBA);
+}
+
+static const EnumPropertyItem *rna_GeometryNodeAttribute_type_itemf(bContext *UNUSED(C),
+ PointerRNA *UNUSED(ptr),
+ PropertyRNA *UNUSED(prop),
+ bool *r_free)
+{
+ *r_free = true;
+ return itemf_function_check(node_socket_data_type_items, attribute_type_supported);
+}
+
static bool attribute_clamp_type_supported(const EnumPropertyItem *item)
{
return ELEM(item->value, CD_PROP_FLOAT, CD_PROP_FLOAT3, CD_PROP_INT32, CD_PROP_COLOR);
@@ -9174,6 +9190,46 @@ static void def_geo_attribute_fill(StructRNA *srna)
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
}
+static void def_geo_attribute_extract(StructRNA *srna)
+{
+ PropertyRNA *prop;
+
+ RNA_def_struct_sdna_from(srna, "NodeGeometryAttributeExtract", "storage");
+
+ prop = RNA_def_property(srna, "data_type", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_sdna(prop, NULL, "data_type");
+ RNA_def_property_enum_items(prop, rna_enum_attribute_type_items);
+ RNA_def_property_enum_funcs(prop, NULL, NULL, "rna_GeometryNodeAttributeFill_type_itemf");
+ RNA_def_property_enum_default(prop, CD_PROP_FLOAT);
+ RNA_def_property_ui_text(prop, "Data Type", "Type of data stored in attribute");
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_GeometryNode_socket_update");
+
+ prop = RNA_def_property(srna, "delete_persistent", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_ui_text(
+ prop, "Delete Persistent", "Delete the persistent attribute with the given name");
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
+}
+
+static void def_geo_attribute_freeze(StructRNA *srna)
+{
+ PropertyRNA *prop;
+
+ RNA_def_struct_sdna_from(srna, "NodeGeometryAttributeFreeze", "storage");
+
+ prop = RNA_def_property(srna, "data_type", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_items(prop, rna_enum_attribute_type_items);
+ RNA_def_property_enum_funcs(prop, NULL, NULL, "rna_GeometryNodeAttributeFill_type_itemf");
+ RNA_def_property_enum_default(prop, CD_PROP_FLOAT);
+ RNA_def_property_ui_text(prop, "Data Type", "Type of data stored in attribute");
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_GeometryNode_socket_update");
+
+ prop = RNA_def_property(srna, "domain", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_items(prop, rna_enum_attribute_domain_items);
+ RNA_def_property_enum_default(prop, ATTR_DOMAIN_POINT);
+ RNA_def_property_ui_text(prop, "Domain", "");
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
+}
+
static void def_geo_attribute_convert(StructRNA *srna)
{
PropertyRNA *prop;
@@ -9194,6 +9250,18 @@ static void def_geo_attribute_convert(StructRNA *srna)
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
}
+static void def_geo_delete(StructRNA *srna)
+{
+ PropertyRNA *prop;
+
+ prop = RNA_def_property(srna, "domain", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_sdna(prop, NULL, "custom1");
+ RNA_def_property_enum_items(prop, rna_enum_attribute_domain_no_face_corner_items);
+ RNA_def_property_enum_default(prop, ATTR_DOMAIN_POINT);
+ RNA_def_property_ui_text(prop, "Domain", "");
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
+}
+
static void def_geo_attribute_math(StructRNA *srna)
{
PropertyRNA *prop;
@@ -9608,6 +9676,25 @@ static void def_geo_curve_primitive_line(StructRNA *srna)
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
}
+static void def_geo_extrude_and_move(StructRNA *srna)
+{
+ PropertyRNA *prop;
+
+ static const EnumPropertyItem rna_node_geometry_extrude_domain_items[] = {
+ {0, "VERTEX", 0, "Vertex", "Extrude Vertices"},
+ {1, "EDGE", 0, "Edge", "Extrude Edges"},
+ {2, "FACE", 0, "Face", "Extrude Faces"},
+ {0, NULL, 0, NULL, NULL},
+ };
+
+ prop = RNA_def_property(srna, "extrude_mode", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_sdna(prop, NULL, "custom1");
+ RNA_def_property_enum_items(prop, rna_node_geometry_extrude_domain_items);
+ RNA_def_property_enum_default(prop, GEO_NODE_POINT_DISTRIBUTE_RANDOM);
+ RNA_def_property_ui_text(prop, "Extrude Mode", "Select mesh domain to extrude");
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
+}
+
static void def_geo_point_rotate(StructRNA *srna)
{
static const EnumPropertyItem type_items[] = {
@@ -9668,7 +9755,7 @@ static void def_geo_point_rotate(StructRNA *srna)
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
}
-static void def_geo_align_rotation_to_vector(StructRNA *srna)
+static void def_fn_align_rotation_to_vector(StructRNA *srna)
{
static const EnumPropertyItem axis_items[] = {
{GEO_NODE_ALIGN_ROTATION_TO_VECTOR_AXIS_X,
@@ -9715,7 +9802,7 @@ static void def_geo_align_rotation_to_vector(StructRNA *srna)
PropertyRNA *prop;
- RNA_def_struct_sdna_from(srna, "NodeGeometryAlignRotationToVector", "storage");
+ RNA_def_struct_sdna_from(srna, "FunctionNodeAlignRotationToVector", "storage");
prop = RNA_def_property(srna, "axis", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_items(prop, axis_items);
@@ -9726,16 +9813,6 @@ static void def_geo_align_rotation_to_vector(StructRNA *srna)
RNA_def_property_enum_items(prop, pivot_axis_items);
RNA_def_property_ui_text(prop, "Pivot Axis", "Axis to rotate around");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
-
- prop = RNA_def_property(srna, "input_type_factor", PROP_ENUM, PROP_NONE);
- RNA_def_property_enum_items(prop, rna_node_geometry_attribute_input_type_items_float);
- RNA_def_property_ui_text(prop, "Input Type Factor", "");
- RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
-
- prop = RNA_def_property(srna, "input_type_vector", PROP_ENUM, PROP_NONE);
- RNA_def_property_enum_items(prop, rna_node_geometry_attribute_input_type_items_vector);
- RNA_def_property_ui_text(prop, "Input Type Vector", "");
- RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
}
static void def_geo_point_scale(StructRNA *srna)
@@ -10108,18 +10185,6 @@ static void def_geo_curve_resample(StructRNA *srna)
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
}
-static void def_geo_curve_subdivide(StructRNA *srna)
-{
- PropertyRNA *prop;
-
- RNA_def_struct_sdna_from(srna, "NodeGeometryCurveSubdivide", "storage");
-
- prop = RNA_def_property(srna, "cuts_type", PROP_ENUM, PROP_NONE);
- RNA_def_property_enum_items(prop, rna_node_geometry_attribute_input_type_items_int);
- RNA_def_property_ui_text(prop, "Cuts Type", "");
- RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
-}
-
static void def_geo_curve_to_points(StructRNA *srna)
{
PropertyRNA *prop;
@@ -10249,15 +10314,17 @@ static void def_geo_raycast(StructRNA *srna)
RNA_def_property_enum_items(prop, mapping_items);
RNA_def_property_ui_text(prop, "Mapping", "Mapping from the target geometry to hit points");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
+}
- prop = RNA_def_property(srna, "input_type_ray_direction", PROP_ENUM, PROP_NONE);
- RNA_def_property_enum_items(prop, rna_node_geometry_attribute_input_type_items_vector);
- RNA_def_property_ui_text(prop, "Input Type Ray Direction", "");
- RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
+static void def_geo_attribute(StructRNA *srna)
+{
+ PropertyRNA *prop;
- prop = RNA_def_property(srna, "input_type_ray_length", PROP_ENUM, PROP_NONE);
- RNA_def_property_enum_items(prop, rna_node_geometry_attribute_input_type_items_float);
- RNA_def_property_ui_text(prop, "Input Type Ray Length", "");
+ RNA_def_struct_sdna_from(srna, "NodeGeometryAttribute", "storage");
+ prop = RNA_def_property(srna, "output_type", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_items(prop, node_socket_data_type_items);
+ RNA_def_property_enum_funcs(prop, NULL, NULL, "rna_GeometryNodeAttribute_type_itemf");
+ RNA_def_property_ui_text(prop, "Output Type", "");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
}
diff --git a/source/blender/modifiers/intern/MOD_nodes.cc b/source/blender/modifiers/intern/MOD_nodes.cc
index 620c7ef438a..98521b43f52 100644
--- a/source/blender/modifiers/intern/MOD_nodes.cc
+++ b/source/blender/modifiers/intern/MOD_nodes.cc
@@ -50,6 +50,7 @@
#include "BKE_attribute_math.hh"
#include "BKE_customdata.h"
+#include "BKE_field.hh"
#include "BKE_geometry_set_instances.hh"
#include "BKE_global.h"
#include "BKE_idprop.h"
@@ -87,6 +88,8 @@
#include "FN_multi_function.hh"
+#include "WM_types.h"
+
using blender::destruct_ptr;
using blender::float3;
using blender::FunctionRef;
@@ -311,23 +314,39 @@ struct SocketPropertyType {
void (*init_cpp_value)(const IDProperty &property, void *r_value);
};
+static const std::string use_attribute_suffix = "_use_attribute";
+static const std::string attribute_name_suffix = "_attribute_name";
+
static IDProperty *socket_add_property(IDProperty *settings_prop_group,
IDProperty *ui_container,
const SocketPropertyType &property_type,
const bNodeSocket &socket)
{
- const char *new_prop_name = socket.identifier;
+ StringRefNull new_prop_name = socket.identifier;
/* Add the property actually storing the data to the modifier's group. */
- IDProperty *prop = property_type.create_prop(socket, new_prop_name);
+ IDProperty *prop = property_type.create_prop(socket, new_prop_name.c_str());
IDP_AddToGroup(settings_prop_group, prop);
+ {
+ IDPropertyTemplate idprop = {0};
+ IDProperty *is_attribute_prop = IDP_New(
+ IDP_INT, &idprop, (new_prop_name + use_attribute_suffix).c_str());
+ IDP_AddToGroup(settings_prop_group, is_attribute_prop);
+ }
+ {
+ IDPropertyTemplate idprop = {0};
+ IDProperty *attribute_prop = IDP_New(
+ IDP_STRING, &idprop, (new_prop_name + attribute_name_suffix).c_str());
+ IDP_AddToGroup(settings_prop_group, attribute_prop);
+ }
+
prop->flag |= IDP_FLAG_OVERRIDABLE_LIBRARY;
/* Make the group in the UI container group to hold the property's UI settings. */
IDProperty *prop_ui_group;
{
IDPropertyTemplate idprop = {0};
- prop_ui_group = IDP_New(IDP_GROUP, &idprop, new_prop_name);
+ prop_ui_group = IDP_New(IDP_GROUP, &idprop, new_prop_name.c_str());
IDP_AddToGroup(ui_container, prop_ui_group);
}
@@ -402,12 +421,15 @@ static const SocketPropertyType *get_socket_property_type(const bNodeSocket &bso
},
[](const IDProperty &property) { return ELEM(property.type, IDP_FLOAT, IDP_DOUBLE); },
[](const IDProperty &property, void *r_value) {
+ float value;
if (property.type == IDP_FLOAT) {
- *(float *)r_value = IDP_Float(&property);
+ value = IDP_Float(&property);
}
else if (property.type == IDP_DOUBLE) {
- *(float *)r_value = (float)IDP_Double(&property);
+ value = (float)IDP_Double(&property);
}
+ new (r_value) blender::bke::FieldRef<float>(
+ blender::bke::FieldPtr{new blender::bke::ConstantField<float>(value)});
},
};
return &float_type;
@@ -442,7 +464,11 @@ static const SocketPropertyType *get_socket_property_type(const bNodeSocket &bso
return (PropertyType)((bNodeSocketValueInt *)socket.default_value)->subtype;
},
[](const IDProperty &property) { return property.type == IDP_INT; },
- [](const IDProperty &property, void *r_value) { *(int *)r_value = IDP_Int(&property); },
+ [](const IDProperty &property, void *r_value) {
+ int value = IDP_Int(&property);
+ new (r_value) blender::bke::FieldRef<int>(
+ blender::bke::FieldPtr{new blender::bke::ConstantField<int>(value)});
+ },
};
return &int_type;
}
@@ -486,7 +512,10 @@ static const SocketPropertyType *get_socket_property_type(const bNodeSocket &bso
property.len == 3;
},
[](const IDProperty &property, void *r_value) {
- copy_v3_v3((float *)r_value, (const float *)IDP_Array(&property));
+ blender::float3 value;
+ copy_v3_v3(value, (const float *)IDP_Array(&property));
+ new (r_value) blender::bke::FieldRef<blender::float3>(
+ blender::bke::FieldPtr{new blender::bke::ConstantField<blender::float3>(value)});
},
};
return &vector_type;
@@ -518,7 +547,9 @@ static const SocketPropertyType *get_socket_property_type(const bNodeSocket &bso
nullptr,
[](const IDProperty &property) { return property.type == IDP_INT; },
[](const IDProperty &property, void *r_value) {
- *(bool *)r_value = IDP_Int(&property) != 0;
+ bool value = IDP_Int(&property) != 0;
+ new (r_value) blender::bke::FieldRef<bool>(
+ blender::bke::FieldPtr{new blender::bke::ConstantField<bool>(value)});
},
};
return &boolean_type;
@@ -674,6 +705,24 @@ void MOD_nodes_update_interface(Object *object, NodesModifierData *nmd)
if (old_prop != nullptr && property_type->is_correct_type(*old_prop)) {
IDP_CopyPropertyContent(new_prop, old_prop);
}
+
+ std::string use_attribute_identifier = socket->identifier + use_attribute_suffix;
+ IDProperty *new_prop_use_attribute = IDP_GetPropertyFromGroup(
+ nmd->settings.properties, use_attribute_identifier.c_str());
+ IDProperty *old_prop_use_attribute = IDP_GetPropertyFromGroup(
+ old_properties, use_attribute_identifier.c_str());
+ if (old_prop_use_attribute != nullptr) {
+ IDP_CopyPropertyContent(new_prop_use_attribute, old_prop_use_attribute);
+ }
+
+ std::string attribute_name_identifier = socket->identifier + attribute_name_suffix;
+ IDProperty *new_prop_attribute_name = IDP_GetPropertyFromGroup(
+ nmd->settings.properties, attribute_name_identifier.c_str());
+ IDProperty *old_prop_attribute_name = IDP_GetPropertyFromGroup(
+ old_properties, attribute_name_identifier.c_str());
+ if (old_prop_attribute_name != nullptr) {
+ IDP_CopyPropertyContent(new_prop_attribute_name, old_prop_attribute_name);
+ }
}
}
@@ -727,7 +776,13 @@ static void initialize_group_input(NodesModifierData &nmd,
}
const IDProperty *property = IDP_GetPropertyFromGroup(nmd.settings.properties,
socket.identifier);
- if (property == nullptr) {
+
+ const IDProperty *property_use_attribute = IDP_GetPropertyFromGroup(
+ nmd.settings.properties, (socket.identifier + use_attribute_suffix).c_str());
+ const IDProperty *property_attribute_name = IDP_GetPropertyFromGroup(
+ nmd.settings.properties, (socket.identifier + attribute_name_suffix).c_str());
+ if (property == nullptr || property_use_attribute == nullptr ||
+ property_attribute_name == nullptr) {
socket.typeinfo->get_geometry_nodes_cpp_value(socket, r_value);
return;
}
@@ -735,7 +790,19 @@ static void initialize_group_input(NodesModifierData &nmd,
socket.typeinfo->get_geometry_nodes_cpp_value(socket, r_value);
return;
}
- property_type->init_cpp_value(*property, r_value);
+ const bool use_attribute = IDP_Int(property_use_attribute) != 0;
+ if (use_attribute &&
+ ELEM(socket.type, SOCK_FLOAT, SOCK_VECTOR, SOCK_RGBA, SOCK_INT, SOCK_BOOLEAN)) {
+ const char *attribute_name = IDP_String(property_attribute_name);
+ blender::bke::FieldPtr attribute_field = new blender::bke::PersistentAttributeField(
+ attribute_name, *socket.typeinfo->get_base_cpp_type());
+ const blender::bke::FieldRefCPPType &field_cpp_type =
+ dynamic_cast<const blender::bke::FieldRefCPPType &>(cpp_type);
+ field_cpp_type.construct(r_value, std::move(attribute_field));
+ }
+ else {
+ property_type->init_cpp_value(*property, r_value);
+ }
}
static Vector<SpaceSpreadsheet *> find_spreadsheet_editors(Main *bmain)
@@ -1046,6 +1113,7 @@ static void modifyGeometrySet(ModifierData *md,
* the node socket identifier for the property names, since they are unique, but also having
* the correct label displayed in the UI. */
static void draw_property_for_socket(uiLayout *layout,
+ NodesModifierData *nmd,
PointerRNA *bmain_ptr,
PointerRNA *md_ptr,
const IDProperty *modifier_props,
@@ -1065,12 +1133,30 @@ static void draw_property_for_socket(uiLayout *layout,
return;
}
+ IDProperty *is_attribute_property = IDP_GetPropertyFromGroup(
+ modifier_props, (socket.identifier + use_attribute_suffix).c_str());
+ if (is_attribute_property == nullptr) {
+ return;
+ }
+
char socket_id_esc[sizeof(socket.identifier) * 2];
BLI_str_escape(socket_id_esc, socket.identifier, sizeof(socket_id_esc));
char rna_path[sizeof(socket_id_esc) + 4];
BLI_snprintf(rna_path, ARRAY_SIZE(rna_path), "[\"%s\"]", socket_id_esc);
+ char rna_path_use_attribute[1024];
+ BLI_snprintf(rna_path_use_attribute,
+ ARRAY_SIZE(rna_path_use_attribute),
+ "[\"%s\"]",
+ (socket_id_esc + use_attribute_suffix).c_str());
+
+ char rna_path_attribute[1024];
+ BLI_snprintf(rna_path_attribute,
+ ARRAY_SIZE(rna_path_attribute),
+ "[\"%s\"]",
+ (socket_id_esc + attribute_name_suffix).c_str());
+
/* Use #uiItemPointerR to draw pointer properties because #uiItemR would not have enough
* information about what type of ID to select for editing the values. This is because
* pointer IDProperties contain no information about their type. */
@@ -1098,8 +1184,36 @@ static void draw_property_for_socket(uiLayout *layout,
uiItemPointerR(layout, md_ptr, rna_path, bmain_ptr, "textures", socket.name, ICON_TEXTURE);
break;
}
- default:
+ case SOCK_FLOAT:
+ case SOCK_VECTOR:
+ case SOCK_RGBA:
+ case SOCK_INT:
+ case SOCK_BOOLEAN: {
+ uiLayout *row = uiLayoutRow(layout, true);
+ const int use_attribute = RNA_int_get(md_ptr, rna_path_use_attribute) != 0;
+ if (use_attribute) {
+ uiItemR(row, md_ptr, rna_path_attribute, 0, socket.name, ICON_NONE);
+ }
+ else {
+ uiItemR(row, md_ptr, rna_path, 0, socket.name, ICON_NONE);
+ }
+ PointerRNA props;
+ uiItemFullO(row,
+ "object.geometry_nodes_modifier_value_or_attribute_toggle",
+ "",
+ ICON_SPREADSHEET,
+ nullptr,
+ WM_OP_INVOKE_DEFAULT,
+ 0,
+ &props);
+ RNA_string_set(&props, "modifier_name", nmd->modifier.name);
+ RNA_string_set(&props, "prop_path", rna_path_use_attribute);
+ break;
+ }
+ default: {
uiItemR(layout, md_ptr, rna_path, 0, socket.name, ICON_NONE);
+ break;
+ }
}
}
@@ -1130,7 +1244,7 @@ static void panel_draw(const bContext *C, Panel *panel)
RNA_main_pointer_create(bmain, &bmain_ptr);
LISTBASE_FOREACH (bNodeSocket *, socket, &nmd->node_group->inputs) {
- draw_property_for_socket(layout, &bmain_ptr, ptr, nmd->settings.properties, *socket);
+ draw_property_for_socket(layout, nmd, &bmain_ptr, ptr, nmd->settings.properties, *socket);
}
}
diff --git a/source/blender/modifiers/intern/MOD_nodes_evaluator.cc b/source/blender/modifiers/intern/MOD_nodes_evaluator.cc
index 5646e37707c..5c529d5295b 100644
--- a/source/blender/modifiers/intern/MOD_nodes_evaluator.cc
+++ b/source/blender/modifiers/intern/MOD_nodes_evaluator.cc
@@ -21,6 +21,7 @@
#include "DEG_depsgraph_query.h"
+#include "FN_cpp_type_make.hh"
#include "FN_generic_value_map.hh"
#include "FN_multi_function.hh"
@@ -30,8 +31,15 @@
#include "BLI_task.hh"
#include "BLI_vector_set.hh"
+MAKE_FIELD_REF_CPP_TYPE(FloatFieldRef, float)
+MAKE_FIELD_REF_CPP_TYPE(IntFieldRef, int)
+MAKE_FIELD_REF_CPP_TYPE(BoolFieldRef, bool)
+MAKE_FIELD_REF_CPP_TYPE(Float3FieldRef, blender::float3)
+MAKE_FIELD_REF_CPP_TYPE(ColorFieldRef, blender::ColorGeometry4f)
+
namespace blender::modifiers::geometry_nodes {
+using bke::FieldPtr;
using fn::CPPType;
using fn::GValueMap;
using nodes::GeoNodeExecParams;
@@ -858,11 +866,9 @@ class GeometryNodesEvaluator {
const MultiFunction &fn,
NodeState &node_state)
{
- MFContextBuilder fn_context;
- MFParamsBuilder fn_params{fn, 1};
LinearAllocator<> &allocator = local_allocators_.local();
- /* Prepare the inputs for the multi function. */
+ Vector<FieldPtr> input_fields;
for (const int i : node->inputs().index_range()) {
const InputSocketRef &socket_ref = node->input(i);
if (!socket_ref.is_available()) {
@@ -873,23 +879,33 @@ class GeometryNodesEvaluator {
BLI_assert(input_state.was_ready_for_execution);
SingleInputValue &single_value = *input_state.value.single;
BLI_assert(single_value.value != nullptr);
- fn_params.add_readonly_single_input(GPointer{*input_state.type, single_value.value});
- }
- /* Prepare the outputs for the multi function. */
- Vector<GMutablePointer> outputs;
- for (const int i : node->outputs().index_range()) {
- const OutputSocketRef &socket_ref = node->output(i);
- if (!socket_ref.is_available()) {
- continue;
+ if (input_state.type->is<bke::FieldRef<float>>()) {
+ bke::FieldRef<float> field = *(bke::FieldRef<float> *)single_value.value;
+ input_fields.append(field.field());
+ }
+ else if (input_state.type->is<bke::FieldRef<int>>()) {
+ bke::FieldRef<int> field = *(bke::FieldRef<int> *)single_value.value;
+ input_fields.append(field.field());
+ }
+ else if (input_state.type->is<bke::FieldRef<float3>>()) {
+ bke::FieldRef<float3> field = *(bke::FieldRef<float3> *)single_value.value;
+ input_fields.append(field.field());
+ }
+ else if (input_state.type->is<bke::FieldRef<bool>>()) {
+ bke::FieldRef<bool> field = *(bke::FieldRef<bool> *)single_value.value;
+ input_fields.append(field.field());
+ }
+ else if (input_state.type->is<bke::FieldRef<blender::ColorGeometry4f>>()) {
+ bke::FieldRef<blender::ColorGeometry4f> field =
+ *(bke::FieldRef<blender::ColorGeometry4f> *)single_value.value;
+ input_fields.append(field.field());
+ }
+ else {
+ /* Not yet supported. */
+ BLI_assert_unreachable();
}
- const CPPType &type = *get_socket_cpp_type(socket_ref);
- void *buffer = allocator.allocate(type.size(), type.alignment());
- fn_params.add_uninitialized_single_output(GMutableSpan{type, buffer, 1});
- outputs.append({type, buffer});
}
- fn.call(IndexRange(1), fn_params, fn_context);
-
/* Forward the computed outputs. */
int output_index = 0;
for (const int i : node->outputs().index_range()) {
@@ -897,10 +913,67 @@ class GeometryNodesEvaluator {
if (!socket_ref.is_available()) {
continue;
}
+ const int output_param_index = input_fields.size() + output_index;
OutputState &output_state = node_state.outputs[i];
const DOutputSocket socket{node.context(), &socket_ref};
- GMutablePointer value = outputs[output_index];
- this->forward_output(socket, value);
+ bke::FieldPtr out_field = new bke::MultiFunctionField(input_fields, fn, output_param_index);
+
+ eNodeSocketDatatype socket_data_type = (eNodeSocketDatatype)socket->typeinfo()->type;
+
+ {
+ bke::FieldInputs field_inputs = out_field->prepare_inputs();
+ if (field_inputs.tot_inputs() == 0) {
+ bke::FieldOutput field_output = out_field->evaluate(IndexRange(1), field_inputs);
+ const fn::GVArray &varray = field_output.varray_ref();
+ BUFFER_FOR_CPP_TYPE_VALUE(varray.type(), buffer);
+ varray.get_to_uninitialized(0, buffer);
+ if (socket_data_type == SOCK_FLOAT) {
+ out_field = new bke::ConstantField<float>(*(float *)buffer);
+ }
+ else if (socket_data_type == SOCK_VECTOR) {
+ out_field = new bke::ConstantField<float3>(*(float3 *)buffer);
+ }
+ else if (socket_data_type == SOCK_BOOLEAN) {
+ out_field = new bke::ConstantField<bool>(*(bool *)buffer);
+ }
+ else if (socket_data_type == SOCK_RGBA) {
+ out_field = new bke::ConstantField<ColorGeometry4f>(*(ColorGeometry4f *)buffer);
+ }
+ else if (socket_data_type == SOCK_INT) {
+ out_field = new bke::ConstantField<int>(*(int *)buffer);
+ }
+ }
+ }
+
+ if (socket_data_type == SOCK_FLOAT) {
+ bke::FieldRef<float> *field_ref =
+ allocator.construct<bke::FieldRef<float>>(out_field).release();
+ this->forward_output(socket, field_ref);
+ }
+ else if (socket_data_type == SOCK_VECTOR) {
+ bke::FieldRef<float3> *field_ref =
+ allocator.construct<bke::FieldRef<float3>>(out_field).release();
+ this->forward_output(socket, field_ref);
+ }
+ else if (socket_data_type == SOCK_BOOLEAN) {
+ bke::FieldRef<bool> *field_ref =
+ allocator.construct<bke::FieldRef<bool>>(out_field).release();
+ this->forward_output(socket, field_ref);
+ }
+ else if (socket_data_type == SOCK_RGBA) {
+ bke::FieldRef<blender::ColorGeometry4f> *field_ref =
+ allocator.construct<bke::FieldRef<blender::ColorGeometry4f>>(out_field).release();
+ this->forward_output(socket, field_ref);
+ }
+ else if (socket_data_type == SOCK_INT) {
+ bke::FieldRef<int> *field_ref =
+ allocator.construct<bke::FieldRef<int>>(out_field).release();
+ this->forward_output(socket, field_ref);
+ }
+ else {
+ /* Not yet supported. */
+ BLI_assert_unreachable();
+ }
output_state.has_been_computed = true;
output_index++;
}
@@ -1389,7 +1462,21 @@ class GeometryNodesEvaluator {
return;
}
- if (conversions_.is_convertible(from_type, to_type)) {
+ const bke::FieldRefCPPType *from_field_type = dynamic_cast<const bke::FieldRefCPPType *>(
+ &from_type);
+ const bke::FieldRefCPPType *to_field_type = dynamic_cast<const bke::FieldRefCPPType *>(
+ &to_type);
+
+ if (from_field_type != nullptr && to_field_type != nullptr &&
+ conversions_.is_convertible(from_field_type->field_type(), to_field_type->field_type())) {
+ const MultiFunction &fn = *conversions_.get_conversion_multi_function(
+ MFDataType::ForSingle(from_field_type->field_type()),
+ MFDataType::ForSingle(to_field_type->field_type()));
+ FieldPtr old_field = from_field_type->get_field(from_value);
+ FieldPtr new_field = new bke::MultiFunctionField({old_field}, fn, 1);
+ to_field_type->construct(to_value, std::move(new_field));
+ }
+ else if (conversions_.is_convertible(from_type, to_type)) {
/* Do the conversion if possible. */
conversions_.convert_to_uninitialized(from_type, to_type, from_value, to_value);
}
diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt
index 8680fcee49a..9335950a062 100644
--- a/source/blender/nodes/CMakeLists.txt
+++ b/source/blender/nodes/CMakeLists.txt
@@ -138,15 +138,18 @@ set(SRC
function/nodes/node_fn_input_string.cc
function/nodes/node_fn_input_vector.cc
function/nodes/node_fn_random_float.cc
+ function/nodes/node_fn_align_rotation_to_vector.cc
function/node_function_util.cc
- geometry/nodes/node_geo_align_rotation_to_vector.cc
+ geometry/nodes/node_geo_attribute_freeze.cc
+ geometry/nodes/node_geo_attribute.cc
geometry/nodes/node_geo_attribute_clamp.cc
geometry/nodes/node_geo_attribute_color_ramp.cc
geometry/nodes/node_geo_attribute_combine_xyz.cc
geometry/nodes/node_geo_attribute_compare.cc
geometry/nodes/node_geo_attribute_convert.cc
geometry/nodes/node_geo_attribute_curve_map.cc
+ geometry/nodes/node_geo_attribute_extract.cc
geometry/nodes/node_geo_attribute_fill.cc
geometry/nodes/node_geo_attribute_map_range.cc
geometry/nodes/node_geo_attribute_math.cc
@@ -166,6 +169,7 @@ set(SRC
geometry/nodes/node_geo_convex_hull.cc
geometry/nodes/node_geo_curve_endpoints.cc
geometry/nodes/node_geo_curve_length.cc
+ geometry/nodes/node_geo_curve_parameter.cc
geometry/nodes/node_geo_curve_primitive_bezier_segment.cc
geometry/nodes/node_geo_curve_primitive_circle.cc
geometry/nodes/node_geo_curve_primitive_line.cc
@@ -184,6 +188,10 @@ set(SRC
geometry/nodes/node_geo_curve_trim.cc
geometry/nodes/node_geo_delete_geometry.cc
geometry/nodes/node_geo_edge_split.cc
+ geometry/nodes/node_geo_evaluate_curve.cc
+ geometry/nodes/node_geo_extrude.cc
+ geometry/nodes/node_geo_extrude_and_move.cc
+ geometry/nodes/node_geo_index.cc
geometry/nodes/node_geo_input_material.cc
geometry/nodes/node_geo_is_viewport.cc
geometry/nodes/node_geo_join_geometry.cc
@@ -199,6 +207,7 @@ set(SRC
geometry/nodes/node_geo_mesh_primitive_uv_sphere.cc
geometry/nodes/node_geo_mesh_subdivide.cc
geometry/nodes/node_geo_mesh_to_curve.cc
+ geometry/nodes/node_geo_normal.cc
geometry/nodes/node_geo_object_info.cc
geometry/nodes/node_geo_point_distribute.cc
geometry/nodes/node_geo_point_instance.cc
@@ -207,9 +216,12 @@ set(SRC
geometry/nodes/node_geo_point_separate.cc
geometry/nodes/node_geo_point_translate.cc
geometry/nodes/node_geo_points_to_volume.cc
+ geometry/nodes/node_geo_position.cc
geometry/nodes/node_geo_raycast.cc
+ geometry/nodes/node_geo_sample_mesh_surface.cc
geometry/nodes/node_geo_select_by_material.cc
geometry/nodes/node_geo_separate_components.cc
+ geometry/nodes/node_geo_set_position.cc
geometry/nodes/node_geo_subdivision_surface.cc
geometry/nodes/node_geo_switch.cc
geometry/nodes/node_geo_transform.cc
@@ -289,7 +301,7 @@ set(SRC
shader/nodes/node_shader_tex_image.c
shader/nodes/node_shader_tex_magic.c
shader/nodes/node_shader_tex_musgrave.c
- shader/nodes/node_shader_tex_noise.c
+ shader/nodes/node_shader_tex_noise.cc
shader/nodes/node_shader_tex_pointdensity.c
shader/nodes/node_shader_tex_sky.c
shader/nodes/node_shader_tex_voronoi.c
diff --git a/source/blender/nodes/NOD_function.h b/source/blender/nodes/NOD_function.h
index 29f1a465491..ec0c2fcdc10 100644
--- a/source/blender/nodes/NOD_function.h
+++ b/source/blender/nodes/NOD_function.h
@@ -26,6 +26,7 @@ void register_node_type_fn_float_to_int(void);
void register_node_type_fn_input_string(void);
void register_node_type_fn_input_vector(void);
void register_node_type_fn_random_float(void);
+void register_node_type_fn_align_rotation_to_vector(void);
#ifdef __cplusplus
}
diff --git a/source/blender/nodes/NOD_geometry.h b/source/blender/nodes/NOD_geometry.h
index 856d787c8d0..1f648789981 100644
--- a/source/blender/nodes/NOD_geometry.h
+++ b/source/blender/nodes/NOD_geometry.h
@@ -29,7 +29,10 @@ void register_node_tree_type_geo(void);
void register_node_type_geo_group(void);
void register_node_type_geo_custom_group(bNodeType *ntype);
-void register_node_type_geo_align_rotation_to_vector(void);
+void register_node_type_geo_extrude(void);
+
+void register_node_type_geo_attribute_freeze(void);
+void register_node_type_geo_attribute(void);
void register_node_type_geo_attribute_clamp(void);
void register_node_type_geo_attribute_color_ramp(void);
void register_node_type_geo_attribute_combine_xyz(void);
@@ -40,6 +43,7 @@ void register_node_type_geo_attribute_fill(void);
void register_node_type_geo_attribute_map_range(void);
void register_node_type_geo_attribute_math(void);
void register_node_type_geo_attribute_mix(void);
+void register_node_type_geo_attribute_extract(void);
void register_node_type_geo_attribute_proximity(void);
void register_node_type_geo_attribute_randomize(void);
void register_node_type_geo_attribute_remove(void);
@@ -53,6 +57,7 @@ void register_node_type_geo_collection_info(void);
void register_node_type_geo_convex_hull(void);
void register_node_type_geo_curve_endpoints(void);
void register_node_type_geo_curve_length(void);
+void register_node_type_geo_curve_parameter(void);
void register_node_type_geo_curve_primitive_bezier_segment(void);
void register_node_type_geo_curve_primitive_circle(void);
void register_node_type_geo_curve_primitive_line(void);
@@ -70,6 +75,8 @@ void register_node_type_geo_curve_to_points(void);
void register_node_type_geo_curve_trim(void);
void register_node_type_geo_delete_geometry(void);
void register_node_type_geo_edge_split(void);
+void register_node_type_geo_evaluate_curve(void);
+void register_node_type_geo_index(void);
void register_node_type_geo_input_material(void);
void register_node_type_geo_is_viewport(void);
void register_node_type_geo_join_geometry(void);
@@ -84,6 +91,7 @@ void register_node_type_geo_mesh_primitive_ico_sphere(void);
void register_node_type_geo_mesh_primitive_line(void);
void register_node_type_geo_mesh_primitive_uv_sphere(void);
void register_node_type_geo_mesh_subdivide(void);
+void register_node_type_geo_normal(void);
void register_node_type_geo_mesh_to_curve(void);
void register_node_type_geo_object_info(void);
void register_node_type_geo_point_distribute(void);
@@ -93,17 +101,21 @@ void register_node_type_geo_point_scale(void);
void register_node_type_geo_point_separate(void);
void register_node_type_geo_point_translate(void);
void register_node_type_geo_points_to_volume(void);
+void register_node_type_geo_position(void);
void register_node_type_geo_raycast(void);
+void register_node_type_geo_sample_mesh_surface(void);
void register_node_type_geo_sample_texture(void);
void register_node_type_geo_select_by_handle_type(void);
void register_node_type_geo_select_by_material(void);
void register_node_type_geo_separate_components(void);
+void register_node_type_geo_set_position(void);
void register_node_type_geo_subdivision_surface(void);
void register_node_type_geo_switch(void);
void register_node_type_geo_transform(void);
void register_node_type_geo_triangulate(void);
void register_node_type_geo_viewer(void);
void register_node_type_geo_volume_to_mesh(void);
+void register_node_type_geo_extrude_and_move(void);
#ifdef __cplusplus
}
diff --git a/source/blender/nodes/NOD_geometry_exec.hh b/source/blender/nodes/NOD_geometry_exec.hh
index d6a23051c0b..779d8ed59bf 100644
--- a/source/blender/nodes/NOD_geometry_exec.hh
+++ b/source/blender/nodes/NOD_geometry_exec.hh
@@ -19,6 +19,7 @@
#include "FN_generic_value_map.hh"
#include "BKE_attribute_access.hh"
+#include "BKE_field.hh"
#include "BKE_geometry_set.hh"
#include "BKE_geometry_set_instances.hh"
@@ -32,6 +33,7 @@ struct ModifierData;
namespace blender::nodes {
+using bke::FieldPtr;
using bke::geometry_set_realize_instances;
using bke::OutputAttribute;
using bke::OutputAttribute_Typed;
@@ -142,11 +144,28 @@ class GeoNodeExecParams {
*/
template<typename T> T extract_input(StringRef identifier)
{
+ if constexpr (std::is_same_v<T, int> || std::is_same_v<T, float> ||
+ std::is_same_v<T, float3> || std::is_same_v<T, ColorGeometry4f> ||
+ std::is_same_v<T, bool>) {
+#ifdef DEBUGm
+ this->check_input_access(identifier, &CPPType::get<bke::FieldRef<T>>());
+#endif
+ GMutablePointer gvalue = this->extract_input(identifier);
+ BLI_assert(gvalue.is_type<bke::FieldRef<T>>());
+ bke::FieldRef<T> field = gvalue.relocate_out<bke::FieldRef<T>>();
+ bke::FieldInputs inputs = field->prepare_inputs();
+ bke::FieldOutput output = field->evaluate(IndexRange(1), inputs);
+ T value;
+ output.varray_ref().get(0, &value);
+ return value;
+ }
+ else {
#ifdef DEBUG
- this->check_input_access(identifier, &CPPType::get<T>());
+ this->check_input_access(identifier, &CPPType::get<T>());
#endif
- GMutablePointer gvalue = this->extract_input(identifier);
- return gvalue.relocate_out<T>();
+ GMutablePointer gvalue = this->extract_input(identifier);
+ return gvalue.relocate_out<T>();
+ }
}
/**
@@ -167,14 +186,40 @@ class GeoNodeExecParams {
/**
* Get the input value for the input socket with the given identifier.
*/
- template<typename T> const T &get_input(StringRef identifier) const
+ template<typename T> T get_input(StringRef identifier) const
+ {
+ GPointer gvalue = provider_->get_input(identifier);
+ if constexpr (std::is_same_v<T, int> || std::is_same_v<T, float> ||
+ std::is_same_v<T, float3> || std::is_same_v<T, ColorGeometry4f> ||
+ std::is_same_v<T, bool>) {
+#ifdef DEBUG
+ this->check_input_access(identifier, &CPPType::get<bke::FieldRef<T>>());
+#endif
+ BLI_assert(gvalue.is_type<bke::FieldRef<T>>());
+ bke::FieldRef<T> field = *gvalue.get<bke::FieldRef<T>>();
+ bke::FieldInputs inputs = field->prepare_inputs();
+ bke::FieldOutput output = field->evaluate(IndexRange(1), inputs);
+ T value;
+ output.varray_ref().get(0, &value);
+ return value;
+ }
+ else {
+#ifdef DEBUG
+ this->check_input_access(identifier, &CPPType::get<T>());
+#endif
+ BLI_assert(gvalue.is_type<T>());
+ return *(const T *)gvalue.get();
+ }
+ }
+
+ template<typename T> bke::FieldRef<T> get_input_field(StringRef identifier) const
{
#ifdef DEBUG
- this->check_input_access(identifier, &CPPType::get<T>());
+ this->check_input_access(identifier, &CPPType::get<bke::FieldRef<T>>());
#endif
GPointer gvalue = provider_->get_input(identifier);
- BLI_assert(gvalue.is_type<T>());
- return *(const T *)gvalue.get();
+ BLI_assert(gvalue.is_type<bke::FieldRef<T>>());
+ return *(const bke::FieldRef<T> *)gvalue.get<bke::FieldRef<T>>();
}
/**
diff --git a/source/blender/nodes/NOD_geometry_nodes_eval_log.hh b/source/blender/nodes/NOD_geometry_nodes_eval_log.hh
index 00d97b24646..27f67d7d403 100644
--- a/source/blender/nodes/NOD_geometry_nodes_eval_log.hh
+++ b/source/blender/nodes/NOD_geometry_nodes_eval_log.hh
@@ -299,6 +299,7 @@ class ModifierLog {
static const NodeLog *find_node_by_spreadsheet_editor_context(
const SpaceSpreadsheet &sspreadsheet);
void foreach_node_log(FunctionRef<void(const NodeLog &)> fn) const;
+ Vector<const GeometryAttributeInfo *> lookup_available_attributes() const;
private:
using LogByTreeContext = Map<const DTreeContext *, TreeLog *>;
diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h
index 4da8648173d..8266e22eb81 100644
--- a/source/blender/nodes/NOD_static_types.h
+++ b/source/blender/nodes/NOD_static_types.h
@@ -267,19 +267,24 @@ DefNode(FunctionNode, FN_NODE_FLOAT_TO_INT, def_float_to_int, "FLOAT_TO_INT", Fl
DefNode(FunctionNode, FN_NODE_INPUT_STRING, def_fn_input_string, "INPUT_STRING", InputString, "String", "")
DefNode(FunctionNode, FN_NODE_INPUT_VECTOR, def_fn_input_vector, "INPUT_VECTOR", InputVector, "Vector", "")
DefNode(FunctionNode, FN_NODE_RANDOM_FLOAT, 0, "RANDOM_FLOAT", RandomFloat, "Random Float", "")
+DefNode(FunctionNode, FN_NODE_ALIGN_ROTATION_TO_VECTOR, def_fn_align_rotation_to_vector, "ALIGN_ROTATION_TO_VECTOR", AlignRotationToVector, "Align Rotation to Vector", "")
+
+
+DefNode(GeometryNode, GEO_NODE_EXTRUDE, 0, "EXTRUDE", Extrude, "Mesh Extrude", "")
+DefNode(GeometryNode, GEO_NODE_EXTRUDE_AND_MOVE, def_geo_extrude_and_move, "EXTRUDE_AND_MOVE", ExtrudeAndMove, "Mesh Extrude And Move", "")
-DefNode(GeometryNode, GEO_NODE_ALIGN_ROTATION_TO_VECTOR, def_geo_align_rotation_to_vector, "ALIGN_ROTATION_TO_VECTOR", AlignRotationToVector, "Align Rotation to Vector", "")
DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_CLAMP, def_geo_attribute_clamp, "ATTRIBUTE_CLAMP", AttributeClamp, "Attribute Clamp", "")
DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_COLOR_RAMP, def_geo_attribute_color_ramp, "ATTRIBUTE_COLOR_RAMP", AttributeColorRamp, "Attribute Color Ramp", "")
DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_COMBINE_XYZ, def_geo_attribute_combine_xyz, "ATTRIBUTE_COMBINE_XYZ", AttributeCombineXYZ, "Attribute Combine XYZ", "")
DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_COMPARE, def_geo_attribute_attribute_compare, "ATTRIBUTE_COMPARE", AttributeCompare, "Attribute Compare", "")
DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_CONVERT, def_geo_attribute_convert, "ATTRIBUTE_CONVERT", AttributeConvert, "Attribute Convert", "")
DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_CURVE_MAP, def_geo_attribute_curve_map, "ATTRIBUTE_CURVE_MAP", AttributeCurveMap, "Attribute Curve Map", "")
-DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_FILL, def_geo_attribute_fill, "ATTRIBUTE_FILL", AttributeFill, "Attribute Fill", "")
+DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_EXTRACT, def_geo_attribute_extract, "ATTRIBUTE_EXTRACT", AttributeExtract, "Attribute Extract", "")
+DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_FILL, def_geo_attribute_fill, "ATTRIBUTE_FILL", AttributeFill, "Store Persistent Attribute", "")
DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_MAP_RANGE, def_geo_attribute_map_range, "ATTRIBUTE_MAP_RANGE", AttributeMapRange, "Attribute Map Range", "")
DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_MATH, def_geo_attribute_math, "ATTRIBUTE_MATH", AttributeMath, "Attribute Math", "")
DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_MIX, def_geo_attribute_mix, "ATTRIBUTE_MIX", AttributeMix, "Attribute Mix", "")
-DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_PROXIMITY, def_geo_attribute_proximity, "ATTRIBUTE_PROXIMITY", AttributeProximity, "Attribute Proximity", "")
+DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_PROXIMITY, def_geo_attribute_proximity, "ATTRIBUTE_PROXIMITY", AttributeProximity, "Proximity", "")
DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_RANDOMIZE, def_geo_attribute_randomize, "ATTRIBUTE_RANDOMIZE", AttributeRandomize, "Attribute Randomize", "")
DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_REMOVE, 0, "ATTRIBUTE_REMOVE", AttributeRemove, "Attribute Remove", "")
DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_SAMPLE_TEXTURE, 0, "ATTRIBUTE_SAMPLE_TEXTURE", AttributeSampleTexture, "Attribute Sample Texture", "")
@@ -287,12 +292,14 @@ DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_SEPARATE_XYZ, def_geo_attribute_separat
DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_TRANSFER, def_geo_attribute_transfer, "ATTRIBUTE_TRANSFER", AttributeTransfer, "Attribute Transfer", "")
DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_VECTOR_MATH, def_geo_attribute_vector_math, "ATTRIBUTE_VECTOR_MATH", AttributeVectorMath, "Attribute Vector Math", "")
DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_VECTOR_ROTATE, def_geo_attribute_vector_rotate, "ATTRIBUTE_VECTOR_ROTATE", AttributeVectorRotate, "Attribute Vector Rotate", "")
+DefNode(GeometryNode, GEO_NODE_ATTRIBUTE, def_geo_attribute, "ATTRIBUTE", Attribute, "Attribute", "")
DefNode(GeometryNode, GEO_NODE_BOOLEAN, def_geo_boolean, "BOOLEAN", Boolean, "Boolean", "")
DefNode(GeometryNode, GEO_NODE_BOUNDING_BOX, 0, "BOUNDING_BOX", BoundBox, "Bounding Box", "")
DefNode(GeometryNode, GEO_NODE_COLLECTION_INFO, def_geo_collection_info, "COLLECTION_INFO", CollectionInfo, "Collection Info", "")
DefNode(GeometryNode, GEO_NODE_CONVEX_HULL, 0, "CONVEX_HULL", ConvexHull, "Convex Hull", "")
DefNode(GeometryNode, GEO_NODE_CURVE_ENDPOINTS, 0, "CURVE_ENDPOINTS", CurveEndpoints, "Curve Endpoints", "")
DefNode(GeometryNode, GEO_NODE_CURVE_LENGTH, 0, "CURVE_LENGTH", CurveLength, "Curve Length", "")
+DefNode(GeometryNode, GEO_NODE_CURVE_PARAMETER, 0, "CURVE_PARAMETER", CurveParameter, "Curve Parameter", "")
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", "")
@@ -305,12 +312,14 @@ DefNode(GeometryNode, GEO_NODE_CURVE_REVERSE, 0, "CURVE_REVERSE", CurveReverse,
DefNode(GeometryNode, GEO_NODE_CURVE_SET_HANDLES, def_geo_curve_set_handles, "CURVE_SET_HANDLES", CurveSetHandles, "Set Handle Type", "")
DefNode(GeometryNode, GEO_NODE_CURVE_SELECT_HANDLES, def_geo_curve_select_handles, "CURVE_SELECT_HANDLES", CurveSelectHandles, "Select by Handle Type", "")
DefNode(GeometryNode, GEO_NODE_CURVE_SPLINE_TYPE, def_geo_curve_spline_type, "CURVE_SPLINE_TYPE", CurveSplineType, "Set Spline Type", "")
-DefNode(GeometryNode, GEO_NODE_CURVE_SUBDIVIDE, def_geo_curve_subdivide, "CURVE_SUBDIVIDE", CurveSubdivide, "Curve Subdivide", "")
+DefNode(GeometryNode, GEO_NODE_CURVE_SUBDIVIDE, 0, "CURVE_SUBDIVIDE", CurveSubdivide, "Curve Subdivide", "")
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_CURVE_TRIM, def_geo_curve_trim, "CURVE_TRIM", CurveTrim, "Curve Trim", "")
-DefNode(GeometryNode, GEO_NODE_DELETE_GEOMETRY, 0, "DELETE_GEOMETRY", DeleteGeometry, "Delete Geometry", "")
+DefNode(GeometryNode, GEO_NODE_DELETE_GEOMETRY, def_geo_delete, "DELETE_GEOMETRY", DeleteGeometry, "Delete Geometry", "")
+DefNode(GeometryNode, GEO_NODE_EVALUATE_CURVE, 0, "EVALUATE_CURVE", EvaluateCurve, "Evaluate Curve", "")
DefNode(GeometryNode, GEO_NODE_EDGE_SPLIT, 0, "EDGE_SPLIT", EdgeSplit, "Edge Split", "")
+DefNode(GeometryNode, GEO_NODE_INDEX, 0, "INDEX", Index, "Index", "")
DefNode(GeometryNode, GEO_NODE_INPUT_MATERIAL, def_geo_input_material, "INPUT_MATERIAL", InputMaterial, "Material", "")
DefNode(GeometryNode, GEO_NODE_IS_VIEWPORT, 0, "IS_VIEWPORT", IsViewport, "Is Viewport", "")
DefNode(GeometryNode, GEO_NODE_JOIN_GEOMETRY, 0, "JOIN_GEOMETRY", JoinGeometry, "Join Geometry", "")
@@ -326,6 +335,7 @@ DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_LINE, def_geo_mesh_line, "MESH_PRI
DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_UV_SPHERE, 0, "MESH_PRIMITIVE_UV_SPHERE", MeshUVSphere, "UV Sphere", "")
DefNode(GeometryNode, GEO_NODE_MESH_SUBDIVIDE, 0, "MESH_SUBDIVIDE", MeshSubdivide, "Mesh Subdivide", "")
DefNode(GeometryNode, GEO_NODE_MESH_TO_CURVE, 0, "MESH_TO_CURVE", MeshToCurve, "Mesh to Curve", "")
+DefNode(GeometryNode, GEO_NODE_NORMAL, 0, "NORMAL", Normal, "Face Normal", "")
DefNode(GeometryNode, GEO_NODE_OBJECT_INFO, def_geo_object_info, "OBJECT_INFO", ObjectInfo, "Object Info", "")
DefNode(GeometryNode, GEO_NODE_POINT_DISTRIBUTE, def_geo_point_distribute, "POINT_DISTRIBUTE", PointDistribute, "Point Distribute", "")
DefNode(GeometryNode, GEO_NODE_POINT_INSTANCE, def_geo_point_instance, "POINT_INSTANCE", PointInstance, "Point Instance", "")
@@ -334,8 +344,11 @@ DefNode(GeometryNode, GEO_NODE_POINT_SCALE, def_geo_point_scale, "POINT_SCALE",
DefNode(GeometryNode, GEO_NODE_POINT_SEPARATE, 0, "POINT_SEPARATE", PointSeparate, "Point Separate", "")
DefNode(GeometryNode, GEO_NODE_POINT_TRANSLATE, def_geo_point_translate, "POINT_TRANSLATE", PointTranslate, "Point Translate", "")
DefNode(GeometryNode, GEO_NODE_POINTS_TO_VOLUME, def_geo_points_to_volume, "POINTS_TO_VOLUME", PointsToVolume, "Points to Volume", "")
+DefNode(GeometryNode, GEO_NODE_POSITION, 0, "POSITION", Position, "Position", "")
DefNode(GeometryNode, GEO_NODE_RAYCAST, def_geo_raycast, "RAYCAST", Raycast, "Raycast", "")
+DefNode(GeometryNode, GEO_NODE_SAMPLE_MESH_SURFACE, 0, "SAMPLE_MESH_SURFACE", SampleMeshSurface, "Sample Mesh Surface", "")
DefNode(GeometryNode, GEO_NODE_SELECT_BY_MATERIAL, 0, "SELECT_BY_MATERIAL", SelectByMaterial, "Select by Material", "")
+DefNode(GeometryNode, GEO_NODE_SET_POSITION, 0, "SET_POSITION", SetPosition, "Set Position", "")
DefNode(GeometryNode, GEO_NODE_SEPARATE_COMPONENTS, 0, "SEPARATE_COMPONENTS", SeparateComponents, "Separate Components", "")
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", "")
@@ -343,6 +356,7 @@ DefNode(GeometryNode, GEO_NODE_TRANSFORM, 0, "TRANSFORM", Transform, "Transform"
DefNode(GeometryNode, GEO_NODE_TRIANGULATE, def_geo_triangulate, "TRIANGULATE", Triangulate, "Triangulate", "")
DefNode(GeometryNode, GEO_NODE_VIEWER, 0, "VIEWER", Viewer, "Viewer", "")
DefNode(GeometryNode, GEO_NODE_VOLUME_TO_MESH, def_geo_volume_to_mesh, "VOLUME_TO_MESH", VolumeToMesh, "Volume to Mesh", "")
+DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_FREEZE, def_geo_attribute_freeze, "ATTRIBUTE_FREEZE", AttributeFreeze, "Attribute Freeze", "")
/* undefine macros */
#undef DefNode
diff --git a/source/blender/nodes/function/nodes/node_fn_align_rotation_to_vector.cc b/source/blender/nodes/function/nodes/node_fn_align_rotation_to_vector.cc
new file mode 100644
index 00000000000..fae61d67a48
--- /dev/null
+++ b/source/blender/nodes/function/nodes/node_fn_align_rotation_to_vector.cc
@@ -0,0 +1,191 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "BLI_math_rotation.h"
+#include "BLI_task.hh"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "node_function_util.hh"
+
+using blender::float3;
+
+static bNodeSocketTemplate fn_node_align_rotation_to_vector_in[] = {
+ {SOCK_VECTOR, N_("Rotation"), 1.0, 0.0, 0.0, 0.0, -FLT_MAX, FLT_MAX, PROP_EULER},
+ {SOCK_FLOAT, N_("Factor"), 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, PROP_FACTOR},
+ {SOCK_VECTOR, N_("Vector"), 0.0, 0.0, 1.0, 0.0, -FLT_MAX, FLT_MAX},
+ {-1, ""},
+};
+
+static bNodeSocketTemplate fn_node_align_rotation_to_vector_out[] = {
+ {SOCK_VECTOR, N_("Rotation")},
+ {-1, ""},
+};
+
+static void fn_node_align_rotation_to_vector_layout(uiLayout *layout,
+ bContext *UNUSED(C),
+ PointerRNA *ptr)
+{
+ uiItemR(layout, ptr, "axis", UI_ITEM_R_EXPAND, nullptr, ICON_NONE);
+ uiLayoutSetPropSep(layout, true);
+ uiLayoutSetPropDecorate(layout, false);
+ uiItemR(layout, ptr, "pivot_axis", 0, IFACE_("Pivot"), ICON_NONE);
+}
+
+static void fn_node_align_rotation_to_vector_init(bNodeTree *UNUSED(ntree), bNode *node)
+{
+ FunctionNodeAlignRotationToVector *node_storage = (FunctionNodeAlignRotationToVector *)
+ MEM_callocN(sizeof(FunctionNodeAlignRotationToVector), __func__);
+ node_storage->axis = GEO_NODE_ALIGN_ROTATION_TO_VECTOR_AXIS_X;
+ node->storage = node_storage;
+}
+
+static float3 align_rotations_auto_pivot(const float3 vector,
+ const float factor,
+ const float3 local_main_axis,
+ const float3 old_rotation_euler)
+{
+ if (is_zero_v3(vector)) {
+ return float3(0);
+ }
+
+ float old_rotation[3][3];
+ eul_to_mat3(old_rotation, old_rotation_euler);
+ float3 old_axis;
+ mul_v3_m3v3(old_axis, old_rotation, local_main_axis);
+
+ const float3 new_axis = vector.normalized();
+ float3 rotation_axis = float3::cross_high_precision(old_axis, new_axis);
+ if (is_zero_v3(rotation_axis)) {
+ /* The vectors are linearly dependent, so we fall back to another axis. */
+ rotation_axis = float3::cross_high_precision(old_axis, float3(1, 0, 0));
+ if (is_zero_v3(rotation_axis)) {
+ /* This is now guaranteed to not be zero. */
+ rotation_axis = float3::cross_high_precision(old_axis, float3(0, 1, 0));
+ }
+ }
+
+ const float full_angle = angle_normalized_v3v3(old_axis, new_axis);
+ const float angle = factor * full_angle;
+
+ float rotation[3][3];
+ axis_angle_to_mat3(rotation, rotation_axis, angle);
+
+ float new_rotation_matrix[3][3];
+ mul_m3_m3m3(new_rotation_matrix, rotation, old_rotation);
+
+ float3 new_rotation;
+ mat3_to_eul(new_rotation, new_rotation_matrix);
+
+ return new_rotation;
+}
+
+static float3 align_rotations_fixed_pivot(const float3 vector,
+ const float factor,
+ const float3 local_main_axis,
+ const float3 local_pivot_axis,
+ const float3 old_rotation_euler)
+{
+ if (is_zero_v3(vector)) {
+ return float3(0);
+ }
+
+ float old_rotation[3][3];
+ eul_to_mat3(old_rotation, old_rotation_euler);
+ float3 old_axis;
+ mul_v3_m3v3(old_axis, old_rotation, local_main_axis);
+ float3 pivot_axis;
+ mul_v3_m3v3(pivot_axis, old_rotation, local_pivot_axis);
+
+ float full_angle = angle_signed_on_axis_v3v3_v3(vector, old_axis, pivot_axis);
+ if (full_angle > M_PI) {
+ /* Make sure the point is rotated as little as possible. */
+ full_angle -= 2.0f * M_PI;
+ }
+ const float angle = factor * full_angle;
+
+ float rotation[3][3];
+ axis_angle_to_mat3(rotation, pivot_axis, angle);
+
+ float new_rotation_matrix[3][3];
+ mul_m3_m3m3(new_rotation_matrix, rotation, old_rotation);
+
+ float3 new_rotation;
+ mat3_to_eul(new_rotation, new_rotation_matrix);
+
+ return new_rotation;
+}
+
+static const blender::fn::MultiFunction &get_multi_function(bNode &node)
+{
+ const FunctionNodeAlignRotationToVector &storage = *(const FunctionNodeAlignRotationToVector *)
+ node.storage;
+
+ float3 local_main_axis{0, 0, 0};
+ local_main_axis[storage.axis] = 1;
+
+ if (storage.pivot_axis == GEO_NODE_ALIGN_ROTATION_TO_VECTOR_PIVOT_AXIS_AUTO) {
+ static blender::fn::CustomMF_SI_SI_SI_SO<float3, float, float3, float3> auto_pivot{
+ "Align Rotation Auto Pivot",
+ [local_main_axis](float3 rotation, float factor, float3 vector) {
+ return align_rotations_auto_pivot(vector, factor, local_main_axis, rotation);
+ }};
+ return auto_pivot;
+ }
+ float3 local_pivot_axis{0, 0, 0};
+ local_pivot_axis[storage.pivot_axis - 1] = 1;
+
+ if (local_main_axis == local_pivot_axis) {
+ return blender::fn::dummy_multi_function;
+ }
+
+ static blender::fn::CustomMF_SI_SI_SI_SO<float3, float, float3, float3> fixed_pivot{
+ "Align Rotation Fixed Pivot",
+ [local_main_axis, local_pivot_axis](float3 rotation, float factor, float3 vector) {
+ return align_rotations_fixed_pivot(
+ vector, factor, local_main_axis, local_pivot_axis, rotation);
+ }};
+ return fixed_pivot;
+}
+
+static void fn_node_align_rotation_to_vector_expand_in_mf_network(
+ blender::nodes::NodeMFNetworkBuilder &builder)
+{
+ const blender::fn::MultiFunction &fn = get_multi_function(builder.bnode());
+ builder.set_matching_fn(fn);
+}
+
+void register_node_type_fn_align_rotation_to_vector()
+{
+ static bNodeType ntype;
+
+ fn_node_type_base(&ntype,
+ FN_NODE_ALIGN_ROTATION_TO_VECTOR,
+ "Align Rotation to Vector",
+ NODE_CLASS_OP_VECTOR,
+ 0);
+ node_type_socket_templates(
+ &ntype, fn_node_align_rotation_to_vector_in, fn_node_align_rotation_to_vector_out);
+ node_type_init(&ntype, fn_node_align_rotation_to_vector_init);
+ node_type_storage(&ntype,
+ "FunctionNodeAlignRotationToVector",
+ node_free_standard_storage,
+ node_copy_standard_storage);
+ ntype.expand_in_mf_network = fn_node_align_rotation_to_vector_expand_in_mf_network;
+ ntype.draw_buttons = fn_node_align_rotation_to_vector_layout;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/node_geometry_util.cc b/source/blender/nodes/geometry/node_geometry_util.cc
index 46e9d36c09c..f03b1e966e4 100644
--- a/source/blender/nodes/geometry/node_geometry_util.cc
+++ b/source/blender/nodes/geometry/node_geometry_util.cc
@@ -23,11 +23,48 @@
#include "BKE_mesh.h"
#include "BKE_mesh_runtime.h"
#include "BKE_pointcloud.h"
+#include "BKE_spline.hh"
+
+#include "NOD_type_conversions.hh"
namespace blender::nodes {
using bke::GeometryInstanceGroup;
+static void update_multi_type_socket_availabilities(ListBase &socket_list,
+ const StringRef name,
+ const CustomDataType type,
+ const bool name_is_available)
+{
+ LISTBASE_FOREACH (bNodeSocket *, socket, &socket_list) {
+ if (name == socket->name) {
+ const bool socket_is_available = name_is_available &&
+ ((socket->type == SOCK_STRING && type == CD_PROP_STRING) ||
+ (socket->type == SOCK_FLOAT && type == CD_PROP_FLOAT) ||
+ (socket->type == SOCK_INT && type == CD_PROP_INT32) ||
+ (socket->type == SOCK_VECTOR && type == CD_PROP_FLOAT3) ||
+ (socket->type == SOCK_RGBA && type == CD_PROP_COLOR));
+ nodeSetSocketAvailability(socket, socket_is_available);
+ }
+ }
+}
+
+void update_multi_type_input_socket_availabilities(bNode &node,
+ const StringRef name,
+ const CustomDataType type,
+ const bool name_is_available)
+{
+ update_multi_type_socket_availabilities(node.inputs, name, type, name_is_available);
+}
+
+void update_multi_type_output_socket_availabilities(bNode &node,
+ const StringRef name,
+ const CustomDataType type,
+ const bool name_is_available)
+{
+ update_multi_type_socket_availabilities(node.outputs, name, type, name_is_available);
+}
+
/**
* Update the availability of a group of input sockets with the same name,
* used for switching between attribute inputs or single values.
@@ -55,6 +92,192 @@ void update_attribute_input_socket_availabilities(bNode &node,
}
}
+void prepare_field_inputs(bke::FieldInputs &field_inputs,
+ const GeometryComponent &component,
+ const AttributeDomain domain,
+ Vector<std::unique_ptr<bke::FieldInputValue>> &r_values)
+{
+ const int domain_size = component.attribute_domain_size(domain);
+ for (const bke::FieldInputKey &key : field_inputs) {
+ std::unique_ptr<bke::FieldInputValue> input_value;
+ if (const bke::PersistentAttributeFieldInputKey *persistent_attribute_key =
+ dynamic_cast<const bke::PersistentAttributeFieldInputKey *>(&key)) {
+ const StringRef name = persistent_attribute_key->name();
+ const CPPType &cpp_type = persistent_attribute_key->type();
+ const CustomDataType type = bke::cpp_type_to_custom_data_type(cpp_type);
+ GVArrayPtr attribute = component.attribute_get_for_read(name, domain, type);
+ input_value = std::make_unique<bke::GVArrayFieldInputValue>(std::move(attribute));
+ }
+ else if (dynamic_cast<const bke::IndexFieldInputKey *>(&key) != nullptr) {
+ auto index_func = [](int i) { return i; };
+ VArrayPtr<int> index_varray = std::make_unique<VArray_For_Func<int, decltype(index_func)>>(
+ domain_size, index_func);
+ GVArrayPtr index_gvarray = std::make_unique<fn::GVArray_For_VArray<int>>(
+ std::move(index_varray));
+ input_value = std::make_unique<bke::GVArrayFieldInputValue>(std::move(index_gvarray));
+ }
+ else if (const bke::AnonymousAttributeFieldInputKey *anonymous_attribute_key =
+ dynamic_cast<const bke::AnonymousAttributeFieldInputKey *>(&key)) {
+ const AnonymousCustomDataLayerID &layer_id = anonymous_attribute_key->layer_id();
+ ReadAttributeLookup attribute = component.attribute_try_get_anonymous_for_read(layer_id);
+ if (!attribute) {
+ continue;
+ }
+ GVArrayPtr varray = std::move(attribute.varray);
+ if (attribute.domain != domain) {
+ /* TODO: Not all boolean attributes are selections. */
+ if (varray->type().is<bool>() && component.type() == GEO_COMPONENT_TYPE_MESH) {
+ const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component);
+ VArrayPtr<bool> varray_bool = std::make_unique<fn::VArray_For_GVArray<bool>>(
+ std::move(varray));
+ varray_bool = mesh_component.adapt_selection(
+ std::move(varray_bool), attribute.domain, domain);
+ if (!varray_bool) {
+ continue;
+ }
+ varray = std::make_unique<fn::GVArray_For_VArray<bool>>(std::move(varray_bool));
+ }
+ else {
+ varray = component.attribute_try_adapt_domain(
+ std::move(varray), attribute.domain, domain);
+ }
+ }
+ if (!varray) {
+ continue;
+ }
+ const CPPType &type = anonymous_attribute_key->type();
+ if (varray->type() != type) {
+ const blender::nodes::DataTypeConversions &conversions = get_implicit_type_conversions();
+ varray = conversions.try_convert(std::move(varray), type);
+ }
+ if (!varray) {
+ continue;
+ }
+ input_value = std::make_unique<bke::GVArrayFieldInputValue>(std::move(varray));
+ }
+ else if (dynamic_cast<const bke::CurveParameterFieldInputKey *>(&key)) {
+ if (component.type() != GEO_COMPONENT_TYPE_CURVE) {
+ continue;
+ }
+ const CurveComponent &curve_component = static_cast<const CurveComponent &>(component);
+ const CurveEval *curve = curve_component.get_for_read();
+ if (curve == nullptr) {
+ continue;
+ }
+
+ Span<SplinePtr> splines = curve->splines();
+ Array<int> offsets = curve->control_point_offsets();
+
+ Array<float> parameters(offsets.last());
+
+ for (const int i_spline : splines.index_range()) {
+ const int offset = offsets[i_spline];
+ MutableSpan<float> spline_parameters = parameters.as_mutable_span().slice(
+ offset, offsets[i_spline + 1] - offset);
+ spline_parameters.first() = 0.0f;
+
+ const Spline &spline = *splines[i_spline];
+ const Span<float> lengths_eval = spline.evaluated_lengths();
+ const float total_length_inv = spline.length() == 0.0f ? 0.0f : 1.0f / spline.length();
+ switch (spline.type()) {
+ case Spline::Type::Bezier: {
+ const BezierSpline &bezier_spline = static_cast<const BezierSpline &>(spline);
+ const Span<int> control_point_offsets = bezier_spline.control_point_offsets();
+ for (const int i : IndexRange(1, spline.size() - 1)) {
+ spline_parameters[i] = lengths_eval[control_point_offsets[i] - 1];
+ }
+ break;
+ }
+ case Spline::Type::Poly: {
+ if (spline.is_cyclic()) {
+ spline_parameters.drop_front(1).copy_from(lengths_eval.drop_back(1));
+ }
+ else {
+ spline_parameters.drop_front(1).copy_from(lengths_eval);
+ }
+ break;
+ }
+ case Spline::Type::NURBS: {
+ /* Instead of doing something totally arbirary and wrong for the prototype, just do
+ * nothing currently. Consult NURBS experts or something or document this heavily if
+ * it ever makes it to master. */
+ parameters.as_mutable_span().slice(offset, offsets[i_spline + 1]).fill(0.0f);
+ break;
+ }
+ }
+
+ for (float &parameter : spline_parameters) {
+ parameter *= total_length_inv;
+ }
+ }
+ GVArrayPtr varray = std::make_unique<fn::GVArray_For_ArrayContainer<Array<float>>>(
+ std::move(parameters));
+ input_value = std::make_unique<bke::GVArrayFieldInputValue>(std::move(varray));
+ }
+
+ field_inputs.set_input(key, *input_value);
+ r_values.append(std::move(input_value));
+ }
+}
+
+template<typename T>
+void fill_attribute_impl(GeometryComponent &component,
+ OutputAttribute &attribute,
+ const bke::Field &field)
+{
+ const AttributeDomain domain = attribute.domain();
+ const int domain_size = attribute->size();
+ bke::FieldInputs field_inputs = field.prepare_inputs();
+ Vector<std::unique_ptr<bke::FieldInputValue>> input_values;
+ prepare_field_inputs(field_inputs, component, domain, input_values);
+ bke::FieldOutput field_output = field.evaluate(IndexMask(domain_size), field_inputs);
+ for (const int i : IndexRange(domain_size)) {
+ T value;
+ field_output.varray_ref().get(i, &value);
+ attribute->set_by_copy(i, &value);
+ }
+}
+
+void try_freeze_field_on_geometry(GeometryComponent &component,
+ const AnonymousCustomDataLayerID &layer_id,
+ AttributeDomain domain,
+ const bke::Field &field)
+{
+ const CustomDataType data_type = bke::cpp_type_to_custom_data_type(field.output_type());
+ OutputAttribute attribute = component.attribute_try_get_anonymous_for_output(
+ layer_id, domain, data_type);
+ if (!attribute) {
+ return;
+ }
+
+ switch (data_type) {
+ case CD_PROP_FLOAT: {
+ fill_attribute_impl<float>(component, attribute, field);
+ break;
+ }
+ case CD_PROP_FLOAT3: {
+ fill_attribute_impl<float3>(component, attribute, field);
+ break;
+ }
+ case CD_PROP_COLOR: {
+ fill_attribute_impl<ColorGeometry4f>(component, attribute, field);
+ break;
+ }
+ case CD_PROP_BOOL: {
+ fill_attribute_impl<bool>(component, attribute, field);
+ break;
+ }
+ case CD_PROP_INT32: {
+ fill_attribute_impl<int>(component, attribute, field);
+ break;
+ }
+ default:
+ break;
+ }
+
+ attribute.save();
+}
+
} // namespace blender::nodes
bool geo_node_poll_default(bNodeType *UNUSED(ntype),
diff --git a/source/blender/nodes/geometry/node_geometry_util.hh b/source/blender/nodes/geometry/node_geometry_util.hh
index 956b7b8a005..7ffacee88c4 100644
--- a/source/blender/nodes/geometry/node_geometry_util.hh
+++ b/source/blender/nodes/geometry/node_geometry_util.hh
@@ -46,6 +46,16 @@ void update_attribute_input_socket_availabilities(bNode &node,
const GeometryNodeAttributeInputMode mode,
const bool name_is_available = true);
+void update_multi_type_input_socket_availabilities(bNode &node,
+ const StringRef name,
+ const CustomDataType type,
+ const bool name_is_available = true);
+
+void update_multi_type_output_socket_availabilities(bNode &node,
+ const StringRef name,
+ const CustomDataType type,
+ const bool name_is_available = true);
+
Array<uint32_t> get_geometry_element_ids_as_uints(const GeometryComponent &component,
const AttributeDomain domain);
@@ -92,4 +102,14 @@ void curve_create_default_rotation_attribute(Span<float3> tangents,
Span<float3> normals,
MutableSpan<float3> rotations);
+void prepare_field_inputs(bke::FieldInputs &field_inputs,
+ const GeometryComponent &component,
+ const AttributeDomain domain,
+ Vector<std::unique_ptr<bke::FieldInputValue>> &r_values);
+
+void try_freeze_field_on_geometry(GeometryComponent &component,
+ const AnonymousCustomDataLayerID &layer_id,
+ AttributeDomain domain,
+ const bke::Field &field);
+
} // namespace blender::nodes
diff --git a/source/blender/nodes/geometry/nodes/node_geo_align_rotation_to_vector.cc b/source/blender/nodes/geometry/nodes/node_geo_align_rotation_to_vector.cc
deleted file mode 100644
index 9b6824fdb5c..00000000000
--- a/source/blender/nodes/geometry/nodes/node_geo_align_rotation_to_vector.cc
+++ /dev/null
@@ -1,242 +0,0 @@
-/*
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-
-#include "BLI_math_rotation.h"
-#include "BLI_task.hh"
-
-#include "UI_interface.h"
-#include "UI_resources.h"
-
-#include "node_geometry_util.hh"
-
-static bNodeSocketTemplate geo_node_align_rotation_to_vector_in[] = {
- {SOCK_GEOMETRY, N_("Geometry")},
- {SOCK_STRING, N_("Factor")},
- {SOCK_FLOAT, N_("Factor"), 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, PROP_FACTOR},
- {SOCK_STRING, N_("Vector")},
- {SOCK_VECTOR, N_("Vector"), 0.0, 0.0, 1.0, 0.0, -FLT_MAX, FLT_MAX, PROP_ANGLE},
- {-1, ""},
-};
-
-static bNodeSocketTemplate geo_node_align_rotation_to_vector_out[] = {
- {SOCK_GEOMETRY, N_("Geometry")},
- {-1, ""},
-};
-
-static void geo_node_align_rotation_to_vector_layout(uiLayout *layout,
- bContext *UNUSED(C),
- PointerRNA *ptr)
-{
- uiItemR(layout, ptr, "axis", UI_ITEM_R_EXPAND, nullptr, ICON_NONE);
- uiLayoutSetPropSep(layout, true);
- uiLayoutSetPropDecorate(layout, false);
- uiItemR(layout, ptr, "pivot_axis", 0, IFACE_("Pivot"), ICON_NONE);
- uiLayout *col = uiLayoutColumn(layout, false);
- uiItemR(col, ptr, "input_type_factor", 0, IFACE_("Factor"), ICON_NONE);
- uiItemR(col, ptr, "input_type_vector", 0, IFACE_("Vector"), ICON_NONE);
-}
-
-namespace blender::nodes {
-
-static void geo_node_align_rotation_to_vector_init(bNodeTree *UNUSED(ntree), bNode *node)
-{
- NodeGeometryAlignRotationToVector *node_storage = (NodeGeometryAlignRotationToVector *)
- MEM_callocN(sizeof(NodeGeometryAlignRotationToVector), __func__);
-
- node_storage->axis = GEO_NODE_ALIGN_ROTATION_TO_VECTOR_AXIS_X;
- node_storage->input_type_factor = GEO_NODE_ATTRIBUTE_INPUT_FLOAT;
- node_storage->input_type_vector = GEO_NODE_ATTRIBUTE_INPUT_VECTOR;
-
- node->storage = node_storage;
-}
-
-static void geo_node_align_rotation_to_vector_update(bNodeTree *UNUSED(ntree), bNode *node)
-{
- NodeGeometryAlignRotationToVector *node_storage = (NodeGeometryAlignRotationToVector *)
- node->storage;
- update_attribute_input_socket_availabilities(
- *node, "Factor", (GeometryNodeAttributeInputMode)node_storage->input_type_factor);
- update_attribute_input_socket_availabilities(
- *node, "Vector", (GeometryNodeAttributeInputMode)node_storage->input_type_vector);
-}
-
-static void align_rotations_auto_pivot(const VArray<float3> &vectors,
- const VArray<float> &factors,
- const float3 local_main_axis,
- const MutableSpan<float3> rotations)
-{
- threading::parallel_for(IndexRange(vectors.size()), 128, [&](IndexRange range) {
- for (const int i : range) {
- const float3 vector = vectors[i];
- if (is_zero_v3(vector)) {
- continue;
- }
-
- float old_rotation[3][3];
- eul_to_mat3(old_rotation, rotations[i]);
- float3 old_axis;
- mul_v3_m3v3(old_axis, old_rotation, local_main_axis);
-
- const float3 new_axis = vector.normalized();
- float3 rotation_axis = float3::cross_high_precision(old_axis, new_axis);
- if (is_zero_v3(rotation_axis)) {
- /* The vectors are linearly dependent, so we fall back to another axis. */
- rotation_axis = float3::cross_high_precision(old_axis, float3(1, 0, 0));
- if (is_zero_v3(rotation_axis)) {
- /* This is now guaranteed to not be zero. */
- rotation_axis = float3::cross_high_precision(old_axis, float3(0, 1, 0));
- }
- }
-
- const float full_angle = angle_normalized_v3v3(old_axis, new_axis);
- const float angle = factors[i] * full_angle;
-
- float rotation[3][3];
- axis_angle_to_mat3(rotation, rotation_axis, angle);
-
- float new_rotation_matrix[3][3];
- mul_m3_m3m3(new_rotation_matrix, rotation, old_rotation);
-
- float3 new_rotation;
- mat3_to_eul(new_rotation, new_rotation_matrix);
-
- rotations[i] = new_rotation;
- }
- });
-}
-
-static void align_rotations_fixed_pivot(const VArray<float3> &vectors,
- const VArray<float> &factors,
- const float3 local_main_axis,
- const float3 local_pivot_axis,
- const MutableSpan<float3> rotations)
-{
- if (local_main_axis == local_pivot_axis) {
- /* Can't compute any meaningful rotation angle in this case. */
- return;
- }
-
- threading::parallel_for(IndexRange(vectors.size()), 128, [&](IndexRange range) {
- for (const int i : range) {
- const float3 vector = vectors[i];
- if (is_zero_v3(vector)) {
- continue;
- }
-
- float old_rotation[3][3];
- eul_to_mat3(old_rotation, rotations[i]);
- float3 old_axis;
- mul_v3_m3v3(old_axis, old_rotation, local_main_axis);
- float3 pivot_axis;
- mul_v3_m3v3(pivot_axis, old_rotation, local_pivot_axis);
-
- float full_angle = angle_signed_on_axis_v3v3_v3(vector, old_axis, pivot_axis);
- if (full_angle > M_PI) {
- /* Make sure the point is rotated as little as possible. */
- full_angle -= 2.0f * M_PI;
- }
- const float angle = factors[i] * full_angle;
-
- float rotation[3][3];
- axis_angle_to_mat3(rotation, pivot_axis, angle);
-
- float new_rotation_matrix[3][3];
- mul_m3_m3m3(new_rotation_matrix, rotation, old_rotation);
-
- float3 new_rotation;
- mat3_to_eul(new_rotation, new_rotation_matrix);
-
- rotations[i] = new_rotation;
- }
- });
-}
-
-static void align_rotations_on_component(GeometryComponent &component,
- const GeoNodeExecParams &params)
-{
- const bNode &node = params.node();
- const NodeGeometryAlignRotationToVector &storage = *(const NodeGeometryAlignRotationToVector *)
- node.storage;
-
- OutputAttribute_Typed<float3> rotations = component.attribute_try_get_for_output<float3>(
- "rotation", ATTR_DOMAIN_POINT, {0, 0, 0});
- if (!rotations) {
- return;
- }
-
- GVArray_Typed<float> factors = params.get_input_attribute<float>(
- "Factor", component, ATTR_DOMAIN_POINT, 1.0f);
- GVArray_Typed<float3> vectors = params.get_input_attribute<float3>(
- "Vector", component, ATTR_DOMAIN_POINT, {0, 0, 1});
-
- float3 local_main_axis{0, 0, 0};
- local_main_axis[storage.axis] = 1;
- if (storage.pivot_axis == GEO_NODE_ALIGN_ROTATION_TO_VECTOR_PIVOT_AXIS_AUTO) {
- align_rotations_auto_pivot(vectors, factors, local_main_axis, rotations.as_span());
- }
- else {
- float3 local_pivot_axis{0, 0, 0};
- local_pivot_axis[storage.pivot_axis - 1] = 1;
- align_rotations_fixed_pivot(
- vectors, factors, local_main_axis, local_pivot_axis, rotations.as_span());
- }
-
- rotations.save();
-}
-
-static void geo_node_align_rotation_to_vector_exec(GeoNodeExecParams params)
-{
- GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
-
- geometry_set = geometry_set_realize_instances(geometry_set);
-
- if (geometry_set.has<MeshComponent>()) {
- align_rotations_on_component(geometry_set.get_component_for_write<MeshComponent>(), params);
- }
- if (geometry_set.has<PointCloudComponent>()) {
- align_rotations_on_component(geometry_set.get_component_for_write<PointCloudComponent>(),
- params);
- }
- if (geometry_set.has<CurveComponent>()) {
- align_rotations_on_component(geometry_set.get_component_for_write<CurveComponent>(), params);
- }
-
- params.set_output("Geometry", geometry_set);
-}
-
-} // namespace blender::nodes
-
-void register_node_type_geo_align_rotation_to_vector()
-{
- static bNodeType ntype;
-
- geo_node_type_base(&ntype,
- GEO_NODE_ALIGN_ROTATION_TO_VECTOR,
- "Align Rotation to Vector",
- NODE_CLASS_GEOMETRY,
- 0);
- node_type_socket_templates(
- &ntype, geo_node_align_rotation_to_vector_in, geo_node_align_rotation_to_vector_out);
- node_type_init(&ntype, blender::nodes::geo_node_align_rotation_to_vector_init);
- node_type_update(&ntype, blender::nodes::geo_node_align_rotation_to_vector_update);
- node_type_storage(&ntype,
- "NodeGeometryAlignRotationToVector",
- node_free_standard_storage,
- node_copy_standard_storage);
- ntype.geometry_node_execute = blender::nodes::geo_node_align_rotation_to_vector_exec;
- ntype.draw_buttons = geo_node_align_rotation_to_vector_layout;
- nodeRegisterType(&ntype);
-}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute.cc
new file mode 100644
index 00000000000..a0f6dc5add1
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_attribute.cc
@@ -0,0 +1,119 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "node_geometry_util.hh"
+
+static bNodeSocketTemplate geo_node_attribute_in[] = {
+ {SOCK_STRING, N_("Name")},
+ {-1, ""},
+};
+
+static bNodeSocketTemplate geo_node_attribute_out[] = {
+ {SOCK_FLOAT, N_("Attribute")},
+ {SOCK_INT, N_("Attribute")},
+ {SOCK_BOOLEAN, N_("Attribute")},
+ {SOCK_VECTOR, N_("Attribute")},
+ {SOCK_RGBA, N_("Attribute")},
+ {-1, ""},
+};
+
+static void geo_node_attribute_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
+{
+ uiItemR(layout, ptr, "output_type", 0, "", ICON_NONE);
+}
+
+static void geo_node_attribute_init(bNodeTree *UNUSED(tree), bNode *node)
+{
+ NodeGeometryAttribute *data = (NodeGeometryAttribute *)MEM_callocN(sizeof(NodeGeometryAttribute),
+ __func__);
+ data->output_type = SOCK_FLOAT;
+ node->storage = data;
+}
+
+namespace blender::nodes {
+
+static void geo_node_attribute_update(bNodeTree *UNUSED(ntree), bNode *node)
+{
+ NodeGeometryAttribute *node_storage = (NodeGeometryAttribute *)node->storage;
+
+ LISTBASE_FOREACH (bNodeSocket *, socket, &node->outputs) {
+ nodeSetSocketAvailability(socket,
+ socket->type == (eNodeSocketDatatype)node_storage->output_type);
+ }
+}
+
+static const CPPType *get_cpp_type(const eNodeSocketDatatype data_type)
+{
+ switch (data_type) {
+ case SOCK_FLOAT:
+ return &CPPType::get<float>();
+ case SOCK_VECTOR:
+ return &CPPType::get<float3>();
+ case SOCK_RGBA:
+ return &CPPType::get<ColorGeometry4f>();
+ case SOCK_BOOLEAN:
+ return &CPPType::get<bool>();
+ case SOCK_INT:
+ return &CPPType::get<int>();
+ default:
+ return nullptr;
+ }
+}
+
+static void geo_node_attribute_exec(GeoNodeExecParams params)
+{
+ NodeGeometryAttribute *node_storage = (NodeGeometryAttribute *)params.node().storage;
+ std::string name = params.extract_input<std::string>("Name");
+
+ const CPPType *cpp_type = get_cpp_type((eNodeSocketDatatype)node_storage->output_type);
+ BLI_assert(cpp_type != nullptr);
+ bke::FieldPtr field = new bke::PersistentAttributeField(std::move(name), *cpp_type);
+ if (cpp_type->is<float>()) {
+ params.set_output("Attribute", bke::FieldRef<float>(std::move(field)));
+ }
+ else if (cpp_type->is<int>()) {
+ params.set_output("Attribute_001", bke::FieldRef<int>(std::move(field)));
+ }
+ else if (cpp_type->is<bool>()) {
+ params.set_output("Attribute_002", bke::FieldRef<bool>(std::move(field)));
+ }
+ else if (cpp_type->is<float3>()) {
+ params.set_output("Attribute_003", bke::FieldRef<float3>(std::move(field)));
+ }
+ else if (cpp_type->is<ColorGeometry4f>()) {
+ params.set_output("Attribute_004", bke::FieldRef<ColorGeometry4f>(std::move(field)));
+ }
+}
+
+} // namespace blender::nodes
+
+void register_node_type_geo_attribute()
+{
+ static bNodeType ntype;
+
+ geo_node_type_base(&ntype, GEO_NODE_ATTRIBUTE, "Attribute", NODE_CLASS_INPUT, 0);
+ node_type_socket_templates(&ntype, geo_node_attribute_in, geo_node_attribute_out);
+ node_type_init(&ntype, geo_node_attribute_init);
+ node_type_update(&ntype, blender::nodes::geo_node_attribute_update);
+ node_type_storage(
+ &ntype, "NodeGeometryAttribute", node_free_standard_storage, node_copy_standard_storage);
+ ntype.geometry_node_execute = blender::nodes::geo_node_attribute_exec;
+ ntype.draw_buttons = geo_node_attribute_layout;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_extract.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_extract.cc
new file mode 100644
index 00000000000..bb362916b52
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_extract.cc
@@ -0,0 +1,182 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "node_geometry_util.hh"
+
+static bNodeSocketTemplate geo_node_attribute_extract_in[] = {
+ {SOCK_GEOMETRY, N_("Geometry")},
+ {SOCK_STRING, N_("Attribute")},
+ {-1, ""},
+};
+
+static bNodeSocketTemplate geo_node_attribute_extract_out[] = {
+ {SOCK_GEOMETRY, N_("Geometry")},
+ {SOCK_VECTOR, N_("Value")},
+ {SOCK_FLOAT, N_("Value")},
+ {SOCK_RGBA, N_("Value")},
+ {SOCK_BOOLEAN, N_("Value")},
+ {SOCK_INT, N_("Value")},
+ {-1, ""},
+};
+
+static void geo_node_attribute_extract_layout(uiLayout *layout,
+ bContext *UNUSED(C),
+ PointerRNA *ptr)
+{
+ uiItemR(layout, ptr, "data_type", 0, "", ICON_NONE);
+ uiItemR(layout, ptr, "delete_persistent", 0, nullptr, ICON_NONE);
+}
+
+static void geo_node_attribute_extract_init(bNodeTree *UNUSED(tree), bNode *node)
+{
+ NodeGeometryAttributeExtract *storage = (NodeGeometryAttributeExtract *)MEM_callocN(
+ sizeof(NodeGeometryAttributeExtract), __func__);
+ storage->data_type = CD_PROP_FLOAT;
+ storage->delete_persistent = false;
+ node->storage = storage;
+}
+
+static void geo_node_attribute_extract_update(bNodeTree *UNUSED(ntree), bNode *node)
+{
+
+ const NodeGeometryAttributeExtract &storage = *(const NodeGeometryAttributeExtract *)
+ node->storage;
+
+ bNodeSocket *socket_value_vector = (bNodeSocket *)BLI_findlink(&node->outputs, 1);
+ bNodeSocket *socket_value_float = socket_value_vector->next;
+ bNodeSocket *socket_value_color4f = socket_value_float->next;
+ bNodeSocket *socket_value_boolean = socket_value_color4f->next;
+ bNodeSocket *socket_value_int32 = socket_value_boolean->next;
+
+ const CustomDataType data_type = (CustomDataType)storage.data_type;
+
+ nodeSetSocketAvailability(socket_value_vector, data_type == CD_PROP_FLOAT3);
+ nodeSetSocketAvailability(socket_value_float, data_type == CD_PROP_FLOAT);
+ nodeSetSocketAvailability(socket_value_color4f, data_type == CD_PROP_COLOR);
+ nodeSetSocketAvailability(socket_value_boolean, data_type == CD_PROP_BOOL);
+ nodeSetSocketAvailability(socket_value_int32, data_type == CD_PROP_INT32);
+}
+
+namespace blender::nodes {
+
+static void convert_attribute(GeometryComponent &component,
+ const StringRef attribute_name,
+ const AnonymousCustomDataLayerID &layer_id,
+ bool delete_persistent)
+{
+ ReadAttributeLookup attribute_lookup = component.attribute_try_get_for_read(attribute_name);
+ if (!attribute_lookup) {
+ return;
+ }
+ const GVArray &varray = *attribute_lookup.varray;
+ const CPPType &cpp_type = varray.type();
+ const CustomDataType data_type = bke::cpp_type_to_custom_data_type(cpp_type);
+ component.attribute_try_create_anonymous(
+ layer_id, attribute_lookup.domain, data_type, AttributeInitVArray(&varray));
+
+ if (delete_persistent) {
+ component.attribute_try_delete(attribute_name);
+ }
+}
+
+static void geo_node_attribute_extract_exec(GeoNodeExecParams params)
+{
+ const bNode &node = params.node();
+ const NodeGeometryAttributeExtract &storage = *(const NodeGeometryAttributeExtract *)
+ node.storage;
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
+
+ geometry_set = geometry_set_realize_instances(geometry_set);
+
+ const CustomDataType data_type = static_cast<CustomDataType>(storage.data_type);
+ const CPPType *cpp_type = bke::custom_data_type_to_cpp_type(data_type);
+ const bool delete_persistent = storage.delete_persistent;
+
+ const std::string attribute_name = params.get_input<std::string>("Attribute");
+ AnonymousCustomDataLayerID *layer_id = CustomData_anonymous_id_new(attribute_name.c_str());
+ auto *output_field = new bke::AnonymousAttributeField(*layer_id, *cpp_type);
+
+ if (geometry_set.has<MeshComponent>()) {
+ convert_attribute(geometry_set.get_component_for_write<MeshComponent>(),
+ attribute_name,
+ *layer_id,
+ delete_persistent);
+ }
+ if (geometry_set.has<PointCloudComponent>()) {
+ convert_attribute(geometry_set.get_component_for_write<PointCloudComponent>(),
+ attribute_name,
+ *layer_id,
+ delete_persistent);
+ }
+ if (geometry_set.has<CurveComponent>()) {
+ convert_attribute(geometry_set.get_component_for_write<CurveComponent>(),
+ attribute_name,
+ *layer_id,
+ delete_persistent);
+ }
+
+ params.set_output("Geometry", geometry_set);
+
+ switch (data_type) {
+ case CD_PROP_FLOAT: {
+ params.set_output("Value_001", bke::FieldRef<float>(output_field));
+ break;
+ }
+ case CD_PROP_FLOAT3: {
+ params.set_output("Value", bke::FieldRef<float3>(output_field));
+ break;
+ }
+ case CD_PROP_INT32: {
+ params.set_output("Value_004", bke::FieldRef<int>(output_field));
+ break;
+ }
+ case CD_PROP_BOOL: {
+ params.set_output("Value_003", bke::FieldRef<bool>(output_field));
+ break;
+ }
+ case CD_PROP_COLOR: {
+ params.set_output("Value_002", bke::FieldRef<ColorGeometry4f>(output_field));
+ break;
+ }
+ default: {
+ BLI_assert_unreachable();
+ }
+ }
+}
+
+} // namespace blender::nodes
+
+void register_node_type_geo_attribute_extract()
+{
+ static bNodeType ntype;
+
+ geo_node_type_base(
+ &ntype, GEO_NODE_ATTRIBUTE_EXTRACT, "Attribute Extract", NODE_CLASS_ATTRIBUTE, 0);
+ node_type_socket_templates(
+ &ntype, geo_node_attribute_extract_in, geo_node_attribute_extract_out);
+ node_type_init(&ntype, geo_node_attribute_extract_init);
+ node_type_update(&ntype, geo_node_attribute_extract_update);
+ node_type_storage(&ntype,
+ "NodeGeometryAttributeExtract",
+ node_free_standard_storage,
+ node_copy_standard_storage);
+ ntype.geometry_node_execute = blender::nodes::geo_node_attribute_extract_exec;
+ ntype.draw_buttons = geo_node_attribute_extract_layout;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_fill.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_fill.cc
index 389abe3b2aa..7a47a3524b2 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_attribute_fill.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_fill.cc
@@ -22,10 +22,10 @@
static bNodeSocketTemplate geo_node_attribute_fill_in[] = {
{SOCK_GEOMETRY, N_("Geometry")},
{SOCK_STRING, N_("Attribute")},
- {SOCK_VECTOR, N_("Value"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX},
- {SOCK_FLOAT, N_("Value"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX},
- {SOCK_RGBA, N_("Value"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX},
- {SOCK_BOOLEAN, N_("Value"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX},
+ {SOCK_VECTOR, N_("Value"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX, PROP_NONE, SOCK_FIELD},
+ {SOCK_FLOAT, N_("Value"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX, PROP_NONE, SOCK_FIELD},
+ {SOCK_RGBA, N_("Value"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX, PROP_NONE, SOCK_FIELD},
+ {SOCK_BOOLEAN, N_("Value"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX, PROP_NONE, SOCK_FIELD},
{SOCK_INT, N_("Value"), 0, 0, 0, 0, -10000000.0f, 10000000.0f},
{-1, ""},
};
@@ -78,6 +78,26 @@ static AttributeDomain get_result_domain(const GeometryComponent &component, con
return ATTR_DOMAIN_POINT;
}
+template<typename T>
+void fill_attribute_impl(GeometryComponent &component,
+ OutputAttribute &attribute,
+ const GeoNodeExecParams &params,
+ const StringRef input_name)
+{
+ const AttributeDomain domain = attribute.domain();
+ const int domain_size = attribute->size();
+ bke::FieldRef<T> value_field = params.get_input_field<T>(input_name);
+ bke::FieldInputs field_inputs = value_field->prepare_inputs();
+ Vector<std::unique_ptr<bke::FieldInputValue>> input_values;
+ prepare_field_inputs(field_inputs, component, domain, input_values);
+ bke::FieldOutput field_output = value_field->evaluate(IndexMask(domain_size), field_inputs);
+ for (const int i : IndexRange(domain_size)) {
+ T value;
+ field_output.varray_ref().get(i, &value);
+ attribute->set_by_copy(i, &value);
+ }
+}
+
static void fill_attribute(GeometryComponent &component, const GeoNodeExecParams &params)
{
const std::string attribute_name = params.get_input<std::string>("Attribute");
@@ -100,28 +120,23 @@ static void fill_attribute(GeometryComponent &component, const GeoNodeExecParams
switch (data_type) {
case CD_PROP_FLOAT: {
- const float value = params.get_input<float>("Value_001");
- attribute->fill(&value);
+ fill_attribute_impl<float>(component, attribute, params, "Value_001");
break;
}
case CD_PROP_FLOAT3: {
- const float3 value = params.get_input<float3>("Value");
- attribute->fill(&value);
+ fill_attribute_impl<float3>(component, attribute, params, "Value");
break;
}
case CD_PROP_COLOR: {
- const ColorGeometry4f value = params.get_input<ColorGeometry4f>("Value_002");
- attribute->fill(&value);
+ fill_attribute_impl<ColorGeometry4f>(component, attribute, params, "Value_002");
break;
}
case CD_PROP_BOOL: {
- const bool value = params.get_input<bool>("Value_003");
- attribute->fill(&value);
+ fill_attribute_impl<bool>(component, attribute, params, "Value_003");
break;
}
case CD_PROP_INT32: {
- const int value = params.get_input<int>("Value_004");
- attribute->fill(&value);
+ fill_attribute_impl<int>(component, attribute, params, "Value_004");
break;
}
default:
@@ -156,7 +171,8 @@ void register_node_type_geo_attribute_fill()
{
static bNodeType ntype;
- geo_node_type_base(&ntype, GEO_NODE_ATTRIBUTE_FILL, "Attribute Fill", NODE_CLASS_ATTRIBUTE, 0);
+ geo_node_type_base(
+ &ntype, GEO_NODE_ATTRIBUTE_FILL, "Store Persistent Attribute", NODE_CLASS_ATTRIBUTE, 0);
node_type_socket_templates(&ntype, geo_node_attribute_fill_in, geo_node_attribute_fill_out);
node_type_init(&ntype, geo_node_attribute_fill_init);
node_type_update(&ntype, geo_node_attribute_fill_update);
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_freeze.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_freeze.cc
new file mode 100644
index 00000000000..8404f884373
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_freeze.cc
@@ -0,0 +1,204 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "BKE_attribute_math.hh"
+
+#include "node_geometry_util.hh"
+
+static bNodeSocketTemplate geo_node_attribute_freeze_in[] = {
+ {SOCK_GEOMETRY, N_("Geometry")},
+ {SOCK_VECTOR, N_("Value"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX, PROP_NONE, SOCK_FIELD},
+ {SOCK_FLOAT, N_("Value"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX, PROP_NONE, SOCK_FIELD},
+ {SOCK_RGBA, N_("Value"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX, PROP_NONE, SOCK_FIELD},
+ {SOCK_BOOLEAN, N_("Value"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX, PROP_NONE, SOCK_FIELD},
+ {SOCK_INT, N_("Value"), 0, 0, 0, 0, -10000000.0f, 10000000.0f, PROP_NONE, SOCK_FIELD},
+ {-1, ""},
+};
+
+static bNodeSocketTemplate geo_node_attribute_freeze_out[] = {
+ {SOCK_GEOMETRY, N_("Geometry")},
+ {SOCK_VECTOR, N_("Attribute"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX},
+ {SOCK_FLOAT, N_("Attribute"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX},
+ {SOCK_RGBA, N_("Attribute"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX},
+ {SOCK_BOOLEAN, N_("Attribute"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX},
+ {SOCK_INT, N_("Attribute"), 0, 0, 0, 0, -10000000.0f, 10000000.0f},
+ {-1, ""},
+};
+
+static void geo_node_attribute_freeze_layout(uiLayout *layout,
+ bContext *UNUSED(C),
+ PointerRNA *ptr)
+{
+ uiLayoutSetPropSep(layout, true);
+ uiLayoutSetPropDecorate(layout, false);
+ uiItemR(layout, ptr, "domain", 0, "", ICON_NONE);
+ uiItemR(layout, ptr, "data_type", 0, "", ICON_NONE);
+}
+
+static void geo_node_attribute_freeze_init(bNodeTree *UNUSED(tree), bNode *node)
+{
+ NodeGeometryAttributeFreeze *data = (NodeGeometryAttributeFreeze *)MEM_callocN(
+ sizeof(NodeGeometryAttributeFreeze), __func__);
+ data->data_type = CD_PROP_FLOAT;
+ data->domain = ATTR_DOMAIN_POINT;
+
+ node->storage = data;
+}
+
+static void geo_node_attribute_freeze_update(bNodeTree *UNUSED(ntree), bNode *node)
+{
+ const NodeGeometryAttributeFreeze &storage = *(const NodeGeometryAttributeFreeze *)node->storage;
+ const CustomDataType data_type = static_cast<CustomDataType>(storage.data_type);
+
+ bNodeSocket *socket_value_attribute_name = (bNodeSocket *)node->inputs.first;
+ bNodeSocket *socket_value_vector = socket_value_attribute_name->next;
+ bNodeSocket *socket_value_float = socket_value_vector->next;
+ bNodeSocket *socket_value_color4f = socket_value_float->next;
+ bNodeSocket *socket_value_boolean = socket_value_color4f->next;
+ bNodeSocket *socket_value_int32 = socket_value_boolean->next;
+
+ nodeSetSocketAvailability(socket_value_vector, data_type == CD_PROP_FLOAT3);
+ nodeSetSocketAvailability(socket_value_float, data_type == CD_PROP_FLOAT);
+ nodeSetSocketAvailability(socket_value_color4f, data_type == CD_PROP_COLOR);
+ nodeSetSocketAvailability(socket_value_boolean, data_type == CD_PROP_BOOL);
+ nodeSetSocketAvailability(socket_value_int32, data_type == CD_PROP_INT32);
+
+ bNodeSocket *out_socket_value_attribute_name = (bNodeSocket *)node->outputs.first;
+ bNodeSocket *out_socket_value_vector = out_socket_value_attribute_name->next;
+ bNodeSocket *out_socket_value_float = out_socket_value_vector->next;
+ bNodeSocket *out_socket_value_color4f = out_socket_value_float->next;
+ bNodeSocket *out_socket_value_boolean = out_socket_value_color4f->next;
+ bNodeSocket *out_socket_value_int32 = out_socket_value_boolean->next;
+
+ nodeSetSocketAvailability(out_socket_value_vector, data_type == CD_PROP_FLOAT3);
+ nodeSetSocketAvailability(out_socket_value_float, data_type == CD_PROP_FLOAT);
+ nodeSetSocketAvailability(out_socket_value_color4f, data_type == CD_PROP_COLOR);
+ nodeSetSocketAvailability(out_socket_value_boolean, data_type == CD_PROP_BOOL);
+ nodeSetSocketAvailability(out_socket_value_int32, data_type == CD_PROP_INT32);
+}
+
+namespace blender::nodes {
+
+template<typename T>
+void set_output_field(GeoNodeExecParams &params,
+ AnonymousCustomDataLayerID &layer_id,
+ const StringRef output_name)
+{
+ params.set_output(
+ output_name,
+ bke::FieldRef<T>(new bke::AnonymousAttributeField(layer_id, CPPType::get<T>())));
+}
+
+static void set_output_field(GeoNodeExecParams &params,
+ AnonymousCustomDataLayerID &layer_id,
+ const CustomDataType data_type)
+{
+ switch (data_type) {
+ case CD_PROP_FLOAT: {
+ set_output_field<float>(params, layer_id, "Attribute_001");
+ break;
+ }
+ case CD_PROP_FLOAT3: {
+ set_output_field<float3>(params, layer_id, "Attribute");
+ break;
+ }
+ case CD_PROP_COLOR: {
+ set_output_field<ColorGeometry4f>(params, layer_id, "Attribute_002");
+ break;
+ }
+ case CD_PROP_BOOL: {
+ set_output_field<bool>(params, layer_id, "Attribute_003");
+ break;
+ }
+ case CD_PROP_INT32: {
+ set_output_field<int>(params, layer_id, "Attribute_004");
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+static void geo_node_attribute_freeze_exec(GeoNodeExecParams params)
+{
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
+
+ geometry_set = bke::geometry_set_realize_instances(geometry_set);
+
+ const bNode &node = params.node();
+ const NodeGeometryAttributeFreeze &storage = *(const NodeGeometryAttributeFreeze *)node.storage;
+ const CustomDataType data_type = static_cast<CustomDataType>(storage.data_type);
+ const AttributeDomain domain = static_cast<AttributeDomain>(storage.domain);
+
+ AnonymousCustomDataLayerID *id = CustomData_anonymous_id_new("Attribute Freeze");
+
+ FieldPtr field;
+ switch (data_type) {
+ case CD_PROP_FLOAT:
+ field = params.get_input_field<float>("Value_001").field();
+ break;
+ case CD_PROP_FLOAT3:
+ field = params.get_input_field<float3>("Value").field();
+ break;
+ case CD_PROP_COLOR:
+ field = params.get_input_field<ColorGeometry4f>("Value_002").field();
+ break;
+ case CD_PROP_BOOL:
+ field = params.get_input_field<bool>("Value_003").field();
+ break;
+ case CD_PROP_INT32:
+ field = params.get_input_field<int>("Value_004").field();
+ break;
+ default:
+ break;
+ }
+
+ static const Array<GeometryComponentType> types = {
+ GEO_COMPONENT_TYPE_MESH, GEO_COMPONENT_TYPE_POINT_CLOUD, GEO_COMPONENT_TYPE_CURVE};
+ for (const GeometryComponentType type : types) {
+ if (geometry_set.has(type)) {
+ GeometryComponent &component = geometry_set.get_component_for_write(type);
+ try_freeze_field_on_geometry(component, *id, domain, *field);
+ }
+ }
+
+ set_output_field(params, *id, data_type);
+
+ params.set_output("Geometry", geometry_set);
+}
+
+} // namespace blender::nodes
+
+void register_node_type_geo_attribute_freeze()
+{
+ static bNodeType ntype;
+
+ geo_node_type_base(
+ &ntype, GEO_NODE_ATTRIBUTE_FREEZE, "Attribute Freeze", NODE_CLASS_ATTRIBUTE, 0);
+ node_type_socket_templates(&ntype, geo_node_attribute_freeze_in, geo_node_attribute_freeze_out);
+ node_type_storage(&ntype,
+ "NodeGeometryAttributeFreeze",
+ node_free_standard_storage,
+ node_copy_standard_storage);
+ node_type_init(&ntype, geo_node_attribute_freeze_init);
+ node_type_update(&ntype, geo_node_attribute_freeze_update);
+ ntype.geometry_node_execute = blender::nodes::geo_node_attribute_freeze_exec;
+ ntype.draw_buttons = geo_node_attribute_freeze_layout;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_proximity.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_proximity.cc
index d71cb09f1bd..cb253317eea 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_attribute_proximity.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_proximity.cc
@@ -31,13 +31,13 @@
static bNodeSocketTemplate geo_node_attribute_proximity_in[] = {
{SOCK_GEOMETRY, N_("Geometry")},
{SOCK_GEOMETRY, N_("Target")},
- {SOCK_STRING, N_("Distance")},
- {SOCK_STRING, N_("Position")},
{-1, ""},
};
static bNodeSocketTemplate geo_node_attribute_proximity_out[] = {
{SOCK_GEOMETRY, N_("Geometry")},
+ {SOCK_FLOAT, N_("Distance")},
+ {SOCK_VECTOR, N_("Position")},
{-1, ""},
};
@@ -66,9 +66,7 @@ static void proximity_calc(MutableSpan<float> distance_span,
BVHTreeFromMesh &tree_data_mesh,
BVHTreeFromPointCloud &tree_data_pointcloud,
const bool bvh_mesh_success,
- const bool bvh_pointcloud_success,
- const bool store_distances,
- const bool store_locations)
+ const bool bvh_pointcloud_success)
{
IndexRange range = positions.index_range();
threading::parallel_for(range, 512, [&](IndexRange range) {
@@ -107,18 +105,18 @@ static void proximity_calc(MutableSpan<float> distance_span,
}
if (nearest_from_pointcloud.dist_sq < nearest_from_mesh.dist_sq) {
- if (store_distances) {
+ if (!distance_span.is_empty()) {
distance_span[i] = sqrtf(nearest_from_pointcloud.dist_sq);
}
- if (store_locations) {
+ if (!location_span.is_empty()) {
location_span[i] = nearest_from_pointcloud.co;
}
}
else {
- if (store_distances) {
+ if (!distance_span.is_empty()) {
distance_span[i] = sqrtf(nearest_from_mesh.dist_sq);
}
- if (store_locations) {
+ if (!location_span.is_empty()) {
location_span[i] = nearest_from_mesh.co;
}
}
@@ -161,25 +159,24 @@ static bool bvh_from_pointcloud(const PointCloud *target_pointcloud,
}
static void attribute_calc_proximity(GeometryComponent &component,
- GeometrySet &geometry_set_target,
- GeoNodeExecParams &params)
+ const GeometrySet &geometry_set_target,
+ const AnonymousCustomDataLayerID *distance_id,
+ const AnonymousCustomDataLayerID *location_id,
+ const GeoNodeExecParams &params)
{
- /* This node works on the "point" domain, since that is where positions are stored. */
- const AttributeDomain result_domain = ATTR_DOMAIN_POINT;
-
- const std::string distance_attribute_name = params.get_input<std::string>("Distance");
- OutputAttribute_Typed<float> distance_attribute =
- component.attribute_try_get_for_output_only<float>(distance_attribute_name, result_domain);
-
- const std::string location_attribute_name = params.get_input<std::string>("Position");
- OutputAttribute_Typed<float3> location_attribute =
- component.attribute_try_get_for_output_only<float3>(location_attribute_name, result_domain);
-
- ReadAttributeLookup position_attribute = component.attribute_try_get_for_read("position");
- if (!position_attribute || (!distance_attribute && !location_attribute)) {
- return;
+ GVArray_Typed<float3> positions = component.attribute_get_for_read<float3>(
+ "position", ATTR_DOMAIN_POINT, {0, 0, 0});
+
+ std::optional<OutputAttribute_Typed<float3>> location_attribute;
+ std::optional<OutputAttribute_Typed<float>> distance_attribute;
+ if (location_id != nullptr) {
+ location_attribute.emplace(component.attribute_try_get_anonymous_for_output_only<float3>(
+ *location_id, ATTR_DOMAIN_POINT));
+ }
+ if (distance_id != nullptr) {
+ distance_attribute.emplace(component.attribute_try_get_anonymous_for_output_only<float>(
+ *distance_id, ATTR_DOMAIN_POINT));
}
- BLI_assert(position_attribute.varray->type().is<float3>());
const bNode &node = params.node();
const NodeGeometryAttributeProximity &storage = *(const NodeGeometryAttributeProximity *)
@@ -202,21 +199,13 @@ static void attribute_calc_proximity(GeometryComponent &component,
tree_data_pointcloud);
}
- GVArray_Typed<float3> positions{*position_attribute.varray};
- MutableSpan<float> distance_span = distance_attribute ? distance_attribute.as_span() :
- MutableSpan<float>();
- MutableSpan<float3> location_span = location_attribute ? location_attribute.as_span() :
- MutableSpan<float3>();
-
- proximity_calc(distance_span,
- location_span,
+ proximity_calc(distance_attribute ? distance_attribute->as_span() : MutableSpan<float>(),
+ location_attribute ? location_attribute->as_span() : MutableSpan<float3>(),
positions,
tree_data_mesh,
tree_data_pointcloud,
bvh_mesh_success,
- bvh_pointcloud_success,
- distance_attribute, /* Boolean. */
- location_attribute); /* Boolean. */
+ bvh_pointcloud_success);
if (bvh_mesh_success) {
free_bvhtree_from_mesh(&tree_data_mesh);
@@ -225,11 +214,14 @@ static void attribute_calc_proximity(GeometryComponent &component,
free_bvhtree_from_pointcloud(&tree_data_pointcloud);
}
- if (distance_attribute) {
- distance_attribute.save();
- }
if (location_attribute) {
- location_attribute.save();
+ location_attribute->save();
+ }
+ if (distance_attribute) {
+ for (const int i : IndexRange(distance_attribute->as_span().size())) {
+ std::cout << distance_attribute->as_span()[i] << "\n";
+ }
+ distance_attribute->save();
}
}
@@ -244,20 +236,41 @@ static void geo_node_attribute_proximity_exec(GeoNodeExecParams params)
* for the target geometry set. However, the generic BVH API complicates this. */
geometry_set_target = geometry_set_realize_instances(geometry_set_target);
- if (geometry_set.has<MeshComponent>()) {
- attribute_calc_proximity(
- geometry_set.get_component_for_write<MeshComponent>(), geometry_set_target, params);
+ AnonymousCustomDataLayerID *distance = params.output_is_required("Distance") ?
+ CustomData_anonymous_id_new("Distance") :
+ nullptr;
+ AnonymousCustomDataLayerID *location = params.output_is_required("Position") ?
+ CustomData_anonymous_id_new("Position") :
+ nullptr;
+ if (!distance && !location) {
+ params.set_output("Geometry", std::move(geometry_set));
+ return;
+ }
+
+ static const Array<GeometryComponentType> types = {
+ GEO_COMPONENT_TYPE_MESH, GEO_COMPONENT_TYPE_POINT_CLOUD, GEO_COMPONENT_TYPE_CURVE};
+ for (const GeometryComponentType type : types) {
+ if (geometry_set.has(type)) {
+ attribute_calc_proximity(geometry_set.get_component_for_write(type),
+ geometry_set_target,
+ distance,
+ location,
+ params);
+ }
}
- if (geometry_set.has<PointCloudComponent>()) {
- attribute_calc_proximity(
- geometry_set.get_component_for_write<PointCloudComponent>(), geometry_set_target, params);
+
+ if (distance != nullptr) {
+ params.set_output(
+ "Distance",
+ bke::FieldRef<float>(new bke::AnonymousAttributeField(*distance, CPPType::get<float>())));
}
- if (geometry_set.has<CurveComponent>()) {
- attribute_calc_proximity(
- geometry_set.get_component_for_write<CurveComponent>(), geometry_set_target, params);
+ if (location != nullptr) {
+ params.set_output("Position",
+ bke::FieldRef<float3>(
+ new bke::AnonymousAttributeField(*location, CPPType::get<float3>())));
}
- params.set_output("Geometry", geometry_set);
+ params.set_output("Geometry", std::move(geometry_set));
}
} // namespace blender::nodes
@@ -266,8 +279,7 @@ void register_node_type_geo_attribute_proximity()
{
static bNodeType ntype;
- geo_node_type_base(
- &ntype, GEO_NODE_ATTRIBUTE_PROXIMITY, "Attribute Proximity", NODE_CLASS_ATTRIBUTE, 0);
+ geo_node_type_base(&ntype, GEO_NODE_ATTRIBUTE_PROXIMITY, "Proximity", NODE_CLASS_GEOMETRY, 0);
node_type_socket_templates(
&ntype, geo_node_attribute_proximity_in, geo_node_attribute_proximity_out);
node_type_init(&ntype, geo_attribute_proximity_init);
diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_length.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_length.cc
index 306085e3b75..56f2934de85 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_curve_length.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_length.cc
@@ -42,7 +42,7 @@ static void geo_node_curve_length_exec(GeoNodeExecParams params)
for (const SplinePtr &spline : curve.splines()) {
length += spline->length();
}
- params.set_output("Length", length);
+ params.set_output("Length", bke::FieldRef<float>(new bke::ConstantField(length)));
}
} // namespace blender::nodes
diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_parameter.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_parameter.cc
new file mode 100644
index 00000000000..d105a9ff886
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_parameter.cc
@@ -0,0 +1,42 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "node_geometry_util.hh"
+
+static bNodeSocketTemplate geo_node_curve_parameter_out[] = {
+ {SOCK_FLOAT, N_("Parameter")},
+ {-1, ""},
+};
+
+namespace blender::nodes {
+
+static void geo_node_curve_parameter_exec(GeoNodeExecParams params)
+{
+ FieldPtr curve_parameter_field = new bke::CurveParameterField();
+ params.set_output("Parameter", bke::FieldRef<float>(std::move(curve_parameter_field)));
+}
+
+} // namespace blender::nodes
+
+void register_node_type_geo_curve_parameter()
+{
+ static bNodeType ntype;
+
+ geo_node_type_base(&ntype, GEO_NODE_CURVE_PARAMETER, "Curve Parameter", NODE_CLASS_INPUT, 0);
+ node_type_socket_templates(&ntype, nullptr, geo_node_curve_parameter_out);
+ ntype.geometry_node_execute = blender::nodes::geo_node_curve_parameter_exec;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_select_by_handle_type.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_select_by_handle_type.cc
index fb21c05b0c0..84ee745869c 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_curve_select_by_handle_type.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_select_by_handle_type.cc
@@ -25,12 +25,12 @@
static bNodeSocketTemplate geo_node_select_by_handle_type_in[] = {
{SOCK_GEOMETRY, N_("Geometry")},
- {SOCK_STRING, N_("Selection")},
{-1, ""},
};
static bNodeSocketTemplate geo_node_select_by_handle_type_out[] = {
{SOCK_GEOMETRY, N_("Geometry")},
+ {SOCK_BOOLEAN, N_("Selection")},
{-1, ""},
};
@@ -112,10 +112,11 @@ static void geo_node_select_by_handle_type_exec(GeoNodeExecParams params)
CurveComponent &curve_component = geometry_set.get_component_for_write<CurveComponent>();
const CurveEval *curve = curve_component.get_for_read();
+ AnonymousCustomDataLayerID *id = CustomData_anonymous_id_new("Selection");
+
if (curve != nullptr) {
- const std::string selection_name = params.extract_input<std::string>("Selection");
OutputAttribute_Typed<bool> selection =
- curve_component.attribute_try_get_for_output_only<bool>(selection_name, ATTR_DOMAIN_POINT);
+ curve_component.attribute_try_get_anonymous_for_output_only<bool>(*id, ATTR_DOMAIN_POINT);
if (selection) {
select_curve_by_handle_type(*curve, handle_type, mode, selection.as_span());
selection.save();
@@ -123,6 +124,9 @@ static void geo_node_select_by_handle_type_exec(GeoNodeExecParams params)
}
params.set_output("Geometry", std::move(geometry_set));
+ params.set_output(
+ "Selection",
+ bke::FieldRef<bool>(new bke::AnonymousAttributeField(*id, CPPType::get<bool>())));
}
} // namespace blender::nodes
diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_set_handles.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_set_handles.cc
index 72bd8ab188d..7583b9ec82d 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_curve_set_handles.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_set_handles.cc
@@ -23,7 +23,7 @@
static bNodeSocketTemplate geo_node_curve_set_handles_in[] = {
{SOCK_GEOMETRY, N_("Curve")},
- {SOCK_STRING, N_("Selection")},
+ {SOCK_BOOLEAN, N_("Selection"), 1, 0, 0, 0, 0, 0, PROP_NONE, SOCK_HIDE_VALUE | SOCK_FIELD},
{-1, ""},
};
@@ -87,9 +87,14 @@ static void geo_node_curve_set_handles_exec(GeoNodeExecParams params)
CurveEval &curve = *curve_component.get_for_write();
MutableSpan<SplinePtr> splines = curve.splines();
- const std::string selection_name = params.extract_input<std::string>("Selection");
- GVArray_Typed<bool> selection = curve_component.attribute_get_for_read(
- selection_name, ATTR_DOMAIN_POINT, true);
+ bke::FieldRef<bool> field = params.get_input_field<bool>("Selection");
+ bke::FieldInputs field_inputs = field->prepare_inputs();
+ Vector<std::unique_ptr<bke::FieldInputValue>> field_input_values;
+ prepare_field_inputs(field_inputs, curve_component, ATTR_DOMAIN_POINT, field_input_values);
+ bke::FieldOutput field_output = field->evaluate(
+ IndexRange(curve_component.attribute_domain_size(ATTR_DOMAIN_POINT)), field_inputs);
+
+ GVArray_Typed<bool> selection{field_output.varray_ref()};
const BezierSpline::HandleType new_handle_type = handle_type_from_input_type(type);
int point_index = 0;
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 fe3f42625ae..e4ae5fc21e6 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
@@ -25,7 +25,7 @@
static bNodeSocketTemplate geo_node_curve_spline_type_in[] = {
{SOCK_GEOMETRY, N_("Curve")},
- {SOCK_STRING, N_("Selection")},
+ {SOCK_BOOLEAN, N_("Selection"), 1, 0, 0, 0, 0, 0, PROP_NONE, SOCK_HIDE_VALUE | SOCK_FIELD},
{-1, ""},
};
@@ -255,12 +255,17 @@ static void geo_node_curve_spline_type_exec(GeoNodeExecParams params)
return;
}
- const CurveComponent *curve_component = geometry_set.get_component_for_read<CurveComponent>();
- const CurveEval &curve = *curve_component->get_for_read();
+ const CurveComponent &curve_component = *geometry_set.get_component_for_read<CurveComponent>();
+ const CurveEval &curve = *curve_component.get_for_read();
- const std::string selection_name = params.extract_input<std::string>("Selection");
- GVArray_Typed<bool> selection = curve_component->attribute_get_for_read(
- selection_name, ATTR_DOMAIN_CURVE, true);
+ bke::FieldRef<bool> field = params.get_input_field<bool>("Selection");
+ bke::FieldInputs field_inputs = field->prepare_inputs();
+ Vector<std::unique_ptr<bke::FieldInputValue>> field_input_values;
+ prepare_field_inputs(field_inputs, curve_component, ATTR_DOMAIN_CURVE, field_input_values);
+ bke::FieldOutput field_output = field->evaluate(
+ IndexRange(curve_component.attribute_domain_size(ATTR_DOMAIN_CURVE)), field_inputs);
+
+ GVArray_Typed<bool> selection{field_output.varray_ref()};
std::unique_ptr<CurveEval> new_curve = std::make_unique<CurveEval>();
for (const int i : curve.splines().index_range()) {
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 7908c26e2dc..4fc3d66b0d4 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc
@@ -31,8 +31,7 @@ using blender::fn::GVArray_Typed;
static bNodeSocketTemplate geo_node_curve_subdivide_in[] = {
{SOCK_GEOMETRY, N_("Geometry")},
- {SOCK_STRING, N_("Cuts")},
- {SOCK_INT, N_("Cuts"), 1, 0, 0, 0, 0, 1000},
+ {SOCK_INT, N_("Cuts"), 1, 0, 0, 0, 0, 1000, PROP_NONE, SOCK_FIELD},
{-1, ""},
};
@@ -41,32 +40,8 @@ static bNodeSocketTemplate geo_node_curve_subdivide_out[] = {
{-1, ""},
};
-static void geo_node_curve_subdivide_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
-{
- uiLayoutSetPropSep(layout, true);
- uiLayoutSetPropDecorate(layout, false);
- uiItemR(layout, ptr, "cuts_type", 0, IFACE_("Cuts"), ICON_NONE);
-}
-
-static void geo_node_curve_subdivide_init(bNodeTree *UNUSED(tree), bNode *node)
-{
- NodeGeometryCurveSubdivide *data = (NodeGeometryCurveSubdivide *)MEM_callocN(
- sizeof(NodeGeometryCurveSubdivide), __func__);
-
- data->cuts_type = GEO_NODE_ATTRIBUTE_INPUT_INTEGER;
- node->storage = data;
-}
-
namespace blender::nodes {
-static void geo_node_curve_subdivide_update(bNodeTree *UNUSED(ntree), bNode *node)
-{
- NodeGeometryPointTranslate &node_storage = *(NodeGeometryPointTranslate *)node->storage;
-
- update_attribute_input_socket_availabilities(
- *node, "Cuts", (GeometryNodeAttributeInputMode)node_storage.input_type);
-}
-
static Array<int> get_subdivided_offsets(const Spline &spline,
const VArray<int> &cuts,
const int spline_offset)
@@ -363,10 +338,18 @@ static void geo_node_subdivide_exec(GeoNodeExecParams params)
}
const CurveComponent &component = *geometry_set.get_component_for_read<CurveComponent>();
- GVArray_Typed<int> cuts = params.get_input_attribute<int>(
- "Cuts", component, ATTR_DOMAIN_POINT, 0);
+
+ bke::FieldRef<int> field = params.get_input_field<int>("Cuts");
+ bke::FieldInputs field_inputs = field->prepare_inputs();
+ Vector<std::unique_ptr<bke::FieldInputValue>> field_input_values;
+ prepare_field_inputs(field_inputs, component, ATTR_DOMAIN_POINT, field_input_values);
+ bke::FieldOutput field_output = field->evaluate(
+ IndexRange(component.attribute_domain_size(ATTR_DOMAIN_POINT)), field_inputs);
+
+ GVArray_Typed<int> cuts{field_output.varray_ref()};
+
if (cuts->is_single() && cuts->get_internal_single() < 1) {
- params.set_output("Geometry", geometry_set);
+ params.set_output("Geometry", std::move(geometry_set));
return;
}
@@ -383,13 +366,6 @@ void register_node_type_geo_curve_subdivide()
geo_node_type_base(&ntype, GEO_NODE_CURVE_SUBDIVIDE, "Curve Subdivide", NODE_CLASS_GEOMETRY, 0);
node_type_socket_templates(&ntype, geo_node_curve_subdivide_in, geo_node_curve_subdivide_out);
- ntype.draw_buttons = geo_node_curve_subdivide_layout;
- node_type_storage(&ntype,
- "NodeGeometryCurveSubdivide",
- node_free_standard_storage,
- node_copy_standard_storage);
- node_type_init(&ntype, geo_node_curve_subdivide_init);
- node_type_update(&ntype, blender::nodes::geo_node_curve_subdivide_update);
ntype.geometry_node_execute = blender::nodes::geo_node_subdivide_exec;
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 8c697275f88..299c5eff75d 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc
@@ -24,6 +24,9 @@
#include "BKE_pointcloud.h"
#include "BKE_spline.hh"
+#include "UI_interface.h"
+#include "UI_resources.h"
+
#include "node_geometry_util.hh"
using blender::bke::CustomDataAttributes;
@@ -45,8 +48,7 @@ extern void copy_masked_polys_to_new_mesh(const Mesh &src_mesh,
static bNodeSocketTemplate geo_node_delete_geometry_in[] = {
{SOCK_GEOMETRY, N_("Geometry")},
- {SOCK_STRING, N_("Selection")},
- {SOCK_BOOLEAN, N_("Invert")},
+ {SOCK_BOOLEAN, N_("Selection"), 0, 0, 0, 0, 0, 0, PROP_NONE, SOCK_HIDE_VALUE | SOCK_FIELD},
{-1, ""},
};
@@ -55,6 +57,18 @@ static bNodeSocketTemplate geo_node_delete_geometry_out[] = {
{-1, ""},
};
+static void geo_node_delete_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
+{
+ uiLayoutSetPropSep(layout, true);
+ uiLayoutSetPropDecorate(layout, false);
+ uiItemR(layout, ptr, "domain", 0, "", ICON_NONE);
+}
+
+static void geo_node_delete_init(bNodeTree *UNUSED(tree), bNode *node)
+{
+ node->custom1 = ATTR_DOMAIN_POINT;
+}
+
namespace blender::nodes {
template<typename T> static void copy_data(Span<T> data, MutableSpan<T> r_data, IndexMask mask)
@@ -101,14 +115,21 @@ static void copy_dynamic_attributes(const CustomDataAttributes &src,
std::optional<GSpan> src_attribute = src.get_for_read(name);
BLI_assert(src_attribute);
- if (!dst.create(name, 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> new_attribute;
+ if (meta_data.anonymous_layer_id) {
+ if (!dst.create_anonymous(*meta_data.anonymous_layer_id, meta_data.data_type)) {
+ BLI_assert_unreachable();
+ }
+ new_attribute = dst.get_anonymous_for_write(*meta_data.anonymous_layer_id);
+ }
+ else {
+ if (!dst.create(name, meta_data.data_type)) {
+ BLI_assert_unreachable();
+ }
+ new_attribute = dst.get_for_write(name);
}
- std::optional<GMutableSpan> new_attribute = dst.get_for_write(name);
BLI_assert(new_attribute);
-
attribute_math::convert_to_static_type(new_attribute->type(), [&](auto dummy) {
using T = decltype(dummy);
copy_data(src_attribute->typed<T>(), new_attribute->typed<T>(), mask);
@@ -130,7 +151,8 @@ static SplinePtr spline_delete(const Spline &spline, const IndexMask mask)
}
static std::unique_ptr<CurveEval> curve_delete(const CurveEval &input_curve,
- const StringRef name,
+ const AttributeDomain domain,
+ const Span<bool> selection,
const bool invert)
{
Span<SplinePtr> input_splines = input_curve.splines();
@@ -139,8 +161,7 @@ static std::unique_ptr<CurveEval> curve_delete(const CurveEval &input_curve,
/* Keep track of which splines were copied to the result to copy spline domain attributes. */
Vector<int64_t> copied_splines;
- if (input_curve.attributes.get_for_read(name)) {
- GVArray_Typed<bool> selection = input_curve.attributes.get_for_read<bool>(name, false);
+ if (domain == ATTR_DOMAIN_CURVE) {
for (const int i : input_splines.index_range()) {
if (selection[i] == invert) {
output_curve->add_spline(input_splines[i]->copy());
@@ -148,17 +169,18 @@ static std::unique_ptr<CurveEval> curve_delete(const CurveEval &input_curve,
}
}
}
- else {
+ else if (domain == ATTR_DOMAIN_POINT) {
/* Reuse index vector for each spline. */
Vector<int64_t> indices_to_copy;
+ Array<int> offsets = input_curve.control_point_offsets();
+
for (const int i : input_splines.index_range()) {
const Spline &spline = *input_splines[i];
- GVArray_Typed<bool> selection = spline.attributes.get_for_read<bool>(name, false);
indices_to_copy.clear();
for (const int i_point : IndexRange(spline.size())) {
- if (selection[i_point] == invert) {
+ if (selection[offsets[i] + i_point] == invert) {
indices_to_copy.append(i_point);
}
}
@@ -187,11 +209,24 @@ static std::unique_ptr<CurveEval> curve_delete(const CurveEval &input_curve,
static void delete_curve_selection(const CurveComponent &in_component,
CurveComponent &r_component,
- const StringRef selection_name,
+ const GeoNodeExecParams &params,
const bool invert)
{
+ const AttributeDomain selection_domain = static_cast<AttributeDomain>(params.node().custom1);
+
+ bke::FieldRef<bool> field = params.get_input_field<bool>("Selection");
+ bke::FieldInputs field_inputs = field->prepare_inputs();
+ Vector<std::unique_ptr<bke::FieldInputValue>> field_input_values;
+ prepare_field_inputs(field_inputs, in_component, selection_domain, field_input_values);
+ bke::FieldOutput field_output = field->evaluate(
+ IndexRange(in_component.attribute_domain_size(selection_domain)), field_inputs);
+
+ GVArray_Typed<bool> selection_gvarray{field_output.varray_ref()};
+ VArray_Span<bool> selection{selection_gvarray};
+
std::unique_ptr<CurveEval> r_curve = curve_delete(
- *in_component.get_for_read(), selection_name, invert);
+ *in_component.get_for_read(), selection_domain, selection, invert);
+
if (r_curve) {
r_component.replace(r_curve.release());
}
@@ -202,12 +237,20 @@ static void delete_curve_selection(const CurveComponent &in_component,
static void delete_point_cloud_selection(const PointCloudComponent &in_component,
PointCloudComponent &out_component,
- const StringRef selection_name,
+ const GeoNodeExecParams &params,
const bool invert)
{
- const GVArray_Typed<bool> selection_attribute = in_component.attribute_get_for_read<bool>(
- selection_name, ATTR_DOMAIN_POINT, false);
- VArray_Span<bool> selection{selection_attribute};
+ const AttributeDomain selection_domain = static_cast<AttributeDomain>(params.node().custom1);
+
+ bke::FieldRef<bool> field = params.get_input_field<bool>("Selection");
+ bke::FieldInputs field_inputs = field->prepare_inputs();
+ Vector<std::unique_ptr<bke::FieldInputValue>> field_input_values;
+ prepare_field_inputs(field_inputs, in_component, selection_domain, field_input_values);
+ bke::FieldOutput field_output = field->evaluate(
+ IndexRange(in_component.attribute_domain_size(selection_domain)), field_inputs);
+
+ GVArray_Typed<bool> selection_gvarray{field_output.varray_ref()};
+ VArray_Span<bool> selection{selection_gvarray};
const int total = selection.count(invert);
if (total == 0) {
@@ -567,34 +610,21 @@ static Mesh *delete_mesh_selection(const Mesh &mesh_in,
return result;
}
-static AttributeDomain get_mesh_selection_domain(MeshComponent &component, const StringRef name)
-{
- std::optional<AttributeMetaData> selection_attribute = component.attribute_get_meta_data(name);
- if (!selection_attribute) {
- /* The node will not do anything in this case, but this function must return something. */
- return ATTR_DOMAIN_POINT;
- }
-
- /* Corners can't be deleted separately, so interpolate corner attributes
- * to the face domain. Note that this choice is somewhat arbitrary. */
- if (selection_attribute->domain == ATTR_DOMAIN_CORNER) {
- return ATTR_DOMAIN_FACE;
- }
-
- return selection_attribute->domain;
-}
-
static void delete_mesh_selection(MeshComponent &component,
const Mesh &mesh_in,
- const StringRef selection_name,
+ const GeoNodeExecParams &params,
const bool invert)
{
- /* Figure out the best domain to use. */
- const AttributeDomain selection_domain = get_mesh_selection_domain(component, selection_name);
+ const AttributeDomain selection_domain = static_cast<AttributeDomain>(params.node().custom1);
- /* This already checks if the attribute exists, and displays a warning in that case. */
- GVArray_Typed<bool> selection = component.attribute_get_for_read<bool>(
- selection_name, selection_domain, false);
+ bke::FieldRef<bool> field = params.get_input_field<bool>("Selection");
+ bke::FieldInputs field_inputs = field->prepare_inputs();
+ Vector<std::unique_ptr<bke::FieldInputValue>> field_input_values;
+ prepare_field_inputs(field_inputs, component, selection_domain, field_input_values);
+ bke::FieldOutput field_output = field->evaluate(
+ IndexRange(component.attribute_domain_size(selection_domain)), field_inputs);
+
+ GVArray_Typed<bool> selection{field_output.varray_ref()};
/* Check if there is anything to delete. */
bool delete_nothing = true;
@@ -635,30 +665,25 @@ static void geo_node_delete_geometry_exec(GeoNodeExecParams params)
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
geometry_set = bke::geometry_set_realize_instances(geometry_set);
- const bool invert = params.extract_input<bool>("Invert");
- const std::string selection_name = params.extract_input<std::string>("Selection");
- if (selection_name.empty()) {
- params.set_output("Geometry", std::move(geometry_set));
- return;
- }
+ const bool invert = false;
GeometrySet out_set(geometry_set);
if (geometry_set.has<PointCloudComponent>()) {
delete_point_cloud_selection(*geometry_set.get_component_for_read<PointCloudComponent>(),
out_set.get_component_for_write<PointCloudComponent>(),
- selection_name,
+ params,
invert);
}
if (geometry_set.has<MeshComponent>()) {
delete_mesh_selection(out_set.get_component_for_write<MeshComponent>(),
*geometry_set.get_mesh_for_read(),
- selection_name,
+ params,
invert);
}
if (geometry_set.has<CurveComponent>()) {
delete_curve_selection(*geometry_set.get_component_for_read<CurveComponent>(),
out_set.get_component_for_write<CurveComponent>(),
- selection_name,
+ params,
invert);
}
@@ -674,5 +699,7 @@ void register_node_type_geo_delete_geometry()
geo_node_type_base(&ntype, GEO_NODE_DELETE_GEOMETRY, "Delete Geometry", NODE_CLASS_GEOMETRY, 0);
node_type_socket_templates(&ntype, geo_node_delete_geometry_in, geo_node_delete_geometry_out);
ntype.geometry_node_execute = blender::nodes::geo_node_delete_geometry_exec;
+ ntype.draw_buttons = geo_node_delete_layout;
+ node_type_init(&ntype, geo_node_delete_init);
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_evaluate_curve.cc b/source/blender/nodes/geometry/nodes/node_geo_evaluate_curve.cc
new file mode 100644
index 00000000000..b8b79e2e609
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_evaluate_curve.cc
@@ -0,0 +1,178 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "BKE_attribute_math.hh"
+#include "BKE_customdata.h"
+#include "BKE_spline.hh"
+
+#include "node_geometry_util.hh"
+
+static bNodeSocketTemplate geo_node_evaluate_curve_in[] = {
+ {SOCK_GEOMETRY, N_("Curve")},
+ {SOCK_FLOAT,
+ N_("Length"),
+ 0.0f,
+ 0.0f,
+ 0.0f,
+ 0.0f,
+ -FLT_MAX,
+ FLT_MAX,
+ PROP_TRANSLATION,
+ SOCK_FIELD},
+ {SOCK_RGBA, N_("Custom"), 1, 1, 1, 1, 0, 1, PROP_NONE, SOCK_FIELD},
+ {-1, ""},
+};
+
+static bNodeSocketTemplate geo_node_evaluate_curve_out[] = {
+ {SOCK_VECTOR, N_("Position")},
+ {SOCK_VECTOR, N_("Tangent")},
+ {SOCK_VECTOR, N_("Normal")},
+ {SOCK_RGBA, N_("Custom")},
+ {-1, ""},
+};
+
+namespace blender::nodes {
+
+class EvaluateCurveFunction : public fn::MultiFunction {
+ private:
+ GeometrySet geometry_set_;
+ AnonymousCustomDataLayerID *attribute_id_;
+
+ public:
+ EvaluateCurveFunction(GeometrySet geometry_set, AnonymousCustomDataLayerID *attribute_id)
+ : geometry_set_(std::move(geometry_set)), attribute_id_(attribute_id)
+ {
+ static fn::MFSignature signature = create_signature();
+ this->set_signature(&signature);
+ CustomData_anonymous_id_strong_increment(attribute_id_);
+ }
+
+ ~EvaluateCurveFunction() override
+ {
+ CustomData_anonymous_id_strong_decrement(attribute_id_);
+ }
+
+ static fn::MFSignature create_signature()
+ {
+ blender::fn::MFSignatureBuilder signature{"Evaluate Curve"};
+ signature.single_input<float>("Length");
+ signature.single_output<float3>("Position");
+ signature.single_output<float3>("Tangent");
+ signature.single_output<float3>("Normal");
+ signature.single_output<ColorGeometry4f>("Custom");
+ return signature.build();
+ }
+
+ void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override
+ {
+ const VArray<float> &src_lengths = params.readonly_single_input<float>(0, "Length");
+ MutableSpan<float3> sampled_positions = params.uninitialized_single_output<float3>(1,
+ "Position");
+ MutableSpan<float3> sampled_tangents = params.uninitialized_single_output<float3>(2,
+ "Tangent");
+ MutableSpan<float3> sampled_normals = params.uninitialized_single_output<float3>(3, "Normal");
+ MutableSpan<ColorGeometry4f> sampled_custom =
+ params.uninitialized_single_output<ColorGeometry4f>(4, "Custom");
+
+ auto return_default = [&]() {
+ sampled_positions.fill_indices(mask, {0, 0, 0});
+ sampled_tangents.fill_indices(mask, {0, 0, 0});
+ sampled_normals.fill_indices(mask, {0, 0, 0});
+ sampled_custom.fill_indices(mask, {0, 0, 0, 1});
+ };
+
+ if (!geometry_set_.has_curve()) {
+ return return_default();
+ }
+
+ const CurveComponent *curve_component = geometry_set_.get_component_for_read<CurveComponent>();
+ const CurveEval *curve = curve_component->get_for_read();
+ if (curve->splines().is_empty()) {
+ return return_default();
+ }
+ const Spline &spline = *curve->splines()[0];
+ std::optional<GSpan> custom_generic = spline.attributes.get_anonymous_for_read(*attribute_id_);
+ if (!custom_generic) {
+ return return_default();
+ }
+
+ Span<ColorGeometry4f> custom = (*custom_generic).typed<ColorGeometry4f>();
+ GVArray_Typed<ColorGeometry4f> evaluated_custom = spline.interpolate_to_evaluated(custom);
+
+ const float spline_length = spline.length();
+ const Span<float3> evaluated_positions = spline.evaluated_positions();
+ const Span<float3> evaluated_tangents = spline.evaluated_tangents();
+ const Span<float3> evaluated_normals = spline.evaluated_normals();
+ for (const int i : mask) {
+ const float length = std::clamp(src_lengths[i], 0.0f, spline_length);
+ Spline::LookupResult lookup = spline.lookup_evaluated_length(length);
+ const int i1 = lookup.evaluated_index;
+ const int i2 = lookup.next_evaluated_index;
+ sampled_positions[i] = attribute_math::mix2(
+ lookup.factor, evaluated_positions[i1], evaluated_positions[i2]);
+ sampled_tangents[i] = attribute_math::mix2(
+ lookup.factor, evaluated_tangents[i1], evaluated_tangents[i2])
+ .normalized();
+ sampled_normals[i] = attribute_math::mix2(
+ lookup.factor, evaluated_normals[i1], evaluated_normals[i2])
+ .normalized();
+ sampled_custom[i] = attribute_math::mix2(
+ lookup.factor, evaluated_custom[i1], evaluated_custom[i2]);
+ }
+ }
+};
+
+static void geo_node_evaluate_curve_exec(GeoNodeExecParams params)
+{
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Curve");
+ geometry_set = geometry_set_realize_instances(geometry_set);
+
+ FieldPtr curve_field = params.get_input_field<float>("Length").field();
+ bke::FieldRef<ColorGeometry4f> attribute_field = params.get_input_field<ColorGeometry4f>(
+ "Custom");
+
+ AnonymousCustomDataLayerID *layer_id = CustomData_anonymous_id_new("Evaluate Curve");
+ CurveComponent &curve_component = geometry_set.get_component_for_write<CurveComponent>();
+ try_freeze_field_on_geometry(
+ curve_component, *layer_id, ATTR_DOMAIN_POINT, *attribute_field.field());
+
+ auto make_output_field = [&](int out_param_index) -> FieldPtr {
+ auto fn = std::make_unique<EvaluateCurveFunction>(geometry_set, layer_id);
+ return new bke::MultiFunctionField(Vector<FieldPtr>{curve_field},
+ optional_ptr<const fn::MultiFunction>{std::move(fn)},
+ out_param_index);
+ };
+
+ params.set_output("Position", bke::FieldRef<float3>(make_output_field(1)));
+ params.set_output("Tangent", bke::FieldRef<float3>(make_output_field(2)));
+ params.set_output("Normal", bke::FieldRef<float3>(make_output_field(3)));
+ params.set_output("Custom", bke::FieldRef<ColorGeometry4f>(make_output_field(4)));
+}
+
+} // namespace blender::nodes
+
+void register_node_type_geo_evaluate_curve()
+{
+ static bNodeType ntype;
+
+ geo_node_type_base(&ntype, GEO_NODE_EVALUATE_CURVE, "Evaluate Curve", NODE_CLASS_ATTRIBUTE, 0);
+ node_type_socket_templates(&ntype, geo_node_evaluate_curve_in, geo_node_evaluate_curve_out);
+ ntype.geometry_node_execute = blender::nodes::geo_node_evaluate_curve_exec;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_extrude.cc b/source/blender/nodes/geometry/nodes/node_geo_extrude.cc
new file mode 100644
index 00000000000..f8047627ff1
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_extrude.cc
@@ -0,0 +1,206 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+
+#include "BKE_mesh.h"
+#include "BKE_node.h"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "bmesh.h"
+#include "bmesh_tools.h"
+
+#include "node_geometry_util.hh"
+
+static bNodeSocketTemplate geo_node_extrude_in[] = {
+ {SOCK_GEOMETRY, N_("Geometry")},
+ {SOCK_FLOAT, N_("Distance"), 0.0f, 0, 0, 0, FLT_MIN, FLT_MAX, PROP_DISTANCE, SOCK_FIELD},
+ {SOCK_FLOAT, N_("Inset"), 0.0f, 0, 0, 0, FLT_MIN, FLT_MAX, PROP_DISTANCE, SOCK_FIELD},
+ {SOCK_BOOLEAN, N_("Individual")},
+ {SOCK_BOOLEAN, N_("Selection"), 1, 0, 0, 0, 0, 0, PROP_NONE, SOCK_HIDE_VALUE | SOCK_FIELD},
+ {-1, ""},
+};
+
+static bNodeSocketTemplate geo_node_extrude_out[] = {
+ {SOCK_GEOMETRY, N_("Geometry")},
+ {SOCK_BOOLEAN, N_("Top Faces")},
+ {SOCK_BOOLEAN, N_("Side Faces")},
+ {-1, ""},
+};
+
+namespace blender::nodes {
+
+static Mesh *extrude_mesh(const Mesh *mesh,
+ const Span<bool> selection,
+ const Span<float> distance,
+ const Span<float> inset,
+ const bool inset_individual_faces,
+ AnonymousCustomDataLayerID *top_faces_id,
+ AnonymousCustomDataLayerID *side_faces_id)
+{
+ const BMeshCreateParams bmesh_create_params = {true};
+ const BMeshFromMeshParams bmesh_from_mesh_params = {
+ true, 0, 0, 0, {CD_MASK_ORIGINDEX, CD_MASK_ORIGINDEX, CD_MASK_ORIGINDEX}};
+
+ BMesh *bm = BKE_mesh_to_bmesh_ex(mesh, &bmesh_create_params, &bmesh_from_mesh_params);
+
+ BM_select_faces(bm, selection.data());
+ BMOperator op;
+ if (inset_individual_faces) {
+ BMO_op_initf(bm,
+ &op,
+ 0,
+ "inset_individual faces=%hf use_even_offset=%b thickness=%f depth=%f "
+ "thickness_array=%p depth_array=%p use_attributes=%b",
+ BM_ELEM_SELECT,
+ true,
+ inset.first(),
+ distance.first(),
+ inset.data(),
+ distance.data(),
+ true);
+ }
+ else {
+ BMO_op_initf(bm,
+ &op,
+ 0,
+ "inset_region faces=%hf use_boundary=%b use_even_offset=%b thickness=%f depth=%f "
+ "thickness_array=%p depth_array=%p use_attributes=%b",
+ BM_ELEM_SELECT,
+ true,
+ true,
+ inset.first(),
+ distance.first(),
+ inset.data(),
+ distance.data(),
+ true);
+ }
+ BMO_op_exec(bm, &op);
+ BM_tag_new_faces(bm, &op);
+
+ CustomData_MeshMasks cd_mask_extra = {CD_MASK_ORIGINDEX, CD_MASK_ORIGINDEX, CD_MASK_ORIGINDEX};
+ Mesh *result = BKE_mesh_from_bmesh_for_eval_nomain(bm, &cd_mask_extra, mesh);
+
+ MeshComponent component;
+ component.replace(result, GeometryOwnershipType::Editable);
+
+ if (side_faces_id) {
+ OutputAttribute_Typed<bool> attribute =
+ component.attribute_try_get_anonymous_for_output_only<bool>(*side_faces_id,
+ ATTR_DOMAIN_FACE);
+ BM_get_tagged_faces(bm, attribute.as_span().data());
+ attribute.save();
+ }
+ if (top_faces_id) {
+ OutputAttribute_Typed<bool> attribute =
+ component.attribute_try_get_anonymous_for_output_only<bool>(*top_faces_id,
+ ATTR_DOMAIN_FACE);
+ BM_get_selected_faces(bm, attribute.as_span().data());
+ attribute.save();
+ }
+
+ BMO_op_finish(bm, &op);
+ BM_mesh_free(bm);
+
+ BKE_mesh_normals_tag_dirty(result);
+
+ return result;
+}
+
+static void geo_node_extrude_exec(GeoNodeExecParams params)
+{
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
+
+ geometry_set = bke::geometry_set_realize_instances(geometry_set);
+
+ MeshComponent &mesh_component = geometry_set.get_component_for_write<MeshComponent>();
+ if (mesh_component.has_mesh() && mesh_component.get_for_read()->totpoly > 0) {
+ const Mesh *input_mesh = mesh_component.get_for_read();
+
+ bke::FieldRef<bool> field = params.get_input_field<bool>("Selection");
+ bke::FieldInputs field_inputs = field->prepare_inputs();
+ Vector<std::unique_ptr<bke::FieldInputValue>> field_input_values;
+ prepare_field_inputs(field_inputs, mesh_component, ATTR_DOMAIN_FACE, field_input_values);
+ bke::FieldOutput field_output = field->evaluate(IndexRange(input_mesh->totpoly), field_inputs);
+ GVArray_Typed<bool> selection_results{field_output.varray_ref()};
+ VArray_Span<bool> selection{selection_results};
+
+ const bool inset_individual_faces = params.extract_input<bool>("Individual");
+ const AttributeDomain domain = inset_individual_faces ? ATTR_DOMAIN_FACE : ATTR_DOMAIN_POINT;
+
+ bke::FieldRef<float> distance_field = params.get_input_field<float>("Distance");
+ bke::FieldInputs distance_field_inputs = distance_field->prepare_inputs();
+ Vector<std::unique_ptr<bke::FieldInputValue>> distance_field_input_values;
+ prepare_field_inputs(
+ distance_field_inputs, mesh_component, domain, distance_field_input_values);
+ bke::FieldOutput distance_field_output = distance_field->evaluate(
+ IndexRange(mesh_component.attribute_domain_size(domain)), distance_field_inputs);
+ GVArray_Typed<float> distance_results{distance_field_output.varray_ref()};
+ VArray_Span<float> distance{distance_results};
+
+ bke::FieldRef<float> inset_field = params.get_input_field<float>("Inset");
+ bke::FieldInputs inset_field_inputs = inset_field->prepare_inputs();
+ Vector<std::unique_ptr<bke::FieldInputValue>> inset_field_input_values;
+ prepare_field_inputs(inset_field_inputs, mesh_component, domain, inset_field_input_values);
+ bke::FieldOutput inset_field_output = inset_field->evaluate(
+ IndexRange(mesh_component.attribute_domain_size(domain)), inset_field_inputs);
+ GVArray_Typed<float> inset_results{inset_field_output.varray_ref()};
+ VArray_Span<float> inset{inset_results};
+
+ AnonymousCustomDataLayerID *top_faces_id = params.output_is_required("Top Faces") ?
+ CustomData_anonymous_id_new("Top Faces") :
+ nullptr;
+ AnonymousCustomDataLayerID *side_faces_id = params.output_is_required("Side Faces") ?
+ CustomData_anonymous_id_new("Side Faces") :
+ nullptr;
+
+ Mesh *result = extrude_mesh(input_mesh,
+ selection,
+ distance,
+ inset,
+ inset_individual_faces,
+ top_faces_id,
+ side_faces_id);
+ geometry_set.replace_mesh(result);
+
+ if (top_faces_id) {
+ params.set_output("Top Faces",
+ bke::FieldRef<bool>(new bke::AnonymousAttributeField(
+ *top_faces_id, CPPType::get<bool>())));
+ }
+ if (side_faces_id) {
+ params.set_output("Side Faces",
+ bke::FieldRef<bool>(new bke::AnonymousAttributeField(
+ *side_faces_id, CPPType::get<bool>())));
+ }
+ }
+
+ params.set_output("Geometry", std::move(geometry_set));
+}
+} // namespace blender::nodes
+
+void register_node_type_geo_extrude()
+{
+ static bNodeType ntype;
+
+ geo_node_type_base(&ntype, GEO_NODE_EXTRUDE, "Extrude", NODE_CLASS_GEOMETRY, 0);
+ node_type_socket_templates(&ntype, geo_node_extrude_in, geo_node_extrude_out);
+ ntype.geometry_node_execute = blender::nodes::geo_node_extrude_exec;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_extrude_and_move.cc b/source/blender/nodes/geometry/nodes/node_geo_extrude_and_move.cc
new file mode 100644
index 00000000000..d64427bd5a8
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_extrude_and_move.cc
@@ -0,0 +1,291 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+
+#include "BKE_mesh.h"
+#include "BKE_node.h"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "bmesh.h"
+#include "bmesh_tools.h"
+
+#include "node_geometry_util.hh"
+
+static bNodeSocketTemplate geo_node_extrude_and_move_in[] = {
+ {SOCK_GEOMETRY, N_("Geometry")},
+ {SOCK_VECTOR, N_("Offset"), 0.0f, 0.0f, 1.0f, 0.0f, -FLT_MAX, FLT_MAX, PROP_TRANSLATION},
+ {SOCK_BOOLEAN, N_("Selection"), 1, 0, 0, 0, 0, 0, PROP_NONE, SOCK_HIDE_VALUE | SOCK_FIELD},
+ {-1, ""},
+};
+
+static bNodeSocketTemplate geo_node_extrude_and_move_out[] = {
+ {SOCK_GEOMETRY, N_("Geometry")},
+ {SOCK_BOOLEAN, N_("Selection")},
+ {-1, ""},
+};
+
+static void geo_node_extrude_and_move_layout(uiLayout *layout,
+ bContext *UNUSED(C),
+ PointerRNA *ptr)
+{
+ uiItemR(layout, ptr, "extrude_mode", 0, "", ICON_NONE);
+}
+
+static void geo_node_extrude_and_move_init(bNodeTree *UNUSED(ntree), bNode *node)
+{
+ node->custom1 = 0;
+}
+
+namespace blender::nodes {
+
+static Mesh *extrude_vertices(const Mesh *mesh,
+ const Span<bool> selection,
+ const float3 offset,
+ AnonymousCustomDataLayerID *out_selection_id)
+{
+ const BMeshCreateParams bmesh_create_params = {true};
+ const BMeshFromMeshParams bmesh_from_mesh_params = {
+ true, 0, 0, 0, {CD_MASK_ORIGINDEX, CD_MASK_ORIGINDEX, CD_MASK_ORIGINDEX}};
+
+ BMesh *bm = BKE_mesh_to_bmesh_ex(mesh, &bmesh_create_params, &bmesh_from_mesh_params);
+
+ BM_select_vertices(bm, selection.data());
+
+ BMOperator extrude_op;
+ BMO_op_initf(bm, &extrude_op, 0, "extrude_vert_indiv verts=%hv", BM_ELEM_SELECT);
+ BMO_op_exec(bm, &extrude_op);
+
+ float mx[3] = {offset.x, offset.y, offset.z};
+ BMOperator move_op;
+
+ BM_mesh_elem_hflag_disable_all(bm, BM_VERT, BM_ELEM_SELECT, false);
+ BMO_slot_buffer_hflag_enable(
+ bm, extrude_op.slots_out, "verts.out", BM_VERT, BM_ELEM_SELECT, false);
+
+ BMO_op_initf(bm, &move_op, 0, "translate vec=%v verts=%hv", mx, BM_ELEM_SELECT);
+
+ BMO_op_exec(bm, &move_op);
+ BMO_op_finish(bm, &move_op);
+ BMO_op_finish(bm, &extrude_op);
+
+ CustomData_MeshMasks cd_mask_extra = {CD_MASK_ORIGINDEX, CD_MASK_ORIGINDEX, CD_MASK_ORIGINDEX};
+ Mesh *result = BKE_mesh_from_bmesh_for_eval_nomain(bm, &cd_mask_extra, mesh);
+
+ MeshComponent component;
+ component.replace(result, GeometryOwnershipType::Editable);
+
+ if (out_selection_id) {
+ OutputAttribute_Typed<bool> attribute =
+ component.attribute_try_get_anonymous_for_output_only<bool>(*out_selection_id,
+ ATTR_DOMAIN_POINT);
+ BM_get_selected_vertices(bm, attribute.as_span().data());
+ attribute.save();
+ }
+
+ BM_mesh_free(bm);
+
+ BKE_mesh_normals_tag_dirty(result);
+
+ return result;
+}
+
+static Mesh *extrude_edges(const Mesh *mesh,
+ const Span<bool> selection,
+ const float3 offset,
+ AnonymousCustomDataLayerID *out_selection_id)
+{
+ const BMeshCreateParams bmesh_create_params = {true};
+ const BMeshFromMeshParams bmesh_from_mesh_params = {
+ true, 0, 0, 0, {CD_MASK_ORIGINDEX, CD_MASK_ORIGINDEX, CD_MASK_ORIGINDEX}};
+
+ BMesh *bm = BKE_mesh_to_bmesh_ex(mesh, &bmesh_create_params, &bmesh_from_mesh_params);
+
+ BM_select_edges(bm, selection.data());
+
+ BMOperator extrude_op;
+ BMO_op_initf(bm,
+ &extrude_op,
+ 0,
+ "extrude_edge_only edges=%he use_select_history=%b",
+ BM_ELEM_SELECT,
+ true);
+ BMO_op_exec(bm, &extrude_op);
+
+ float o[3] = {offset.x, offset.y, offset.z};
+ BMOperator move_op;
+
+ BM_mesh_elem_hflag_disable_all(bm, BM_VERT, BM_ELEM_SELECT, false);
+ BMO_slot_buffer_hflag_enable(
+ bm, extrude_op.slots_out, "geom.out", BM_VERT, BM_ELEM_SELECT, false);
+
+ BMO_op_initf(bm, &move_op, 0, "translate vec=%v verts=%hv", o, BM_ELEM_SELECT);
+
+ BMO_op_exec(bm, &move_op);
+ BMO_op_finish(bm, &move_op);
+ BMO_op_finish(bm, &extrude_op);
+
+ CustomData_MeshMasks cd_mask_extra = {CD_MASK_ORIGINDEX, CD_MASK_ORIGINDEX, CD_MASK_ORIGINDEX};
+ Mesh *result = BKE_mesh_from_bmesh_for_eval_nomain(bm, &cd_mask_extra, mesh);
+
+ MeshComponent component;
+ component.replace(result, GeometryOwnershipType::Editable);
+
+ if (out_selection_id) {
+ OutputAttribute_Typed<bool> attribute =
+ component.attribute_try_get_anonymous_for_output_only<bool>(*out_selection_id,
+ ATTR_DOMAIN_POINT);
+ BM_get_selected_vertices(bm, attribute.as_span().data());
+ attribute.save();
+ }
+
+ BM_mesh_free(bm);
+
+ BKE_mesh_normals_tag_dirty(result);
+
+ return result;
+}
+
+static Mesh *extrude_faces(const Mesh *mesh,
+ const Span<bool> selection,
+ const float3 offset,
+ AnonymousCustomDataLayerID *out_selection_id)
+{
+ // TODO: - dont execute on a offset with length 0
+ // - Check why selection for edges and faces is wired.
+ // - dedublicate extrude functions
+ // - checkout hans lazy bmesh mechanism
+
+ const BMeshCreateParams bmesh_create_params = {true};
+ const BMeshFromMeshParams bmesh_from_mesh_params = {
+ true, 0, 0, 0, {CD_MASK_ORIGINDEX, CD_MASK_ORIGINDEX, CD_MASK_ORIGINDEX}};
+
+ BMesh *bm = BKE_mesh_to_bmesh_ex(mesh, &bmesh_create_params, &bmesh_from_mesh_params);
+
+ BM_select_faces(bm, selection.data());
+
+ BMOperator extrude_op;
+ BMO_op_initf(bm, &extrude_op, 0, "extrude_face_region geom=%hf", BM_ELEM_SELECT);
+ BMO_op_exec(bm, &extrude_op);
+
+ float o[3] = {offset.x, offset.y, offset.z};
+ BMOperator move_op;
+
+ BM_mesh_elem_hflag_disable_all(bm, BM_VERT, BM_ELEM_SELECT, false);
+ BMO_slot_buffer_hflag_enable(
+ bm, extrude_op.slots_out, "geom.out", BM_VERT, BM_ELEM_SELECT, false);
+
+ BMO_op_initf(bm, &move_op, 0, "translate vec=%v verts=%hv", o, BM_ELEM_SELECT);
+
+ BMO_op_exec(bm, &move_op);
+ BMO_op_finish(bm, &move_op);
+ BMO_op_finish(bm, &extrude_op);
+
+ CustomData_MeshMasks cd_mask_extra = {CD_MASK_ORIGINDEX, CD_MASK_ORIGINDEX, CD_MASK_ORIGINDEX};
+ Mesh *result = BKE_mesh_from_bmesh_for_eval_nomain(bm, &cd_mask_extra, mesh);
+
+ MeshComponent component;
+ component.replace(result, GeometryOwnershipType::Editable);
+
+ if (out_selection_id) {
+ OutputAttribute_Typed<bool> attribute =
+ component.attribute_try_get_anonymous_for_output_only<bool>(*out_selection_id,
+ ATTR_DOMAIN_FACE);
+ BM_get_selected_faces(bm, attribute.as_span().data());
+ // face_map.out
+ // boundary_map.out
+
+ attribute.save();
+ }
+
+ BM_mesh_free(bm);
+
+ BKE_mesh_normals_tag_dirty(result);
+
+ return result;
+}
+
+static void geo_node_extrude_and_move_exec(GeoNodeExecParams params)
+{
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
+
+ geometry_set = bke::geometry_set_realize_instances(geometry_set);
+
+ MeshComponent &mesh_component = geometry_set.get_component_for_write<MeshComponent>();
+ if (mesh_component.has_mesh()) {
+ const Mesh *input_mesh = mesh_component.get_for_read();
+
+ AttributeDomain domain = ATTR_DOMAIN_POINT;
+ int domain_size = input_mesh->totvert;
+ if (params.node().custom1 == 1) {
+ domain = ATTR_DOMAIN_EDGE;
+ domain_size = input_mesh->totedge;
+ }
+ else if (params.node().custom1 == 2) {
+ domain = ATTR_DOMAIN_FACE;
+ domain_size = input_mesh->totpoly;
+ }
+ bke::FieldRef<bool> field = params.get_input_field<bool>("Selection");
+ bke::FieldInputs field_inputs = field->prepare_inputs();
+ Vector<std::unique_ptr<bke::FieldInputValue>> field_input_values;
+ prepare_field_inputs(field_inputs, mesh_component, domain, field_input_values);
+ bke::FieldOutput field_output = field->evaluate(IndexRange(domain_size), field_inputs);
+ GVArray_Typed<bool> selection_results{field_output.varray_ref()};
+ VArray_Span<bool> selection{selection_results};
+
+ AnonymousCustomDataLayerID *out_selection_id = params.output_is_required("Selection") ?
+ CustomData_anonymous_id_new("Selection") :
+ nullptr;
+
+ const float3 offset = params.extract_input<float3>("Offset");
+ Mesh *result;
+ if (params.node().custom1 == 1) {
+ result = extrude_edges(input_mesh, selection, offset, out_selection_id);
+ }
+ else if (params.node().custom1 == 2) {
+ result = extrude_faces(input_mesh, selection, offset, out_selection_id);
+ }
+ else {
+ result = extrude_vertices(input_mesh, selection, offset, out_selection_id);
+ }
+ geometry_set.replace_mesh(result);
+
+ if (out_selection_id) {
+ params.set_output("Selection",
+ bke::FieldRef<bool>(new bke::AnonymousAttributeField(
+ *out_selection_id, CPPType::get<bool>())));
+ }
+ }
+
+ params.set_output("Geometry", std::move(geometry_set));
+}
+} // namespace blender::nodes
+
+void register_node_type_geo_extrude_and_move()
+{
+ static bNodeType ntype;
+
+ geo_node_type_base(
+ &ntype, GEO_NODE_EXTRUDE_AND_MOVE, "Mesh Extrude And Move", NODE_CLASS_GEOMETRY, 0);
+ node_type_socket_templates(&ntype, geo_node_extrude_and_move_in, geo_node_extrude_and_move_out);
+ node_type_init(&ntype, geo_node_extrude_and_move_init);
+ ntype.geometry_node_execute = blender::nodes::geo_node_extrude_and_move_exec;
+ ntype.draw_buttons = geo_node_extrude_and_move_layout;
+ nodeRegisterType(&ntype);
+} \ No newline at end of file
diff --git a/source/blender/nodes/geometry/nodes/node_geo_index.cc b/source/blender/nodes/geometry/nodes/node_geo_index.cc
new file mode 100644
index 00000000000..459c22cd45f
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_index.cc
@@ -0,0 +1,45 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "node_geometry_util.hh"
+
+static bNodeSocketTemplate geo_node_index_out[] = {
+ {SOCK_INT, N_("Index")},
+ {-1, ""},
+};
+
+namespace blender::nodes {
+
+static void geo_node_index_exec(GeoNodeExecParams params)
+{
+ FieldPtr index_field = new bke::IndexField();
+ params.set_output("Index", bke::FieldRef<int>(std::move(index_field)));
+}
+
+} // namespace blender::nodes
+
+void register_node_type_geo_index()
+{
+ static bNodeType ntype;
+
+ geo_node_type_base(&ntype, GEO_NODE_INDEX, "Index", NODE_CLASS_INPUT, 0);
+ node_type_socket_templates(&ntype, nullptr, geo_node_index_out);
+ ntype.geometry_node_execute = blender::nodes::geo_node_index_exec;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_is_viewport.cc b/source/blender/nodes/geometry/nodes/node_geo_is_viewport.cc
index ec875b9f983..ae90af99ab6 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_is_viewport.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_is_viewport.cc
@@ -31,7 +31,8 @@ static void geo_node_is_viewport_exec(GeoNodeExecParams params)
const eEvaluationMode mode = DEG_get_mode(depsgraph);
const bool is_viewport = mode == DAG_EVAL_VIEWPORT;
- params.set_output("Is Viewport", is_viewport);
+ /* This is a field just to avoid a crash, it doesn't seem like it should need to be a field. */
+ params.set_output("Is Viewport", bke::FieldRef<bool>(new bke::ConstantField(is_viewport)));
}
} // namespace blender::nodes
diff --git a/source/blender/nodes/geometry/nodes/node_geo_material_assign.cc b/source/blender/nodes/geometry/nodes/node_geo_material_assign.cc
index 02b2d685bdd..8fe727626f1 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_material_assign.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_material_assign.cc
@@ -36,7 +36,7 @@ static bNodeSocketTemplate geo_node_material_assign_in[] = {
0.0f,
PROP_NONE,
SOCK_HIDE_LABEL},
- {SOCK_STRING, N_("Selection")},
+ {SOCK_BOOLEAN, N_("Selection"), 1, 0, 0, 0, 0, 0, PROP_NONE, SOCK_HIDE_VALUE | SOCK_FIELD},
{-1, ""},
};
@@ -75,8 +75,6 @@ static void assign_material_to_faces(Mesh &mesh, const VArray<bool> &face_mask,
static void geo_node_material_assign_exec(GeoNodeExecParams params)
{
Material *material = params.extract_input<Material *>("Material");
- const std::string mask_name = params.extract_input<std::string>("Selection");
-
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
geometry_set = geometry_set_realize_instances(geometry_set);
@@ -85,9 +83,17 @@ static void geo_node_material_assign_exec(GeoNodeExecParams params)
MeshComponent &mesh_component = geometry_set.get_component_for_write<MeshComponent>();
Mesh *mesh = mesh_component.get_for_write();
if (mesh != nullptr) {
- GVArray_Typed<bool> face_mask = mesh_component.attribute_get_for_read<bool>(
- mask_name, ATTR_DOMAIN_FACE, true);
- assign_material_to_faces(*mesh, face_mask, material);
+
+ bke::FieldRef<bool> field = params.get_input_field<bool>("Selection");
+ bke::FieldInputs field_inputs = field->prepare_inputs();
+ Vector<std::unique_ptr<bke::FieldInputValue>> field_input_values;
+ prepare_field_inputs(field_inputs, mesh_component, ATTR_DOMAIN_FACE, field_input_values);
+ bke::FieldOutput field_output = field->evaluate(
+ IndexRange(mesh_component.attribute_domain_size(ATTR_DOMAIN_FACE)), field_inputs);
+
+ GVArray_Typed<bool> selection{field_output.varray_ref()};
+
+ assign_material_to_faces(*mesh, *selection, material);
}
}
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 637003a46c7..dc1f28b1ee2 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
@@ -29,7 +29,7 @@ using blender::Array;
static bNodeSocketTemplate geo_node_mesh_to_curve_in[] = {
{SOCK_GEOMETRY, N_("Mesh")},
- {SOCK_STRING, N_("Selection")},
+ {SOCK_BOOLEAN, N_("Selection"), 1, 0, 0, 0, 0, 0, PROP_NONE, SOCK_HIDE_VALUE | SOCK_FIELD},
{-1, ""},
};
@@ -258,15 +258,16 @@ static CurveFromEdgesOutput mesh_to_curve(Span<MVert> verts, Span<std::pair<int,
static Vector<std::pair<int, int>> get_selected_edges(GeoNodeExecParams params,
const MeshComponent &component)
{
- const Mesh &mesh = *component.get_for_read();
- const std::string selection_name = params.extract_input<std::string>("Selection");
- if (!selection_name.empty() && !component.attribute_exists(selection_name)) {
- params.error_message_add(NodeWarningType::Error,
- TIP_("No attribute with name \"") + selection_name + "\"");
- }
- GVArray_Typed<bool> selection = component.attribute_get_for_read<bool>(
- selection_name, ATTR_DOMAIN_EDGE, true);
+ bke::FieldRef<bool> field = params.get_input_field<bool>("Selection");
+ bke::FieldInputs field_inputs = field->prepare_inputs();
+ Vector<std::unique_ptr<bke::FieldInputValue>> field_input_values;
+ prepare_field_inputs(field_inputs, component, ATTR_DOMAIN_EDGE, field_input_values);
+ bke::FieldOutput field_output = field->evaluate(
+ IndexRange(component.attribute_domain_size(ATTR_DOMAIN_EDGE)), field_inputs);
+ GVArray_Typed<bool> selection{field_output.varray_ref()};
+
+ const Mesh &mesh = *component.get_for_read();
Vector<std::pair<int, int>> selected_edges;
for (const int i : IndexRange(mesh.totedge)) {
if (selection[i]) {
diff --git a/source/blender/nodes/geometry/nodes/node_geo_normal.cc b/source/blender/nodes/geometry/nodes/node_geo_normal.cc
new file mode 100644
index 00000000000..e0fc7480ce4
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_normal.cc
@@ -0,0 +1,45 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "node_geometry_util.hh"
+
+static bNodeSocketTemplate geo_node_normal_out[] = {
+ {SOCK_VECTOR, N_("Normal")},
+ {-1, ""},
+};
+
+namespace blender::nodes {
+
+static void geo_node_normal_exec(GeoNodeExecParams params)
+{
+ FieldPtr normal_field = new bke::PersistentAttributeField("normal", CPPType::get<float3>());
+ params.set_output("Normal", bke::FieldRef<float3>(std::move(normal_field)));
+}
+
+} // namespace blender::nodes
+
+void register_node_type_geo_normal()
+{
+ static bNodeType ntype;
+
+ geo_node_type_base(&ntype, GEO_NODE_NORMAL, "Face Normal", NODE_CLASS_INPUT, 0);
+ node_type_socket_templates(&ntype, nullptr, geo_node_normal_out);
+ ntype.geometry_node_execute = blender::nodes::geo_node_normal_exec;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_object_info.cc b/source/blender/nodes/geometry/nodes/node_geo_object_info.cc
index 167812d5656..c877ca26dc0 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_object_info.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_object_info.cc
@@ -84,9 +84,9 @@ static void geo_node_object_info_exec(GeoNodeExecParams params)
}
}
- params.set_output("Location", location);
- params.set_output("Rotation", rotation);
- params.set_output("Scale", scale);
+ params.set_output("Location", bke::FieldRef<float3>(new bke::ConstantField<float3>(location)));
+ params.set_output("Rotation", bke::FieldRef<float3>(new bke::ConstantField<float3>(rotation)));
+ params.set_output("Scale", bke::FieldRef<float3>(new bke::ConstantField<float3>(scale)));
params.set_output("Geometry", geometry_set);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_point_distribute.cc b/source/blender/nodes/geometry/nodes/node_geo_point_distribute.cc
index 99930b5ae46..a8c9fcea717 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_point_distribute.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_point_distribute.cc
@@ -42,14 +42,16 @@ using blender::bke::GeometryInstanceGroup;
static bNodeSocketTemplate geo_node_point_distribute_in[] = {
{SOCK_GEOMETRY, N_("Geometry")},
{SOCK_FLOAT, N_("Distance Min"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 100000.0f, PROP_DISTANCE},
- {SOCK_FLOAT, N_("Density Max"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 100000.0f, PROP_NONE},
- {SOCK_STRING, N_("Density Attribute")},
+ {SOCK_FLOAT, N_("Density"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 100000.0f, PROP_NONE, SOCK_FIELD},
{SOCK_INT, N_("Seed"), 0, 0, 0, 0, -10000, 10000},
{-1, ""},
};
static bNodeSocketTemplate geo_node_point_distribute_out[] = {
{SOCK_GEOMETRY, N_("Geometry")},
+ {SOCK_VECTOR, N_("Rotation")},
+ {SOCK_VECTOR, N_("Normal")},
+ {SOCK_INT, N_("ID")},
{-1, ""},
};
@@ -84,7 +86,7 @@ static float3 normal_to_euler_rotation(const float3 normal)
static void sample_mesh_surface(const Mesh &mesh,
const float4x4 &transform,
const float base_density,
- const VArray<float> *density_factors,
+ const VArray<float> &density,
const int seed,
Vector<float3> &r_positions,
Vector<float3> &r_bary_coords,
@@ -105,19 +107,16 @@ static void sample_mesh_surface(const Mesh &mesh,
const float3 v1_pos = transform * float3(mesh.mvert[v1_index].co);
const float3 v2_pos = transform * float3(mesh.mvert[v2_index].co);
- float looptri_density_factor = 1.0f;
- if (density_factors != nullptr) {
- const float v0_density_factor = std::max(0.0f, density_factors->get(v0_loop));
- const float v1_density_factor = std::max(0.0f, density_factors->get(v1_loop));
- const float v2_density_factor = std::max(0.0f, density_factors->get(v2_loop));
- looptri_density_factor = (v0_density_factor + v1_density_factor + v2_density_factor) / 3.0f;
- }
+ const float v0_density = std::max(0.0f, density[v0_loop]);
+ const float v1_density = std::max(0.0f, density[v1_loop]);
+ const float v2_density = std::max(0.0f, density[v2_loop]);
+ float looptri_density = (v0_density + v1_density + v2_density) / 3.0f;
const float area = area_tri_v3(v0_pos, v1_pos, v2_pos);
const int looptri_seed = BLI_hash_int(looptri_index + seed);
RandomNumberGenerator looptri_rng(looptri_seed);
- const float points_amount_fl = area * base_density * looptri_density_factor;
+ const float points_amount_fl = area * base_density * looptri_density;
const float add_point_probability = fractf(points_amount_fl);
const bool add_point = add_point_probability > looptri_rng.get_float();
const int point_amount = (int)points_amount_fl + (int)add_point;
@@ -326,34 +325,59 @@ BLI_NOINLINE static void interpolate_existing_attributes(
i_instance++;
}
-
- attribute_math::convert_to_static_type(output_data_type, [&](auto dummy) {
- using T = decltype(dummy);
-
- GVArray_Span<T> source_span{*source_attribute};
- });
}
attribute_out.save();
}
}
-BLI_NOINLINE static void compute_special_attributes(Span<GeometryInstanceGroup> sets,
- Span<int> instance_start_offsets,
- GeometryComponent &component,
- Span<Vector<float3>> bary_coords_array,
- Span<Vector<int>> looptri_indices_array)
+namespace {
+struct AnonymousAttributeIDs {
+ AnonymousCustomDataLayerID *rotation = nullptr;
+ AnonymousCustomDataLayerID *normal = nullptr;
+ AnonymousCustomDataLayerID *id = nullptr;
+};
+} // namespace
+
+BLI_NOINLINE static void compute_special_attributes(
+ Span<GeometryInstanceGroup> sets,
+ Span<int> instance_start_offsets,
+ GeometryComponent &component,
+ Span<Vector<float3>> bary_coords_array,
+ Span<Vector<int>> looptri_indices_array,
+ const AnonymousAttributeIDs &anonymous_attribute_ids)
{
- OutputAttribute_Typed<int> id_attribute = component.attribute_try_get_for_output_only<int>(
- "id", ATTR_DOMAIN_POINT);
- OutputAttribute_Typed<float3> normal_attribute =
- component.attribute_try_get_for_output_only<float3>("normal", ATTR_DOMAIN_POINT);
- OutputAttribute_Typed<float3> rotation_attribute =
- component.attribute_try_get_for_output_only<float3>("rotation", ATTR_DOMAIN_POINT);
+ MutableSpan<float3> result_rotations;
+ MutableSpan<float3> result_normals;
+ MutableSpan<int> result_ids;
+
+ if (anonymous_attribute_ids.rotation != nullptr) {
+ component.attribute_try_create_anonymous(*anonymous_attribute_ids.rotation,
+ ATTR_DOMAIN_POINT,
+ CD_PROP_FLOAT3,
+ AttributeInitDefault());
+ WriteAttributeLookup rotation_attribute = component.attribute_try_get_anonymous_for_write(
+ *anonymous_attribute_ids.rotation);
+ result_rotations = rotation_attribute.varray->get_internal_span().typed<float3>();
+ }
- MutableSpan<int> result_ids = id_attribute.as_span();
- MutableSpan<float3> result_normals = normal_attribute.as_span();
- MutableSpan<float3> result_rotations = rotation_attribute.as_span();
+ if (anonymous_attribute_ids.normal != nullptr) {
+ component.attribute_try_create_anonymous(*anonymous_attribute_ids.normal,
+ ATTR_DOMAIN_POINT,
+ CD_PROP_FLOAT3,
+ AttributeInitDefault());
+ WriteAttributeLookup normal_attribute = component.attribute_try_get_anonymous_for_write(
+ *anonymous_attribute_ids.normal);
+ result_normals = normal_attribute.varray->get_internal_span().typed<float3>();
+ }
+
+ if (anonymous_attribute_ids.id != nullptr) {
+ component.attribute_try_create_anonymous(
+ *anonymous_attribute_ids.id, ATTR_DOMAIN_POINT, CD_PROP_INT32, AttributeInitDefault());
+ WriteAttributeLookup id_attribute = component.attribute_try_get_anonymous_for_write(
+ *anonymous_attribute_ids.id);
+ result_ids = id_attribute.varray->get_internal_span().typed<int>();
+ }
int i_instance = 0;
for (const GeometryInstanceGroup &set_group : sets) {
@@ -388,19 +412,25 @@ BLI_NOINLINE static void compute_special_attributes(Span<GeometryInstanceGroup>
const float3 v1_pos = float3(mesh.mvert[v1_index].co);
const float3 v2_pos = float3(mesh.mvert[v2_index].co);
- ids[i] = (int)(bary_coord.hash() + (uint64_t)looptri_index);
- normal_tri_v3(normals[i], v0_pos, v1_pos, v2_pos);
- mul_m3_v3(rotation_matrix, normals[i]);
- rotations[i] = normal_to_euler_rotation(normals[i]);
+ if (!result_ids.is_empty()) {
+ ids[i] = (int)(bary_coord.hash() + (uint64_t)looptri_index);
+ }
+ float3 normal;
+ if (!result_normals.is_empty() || !result_rotations.is_empty()) {
+ normal_tri_v3(normal, v0_pos, v1_pos, v2_pos);
+ mul_m3_v3(rotation_matrix, normal);
+ }
+ if (!result_normals.is_empty()) {
+ normals[i] = normal;
+ }
+ if (!result_rotations.is_empty()) {
+ rotations[i] = normal_to_euler_rotation(normal);
+ }
}
i_instance++;
}
}
-
- id_attribute.save();
- normal_attribute.save();
- rotation_attribute.save();
}
BLI_NOINLINE static void add_remaining_point_attributes(
@@ -409,7 +439,8 @@ BLI_NOINLINE static void add_remaining_point_attributes(
const Map<std::string, AttributeKind> &attributes,
GeometryComponent &component,
Span<Vector<float3>> bary_coords_array,
- Span<Vector<int>> looptri_indices_array)
+ Span<Vector<int>> looptri_indices_array,
+ const AnonymousAttributeIDs &anonymous_attribute_ids)
{
interpolate_existing_attributes(set_groups,
instance_start_offsets,
@@ -417,50 +448,50 @@ BLI_NOINLINE static void add_remaining_point_attributes(
component,
bary_coords_array,
looptri_indices_array);
- compute_special_attributes(
- set_groups, instance_start_offsets, component, bary_coords_array, looptri_indices_array);
+ compute_special_attributes(set_groups,
+ instance_start_offsets,
+ component,
+ bary_coords_array,
+ looptri_indices_array,
+ anonymous_attribute_ids);
}
static void distribute_points_random(Span<GeometryInstanceGroup> set_groups,
- const StringRef density_attribute_name,
- const float density,
+ const float base_density,
+ const bke::FieldRef<float> &density_field,
const int seed,
MutableSpan<Vector<float3>> positions_all,
MutableSpan<Vector<float3>> bary_coords_all,
MutableSpan<Vector<int>> looptri_indices_all)
{
- /* If there is an attribute name, the default value for the densities should be zero so that
- * points are only scattered where the attribute exists. Otherwise, just "ignore" the density
- * factors. */
- const bool use_one_default = density_attribute_name.is_empty();
-
int i_instance = 0;
for (const GeometryInstanceGroup &set_group : set_groups) {
const GeometrySet &set = set_group.geometry_set;
const MeshComponent &component = *set.get_component_for_read<MeshComponent>();
- GVArray_Typed<float> density_factors = component.attribute_get_for_read<float>(
- density_attribute_name, ATTR_DOMAIN_CORNER, use_one_default ? 1.0f : 0.0f);
const Mesh &mesh = *component.get_for_read();
+
+ bke::FieldInputs density_field_inputs = density_field->prepare_inputs();
+ Vector<std::unique_ptr<bke::FieldInputValue>> density_field_input_values;
+ prepare_field_inputs(
+ density_field_inputs, component, ATTR_DOMAIN_CORNER, density_field_input_values);
+ bke::FieldOutput density_field_output = density_field->evaluate(IndexRange(mesh.totloop),
+ density_field_inputs);
+ GVArray_Typed<float> density{density_field_output.varray_ref()};
+
for (const float4x4 &transform : set_group.transforms) {
Vector<float3> &positions = positions_all[i_instance];
Vector<float3> &bary_coords = bary_coords_all[i_instance];
Vector<int> &looptri_indices = looptri_indices_all[i_instance];
- sample_mesh_surface(mesh,
- transform,
- density,
- &*density_factors,
- seed,
- positions,
- bary_coords,
- looptri_indices);
+ sample_mesh_surface(
+ mesh, transform, base_density, density, seed, positions, bary_coords, looptri_indices);
i_instance++;
}
}
}
static void distribute_points_poisson_disk(Span<GeometryInstanceGroup> set_groups,
- const StringRef density_attribute_name,
- const float density,
+ const float max_density,
+ const bke::FieldRef<float> &density_field,
const int seed,
const float minimum_distance,
MutableSpan<Vector<float3>> positions_all,
@@ -478,8 +509,14 @@ static void distribute_points_poisson_disk(Span<GeometryInstanceGroup> set_group
Vector<float3> &positions = positions_all[i_instance];
Vector<float3> &bary_coords = bary_coords_all[i_instance];
Vector<int> &looptri_indices = looptri_indices_all[i_instance];
- sample_mesh_surface(
- mesh, transform, density, nullptr, seed, positions, bary_coords, looptri_indices);
+ sample_mesh_surface(mesh,
+ transform,
+ max_density,
+ VArray_For_Single<float>(1.0f, mesh.totloop),
+ seed,
+ positions,
+ bary_coords,
+ looptri_indices);
instance_start_offsets[i_instance] = initial_points_len;
initial_points_len += positions.size();
@@ -487,11 +524,6 @@ static void distribute_points_poisson_disk(Span<GeometryInstanceGroup> set_group
}
}
- /* If there is an attribute name, the default value for the densities should be zero so that
- * points are only scattered where the attribute exists. Otherwise, just "ignore" the density
- * factors. */
- const bool use_one_default = density_attribute_name.is_empty();
-
/* Unlike the other result arrays, the elimination mask in stored as a flat array for every
* point, in order to simplify culling points from the KDTree (which needs to know about all
* points at once). */
@@ -507,8 +539,14 @@ static void distribute_points_poisson_disk(Span<GeometryInstanceGroup> set_group
const GeometrySet &set = set_group.geometry_set;
const MeshComponent &component = *set.get_component_for_read<MeshComponent>();
const Mesh &mesh = *component.get_for_read();
- const GVArray_Typed<float> density_factors = component.attribute_get_for_read<float>(
- density_attribute_name, ATTR_DOMAIN_CORNER, use_one_default ? 1.0f : 0.0f);
+
+ bke::FieldInputs density_field_inputs = density_field->prepare_inputs();
+ Vector<std::unique_ptr<bke::FieldInputValue>> density_field_input_values;
+ prepare_field_inputs(
+ density_field_inputs, component, ATTR_DOMAIN_CORNER, density_field_input_values);
+ bke::FieldOutput density_field_output = density_field->evaluate(IndexRange(mesh.totloop),
+ density_field_inputs);
+ GVArray_Typed<float> density{density_field_output.varray_ref()};
for (const int UNUSED(i_set_instance) : set_group.transforms.index_range()) {
Vector<float3> &positions = positions_all[i_instance];
@@ -518,7 +556,7 @@ static void distribute_points_poisson_disk(Span<GeometryInstanceGroup> set_group
const int offset = instance_start_offsets[i_instance];
update_elimination_mask_based_on_density_factors(
mesh,
- density_factors,
+ density,
bary_coords,
looptri_indices,
elimination_mask.as_mutable_span().slice(offset, positions.size()));
@@ -541,11 +579,10 @@ static void geo_node_point_distribute_exec(GeoNodeExecParams params)
static_cast<GeometryNodePointDistributeMode>(params.node().custom1);
const int seed = params.get_input<int>("Seed") * 5383843;
- const float density = params.extract_input<float>("Density Max");
- const std::string density_attribute_name = params.extract_input<std::string>(
- "Density Attribute");
+ const float density_max = 1.0f;
+ bke::FieldRef<float> density_field = params.get_input_field<float>("Density");
- if (density <= 0.0f) {
+ if (density_max <= 0.0f) {
params.set_output("Geometry", GeometrySet());
return;
}
@@ -586,8 +623,8 @@ static void geo_node_point_distribute_exec(GeoNodeExecParams params)
switch (distribute_method) {
case GEO_NODE_POINT_DISTRIBUTE_RANDOM: {
distribute_points_random(set_groups,
- density_attribute_name,
- density,
+ density_max,
+ density_field,
seed,
positions_all,
bary_coords_all,
@@ -597,8 +634,8 @@ static void geo_node_point_distribute_exec(GeoNodeExecParams params)
case GEO_NODE_POINT_DISTRIBUTE_POISSON: {
const float minimum_distance = params.extract_input<float>("Distance Min");
distribute_points_poisson_disk(set_groups,
- density_attribute_name,
- density,
+ density_max,
+ density_field,
seed,
minimum_distance,
positions_all,
@@ -629,6 +666,17 @@ static void geo_node_point_distribute_exec(GeoNodeExecParams params)
PointCloudComponent &point_component =
geometry_set_out.get_component_for_write<PointCloudComponent>();
+ AnonymousAttributeIDs anonymous_attribute_ids;
+ if (params.output_is_required("Rotation")) {
+ anonymous_attribute_ids.rotation = CustomData_anonymous_id_new("Rotation");
+ }
+ if (params.output_is_required("Normal")) {
+ anonymous_attribute_ids.normal = CustomData_anonymous_id_new("Normal");
+ }
+ if (params.output_is_required("ID")) {
+ anonymous_attribute_ids.id = CustomData_anonymous_id_new("ID");
+ }
+
Map<std::string, AttributeKind> attributes;
bke::geometry_set_gather_instances_attribute_info(
set_groups, {GEO_COMPONENT_TYPE_MESH}, {"position", "normal", "id"}, attributes);
@@ -637,9 +685,26 @@ static void geo_node_point_distribute_exec(GeoNodeExecParams params)
attributes,
point_component,
bary_coords_all,
- looptri_indices_all);
+ looptri_indices_all,
+ anonymous_attribute_ids);
params.set_output("Geometry", std::move(geometry_set_out));
+
+ if (anonymous_attribute_ids.rotation != nullptr) {
+ params.set_output("Rotation",
+ bke::FieldRef<float3>(new bke::AnonymousAttributeField(
+ *anonymous_attribute_ids.rotation, CPPType::get<float3>())));
+ }
+ if (anonymous_attribute_ids.normal != nullptr) {
+ params.set_output("Normal",
+ bke::FieldRef<float3>(new bke::AnonymousAttributeField(
+ *anonymous_attribute_ids.normal, CPPType::get<float3>())));
+ }
+ if (anonymous_attribute_ids.id != nullptr) {
+ params.set_output("ID",
+ bke::FieldRef<int>(new bke::AnonymousAttributeField(
+ *anonymous_attribute_ids.id, CPPType::get<int>())));
+ }
}
} // namespace blender::nodes
diff --git a/source/blender/nodes/geometry/nodes/node_geo_point_instance.cc b/source/blender/nodes/geometry/nodes/node_geo_point_instance.cc
index b119b7b31e9..ea810295f70 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_point_instance.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_point_instance.cc
@@ -38,6 +38,28 @@ static bNodeSocketTemplate geo_node_point_instance_in[] = {
PROP_NONE,
SOCK_HIDE_LABEL},
{SOCK_INT, N_("Seed"), 0, 0, 0, 0, -10000, 10000},
+ {SOCK_VECTOR,
+ N_("Position"),
+ 0.0f,
+ 0.0f,
+ 0.0f,
+ 0.0f,
+ 0.0f,
+ 1.0f,
+ PROP_NONE,
+ SOCK_HIDE_VALUE | SOCK_FIELD},
+ {SOCK_VECTOR,
+ N_("Rotation"),
+ 0.0f,
+ 0.0f,
+ 0.0f,
+ 0.0f,
+ -10000.0f,
+ 10000.0f,
+ PROP_EULER,
+ SOCK_FIELD},
+ {SOCK_VECTOR, N_("Scale"), 1.0f, 1.0f, 1.0f, 0.0f, -10000.0f, 10000.0f, PROP_NONE, SOCK_FIELD},
+ {SOCK_INT, N_("ID"), -1.0f, 0.0f, 0.0f, 0.0f, -10000.0f, 10000.0f, PROP_NONE, SOCK_FIELD},
{-1, ""},
};
@@ -170,13 +192,27 @@ static void add_instances_from_component(InstancesComponent &instances,
const int domain_size = src_geometry.attribute_domain_size(domain);
- GVArray_Typed<float3> positions = src_geometry.attribute_get_for_read<float3>(
- "position", domain, {0, 0, 0});
- GVArray_Typed<float3> rotations = src_geometry.attribute_get_for_read<float3>(
- "rotation", domain, {0, 0, 0});
- GVArray_Typed<float3> scales = src_geometry.attribute_get_for_read<float3>(
- "scale", domain, {1, 1, 1});
- GVArray_Typed<int> id_attribute = src_geometry.attribute_get_for_read<int>("id", domain, -1);
+ bke::FieldRef<float3> position_field = params.get_input_field<float3>("Position");
+ bke::FieldRef<float3> rotation_field = params.get_input_field<float3>("Rotation");
+ bke::FieldRef<float3> scale_field = params.get_input_field<float3>("Scale");
+ bke::FieldRef<int> id_field = params.get_input_field<int>("ID");
+
+ Vector<std::unique_ptr<bke::FieldInputValue>> all_inputs;
+
+ bke::FieldInputs position_inputs = position_field->prepare_inputs();
+ prepare_field_inputs(position_inputs, src_geometry, ATTR_DOMAIN_POINT, all_inputs);
+ bke::FieldInputs rotation_inputs = rotation_field->prepare_inputs();
+ prepare_field_inputs(rotation_inputs, src_geometry, ATTR_DOMAIN_POINT, all_inputs);
+ bke::FieldInputs scale_inputs = scale_field->prepare_inputs();
+ prepare_field_inputs(scale_inputs, src_geometry, ATTR_DOMAIN_POINT, all_inputs);
+ bke::FieldInputs id_inputs = id_field->prepare_inputs();
+ prepare_field_inputs(id_inputs, src_geometry, ATTR_DOMAIN_POINT, all_inputs);
+
+ IndexMask mask = IndexRange(domain_size);
+ bke::FieldOutput positions = position_field->evaluate(mask, position_inputs);
+ bke::FieldOutput rotations = rotation_field->evaluate(mask, rotation_inputs);
+ bke::FieldOutput scales = scale_field->evaluate(mask, scale_inputs);
+ bke::FieldOutput ids = id_field->evaluate(mask, id_inputs);
/* The initial size of the component might be non-zero if there are two component types. */
const int start_len = instances.instances_amount();
@@ -192,21 +228,40 @@ static void add_instances_from_component(InstancesComponent &instances,
threading::parallel_for(IndexRange(domain_size), 1024, [&](IndexRange range) {
for (const int i : range) {
handles[i] = handle;
- transforms[i] = float4x4::from_loc_eul_scale(positions[i], rotations[i], scales[i]);
- instance_ids[i] = id_attribute[i];
+
+ float3 position;
+ positions.varray_ref().get(i, &position);
+ float3 rotation;
+ rotations.varray_ref().get(i, &rotation);
+ float3 scale;
+ scales.varray_ref().get(i, &scale);
+ int id;
+ ids.varray_ref().get(i, &id);
+
+ transforms[i] = float4x4::from_loc_eul_scale(position, rotation, scale);
+ instance_ids[i] = id;
}
});
}
else {
const int seed = params.get_input<int>("Seed");
- Array<uint32_t> ids = get_geometry_element_ids_as_uints(src_geometry, ATTR_DOMAIN_POINT);
threading::parallel_for(IndexRange(domain_size), 1024, [&](IndexRange range) {
for (const int i : range) {
- const int index = BLI_hash_int_2d(ids[i], seed) % possible_handles.size();
+ float3 position;
+ positions.varray_ref().get(i, &position);
+ float3 rotation;
+ rotations.varray_ref().get(i, &rotation);
+ float3 scale;
+ scales.varray_ref().get(i, &scale);
+ int id;
+ ids.varray_ref().get(i, &id);
+
+ const int index = BLI_hash_int_2d(id, seed) % possible_handles.size();
const int handle = possible_handles[index];
handles[i] = handle;
- transforms[i] = float4x4::from_loc_eul_scale(positions[i], rotations[i], scales[i]);
- instance_ids[i] = id_attribute[i];
+
+ transforms[i] = float4x4::from_loc_eul_scale(position, rotation, scale);
+ instance_ids[i] = id;
}
});
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_point_separate.cc b/source/blender/nodes/geometry/nodes/node_geo_point_separate.cc
index fc04d1e275f..64a31454053 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_point_separate.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_point_separate.cc
@@ -25,7 +25,7 @@
static bNodeSocketTemplate geo_node_point_instance_in[] = {
{SOCK_GEOMETRY, N_("Geometry")},
- {SOCK_STRING, N_("Mask")},
+ {SOCK_BOOLEAN, N_("Mask"), 1, 0, 0, 0, 0, 0, PROP_NONE, SOCK_HIDE_VALUE | SOCK_FIELD},
{-1, ""},
};
@@ -99,17 +99,20 @@ static void create_component_points(GeometryComponent &component, const int tota
static void separate_points_from_component(const GeometryComponent &in_component,
GeometryComponent &out_component,
- const StringRef mask_name,
+ const bke::FieldRef<bool> mask_field,
const bool invert)
{
if (!in_component.attribute_domain_supported(ATTR_DOMAIN_POINT) ||
in_component.attribute_domain_size(ATTR_DOMAIN_POINT) == 0) {
return;
}
+ const int tot_in_points = in_component.attribute_domain_size(ATTR_DOMAIN_POINT);
- const GVArray_Typed<bool> mask_attribute = in_component.attribute_get_for_read<bool>(
- mask_name, ATTR_DOMAIN_POINT, false);
- VArray_Span<bool> masks{mask_attribute};
+ bke::FieldInputs field_inputs = mask_field->prepare_inputs();
+ Vector<std::unique_ptr<bke::FieldInputValue>> field_input_values;
+ prepare_field_inputs(field_inputs, in_component, ATTR_DOMAIN_POINT, field_input_values);
+ bke::FieldOutput field_output = mask_field->evaluate(IndexRange(tot_in_points), field_inputs);
+ GVArray_Span<bool> masks{field_output.varray_ref()};
const int total = masks.count(!invert);
if (total == 0) {
@@ -122,7 +125,7 @@ static void separate_points_from_component(const GeometryComponent &in_component
}
static GeometrySet separate_geometry_set(const GeometrySet &set_in,
- const StringRef mask_name,
+ const bke::FieldRef<bool> mask_field,
const bool invert)
{
GeometrySet set_out;
@@ -132,7 +135,7 @@ static GeometrySet separate_geometry_set(const GeometrySet &set_in,
continue;
}
GeometryComponent &out_component = set_out.get_component_for_write(component->type());
- separate_points_from_component(*component, out_component, mask_name, invert);
+ separate_points_from_component(*component, out_component, mask_field, invert);
}
return set_out;
}
@@ -145,7 +148,7 @@ static void geo_node_point_separate_exec(GeoNodeExecParams params)
if (wait_for_inputs) {
return;
}
- const std::string mask_attribute_name = params.get_input<std::string>("Mask");
+ bke::FieldRef<bool> mask_field = params.get_input_field<bool>("Mask");
GeometrySet geometry_set = params.get_input<GeometrySet>("Geometry");
/* TODO: This is not necessary-- the input geometry set can be read only,
@@ -153,12 +156,10 @@ static void geo_node_point_separate_exec(GeoNodeExecParams params)
geometry_set = geometry_set_realize_instances(geometry_set);
if (params.lazy_output_is_required("Geometry 1")) {
- params.set_output("Geometry 1",
- separate_geometry_set(geometry_set, mask_attribute_name, true));
+ params.set_output("Geometry 1", separate_geometry_set(geometry_set, mask_field, true));
}
if (params.lazy_output_is_required("Geometry 2")) {
- params.set_output("Geometry 2",
- separate_geometry_set(geometry_set, mask_attribute_name, false));
+ params.set_output("Geometry 2", separate_geometry_set(geometry_set, mask_field, false));
}
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_point_translate.cc b/source/blender/nodes/geometry/nodes/node_geo_point_translate.cc
index 293f151fe19..e9ec7b110c6 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_point_translate.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_point_translate.cc
@@ -21,8 +21,17 @@
static bNodeSocketTemplate geo_node_point_translate_in[] = {
{SOCK_GEOMETRY, N_("Geometry")},
- {SOCK_STRING, N_("Translation")},
- {SOCK_VECTOR, N_("Translation"), 0.0f, 0.0f, 0.0f, 1.0f, -FLT_MAX, FLT_MAX, PROP_TRANSLATION},
+ {SOCK_VECTOR,
+ N_("Translation"),
+ 0.0f,
+ 0.0f,
+ 0.0f,
+ 1.0f,
+ -FLT_MAX,
+ FLT_MAX,
+ PROP_TRANSLATION,
+ SOCK_FIELD},
+ {SOCK_BOOLEAN, N_("Selection"), 1, 0, 0, 0, 0, 0, PROP_NONE, SOCK_HIDE_VALUE | SOCK_FIELD},
{-1, ""},
};
@@ -31,13 +40,6 @@ static bNodeSocketTemplate geo_node_point_translate_out[] = {
{-1, ""},
};
-static void geo_node_point_translate_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
-{
- uiLayoutSetPropSep(layout, true);
- uiLayoutSetPropDecorate(layout, false);
- uiItemR(layout, ptr, "input_type", 0, IFACE_("Type"), ICON_NONE);
-}
-
namespace blender::nodes {
static void execute_on_component(GeoNodeExecParams params, GeometryComponent &component)
@@ -47,11 +49,29 @@ static void execute_on_component(GeoNodeExecParams params, GeometryComponent &co
if (!position_attribute) {
return;
}
- GVArray_Typed<float3> attribute = params.get_input_attribute<float3>(
- "Translation", component, ATTR_DOMAIN_POINT, {0, 0, 0});
- for (const int i : IndexRange(attribute.size())) {
- position_attribute->set(i, position_attribute->get(i) + attribute[i]);
+ bke::FieldRef<bool> selection_field = params.get_input_field<bool>("Selection");
+ bke::FieldInputs selection_field_inputs = selection_field->prepare_inputs();
+ Vector<std::unique_ptr<bke::FieldInputValue>> selection_field_input_values;
+ prepare_field_inputs(
+ selection_field_inputs, component, ATTR_DOMAIN_POINT, selection_field_input_values);
+ bke::FieldOutput selection_field_output = selection_field->evaluate(
+ IndexRange(component.attribute_domain_size(ATTR_DOMAIN_POINT)), selection_field_inputs);
+ GVArray_Typed<bool> selection{selection_field_output.varray_ref()};
+
+ bke::FieldRef<float3> field = params.get_input_field<float3>("Translation");
+ bke::FieldInputs field_inputs = field->prepare_inputs();
+ Vector<std::unique_ptr<bke::FieldInputValue>> field_input_values;
+ prepare_field_inputs(field_inputs, component, ATTR_DOMAIN_POINT, field_input_values);
+ bke::FieldOutput field_output = field->evaluate(
+ IndexRange(component.attribute_domain_size(ATTR_DOMAIN_POINT)), field_inputs);
+
+ GVArray_Typed<float3> translation{field_output.varray_ref()};
+
+ for (const int i : IndexRange(translation.size())) {
+ if (selection[i]) {
+ position_attribute->set(i, position_attribute->get(i) + translation[i]);
+ }
}
position_attribute.save();
@@ -108,6 +128,5 @@ void register_node_type_geo_point_translate()
node_free_standard_storage,
node_copy_standard_storage);
ntype.geometry_node_execute = blender::nodes::geo_node_point_translate_exec;
- ntype.draw_buttons = geo_node_point_translate_layout;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_points_to_volume.cc b/source/blender/nodes/geometry/nodes/node_geo_points_to_volume.cc
index 65306b1c452..38103309e6c 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
@@ -33,8 +33,7 @@ static bNodeSocketTemplate geo_node_points_to_volume_in[] = {
{SOCK_FLOAT, N_("Density"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, FLT_MAX},
{SOCK_FLOAT, N_("Voxel Size"), 0.3f, 0.0f, 0.0f, 0.0f, 0.01f, FLT_MAX, PROP_DISTANCE},
{SOCK_FLOAT, N_("Voxel Amount"), 64.0f, 0.0f, 0.0f, 0.0f, 0.0f, FLT_MAX},
- {SOCK_STRING, N_("Radius")},
- {SOCK_FLOAT, N_("Radius"), 0.5f, 0.0f, 0.0f, 0.0f, 0.0f, FLT_MAX},
+ {SOCK_FLOAT, N_("Radius"), 0.5f, 0.0f, 0.0f, 0.0f, 0.0f, FLT_MAX, PROP_NONE, SOCK_FIELD},
{-1, ""},
};
@@ -50,7 +49,6 @@ static void geo_node_points_to_volume_layout(uiLayout *layout,
uiLayoutSetPropSep(layout, true);
uiLayoutSetPropDecorate(layout, false);
uiItemR(layout, ptr, "resolution_mode", 0, IFACE_("Resolution"), ICON_NONE);
- uiItemR(layout, ptr, "input_type_radius", 0, IFACE_("Radius"), ICON_NONE);
}
namespace blender::nodes {
@@ -178,8 +176,15 @@ static void gather_point_data_from_component(const GeoNodeExecParams &params,
{
GVArray_Typed<float3> positions = component.attribute_get_for_read<float3>(
"position", ATTR_DOMAIN_POINT, {0, 0, 0});
- GVArray_Typed<float> radii = params.get_input_attribute<float>(
- "Radius", component, ATTR_DOMAIN_POINT, 0.0f);
+
+ bke::FieldRef<float> field = params.get_input_field<float>("Radius");
+ bke::FieldInputs field_inputs = field->prepare_inputs();
+ Vector<std::unique_ptr<bke::FieldInputValue>> field_input_values;
+ prepare_field_inputs(field_inputs, component, ATTR_DOMAIN_POINT, field_input_values);
+ bke::FieldOutput field_output = field->evaluate(
+ IndexRange(component.attribute_domain_size(ATTR_DOMAIN_POINT)), field_inputs);
+
+ GVArray_Typed<float> radii{field_output.varray_ref()};
for (const int i : IndexRange(positions.size())) {
r_positions.append(positions[i]);
diff --git a/source/blender/nodes/geometry/nodes/node_geo_position.cc b/source/blender/nodes/geometry/nodes/node_geo_position.cc
new file mode 100644
index 00000000000..b86928af491
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_position.cc
@@ -0,0 +1,45 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "node_geometry_util.hh"
+
+static bNodeSocketTemplate geo_node_position_out[] = {
+ {SOCK_VECTOR, N_("Position")},
+ {-1, ""},
+};
+
+namespace blender::nodes {
+
+static void geo_node_position_exec(GeoNodeExecParams params)
+{
+ FieldPtr position_field = new bke::PersistentAttributeField("position", CPPType::get<float3>());
+ params.set_output("Position", bke::FieldRef<float3>(std::move(position_field)));
+}
+
+} // namespace blender::nodes
+
+void register_node_type_geo_position()
+{
+ static bNodeType ntype;
+
+ geo_node_type_base(&ntype, GEO_NODE_POSITION, "Position", NODE_CLASS_INPUT, 0);
+ node_type_socket_templates(&ntype, nullptr, geo_node_position_out);
+ ntype.geometry_node_execute = blender::nodes::geo_node_position_exec;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_raycast.cc b/source/blender/nodes/geometry/nodes/node_geo_raycast.cc
index 88e3bf17d43..9d09d808234 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_raycast.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_raycast.cc
@@ -27,21 +27,28 @@
static bNodeSocketTemplate geo_node_raycast_in[] = {
{SOCK_GEOMETRY, N_("Geometry")},
{SOCK_GEOMETRY, N_("Target Geometry")},
- {SOCK_STRING, N_("Ray Direction")},
- {SOCK_VECTOR, N_("Ray Direction"), 0.0, 0.0, 1.0, 0.0, -FLT_MAX, FLT_MAX},
- {SOCK_STRING, N_("Ray Length")},
- {SOCK_FLOAT, N_("Ray Length"), 100.0, 0.0, 0.0, 0.0, 0.0f, FLT_MAX, PROP_DISTANCE},
+ {SOCK_VECTOR,
+ N_("Ray Direction"),
+ 0.0,
+ 0.0,
+ 1.0,
+ 0.0,
+ -FLT_MAX,
+ FLT_MAX,
+ PROP_NONE,
+ SOCK_FIELD},
+ {SOCK_FLOAT, N_("Ray Length"), 100.0, 0.0, 0.0, 0.0, 0.0f, FLT_MAX, PROP_DISTANCE, SOCK_FIELD},
{SOCK_STRING, N_("Target Attribute")},
- {SOCK_STRING, N_("Is Hit")},
- {SOCK_STRING, N_("Hit Position")},
- {SOCK_STRING, N_("Hit Normal")},
- {SOCK_STRING, N_("Hit Distance")},
{SOCK_STRING, N_("Hit Attribute")},
{-1, ""},
};
static bNodeSocketTemplate geo_node_raycast_out[] = {
{SOCK_GEOMETRY, N_("Geometry")},
+ {SOCK_BOOLEAN, N_("Is Hit")},
+ {SOCK_VECTOR, N_("Hit Position")},
+ {SOCK_VECTOR, N_("Hit Normal")},
+ {SOCK_FLOAT, N_("Hit Distance")},
{-1, ""},
};
@@ -50,33 +57,18 @@ static void geo_node_raycast_layout(uiLayout *layout, bContext *UNUSED(C), Point
uiLayoutSetPropSep(layout, true);
uiLayoutSetPropDecorate(layout, false);
uiItemR(layout, ptr, "mapping", 0, IFACE_("Mapping"), ICON_NONE);
- uiItemR(layout, ptr, "input_type_ray_direction", 0, IFACE_("Ray Direction"), ICON_NONE);
- uiItemR(layout, ptr, "input_type_ray_length", 0, IFACE_("Ray Length"), ICON_NONE);
}
static void geo_node_raycast_init(bNodeTree *UNUSED(tree), bNode *node)
{
NodeGeometryRaycast *data = (NodeGeometryRaycast *)MEM_callocN(sizeof(NodeGeometryRaycast),
__func__);
- data->input_type_ray_direction = GEO_NODE_ATTRIBUTE_INPUT_VECTOR;
- data->input_type_ray_length = GEO_NODE_ATTRIBUTE_INPUT_FLOAT;
node->storage = data;
}
namespace blender::nodes {
-static void geo_node_raycast_update(bNodeTree *UNUSED(ntree), bNode *node)
-{
- NodeGeometryRaycast *node_storage = (NodeGeometryRaycast *)node->storage;
- update_attribute_input_socket_availabilities(
- *node,
- "Ray Direction",
- (GeometryNodeAttributeInputMode)node_storage->input_type_ray_direction);
- update_attribute_input_socket_availabilities(
- *node, "Ray Length", (GeometryNodeAttributeInputMode)node_storage->input_type_ray_length);
-}
-
-static void raycast_to_mesh(const Mesh &mesh,
+static void raycast_to_mesh(const Mesh *mesh,
const VArray<float3> &ray_origins,
const VArray<float3> &ray_directions,
const VArray<float> &ray_lengths,
@@ -95,7 +87,7 @@ static void raycast_to_mesh(const Mesh &mesh,
BLI_assert(ray_origins.size() == r_hit_distances.size() || r_hit_distances.is_empty());
BVHTreeFromMesh tree_data;
- BKE_bvhtree_from_mesh_get(&tree_data, &mesh, BVHTREE_FROM_LOOPTRI, 4);
+ BKE_bvhtree_from_mesh_get(&tree_data, mesh, BVHTREE_FROM_LOOPTRI, 4);
if (tree_data.tree == nullptr) {
free_bvhtree_from_mesh(&tree_data);
return;
@@ -170,10 +162,10 @@ static bke::mesh_surface_sample::eAttributeMapMode get_map_mode(
static void raycast_from_points(const GeoNodeExecParams &params,
const GeometrySet &target_geometry,
GeometryComponent &dst_component,
- const StringRef hit_name,
- const StringRef hit_position_name,
- const StringRef hit_normal_name,
- const StringRef hit_distance_name,
+ const AnonymousCustomDataLayerID *hit_id,
+ const AnonymousCustomDataLayerID *hit_position_id,
+ const AnonymousCustomDataLayerID *hit_normal_id,
+ const AnonymousCustomDataLayerID *hit_distance_id,
const Span<std::string> hit_attribute_names,
const Span<std::string> hit_attribute_output_names)
{
@@ -199,55 +191,87 @@ static void raycast_from_points(const GeoNodeExecParams &params,
GVArray_Typed<float3> ray_origins = dst_component.attribute_get_for_read<float3>(
"position", result_domain, {0, 0, 0});
- GVArray_Typed<float3> ray_directions = params.get_input_attribute<float3>(
- "Ray Direction", dst_component, result_domain, {0, 0, 0});
- GVArray_Typed<float> ray_lengths = params.get_input_attribute<float>(
- "Ray Length", dst_component, result_domain, 0);
-
- OutputAttribute_Typed<bool> hit_attribute =
- dst_component.attribute_try_get_for_output_only<bool>(hit_name, result_domain);
- OutputAttribute_Typed<float3> hit_position_attribute =
- dst_component.attribute_try_get_for_output_only<float3>(hit_position_name, result_domain);
- OutputAttribute_Typed<float3> hit_normal_attribute =
- dst_component.attribute_try_get_for_output_only<float3>(hit_normal_name, result_domain);
- OutputAttribute_Typed<float> hit_distance_attribute =
- dst_component.attribute_try_get_for_output_only<float>(hit_distance_name, result_domain);
-
- /* Positions and looptri indices are always needed for interpolation,
- * so create temporary arrays if no output attribute is given. */
+
+ bke::FieldRef<float3> direction_field = params.get_input_field<float3>("Ray Direction");
+ bke::FieldInputs direction_field_inputs = direction_field->prepare_inputs();
+ Vector<std::unique_ptr<bke::FieldInputValue>> direction_field_input_values;
+ prepare_field_inputs(
+ direction_field_inputs, dst_component, ATTR_DOMAIN_POINT, direction_field_input_values);
+ bke::FieldOutput direction_field_output = direction_field->evaluate(
+ IndexRange(ray_origins->size()), direction_field_inputs);
+ GVArray_Typed<float3> ray_directions{direction_field_output.varray_ref()};
+
+ bke::FieldRef<float> ray_length_field = params.get_input_field<float>("Ray Length");
+ bke::FieldInputs ray_length_field_inputs = ray_length_field->prepare_inputs();
+ Vector<std::unique_ptr<bke::FieldInputValue>> ray_length_field_input_values;
+ prepare_field_inputs(
+ ray_length_field_inputs, dst_component, ATTR_DOMAIN_POINT, ray_length_field_input_values);
+ bke::FieldOutput ray_length_field_output = ray_length_field->evaluate(
+ IndexRange(ray_origins->size()), ray_length_field_inputs);
+ GVArray_Typed<float> ray_lengths{ray_length_field_output.varray_ref()};
+
+ std::optional<OutputAttribute_Typed<bool>> is_hit_attribute;
+ std::optional<OutputAttribute_Typed<float3>> hit_position_attribute;
+ std::optional<OutputAttribute_Typed<float3>> hit_normal_attribute;
+ std::optional<OutputAttribute_Typed<float>> hit_distance_attribute;
+
+ if (hit_id != nullptr) {
+ is_hit_attribute.emplace(dst_component.attribute_try_get_anonymous_for_output_only<bool>(
+ *hit_id, ATTR_DOMAIN_POINT));
+ }
+ if (hit_position_id != nullptr) {
+ hit_position_attribute.emplace(
+ dst_component.attribute_try_get_anonymous_for_output_only<float3>(*hit_position_id,
+ ATTR_DOMAIN_POINT));
+ }
+ if (hit_normal_id != nullptr) {
+ hit_normal_attribute.emplace(dst_component.attribute_try_get_anonymous_for_output_only<float3>(
+ *hit_normal_id, ATTR_DOMAIN_POINT));
+ }
+ if (hit_distance_id != nullptr) {
+ hit_distance_attribute.emplace(
+ dst_component.attribute_try_get_anonymous_for_output_only<float>(*hit_distance_id,
+ ATTR_DOMAIN_POINT));
+ }
+
Array<int> hit_indices;
- Array<float3> hit_positions_internal;
if (!hit_attribute_names.is_empty()) {
hit_indices.reinitialize(ray_origins->size());
+ }
- if (!hit_position_attribute) {
- hit_positions_internal.reinitialize(ray_origins->size());
- }
+ MutableSpan<float3> hit_positions;
+ Array<float3> hit_positions_internal;
+ if (hit_position_attribute) {
+ hit_positions = hit_position_attribute->as_span();
+ }
+ else {
+ hit_positions_internal.reinitialize(ray_origins->size());
+ hit_positions = hit_positions_internal;
}
- const MutableSpan<bool> is_hit = hit_attribute ? hit_attribute.as_span() : MutableSpan<bool>();
- const MutableSpan<float3> hit_positions = hit_position_attribute ?
- hit_position_attribute.as_span() :
- hit_positions_internal;
- const MutableSpan<float3> hit_normals = hit_normal_attribute ? hit_normal_attribute.as_span() :
- MutableSpan<float3>();
- const MutableSpan<float> hit_distances = hit_distance_attribute ?
- hit_distance_attribute.as_span() :
- MutableSpan<float>();
-
- raycast_to_mesh(*src_mesh,
+
+ raycast_to_mesh(src_mesh,
ray_origins,
ray_directions,
ray_lengths,
- is_hit,
+ is_hit_attribute ? is_hit_attribute->as_span() : MutableSpan<bool>(),
hit_indices,
hit_positions,
- hit_normals,
- hit_distances);
+ hit_normal_attribute ? hit_normal_attribute->as_span() : MutableSpan<float3>(),
+ hit_distance_attribute ? hit_distance_attribute->as_span() :
+ MutableSpan<float>());
- hit_attribute.save();
- hit_position_attribute.save();
- hit_normal_attribute.save();
- hit_distance_attribute.save();
+ if (is_hit_attribute) {
+ is_hit_attribute->save();
+ }
+ if (hit_position_attribute) {
+ hit_position_attribute->save();
+ }
+ if (hit_normal_attribute) {
+ hit_normal_attribute->save();
+ }
+ if (hit_distance_attribute) {
+ hit_distance_attribute->save();
+ }
/* Custom interpolated attributes */
bke::mesh_surface_sample::MeshAttributeInterpolator interp(src_mesh, hit_positions, hit_indices);
@@ -272,17 +296,31 @@ static void geo_node_raycast_exec(GeoNodeExecParams params)
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
GeometrySet target_geometry_set = params.extract_input<GeometrySet>("Target Geometry");
- const std::string hit_name = params.extract_input<std::string>("Is Hit");
- const std::string hit_position_name = params.extract_input<std::string>("Hit Position");
- const std::string hit_normal_name = params.extract_input<std::string>("Hit Normal");
- const std::string hit_distance_name = params.extract_input<std::string>("Hit Distance");
-
- const Array<std::string> hit_names = {params.extract_input<std::string>("Target Attribute")};
- const Array<std::string> hit_output_names = {params.extract_input<std::string>("Hit Attribute")};
+ const Array<std::string> hit_attribute_names = {
+ params.extract_input<std::string>("Target Attribute")};
+ const Array<std::string> hit_attribute_output_names = {
+ params.extract_input<std::string>("Hit Attribute")};
geometry_set = bke::geometry_set_realize_instances(geometry_set);
target_geometry_set = bke::geometry_set_realize_instances(target_geometry_set);
+ AnonymousCustomDataLayerID *hit_id = nullptr;
+ AnonymousCustomDataLayerID *hit_position_id = nullptr;
+ AnonymousCustomDataLayerID *hit_normal_id = nullptr;
+ AnonymousCustomDataLayerID *hit_distance_id = nullptr;
+ if (params.output_is_required("Is Hit")) {
+ hit_id = CustomData_anonymous_id_new("Is Hit");
+ }
+ if (params.output_is_required("Hit Position")) {
+ hit_position_id = CustomData_anonymous_id_new("Hit Position");
+ }
+ if (params.output_is_required("Hit Normal")) {
+ hit_normal_id = CustomData_anonymous_id_new("Hit Normal");
+ }
+ if (params.output_is_required("Hit Distance")) {
+ hit_distance_id = CustomData_anonymous_id_new("Hit Distance");
+ }
+
static const Array<GeometryComponentType> types = {
GEO_COMPONENT_TYPE_MESH, GEO_COMPONENT_TYPE_POINT_CLOUD, GEO_COMPONENT_TYPE_CURVE};
for (const GeometryComponentType type : types) {
@@ -290,15 +328,36 @@ static void geo_node_raycast_exec(GeoNodeExecParams params)
raycast_from_points(params,
target_geometry_set,
geometry_set.get_component_for_write(type),
- hit_name,
- hit_position_name,
- hit_normal_name,
- hit_distance_name,
- hit_names,
- hit_output_names);
+ hit_id,
+ hit_position_id,
+ hit_normal_id,
+ hit_distance_id,
+ hit_attribute_names,
+ hit_attribute_output_names);
}
}
+ if (hit_id != nullptr) {
+ params.set_output(
+ "Is Hit",
+ bke::FieldRef<bool>(new bke::AnonymousAttributeField(*hit_id, CPPType::get<bool>())));
+ }
+ if (hit_position_id != nullptr) {
+ params.set_output("Hit Position",
+ bke::FieldRef<float3>(new bke::AnonymousAttributeField(
+ *hit_position_id, CPPType::get<float3>())));
+ }
+ if (hit_normal_id != nullptr) {
+ params.set_output("Hit Normal",
+ bke::FieldRef<float3>(new bke::AnonymousAttributeField(
+ *hit_normal_id, CPPType::get<float3>())));
+ }
+ if (hit_distance_id != nullptr) {
+ params.set_output("Hit Distance",
+ bke::FieldRef<float>(new bke::AnonymousAttributeField(
+ *hit_distance_id, CPPType::get<float>())));
+ }
+
params.set_output("Geometry", geometry_set);
}
@@ -310,9 +369,8 @@ void register_node_type_geo_raycast()
geo_node_type_base(&ntype, GEO_NODE_RAYCAST, "Raycast", NODE_CLASS_GEOMETRY, 0);
node_type_socket_templates(&ntype, geo_node_raycast_in, geo_node_raycast_out);
- node_type_size_preset(&ntype, NODE_SIZE_LARGE);
+ node_type_size_preset(&ntype, NODE_SIZE_MIDDLE);
node_type_init(&ntype, geo_node_raycast_init);
- node_type_update(&ntype, blender::nodes::geo_node_raycast_update);
node_type_storage(
&ntype, "NodeGeometryRaycast", node_free_standard_storage, node_copy_standard_storage);
ntype.geometry_node_execute = blender::nodes::geo_node_raycast_exec;
diff --git a/source/blender/nodes/geometry/nodes/node_geo_sample_mesh_surface.cc b/source/blender/nodes/geometry/nodes/node_geo_sample_mesh_surface.cc
new file mode 100644
index 00000000000..6b1beaba2fd
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_sample_mesh_surface.cc
@@ -0,0 +1,193 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "BLI_kdopbvh.h"
+
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+
+#include "BKE_attribute_math.hh"
+#include "BKE_bvhutils.h"
+#include "BKE_customdata.h"
+#include "BKE_mesh_runtime.h"
+#include "BKE_mesh_sample.hh"
+
+#include "node_geometry_util.hh"
+
+static bNodeSocketTemplate geo_node_sample_mesh_surface_in[] = {
+ {SOCK_GEOMETRY, N_("Mesh")},
+ {SOCK_VECTOR,
+ N_("Position"),
+ 0.0f,
+ 0.0f,
+ 0.0f,
+ 1.0f,
+ -FLT_MAX,
+ FLT_MAX,
+ PROP_TRANSLATION,
+ SOCK_HIDE_VALUE | SOCK_FIELD},
+ {SOCK_RGBA, N_("Custom"), 1, 1, 1, 1, 0, 1, PROP_NONE, SOCK_FIELD},
+ {-1, ""},
+};
+
+static bNodeSocketTemplate geo_node_sample_mesh_surface_out[] = {
+ {SOCK_VECTOR, N_("Position")},
+ {SOCK_VECTOR, N_("Normal")},
+ {SOCK_FLOAT, N_("Distance")},
+ {SOCK_RGBA, N_("Custom")},
+ {-1, ""},
+};
+
+namespace blender::nodes {
+
+class SampleMeshSurfaceFunction : public fn::MultiFunction {
+ private:
+ GeometrySet geometry_set_;
+ AnonymousCustomDataLayerID *attribute_id_;
+
+ public:
+ SampleMeshSurfaceFunction(GeometrySet geometry_set, AnonymousCustomDataLayerID *attribute_id)
+ : geometry_set_(std::move(geometry_set)), attribute_id_(attribute_id)
+ {
+ static fn::MFSignature signature = create_signature();
+ this->set_signature(&signature);
+ CustomData_anonymous_id_strong_increment(attribute_id_);
+ }
+
+ ~SampleMeshSurfaceFunction() override
+ {
+ CustomData_anonymous_id_strong_decrement(attribute_id_);
+ }
+
+ static blender::fn::MFSignature create_signature()
+ {
+ blender::fn::MFSignatureBuilder signature{"Sample Mesh Surface"};
+ signature.single_input<float3>("Position");
+ signature.single_output<float3>("Position");
+ signature.single_output<float3>("Normal");
+ signature.single_output<float>("Distance");
+ signature.single_output<ColorGeometry4f>("Custom");
+ return signature.build();
+ }
+
+ void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override
+ {
+ const VArray<float3> &src_positions = params.readonly_single_input<float3>(0, "Position");
+ MutableSpan<float3> sampled_positions = params.uninitialized_single_output<float3>(1,
+ "Position");
+ MutableSpan<float3> sampled_normals = params.uninitialized_single_output<float3>(2, "Normal");
+ MutableSpan<float> sampled_distances = params.uninitialized_single_output<float>(3,
+ "Distance");
+ MutableSpan<ColorGeometry4f> sampled_custom =
+ params.uninitialized_single_output<ColorGeometry4f>(4, "Custom");
+
+ auto return_default = [&]() {
+ sampled_positions.fill_indices(mask, {0, 0, 0});
+ sampled_normals.fill_indices(mask, {0, 0, 0});
+ sampled_distances.fill_indices(mask, 0.0f);
+ sampled_custom.fill_indices(mask, {0, 0, 0, 1});
+ };
+
+ if (!geometry_set_.has_mesh()) {
+ return return_default();
+ }
+
+ const MeshComponent *mesh_component = geometry_set_.get_component_for_read<MeshComponent>();
+ const Mesh *mesh = mesh_component->get_for_read();
+
+ GVArrayPtr attribute_ptr = mesh_component->attribute_try_get_anonymous_for_read(
+ *attribute_id_, ATTR_DOMAIN_CORNER, CD_PROP_COLOR, nullptr);
+ if (!attribute_ptr) {
+ return return_default();
+ }
+ GVArray_Typed<ColorGeometry4f> attribute{*attribute_ptr};
+
+ const MLoopTri *looptris = BKE_mesh_runtime_looptri_ensure(mesh);
+
+ BVHTreeFromMesh tree_data;
+ BKE_bvhtree_from_mesh_get(&tree_data, mesh, BVHTREE_FROM_LOOPTRI, 2);
+
+ for (const int i : mask) {
+ BVHTreeNearest nearest;
+ nearest.dist_sq = FLT_MAX;
+ const float3 src_position = src_positions[i];
+ BLI_bvhtree_find_nearest(
+ tree_data.tree, src_position, &nearest, tree_data.nearest_callback, &tree_data);
+ sampled_positions[i] = nearest.co;
+ sampled_normals[i] = nearest.no;
+ sampled_distances[i] = sqrtf(nearest.dist_sq);
+
+ const MLoopTri &looptri = looptris[nearest.index];
+
+ float3 v1 = mesh->mvert[mesh->mloop[looptri.tri[0]].v].co;
+ float3 v2 = mesh->mvert[mesh->mloop[looptri.tri[1]].v].co;
+ float3 v3 = mesh->mvert[mesh->mloop[looptri.tri[2]].v].co;
+
+ ColorGeometry4f col1 = attribute[looptri.tri[0]];
+ ColorGeometry4f col2 = attribute[looptri.tri[1]];
+ ColorGeometry4f col3 = attribute[looptri.tri[2]];
+
+ float3 bary_coords;
+ interp_weights_tri_v3(bary_coords, v1, v2, v3, nearest.co);
+ ColorGeometry4f final_col = attribute_math::mix3(bary_coords, col1, col2, col3);
+ sampled_custom[i] = final_col;
+ }
+ }
+};
+
+static void geo_node_sample_mesh_surface_exec(GeoNodeExecParams params)
+{
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Mesh");
+ geometry_set = geometry_set_realize_instances(geometry_set);
+
+ FieldPtr position_field = params.get_input_field<float3>("Position").field();
+ bke::FieldRef<ColorGeometry4f> attribute_field = params.get_input_field<ColorGeometry4f>(
+ "Custom");
+
+ AnonymousCustomDataLayerID *layer_id = CustomData_anonymous_id_new("Sample Mesh Surface");
+ MeshComponent &mesh_component = geometry_set.get_component_for_write<MeshComponent>();
+ try_freeze_field_on_geometry(
+ mesh_component, *layer_id, ATTR_DOMAIN_CORNER, *attribute_field.field());
+
+ auto make_output_field = [&](int out_param_index) -> FieldPtr {
+ auto fn = std::make_unique<SampleMeshSurfaceFunction>(geometry_set, layer_id);
+ return new bke::MultiFunctionField(Vector<FieldPtr>{position_field},
+ optional_ptr<const fn::MultiFunction>{std::move(fn)},
+ out_param_index);
+ };
+
+ params.set_output("Position", bke::FieldRef<float3>(make_output_field(1)));
+ params.set_output("Normal", bke::FieldRef<float3>(make_output_field(2)));
+ params.set_output("Distance", bke::FieldRef<float>(make_output_field(3)));
+ params.set_output("Custom", bke::FieldRef<ColorGeometry4f>(make_output_field(4)));
+}
+
+} // namespace blender::nodes
+
+void register_node_type_geo_sample_mesh_surface()
+{
+ static bNodeType ntype;
+
+ geo_node_type_base(
+ &ntype, GEO_NODE_SAMPLE_MESH_SURFACE, "Sample Mesh Surface", NODE_CLASS_ATTRIBUTE, 0);
+ node_type_socket_templates(
+ &ntype, geo_node_sample_mesh_surface_in, geo_node_sample_mesh_surface_out);
+ ntype.geometry_node_execute = blender::nodes::geo_node_sample_mesh_surface_exec;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_select_by_material.cc b/source/blender/nodes/geometry/nodes/node_geo_select_by_material.cc
index ee114741a77..9f9bc85b8f6 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_select_by_material.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_select_by_material.cc
@@ -38,12 +38,12 @@ static bNodeSocketTemplate geo_node_select_by_material_in[] = {
0.0f,
PROP_NONE,
SOCK_HIDE_LABEL},
- {SOCK_STRING, N_("Selection")},
{-1, ""},
};
static bNodeSocketTemplate geo_node_select_by_material_out[] = {
{SOCK_GEOMETRY, N_("Geometry")},
+ {SOCK_BOOLEAN, N_("Selection")},
{-1, ""},
};
@@ -60,6 +60,7 @@ static void select_mesh_by_material(const Mesh &mesh,
material_indices.append(i);
}
}
+
threading::parallel_for(r_selection.index_range(), 1024, [&](IndexRange range) {
for (const int i : range) {
r_selection[i] = material_indices.contains(mesh.mpoly[i].mat_nr);
@@ -70,25 +71,35 @@ static void select_mesh_by_material(const Mesh &mesh,
static void geo_node_select_by_material_exec(GeoNodeExecParams params)
{
Material *material = params.extract_input<Material *>("Material");
- const std::string selection_name = params.extract_input<std::string>("Selection");
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
geometry_set = geometry_set_realize_instances(geometry_set);
+ AnonymousCustomDataLayerID *id = params.output_is_required("Selection") ?
+ CustomData_anonymous_id_new("Selection") :
+ nullptr;
+ if (id == nullptr) {
+ params.set_output("Geometry", std::move(geometry_set));
+ return;
+ }
+
if (geometry_set.has<MeshComponent>()) {
MeshComponent &mesh_component = geometry_set.get_component_for_write<MeshComponent>();
const Mesh *mesh = mesh_component.get_for_read();
if (mesh != nullptr) {
- OutputAttribute_Typed<bool> selection =
- mesh_component.attribute_try_get_for_output_only<bool>(selection_name, ATTR_DOMAIN_FACE);
- if (selection) {
- select_mesh_by_material(*mesh, material, selection.as_span());
- selection.save();
- }
+ mesh_component.attribute_try_create_anonymous(
+ *id, ATTR_DOMAIN_FACE, CD_PROP_BOOL, AttributeInitDefault());
+ WriteAttributeLookup attribute = mesh_component.attribute_try_get_anonymous_for_write(*id);
+ MutableSpan<bool> selection = attribute.varray->get_internal_span().typed<bool>();
+
+ select_mesh_by_material(*mesh, material, selection);
}
}
params.set_output("Geometry", std::move(geometry_set));
+ params.set_output(
+ "Selection",
+ bke::FieldRef<bool>(new bke::AnonymousAttributeField(*id, CPPType::get<bool>())));
}
} // namespace blender::nodes
diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_position.cc b/source/blender/nodes/geometry/nodes/node_geo_set_position.cc
new file mode 100644
index 00000000000..aa99d1503f4
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_set_position.cc
@@ -0,0 +1,109 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "node_geometry_util.hh"
+
+static bNodeSocketTemplate geo_node_set_position_in[] = {
+ {SOCK_GEOMETRY, N_("Geometry")},
+ {SOCK_VECTOR,
+ N_("Position"),
+ 0.0f,
+ 0.0f,
+ 0.0f,
+ 1.0f,
+ -FLT_MAX,
+ FLT_MAX,
+ PROP_TRANSLATION,
+ SOCK_FIELD},
+ {SOCK_BOOLEAN, N_("Selection"), 1, 0, 0, 0, 0, 0, PROP_NONE, SOCK_HIDE_VALUE | SOCK_FIELD},
+ {-1, ""},
+};
+
+static bNodeSocketTemplate geo_node_set_position_out[] = {
+ {SOCK_GEOMETRY, N_("Geometry")},
+ {-1, ""},
+};
+
+namespace blender::nodes {
+
+static void execute_on_component(GeoNodeExecParams params, GeometryComponent &component)
+{
+ OutputAttribute_Typed<float3> position_attribute =
+ component.attribute_try_get_for_output<float3>("position", ATTR_DOMAIN_POINT, {0, 0, 0});
+ if (!position_attribute) {
+ return;
+ }
+
+ bke::FieldRef<bool> selection_field = params.get_input_field<bool>("Selection");
+ bke::FieldInputs selection_field_inputs = selection_field->prepare_inputs();
+ Vector<std::unique_ptr<bke::FieldInputValue>> selection_field_input_values;
+ prepare_field_inputs(
+ selection_field_inputs, component, ATTR_DOMAIN_POINT, selection_field_input_values);
+ bke::FieldOutput selection_field_output = selection_field->evaluate(
+ IndexRange(component.attribute_domain_size(ATTR_DOMAIN_POINT)), selection_field_inputs);
+ GVArray_Typed<bool> selection{selection_field_output.varray_ref()};
+
+ bke::FieldRef<float3> field = params.get_input_field<float3>("Position");
+ bke::FieldInputs field_inputs = field->prepare_inputs();
+ Vector<std::unique_ptr<bke::FieldInputValue>> field_input_values;
+ prepare_field_inputs(field_inputs, component, ATTR_DOMAIN_POINT, field_input_values);
+ bke::FieldOutput field_output = field->evaluate(
+ IndexRange(component.attribute_domain_size(ATTR_DOMAIN_POINT)), field_inputs);
+
+ GVArray_Typed<float3> new_positions{field_output.varray_ref()};
+
+ for (const int i : IndexRange(new_positions.size())) {
+ if (selection[i]) {
+ position_attribute->set(i, new_positions[i]);
+ }
+ }
+
+ position_attribute.save();
+}
+
+static void geo_node_set_position_exec(GeoNodeExecParams params)
+{
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
+
+ geometry_set = geometry_set_realize_instances(geometry_set);
+
+ if (geometry_set.has<MeshComponent>()) {
+ execute_on_component(params, geometry_set.get_component_for_write<MeshComponent>());
+ }
+ if (geometry_set.has<PointCloudComponent>()) {
+ execute_on_component(params, geometry_set.get_component_for_write<PointCloudComponent>());
+ }
+ if (geometry_set.has<CurveComponent>()) {
+ execute_on_component(params, geometry_set.get_component_for_write<CurveComponent>());
+ }
+
+ params.set_output("Geometry", std::move(geometry_set));
+}
+
+} // namespace blender::nodes
+
+void register_node_type_geo_set_position()
+{
+ static bNodeType ntype;
+
+ geo_node_type_base(&ntype, GEO_NODE_SET_POSITION, "Set Position", NODE_CLASS_GEOMETRY, 0);
+ node_type_socket_templates(&ntype, geo_node_set_position_in, geo_node_set_position_out);
+ ntype.geometry_node_execute = blender::nodes::geo_node_set_position_exec;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_switch.cc b/source/blender/nodes/geometry/nodes/node_geo_switch.cc
index c9ce2de1ea1..67b7065ca94 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_switch.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_switch.cc
@@ -22,16 +22,16 @@
static bNodeSocketTemplate geo_node_switch_in[] = {
{SOCK_BOOLEAN, N_("Switch")},
- {SOCK_FLOAT, N_("False"), 0.0, 0.0, 0.0, 0.0, -FLT_MAX, FLT_MAX},
- {SOCK_FLOAT, N_("True"), 0.0, 0.0, 0.0, 0.0, -FLT_MAX, FLT_MAX},
- {SOCK_INT, N_("False"), 0, 0, 0, 0, -100000, 100000},
- {SOCK_INT, N_("True"), 0, 0, 0, 0, -100000, 100000},
- {SOCK_BOOLEAN, N_("False")},
- {SOCK_BOOLEAN, N_("True")},
- {SOCK_VECTOR, N_("False"), 0.0, 0.0, 0.0, 0.0, -FLT_MAX, FLT_MAX},
- {SOCK_VECTOR, N_("True"), 0.0, 0.0, 0.0, 0.0, -FLT_MAX, FLT_MAX},
- {SOCK_RGBA, N_("False"), 0.8, 0.8, 0.8, 1.0},
- {SOCK_RGBA, N_("True"), 0.8, 0.8, 0.8, 1.0},
+ {SOCK_FLOAT, N_("False"), 0.0, 0.0, 0.0, 0.0, -FLT_MAX, FLT_MAX, PROP_NONE, SOCK_FIELD},
+ {SOCK_FLOAT, N_("True"), 0.0, 0.0, 0.0, 0.0, -FLT_MAX, FLT_MAX, PROP_NONE, SOCK_FIELD},
+ {SOCK_INT, N_("False"), 0, 0, 0, 0, -100000, 100000, PROP_NONE, SOCK_FIELD},
+ {SOCK_INT, N_("True"), 0, 0, 0, 0, -100000, 100000, PROP_NONE, SOCK_FIELD},
+ {SOCK_BOOLEAN, N_("False"), 0.0, 0.0, 0.0, 0.0, -FLT_MAX, FLT_MAX, PROP_NONE, SOCK_FIELD},
+ {SOCK_BOOLEAN, N_("True"), 0.0, 0.0, 0.0, 0.0, -FLT_MAX, FLT_MAX, PROP_NONE, SOCK_FIELD},
+ {SOCK_VECTOR, N_("False"), 0.0, 0.0, 0.0, 0.0, -FLT_MAX, FLT_MAX, PROP_NONE, SOCK_FIELD},
+ {SOCK_VECTOR, N_("True"), 0.0, 0.0, 0.0, 0.0, -FLT_MAX, FLT_MAX, PROP_NONE, SOCK_FIELD},
+ {SOCK_RGBA, N_("False"), 0.8, 0.8, 0.8, 1.0, -FLT_MAX, FLT_MAX, PROP_NONE, SOCK_FIELD},
+ {SOCK_RGBA, N_("True"), 0.8, 0.8, 0.8, 1.0, -FLT_MAX, FLT_MAX, PROP_NONE, SOCK_FIELD},
{SOCK_STRING, N_("False")},
{SOCK_STRING, N_("True")},
{SOCK_GEOMETRY, N_("False")},
@@ -92,6 +92,31 @@ static void geo_node_switch_update(bNodeTree *UNUSED(ntree), bNode *node)
}
template<typename T>
+static void output_input_field(GeoNodeExecParams &params,
+ const bool input,
+ const StringRef input_suffix,
+ const StringRef output_identifier)
+{
+ const std::string name_a = "False" + input_suffix;
+ const std::string name_b = "True" + input_suffix;
+ if (input) {
+ params.set_input_unused(name_a);
+ if (params.lazy_require_input(name_b)) {
+ return;
+ }
+
+ params.set_output(output_identifier, params.get_input_field<T>(name_b));
+ }
+ else {
+ params.set_input_unused(name_b);
+ if (params.lazy_require_input(name_a)) {
+ return;
+ }
+ params.set_output(output_identifier, params.get_input_field<T>(name_a));
+ }
+}
+
+template<typename T>
static void output_input(GeoNodeExecParams &params,
const bool input,
const StringRef input_suffix,
@@ -124,19 +149,19 @@ static void geo_node_switch_exec(GeoNodeExecParams params)
const bool input = params.get_input<bool>("Switch");
switch ((eNodeSocketDatatype)storage.input_type) {
case SOCK_FLOAT: {
- output_input<float>(params, input, "", "Output");
+ output_input_field<float>(params, input, "", "Output");
break;
}
case SOCK_INT: {
- output_input<int>(params, input, "_001", "Output_001");
+ output_input_field<int>(params, input, "_001", "Output_001");
break;
}
case SOCK_BOOLEAN: {
- output_input<bool>(params, input, "_002", "Output_002");
+ output_input_field<bool>(params, input, "_002", "Output_002");
break;
}
case SOCK_VECTOR: {
- output_input<float3>(params, input, "_003", "Output_003");
+ output_input_field<float3>(params, input, "_003", "Output_003");
break;
}
case SOCK_RGBA: {
diff --git a/source/blender/nodes/intern/geometry_nodes_eval_log.cc b/source/blender/nodes/intern/geometry_nodes_eval_log.cc
index 7487f11d77d..d07bf30789c 100644
--- a/source/blender/nodes/intern/geometry_nodes_eval_log.cc
+++ b/source/blender/nodes/intern/geometry_nodes_eval_log.cc
@@ -106,6 +106,26 @@ void ModifierLog::foreach_node_log(FunctionRef<void(const NodeLog &)> fn) const
}
}
+Vector<const GeometryAttributeInfo *> ModifierLog::lookup_available_attributes() const
+{
+ Vector<const GeometryAttributeInfo *> attributes;
+ Set<StringRef> names;
+ this->foreach_node_log([&](const NodeLog &node_log) {
+ for (const SocketLog &socket_log : node_log.input_logs()) {
+ const ValueLog *value_log = socket_log.value();
+ if (const GeometryValueLog *geo_value_log = dynamic_cast<const GeometryValueLog *>(
+ value_log)) {
+ for (const GeometryAttributeInfo &attribute : geo_value_log->attributes()) {
+ if (names.add(attribute.name)) {
+ attributes.append(&attribute);
+ }
+ }
+ }
+ }
+ });
+ return attributes;
+}
+
const NodeLog *TreeLog::lookup_node_log(StringRef node_name) const
{
const destruct_ptr<NodeLog> *node_log = node_logs_.lookup_ptr_as(node_name);
@@ -162,6 +182,9 @@ GeometryValueLog::GeometryValueLog(const GeometrySet &geometry_set, bool log_ful
bke::geometry_set_instances_attribute_foreach(
geometry_set,
[&](StringRefNull attribute_name, const AttributeMetaData &meta_data) {
+ if (meta_data.anonymous_layer_id != nullptr) {
+ return true;
+ }
this->attributes_.append({attribute_name, meta_data.domain, meta_data.data_type});
return true;
},
diff --git a/source/blender/nodes/intern/node_socket.cc b/source/blender/nodes/intern/node_socket.cc
index 528616eb23a..c761dfa473a 100644
--- a/source/blender/nodes/intern/node_socket.cc
+++ b/source/blender/nodes/intern/node_socket.cc
@@ -32,6 +32,7 @@
#include "BLI_string.h"
#include "BLI_utildefines.h"
+#include "BKE_field.hh"
#include "BKE_geometry_set.hh"
#include "BKE_lib_id.h"
#include "BKE_node.h"
@@ -610,8 +611,15 @@ static bNodeSocketType *make_socket_type_bool()
socktype->get_base_cpp_value = [](const bNodeSocket &socket, void *r_value) {
*(bool *)r_value = ((bNodeSocketValueBoolean *)socket.default_value)->value;
};
- socktype->get_geometry_nodes_cpp_type = socktype->get_base_cpp_type;
- socktype->get_geometry_nodes_cpp_value = socktype->get_base_cpp_value;
+ socktype->get_geometry_nodes_cpp_type = []() {
+ return &blender::fn::CPPType::get<blender::bke::FieldRef<bool>>();
+ };
+ socktype->get_geometry_nodes_cpp_value = [](const bNodeSocket &socket, void *r_value) {
+ bool value;
+ socket.typeinfo->get_base_cpp_value(socket, &value);
+ new (r_value) blender::bke::FieldRef<bool>(
+ blender::bke::FieldPtr{new blender::bke::ConstantField<bool>(value)});
+ };
return socktype;
}
@@ -622,8 +630,15 @@ static bNodeSocketType *make_socket_type_float(PropertySubType subtype)
socktype->get_base_cpp_value = [](const bNodeSocket &socket, void *r_value) {
*(float *)r_value = ((bNodeSocketValueFloat *)socket.default_value)->value;
};
- socktype->get_geometry_nodes_cpp_type = socktype->get_base_cpp_type;
- socktype->get_geometry_nodes_cpp_value = socktype->get_base_cpp_value;
+ socktype->get_geometry_nodes_cpp_type = []() {
+ return &blender::fn::CPPType::get<blender::bke::FieldRef<float>>();
+ };
+ socktype->get_geometry_nodes_cpp_value = [](const bNodeSocket &socket, void *r_value) {
+ float value;
+ socket.typeinfo->get_base_cpp_value(socket, &value);
+ new (r_value) blender::bke::FieldRef<float>(
+ blender::bke::FieldPtr{new blender::bke::ConstantField<float>(value)});
+ };
return socktype;
}
@@ -634,8 +649,15 @@ static bNodeSocketType *make_socket_type_int(PropertySubType subtype)
socktype->get_base_cpp_value = [](const bNodeSocket &socket, void *r_value) {
*(int *)r_value = ((bNodeSocketValueInt *)socket.default_value)->value;
};
- socktype->get_geometry_nodes_cpp_type = socktype->get_base_cpp_type;
- socktype->get_geometry_nodes_cpp_value = socktype->get_base_cpp_value;
+ socktype->get_geometry_nodes_cpp_type = []() {
+ return &blender::fn::CPPType::get<blender::bke::FieldRef<int>>();
+ };
+ socktype->get_geometry_nodes_cpp_value = [](const bNodeSocket &socket, void *r_value) {
+ int value;
+ socket.typeinfo->get_base_cpp_value(socket, &value);
+ new (r_value) blender::bke::FieldRef<int>(
+ blender::bke::FieldPtr{new blender::bke::ConstantField<int>(value)});
+ };
return socktype;
}
@@ -646,8 +668,22 @@ static bNodeSocketType *make_socket_type_vector(PropertySubType subtype)
socktype->get_base_cpp_value = [](const bNodeSocket &socket, void *r_value) {
*(blender::float3 *)r_value = ((bNodeSocketValueVector *)socket.default_value)->value;
};
- socktype->get_geometry_nodes_cpp_type = socktype->get_base_cpp_type;
- socktype->get_geometry_nodes_cpp_value = socktype->get_base_cpp_value;
+ socktype->get_geometry_nodes_cpp_type = []() {
+ return &blender::fn::CPPType::get<blender::bke::FieldRef<blender::float3>>();
+ };
+ socktype->get_geometry_nodes_cpp_value = [](const bNodeSocket &socket, void *r_value) {
+ if (socket.in_out == SOCK_IN && (socket.flag & SOCK_HIDE_VALUE)) {
+ new (r_value) blender::bke::FieldRef<blender::float3>(
+ blender::bke::FieldPtr{new blender::bke::PersistentAttributeField(
+ "position", blender::fn::CPPType::get<blender::float3>())});
+ }
+ else {
+ blender::float3 value;
+ socket.typeinfo->get_base_cpp_value(socket, &value);
+ new (r_value) blender::bke::FieldRef<blender::float3>(
+ blender::bke::FieldPtr{new blender::bke::ConstantField<blender::float3>(value)});
+ }
+ };
return socktype;
}
@@ -660,8 +696,15 @@ static bNodeSocketType *make_socket_type_rgba()
socktype->get_base_cpp_value = [](const bNodeSocket &socket, void *r_value) {
*(blender::ColorGeometry4f *)r_value = ((bNodeSocketValueRGBA *)socket.default_value)->value;
};
- socktype->get_geometry_nodes_cpp_type = socktype->get_base_cpp_type;
- socktype->get_geometry_nodes_cpp_value = socktype->get_base_cpp_value;
+ socktype->get_geometry_nodes_cpp_type = []() {
+ return &blender::fn::CPPType::get<blender::bke::FieldRef<blender::ColorGeometry4f>>();
+ };
+ socktype->get_geometry_nodes_cpp_value = [](const bNodeSocket &socket, void *r_value) {
+ blender::ColorGeometry4f value;
+ socket.typeinfo->get_base_cpp_value(socket, &value);
+ new (r_value) blender::bke::FieldRef<blender::ColorGeometry4f>(
+ blender::bke::FieldPtr{new blender::bke::ConstantField<blender::ColorGeometry4f>(value)});
+ };
return socktype;
}
diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_noise.c b/source/blender/nodes/shader/nodes/node_shader_tex_noise.c
deleted file mode 100644
index 7b67c2d1f2e..00000000000
--- a/source/blender/nodes/shader/nodes/node_shader_tex_noise.c
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- * The Original Code is Copyright (C) 2005 Blender Foundation.
- * All rights reserved.
- */
-
-#include "../node_shader_util.h"
-
-/* **************** NOISE ******************** */
-
-static bNodeSocketTemplate sh_node_tex_noise_in[] = {
- {SOCK_VECTOR, N_("Vector"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_NONE, SOCK_HIDE_VALUE},
- {SOCK_FLOAT, N_("W"), 0.0f, 0.0f, 0.0f, 0.0f, -1000.0f, 1000.0f},
- {SOCK_FLOAT, N_("Scale"), 5.0f, 0.0f, 0.0f, 0.0f, -1000.0f, 1000.0f},
- {SOCK_FLOAT, N_("Detail"), 2.0f, 0.0f, 0.0f, 0.0f, 0.0f, 16.0f},
- {SOCK_FLOAT, N_("Roughness"), 0.5f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR},
- {SOCK_FLOAT, N_("Distortion"), 0.0f, 0.0f, 0.0f, 0.0f, -1000.0f, 1000.0f},
- {-1, ""},
-};
-
-static bNodeSocketTemplate sh_node_tex_noise_out[] = {
- {SOCK_FLOAT,
- N_("Fac"),
- 0.0f,
- 0.0f,
- 0.0f,
- 0.0f,
- 0.0f,
- 1.0f,
- PROP_FACTOR,
- SOCK_NO_INTERNAL_LINK},
- {SOCK_RGBA, N_("Color"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_NONE, SOCK_NO_INTERNAL_LINK},
- {-1, ""},
-};
-
-static void node_shader_init_tex_noise(bNodeTree *UNUSED(ntree), bNode *node)
-{
- NodeTexNoise *tex = MEM_callocN(sizeof(NodeTexNoise), "NodeTexNoise");
- BKE_texture_mapping_default(&tex->base.tex_mapping, TEXMAP_TYPE_POINT);
- BKE_texture_colormapping_default(&tex->base.color_mapping);
- tex->dimensions = 3;
-
- node->storage = tex;
-}
-
-static int node_shader_gpu_tex_noise(GPUMaterial *mat,
- bNode *node,
- bNodeExecData *UNUSED(execdata),
- GPUNodeStack *in,
- GPUNodeStack *out)
-{
- node_shader_gpu_default_tex_coord(mat, node, &in[0].link);
- node_shader_gpu_tex_mapping(mat, node, in, out);
-
- NodeTexNoise *tex = (NodeTexNoise *)node->storage;
- static const char *names[] = {
- "",
- "node_noise_texture_1d",
- "node_noise_texture_2d",
- "node_noise_texture_3d",
- "node_noise_texture_4d",
- };
- return GPU_stack_link(mat, node, names[tex->dimensions], in, out);
-}
-
-static void node_shader_update_tex_noise(bNodeTree *UNUSED(ntree), bNode *node)
-{
- bNodeSocket *sockVector = nodeFindSocket(node, SOCK_IN, "Vector");
- bNodeSocket *sockW = nodeFindSocket(node, SOCK_IN, "W");
-
- NodeTexNoise *tex = (NodeTexNoise *)node->storage;
- nodeSetSocketAvailability(sockVector, tex->dimensions != 1);
- nodeSetSocketAvailability(sockW, tex->dimensions == 1 || tex->dimensions == 4);
-}
-
-/* node type definition */
-void register_node_type_sh_tex_noise(void)
-{
- static bNodeType ntype;
-
- sh_node_type_base(&ntype, SH_NODE_TEX_NOISE, "Noise Texture", NODE_CLASS_TEXTURE, 0);
- node_type_socket_templates(&ntype, sh_node_tex_noise_in, sh_node_tex_noise_out);
- node_type_init(&ntype, node_shader_init_tex_noise);
- node_type_storage(
- &ntype, "NodeTexNoise", node_free_standard_storage, node_copy_standard_storage);
- node_type_gpu(&ntype, node_shader_gpu_tex_noise);
- node_type_update(&ntype, node_shader_update_tex_noise);
-
- nodeRegisterType(&ntype);
-}
diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_noise.cc b/source/blender/nodes/shader/nodes/node_shader_tex_noise.cc
new file mode 100644
index 00000000000..a9ff82f42ea
--- /dev/null
+++ b/source/blender/nodes/shader/nodes/node_shader_tex_noise.cc
@@ -0,0 +1,224 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2005 Blender Foundation.
+ * All rights reserved.
+ */
+
+#include "BLI_noise.h"
+
+#include "../node_shader_util.h"
+
+/* **************** NOISE ******************** */
+
+static bNodeSocketTemplate sh_node_tex_noise_in[] = {
+ {SOCK_VECTOR, N_("Vector"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_NONE, SOCK_HIDE_VALUE},
+ {SOCK_FLOAT, N_("W"), 0.0f, 0.0f, 0.0f, 0.0f, -1000.0f, 1000.0f},
+ {SOCK_FLOAT, N_("Scale"), 5.0f, 0.0f, 0.0f, 0.0f, -1000.0f, 1000.0f},
+ {SOCK_FLOAT, N_("Detail"), 2.0f, 0.0f, 0.0f, 0.0f, 0.0f, 16.0f},
+ {SOCK_FLOAT, N_("Roughness"), 0.5f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR},
+ {SOCK_FLOAT, N_("Distortion"), 0.0f, 0.0f, 0.0f, 0.0f, -1000.0f, 1000.0f},
+ {-1, ""},
+};
+
+static bNodeSocketTemplate sh_node_tex_noise_out[] = {
+ {SOCK_FLOAT,
+ N_("Fac"),
+ 0.0f,
+ 0.0f,
+ 0.0f,
+ 0.0f,
+ 0.0f,
+ 1.0f,
+ PROP_FACTOR,
+ SOCK_NO_INTERNAL_LINK},
+ {SOCK_RGBA, N_("Color"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_NONE, SOCK_NO_INTERNAL_LINK},
+ {-1, ""},
+};
+
+static void node_shader_init_tex_noise(bNodeTree *UNUSED(ntree), bNode *node)
+{
+ NodeTexNoise *tex = (NodeTexNoise *)MEM_callocN(sizeof(NodeTexNoise), "NodeTexNoise");
+ BKE_texture_mapping_default(&tex->base.tex_mapping, TEXMAP_TYPE_POINT);
+ BKE_texture_colormapping_default(&tex->base.color_mapping);
+ tex->dimensions = 3;
+
+ node->storage = tex;
+}
+
+static int node_shader_gpu_tex_noise(GPUMaterial *mat,
+ bNode *node,
+ bNodeExecData *UNUSED(execdata),
+ GPUNodeStack *in,
+ GPUNodeStack *out)
+{
+ node_shader_gpu_default_tex_coord(mat, node, &in[0].link);
+ node_shader_gpu_tex_mapping(mat, node, in, out);
+
+ NodeTexNoise *tex = (NodeTexNoise *)node->storage;
+ static const char *names[] = {
+ "",
+ "node_noise_texture_1d",
+ "node_noise_texture_2d",
+ "node_noise_texture_3d",
+ "node_noise_texture_4d",
+ };
+ return GPU_stack_link(mat, node, names[tex->dimensions], in, out);
+}
+
+static void node_shader_update_tex_noise(bNodeTree *UNUSED(ntree), bNode *node)
+{
+ bNodeSocket *sockVector = nodeFindSocket(node, SOCK_IN, "Vector");
+ bNodeSocket *sockW = nodeFindSocket(node, SOCK_IN, "W");
+
+ NodeTexNoise *tex = (NodeTexNoise *)node->storage;
+ nodeSetSocketAvailability(sockVector, tex->dimensions != 1);
+ nodeSetSocketAvailability(sockW, tex->dimensions == 1 || tex->dimensions == 4);
+}
+
+class NoiseTextureFunction3D : public blender::fn::MultiFunction {
+
+ public:
+ NoiseTextureFunction3D()
+ {
+ static blender::fn::MFSignature signature = create_signature();
+ this->set_signature(&signature);
+ }
+
+ static blender::fn::MFSignature create_signature()
+ {
+ blender::fn::MFSignatureBuilder signature{"Noise Texture"};
+ signature.single_input<blender::float3>("Vector");
+ signature.single_input<float>("Scale");
+ signature.single_input<float>("Detail");
+ signature.single_input<float>("Roughness");
+ signature.single_input<float>("Distortion");
+ signature.single_output<float>("Fac");
+ signature.single_output<blender::ColorGeometry4f>("Color");
+ return signature.build();
+ }
+
+ void call(blender::IndexMask mask,
+ blender::fn::MFParams params,
+ blender::fn::MFContext UNUSED(context)) const override
+ {
+ const blender::VArray<blender::float3> &vectors =
+ params.readonly_single_input<blender::float3>(0, "Vector");
+ const blender::VArray<float> &scales = params.readonly_single_input<float>(1, "Scale");
+ const blender::VArray<float> &details = params.readonly_single_input<float>(2, "Detail");
+
+ blender::MutableSpan<float> r_values = params.uninitialized_single_output<float>(5, "Fac");
+ blender::MutableSpan<blender::ColorGeometry4f> r_colors =
+ params.uninitialized_single_output<blender::ColorGeometry4f>(6, "Color");
+
+ for (int i : mask) {
+ const blender::float3 vector = vectors[i];
+ const float scale = scales[i];
+ const float noise_size = safe_divide(1.0f, scale);
+ const float detail = details[i];
+ const float noise1 = BLI_noise_generic_turbulence(
+ noise_size, vector.x, vector.y, vector.z, detail, false, 1);
+ const float noise2 = BLI_noise_generic_turbulence(
+ noise_size, vector.y, vector.x + 100.0f, vector.z, detail, false, 1);
+ const float noise3 = BLI_noise_generic_turbulence(
+ noise_size, vector.z + 100.0f, vector.y, vector.x, detail, false, 1);
+ r_values[i] = noise1;
+ r_colors[i] = {noise1, noise2, noise3, 1.0f};
+ }
+ }
+};
+
+class NoiseTextureFunction1D : public blender::fn::MultiFunction {
+
+ public:
+ NoiseTextureFunction1D()
+ {
+ static blender::fn::MFSignature signature = create_signature();
+ this->set_signature(&signature);
+ }
+
+ static blender::fn::MFSignature create_signature()
+ {
+ blender::fn::MFSignatureBuilder signature{"Noise Texture"};
+ signature.single_input<float>("W");
+ signature.single_input<float>("Scale");
+ signature.single_input<float>("Detail");
+ signature.single_input<float>("Roughness");
+ signature.single_input<float>("Distortion");
+ signature.single_output<float>("Fac");
+ signature.single_output<blender::ColorGeometry4f>("Color");
+ return signature.build();
+ }
+
+ void call(blender::IndexMask mask,
+ blender::fn::MFParams params,
+ blender::fn::MFContext UNUSED(context)) const override
+ {
+ const blender::VArray<float> &inputs = params.readonly_single_input<float>(0, "W");
+ const blender::VArray<float> &scales = params.readonly_single_input<float>(1, "Scale");
+ const blender::VArray<float> &details = params.readonly_single_input<float>(2, "Detail");
+
+ blender::MutableSpan<float> r_values = params.uninitialized_single_output<float>(5, "Fac");
+ blender::MutableSpan<blender::ColorGeometry4f> r_colors =
+ params.uninitialized_single_output<blender::ColorGeometry4f>(6, "Color");
+
+ for (int i : mask) {
+ const float value = inputs[i];
+ const float scale = scales[i];
+ const float noise_size = safe_divide(1.0f, scale);
+ const float detail = details[i];
+ const float noise1 = BLI_noise_generic_turbulence(
+ noise_size, value, value, value, detail, false, 1);
+ const float noise2 = BLI_noise_generic_turbulence(
+ noise_size, value, value + 100.0f, value, detail, false, 1);
+ const float noise3 = BLI_noise_generic_turbulence(
+ noise_size, value + 100.0f, value, value, detail, false, 1);
+ r_values[i] = noise1;
+ r_colors[i] = {noise1, noise2, noise3, 1.0f};
+ }
+ }
+};
+
+static void sh_node_tex_noise_expand_in_mf_network(blender::nodes::NodeMFNetworkBuilder &builder)
+{
+ /* TODO: Not only support 1D and 3D. */
+ NodeTexNoise *tex = builder.dnode()->storage<NodeTexNoise>();
+ if (tex->dimensions == 1) {
+ builder.construct_and_set_matching_fn<NoiseTextureFunction1D>();
+ }
+ else if (tex->dimensions == 3) {
+ builder.construct_and_set_matching_fn<NoiseTextureFunction3D>();
+ }
+ else {
+ builder.set_not_implemented();
+ }
+}
+
+/* node type definition */
+void register_node_type_sh_tex_noise(void)
+{
+ static bNodeType ntype;
+
+ sh_fn_node_type_base(&ntype, SH_NODE_TEX_NOISE, "Noise Texture", NODE_CLASS_TEXTURE, 0);
+ node_type_socket_templates(&ntype, sh_node_tex_noise_in, sh_node_tex_noise_out);
+ node_type_init(&ntype, node_shader_init_tex_noise);
+ node_type_storage(
+ &ntype, "NodeTexNoise", node_free_standard_storage, node_copy_standard_storage);
+ node_type_gpu(&ntype, node_shader_gpu_tex_noise);
+ node_type_update(&ntype, node_shader_update_tex_noise);
+ ntype.expand_in_mf_network = sh_node_tex_noise_expand_in_mf_network;
+
+ nodeRegisterType(&ntype);
+}