diff options
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 ¶meter : 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 ¶ms) -{ - 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 ¶ms, + 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 ¶ms) { 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 ¶ms, + 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 ¶ms, + 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 ¶ms) + const GeometrySet &geometry_set_target, + const AnonymousCustomDataLayerID *distance_id, + const AnonymousCustomDataLayerID *location_id, + const GeoNodeExecParams ¶ms) { - /* 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 ¶ms, 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 ¶ms, 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 ¶ms, 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 ¶ms, { 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 ¶ms, 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 ¶ms, 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 ¶ms, + 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 ¶ms, 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); +} |