diff options
Diffstat (limited to 'source/blender/blenkernel')
20 files changed, 2527 insertions, 96 deletions
diff --git a/source/blender/blenkernel/BKE_attribute.h b/source/blender/blenkernel/BKE_attribute.h index aab962d42a6..55a841d8fd1 100644 --- a/source/blender/blenkernel/BKE_attribute.h +++ b/source/blender/blenkernel/BKE_attribute.h @@ -40,13 +40,11 @@ struct ReportList; /* Attribute.domain */ typedef enum AttributeDomain { - ATTR_DOMAIN_VERTEX = 0, /* Mesh Vertex */ + ATTR_DOMAIN_POINT = 0, /* Mesh, Hair or PointCloud Point */ ATTR_DOMAIN_EDGE = 1, /* Mesh Edge */ ATTR_DOMAIN_CORNER = 2, /* Mesh Corner */ ATTR_DOMAIN_POLYGON = 3, /* Mesh Polygon */ - - ATTR_DOMAIN_POINT = 4, /* Hair or PointCloud Point */ - ATTR_DOMAIN_CURVE = 5, /* Hair Curve */ + ATTR_DOMAIN_CURVE = 4, /* Hair Curve */ ATTR_DOMAIN_NUM } AttributeDomain; diff --git a/source/blender/blenkernel/BKE_attribute_access.hh b/source/blender/blenkernel/BKE_attribute_access.hh new file mode 100644 index 00000000000..e58fba36342 --- /dev/null +++ b/source/blender/blenkernel/BKE_attribute_access.hh @@ -0,0 +1,255 @@ +/* + * 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 + +#include <mutex> + +#include "FN_cpp_type.hh" +#include "FN_spans.hh" + +#include "BKE_attribute.h" + +#include "BLI_float3.hh" + +struct Mesh; + +namespace blender::bke { + +using fn::CPPType; + +/** + * This class offers an indirection for reading an attribute. + * This is useful for the following reasons: + * - Blender does not store all attributes the same way. + * The simplest case are custom data layers with primitive types. + * A bit more complex are mesh attributes like the position of vertices, + * which are embedded into the MVert struct. + * Even more complex to access are vertex weights. + * - Sometimes attributes are stored on one domain, but we want to access + * the attribute on a different domain. Therefore, we have to interpolate + * between the domains. + */ +class ReadAttribute { + protected: + const AttributeDomain domain_; + const CPPType &cpp_type_; + const int64_t size_; + + /* Protects the span below, so that no two threads initialize it at the same time. */ + mutable std::mutex span_mutex_; + /* When it is not null, it points to the attribute array or a temporary array that contains all + * the attribute values. */ + mutable void *array_buffer_ = nullptr; + /* Is true when the buffer above is owned by the attribute accessor. */ + mutable bool array_is_temporary_ = false; + + public: + ReadAttribute(AttributeDomain domain, const CPPType &cpp_type, const int64_t size) + : domain_(domain), cpp_type_(cpp_type), size_(size) + { + } + + virtual ~ReadAttribute(); + + AttributeDomain domain() const + { + return domain_; + } + + const CPPType &cpp_type() const + { + return cpp_type_; + } + + int64_t size() const + { + return size_; + } + + void get(const int64_t index, void *r_value) const + { + BLI_assert(index < size_); + this->get_internal(index, r_value); + } + + /* Get a span that contains all attribute values. */ + fn::GSpan get_span() const; + + protected: + /* r_value is expected to be uninitialized. */ + virtual void get_internal(const int64_t index, void *r_value) const = 0; + + virtual void initialize_span() const; +}; + +/** + * This exists for similar reasons as the ReadAttribute class, except that + * it does not deal with interpolation between domains. + */ +class WriteAttribute { + protected: + const AttributeDomain domain_; + const CPPType &cpp_type_; + const int64_t size_; + + /* When not null, this points either to the attribute array or to a temporary array. */ + void *array_buffer_ = nullptr; + /* True, when the buffer points to a temporary array. */ + bool array_is_temporary_ = false; + /* This helps to protect agains forgetting to apply changes done to the array. */ + bool array_should_be_applied_ = false; + + public: + WriteAttribute(AttributeDomain domain, const CPPType &cpp_type, const int64_t size) + : domain_(domain), cpp_type_(cpp_type), size_(size) + { + } + + virtual ~WriteAttribute(); + + AttributeDomain domain() const + { + return domain_; + } + + const CPPType &cpp_type() const + { + return cpp_type_; + } + + int64_t size() const + { + return size_; + } + + void get(const int64_t index, void *r_value) const + { + BLI_assert(index < size_); + this->get_internal(index, r_value); + } + + void set(const int64_t index, const void *value) + { + BLI_assert(index < size_); + this->set_internal(index, value); + } + + /* Get a span that new attribute values can be written into. When all values have been changed, + * #apply_span has to be called. The span might not contain the original attribute values. */ + fn::GMutableSpan get_span(); + /* Write the changes to the span into the actual attribute, if they aren't already. */ + void apply_span(); + + protected: + virtual void get_internal(const int64_t index, void *r_value) const = 0; + virtual void set_internal(const int64_t index, const void *value) = 0; + + virtual void initialize_span(); + virtual void apply_span_if_necessary(); +}; + +using ReadAttributePtr = std::unique_ptr<ReadAttribute>; +using WriteAttributePtr = std::unique_ptr<WriteAttribute>; + +/* This provides type safe access to an attribute. */ +template<typename T> class TypedReadAttribute { + private: + ReadAttributePtr attribute_; + + public: + TypedReadAttribute(ReadAttributePtr attribute) : attribute_(std::move(attribute)) + { + BLI_assert(attribute_); + BLI_assert(attribute_->cpp_type().is<T>()); + } + + int64_t size() const + { + return attribute_->size(); + } + + T operator[](const int64_t index) const + { + BLI_assert(index < attribute_->size()); + T value; + value.~T(); + attribute_->get(index, &value); + return value; + } + + /* Get a span to that contains all attribute values for faster and more convenient access. */ + Span<T> get_span() const + { + return attribute_->get_span().template typed<T>(); + } +}; + +/* This provides type safe access to an attribute. */ +template<typename T> class TypedWriteAttribute { + private: + WriteAttributePtr attribute_; + + public: + TypedWriteAttribute(WriteAttributePtr attribute) : attribute_(std::move(attribute)) + { + BLI_assert(attribute_); + BLI_assert(attribute_->cpp_type().is<T>()); + } + + int64_t size() const + { + return attribute_->size(); + } + + T operator[](const int64_t index) const + { + BLI_assert(index < attribute_->size()); + T value; + value.~T(); + attribute_->get(index, &value); + return value; + } + + void set(const int64_t index, const T &value) + { + attribute_->set(index, &value); + } + + /* Get a span that new values can be written into. Once all values have been updated #apply_span + * has to be called. The span might *not* contain the initial attribute values, so one should + * generally only write to the span. */ + MutableSpan<T> get_span() + { + return attribute_->get_span().typed<T>(); + } + + /* Write back all changes to the actual attribute, if necessary. */ + void apply_span() + { + attribute_->apply_span(); + } +}; + +using FloatReadAttribute = TypedReadAttribute<float>; +using Float3ReadAttribute = TypedReadAttribute<float3>; +using FloatWriteAttribute = TypedWriteAttribute<float>; +using Float3WriteAttribute = TypedWriteAttribute<float3>; + +const CPPType *custom_data_type_to_cpp_type(const CustomDataType type); +CustomDataType cpp_type_to_custom_data_type(const CPPType &type); + +} // namespace blender::bke diff --git a/source/blender/blenkernel/BKE_geometry_set.h b/source/blender/blenkernel/BKE_geometry_set.h new file mode 100644 index 00000000000..026f4d39d51 --- /dev/null +++ b/source/blender/blenkernel/BKE_geometry_set.h @@ -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. + */ + +#pragma once + +/** \file + * \ingroup bke + */ + +#ifdef __cplusplus +extern "C" { +#endif + +struct Object; +struct GeometrySet; + +void BKE_geometry_set_free(struct GeometrySet *geometry_set); + +bool BKE_geometry_set_has_instances(const struct GeometrySet *geometry_set); + +int BKE_geometry_set_instances(const struct GeometrySet *geometry_set, + float (**r_positions)[3], + float (**r_rotations)[3], + float (**r_scales)[3], + struct Object ***r_objects); + +#ifdef __cplusplus +} +#endif diff --git a/source/blender/blenkernel/BKE_geometry_set.hh b/source/blender/blenkernel/BKE_geometry_set.hh new file mode 100644 index 00000000000..ef3ae3c381c --- /dev/null +++ b/source/blender/blenkernel/BKE_geometry_set.hh @@ -0,0 +1,373 @@ +/* + * 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 <iostream> + +#include "BLI_float3.hh" +#include "BLI_hash.hh" +#include "BLI_map.hh" +#include "BLI_set.hh" +#include "BLI_user_counter.hh" + +#include "BKE_attribute_access.hh" +#include "BKE_geometry_set.h" + +struct Mesh; +struct PointCloud; +struct Object; + +/* Each geometry component has a specific type. The type determines what kind of data the component + * stores. Functions modifying a geometry will usually just modify a subset of the component types. + */ +enum class GeometryComponentType { + Mesh = 0, + PointCloud = 1, + Instances = 2, +}; + +enum class GeometryOwnershipType { + /* The geometry is owned. This implies that it can be changed. */ + Owned = 0, + /* The geometry can be changed, but someone else is responsible for freeing it. */ + Editable = 1, + /* The geometry cannot be changed and someone else is responsible for freeing it. */ + ReadOnly = 2, +}; + +/* Make it possible to use the component type as key in hash tables. */ +namespace blender { +template<> struct DefaultHash<GeometryComponentType> { + uint64_t operator()(const GeometryComponentType &value) const + { + return (uint64_t)value; + } +}; +} // namespace blender + +/** + * This is the base class for specialized geometry component types. + */ +class GeometryComponent { + private: + /* The reference count has two purposes. When it becomes zero, the component is freed. When it is + * larger than one, the component becomes immutable. */ + mutable std::atomic<int> users_ = 1; + GeometryComponentType type_; + + public: + GeometryComponent(GeometryComponentType type); + virtual ~GeometryComponent(); + static GeometryComponent *create(GeometryComponentType component_type); + + /* The returned component should be of the same type as the type this is called on. */ + virtual GeometryComponent *copy() const = 0; + + void user_add() const; + void user_remove() const; + bool is_mutable() const; + + GeometryComponentType type() const; + + /* Returns true when the geometry component supports this attribute domain. */ + virtual bool attribute_domain_supported(const AttributeDomain domain) const; + /* Returns true when the given data type is supported in the given domain. */ + virtual bool attribute_domain_with_type_supported(const AttributeDomain domain, + const CustomDataType data_type) const; + /* Can only be used with supported domain types. */ + virtual int attribute_domain_size(const AttributeDomain domain) const; + /* Attributes with these names cannot be created or removed via this api. */ + virtual bool attribute_is_builtin(const blender::StringRef attribute_name) const; + + /* Get read-only access to the highest priority attribute with the given name. + * Returns null if the attribute does not exist. */ + virtual blender::bke::ReadAttributePtr attribute_try_get_for_read( + const blender::StringRef attribute_name) const; + + /* Get read and write access to the highest priority attribute with the given name. + * Returns null if the attribute does not exist. */ + virtual blender::bke::WriteAttributePtr attribute_try_get_for_write( + const blender::StringRef attribute_name); + + /* Get a read-only attribute for the domain based on the given attribute. This can be used to + * interpolate from one domain to another. + * Returns null if the interpolation is not implemented. */ + virtual blender::bke::ReadAttributePtr attribute_try_adapt_domain( + blender::bke::ReadAttributePtr attribute, const AttributeDomain domain) const; + + /* Returns true when the attribute has been deleted. */ + virtual bool attribute_try_delete(const blender::StringRef attribute_name); + + /* Returns true when the attribute has been created. */ + virtual bool attribute_try_create(const blender::StringRef attribute_name, + const AttributeDomain domain, + const CustomDataType data_type); + + virtual blender::Set<std::string> attribute_names() const; + virtual bool is_empty() const; + + /* Get a read-only attribute for the given domain and data type. + * Returns null when it does not exist. */ + blender::bke::ReadAttributePtr attribute_try_get_for_read( + const blender::StringRef attribute_name, + const AttributeDomain domain, + const CustomDataType data_type) const; + + /* Get a read-only attribute for the given domain and data type. + * Returns a constant attribute based on the default value if the attribute does not exist. + * Never returns null. */ + blender::bke::ReadAttributePtr attribute_get_for_read(const blender::StringRef attribute_name, + const AttributeDomain domain, + const CustomDataType data_type, + const void *default_value) const; + + /* Get a typed read-only attribute for the given domain and type. */ + template<typename T> + blender::bke::TypedReadAttribute<T> attribute_get_for_read( + const blender::StringRef attribute_name, + const AttributeDomain domain, + const T &default_value) const + { + const blender::fn::CPPType &cpp_type = blender::fn::CPPType::get<T>(); + const CustomDataType type = blender::bke::cpp_type_to_custom_data_type(cpp_type); + return this->attribute_get_for_read(attribute_name, domain, type, &default_value); + } + + /* Get a read-only dummy attribute that always returns the same value. */ + blender::bke::ReadAttributePtr attribute_get_constant_for_read(const AttributeDomain domain, + const CustomDataType data_type, + const void *value) const; + + /* Get a read-only dummy attribute that always returns the same value. */ + template<typename T> + blender::bke::TypedReadAttribute<T> attribute_get_constant_for_read(const AttributeDomain domain, + const T &value) const + { + const blender::fn::CPPType &cpp_type = blender::fn::CPPType::get<T>(); + const CustomDataType type = blender::bke::cpp_type_to_custom_data_type(cpp_type); + return this->attribute_get_constant_for_read(domain, type, &value); + } + + /** + * Returns the attribute with the given parameters if it exists. + * If an exact match does not exist, other attributes with the same name are deleted and a new + * attribute is created if possible. + */ + blender::bke::WriteAttributePtr attribute_try_ensure_for_write( + const blender::StringRef attribute_name, + const AttributeDomain domain, + const CustomDataType data_type); +}; + +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. + * + * Copying a geometry set is a relatively cheap operation, because it does not copy the referenced + * geometry components. + */ +struct GeometrySet { + private: + using GeometryComponentPtr = blender::UserCounter<class GeometryComponent>; + blender::Map<GeometryComponentType, GeometryComponentPtr> components_; + + public: + GeometryComponent &get_component_for_write(GeometryComponentType component_type); + template<typename Component> Component &get_component_for_write() + { + BLI_STATIC_ASSERT(is_geometry_component_v<Component>, ""); + return static_cast<Component &>(this->get_component_for_write(Component::static_type)); + } + + const GeometryComponent *get_component_for_read(GeometryComponentType component_type) const; + template<typename Component> const Component *get_component_for_read() const + { + BLI_STATIC_ASSERT(is_geometry_component_v<Component>, ""); + return static_cast<const Component *>(get_component_for_read(Component::static_type)); + } + + bool has(const GeometryComponentType component_type) const; + template<typename Component> bool has() const + { + BLI_STATIC_ASSERT(is_geometry_component_v<Component>, ""); + return this->has(Component::static_type); + } + + void remove(const GeometryComponentType component_type); + template<typename Component> void remove() + { + BLI_STATIC_ASSERT(is_geometry_component_v<Component>, ""); + return this->remove(Component::static_type); + } + + void add(const GeometryComponent &component); + + void compute_boundbox_without_instances(blender::float3 *r_min, blender::float3 *r_max) const; + + friend std::ostream &operator<<(std::ostream &stream, const GeometrySet &geometry_set); + friend bool operator==(const GeometrySet &a, const GeometrySet &b); + uint64_t hash() const; + + /* Utility methods for creation. */ + static GeometrySet create_with_mesh( + Mesh *mesh, GeometryOwnershipType ownership = GeometryOwnershipType::Owned); + static GeometrySet create_with_pointcloud( + PointCloud *pointcloud, GeometryOwnershipType ownership = GeometryOwnershipType::Owned); + + /* Utility methods for access. */ + bool has_mesh() const; + bool has_pointcloud() const; + bool has_instances() const; + const Mesh *get_mesh_for_read() const; + const PointCloud *get_pointcloud_for_read() const; + Mesh *get_mesh_for_write(); + PointCloud *get_pointcloud_for_write(); + + /* Utility methods for replacement. */ + void replace_mesh(Mesh *mesh, GeometryOwnershipType ownership = GeometryOwnershipType::Owned); + void replace_pointcloud(PointCloud *pointcloud, + GeometryOwnershipType ownership = GeometryOwnershipType::Owned); +}; + +/** A geometry component that can store a mesh. */ +class MeshComponent : public GeometryComponent { + private: + Mesh *mesh_ = nullptr; + GeometryOwnershipType ownership_ = GeometryOwnershipType::Owned; + /* Due to historical design choices, vertex group data is stored in the mesh, but the vertex + * group names are stored on an object. Since we don't have an object here, we copy over the + * names into this map. */ + blender::Map<std::string, int> vertex_group_names_; + + public: + MeshComponent(); + ~MeshComponent(); + GeometryComponent *copy() const override; + + void clear(); + bool has_mesh() const; + void replace(Mesh *mesh, GeometryOwnershipType ownership = GeometryOwnershipType::Owned); + Mesh *release(); + + void copy_vertex_group_names_from_object(const struct Object &object); + + const Mesh *get_for_read() const; + Mesh *get_for_write(); + + bool attribute_domain_supported(const AttributeDomain domain) const final; + bool attribute_domain_with_type_supported(const AttributeDomain domain, + const CustomDataType data_type) const final; + int attribute_domain_size(const AttributeDomain domain) const final; + bool attribute_is_builtin(const blender::StringRef attribute_name) const final; + + blender::bke::ReadAttributePtr attribute_try_get_for_read( + const blender::StringRef attribute_name) const final; + blender::bke::WriteAttributePtr attribute_try_get_for_write( + const blender::StringRef attribute_name) final; + + bool attribute_try_delete(const blender::StringRef attribute_name) final; + bool attribute_try_create(const blender::StringRef attribute_name, + const AttributeDomain domain, + const CustomDataType data_type) final; + + blender::Set<std::string> attribute_names() const final; + bool is_empty() const final; + + static constexpr inline GeometryComponentType static_type = GeometryComponentType::Mesh; +}; + +/** A geometry component that stores a point cloud. */ +class PointCloudComponent : public GeometryComponent { + private: + PointCloud *pointcloud_ = nullptr; + GeometryOwnershipType ownership_ = GeometryOwnershipType::Owned; + + public: + PointCloudComponent(); + ~PointCloudComponent(); + GeometryComponent *copy() const override; + + void clear(); + bool has_pointcloud() const; + void replace(PointCloud *pointcloud, + GeometryOwnershipType ownership = GeometryOwnershipType::Owned); + PointCloud *release(); + + const PointCloud *get_for_read() const; + PointCloud *get_for_write(); + + bool attribute_domain_supported(const AttributeDomain domain) const final; + bool attribute_domain_with_type_supported(const AttributeDomain domain, + const CustomDataType data_type) const final; + int attribute_domain_size(const AttributeDomain domain) const final; + bool attribute_is_builtin(const blender::StringRef attribute_name) const final; + + blender::bke::ReadAttributePtr attribute_try_get_for_read( + const blender::StringRef attribute_name) const final; + blender::bke::WriteAttributePtr attribute_try_get_for_write( + const blender::StringRef attribute_name) final; + + bool attribute_try_delete(const blender::StringRef attribute_name) final; + bool attribute_try_create(const blender::StringRef attribute_name, + const AttributeDomain domain, + const CustomDataType data_type) final; + + blender::Set<std::string> attribute_names() const final; + bool is_empty() const final; + + static constexpr inline GeometryComponentType static_type = GeometryComponentType::PointCloud; +}; + +/** A geometry component that stores instances. */ +class InstancesComponent : public GeometryComponent { + private: + blender::Vector<blender::float3> positions_; + blender::Vector<blender::float3> rotations_; + blender::Vector<blender::float3> scales_; + blender::Vector<const Object *> objects_; + + public: + InstancesComponent(); + ~InstancesComponent() = default; + GeometryComponent *copy() const override; + + void clear(); + void add_instance(const Object *object, + blender::float3 position, + blender::float3 rotation = {0, 0, 0}, + blender::float3 scale = {1, 1, 1}); + + blender::Span<const Object *> objects() const; + blender::Span<blender::float3> positions() const; + blender::Span<blender::float3> rotations() const; + blender::Span<blender::float3> scales() const; + blender::MutableSpan<blender::float3> positions(); + int instances_amount() const; + + bool is_empty() const final; + + static constexpr inline GeometryComponentType static_type = GeometryComponentType::Instances; +}; diff --git a/source/blender/blenkernel/BKE_modifier.h b/source/blender/blenkernel/BKE_modifier.h index a2c3787bcd2..2491f0953c0 100644 --- a/source/blender/blenkernel/BKE_modifier.h +++ b/source/blender/blenkernel/BKE_modifier.h @@ -43,6 +43,7 @@ struct ModifierData; struct Object; struct Scene; struct bArmature; +struct GeometrySet; typedef enum { /* Should not be used, only for None modifier type */ @@ -246,9 +247,9 @@ typedef struct ModifierTypeInfo { struct Hair *(*modifyHair)(struct ModifierData *md, const struct ModifierEvalContext *ctx, struct Hair *hair); - struct PointCloud *(*modifyPointCloud)(struct ModifierData *md, - const struct ModifierEvalContext *ctx, - struct PointCloud *pointcloud); + void (*modifyPointCloud)(struct ModifierData *md, + const struct ModifierEvalContext *ctx, + struct GeometrySet *geometry_set); struct Volume *(*modifyVolume)(struct ModifierData *md, const struct ModifierEvalContext *ctx, struct Volume *volume); diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h index e6fb9d86b0a..905316a937d 100644 --- a/source/blender/blenkernel/BKE_node.h +++ b/source/blender/blenkernel/BKE_node.h @@ -112,20 +112,26 @@ namespace blender { namespace nodes { class SocketMFNetworkBuilder; class NodeMFNetworkBuilder; +class GeoNodeExecParams; } // namespace nodes namespace fn { +class CPPType; class MFDataType; -} +} // namespace fn } // namespace blender using NodeExpandInMFNetworkFunction = void (*)(blender::nodes::NodeMFNetworkBuilder &builder); -using SocketGetMFDataTypeFunction = blender::fn::MFDataType (*)(); +using NodeGeometryExecFunction = void (*)(blender::nodes::GeoNodeExecParams params); +using SocketGetCPPTypeFunction = const blender::fn::CPPType *(*)(); +using SocketGetCPPValueFunction = void (*)(const struct bNodeSocket &socket, void *r_value); using SocketExpandInMFNetworkFunction = void (*)(blender::nodes::SocketMFNetworkBuilder &builder); #else typedef void *NodeExpandInMFNetworkFunction; -typedef void *SocketGetMFDataTypeFunction; typedef void *SocketExpandInMFNetworkFunction; +typedef void *NodeGeometryExecFunction; +typedef void *SocketGetCPPTypeFunction; +typedef void *SocketGetCPPValueFunction; #endif /** @@ -181,10 +187,12 @@ typedef struct bNodeSocketType { /* Callback to free the socket type. */ void (*free_self)(struct bNodeSocketType *stype); - /* Returns the multi-function data type of this socket type. */ - SocketGetMFDataTypeFunction get_mf_data_type; /* Expands the socket into a multi-function node that outputs the socket value. */ SocketExpandInMFNetworkFunction expand_in_mf_network; + /* Return the CPPType of this socket. */ + SocketGetCPPTypeFunction get_cpp_type; + /* Get the value of this socket in a generic way. */ + SocketGetCPPValueFunction get_cpp_value; } bNodeSocketType; typedef void *(*NodeInitExecFunction)(struct bNodeExecContext *context, @@ -302,6 +310,9 @@ typedef struct bNodeType { /* Expands the bNode into nodes in a multi-function network, which will be evaluated later on. */ NodeExpandInMFNetworkFunction expand_in_mf_network; + /* Execute a geometry node. */ + NodeGeometryExecFunction geometry_node_execute; + /* RNA integration */ ExtensionRNA rna_ext; } bNodeType; @@ -438,7 +449,7 @@ bool ntreeHasType(const struct bNodeTree *ntree, int type); bool ntreeHasTree(const struct bNodeTree *ntree, const struct bNodeTree *lookup); void ntreeUpdateTree(struct Main *main, struct bNodeTree *ntree); void ntreeUpdateAllNew(struct Main *main); -void ntreeUpdateAllUsers(struct Main *main, struct ID *ngroup); +void ntreeUpdateAllUsers(struct Main *main, struct bNodeTree *ngroup); void ntreeGetDependencyList(struct bNodeTree *ntree, struct bNode ***deplist, int *totnodes); @@ -1323,6 +1334,24 @@ int ntreeTexExecTree(struct bNodeTree *ntree, /** \} */ /* -------------------------------------------------------------------- */ +/** \name Geometry Nodes + * \{ */ + +#define GEO_NODE_TRIANGULATE 1000 +#define GEO_NODE_EDGE_SPLIT 1001 +#define GEO_NODE_TRANSFORM 1002 +#define GEO_NODE_BOOLEAN 1003 +#define GEO_NODE_POINT_DISTRIBUTE 1004 +#define GEO_NODE_POINT_INSTANCE 1005 +#define GEO_NODE_SUBDIVISION_SURFACE 1006 +#define GEO_NODE_OBJECT_INFO 1007 +#define GEO_NODE_RANDOM_ATTRIBUTE 1008 +#define GEO_NODE_ATTRIBUTE_MATH 1009 +#define GEO_NODE_JOIN_GEOMETRY 1010 + +/** \} */ + +/* -------------------------------------------------------------------- */ /** \name Function Nodes * \{ */ diff --git a/source/blender/blenkernel/BKE_pointcloud.h b/source/blender/blenkernel/BKE_pointcloud.h index 985a8cc3ca7..d2d390dc786 100644 --- a/source/blender/blenkernel/BKE_pointcloud.h +++ b/source/blender/blenkernel/BKE_pointcloud.h @@ -38,8 +38,10 @@ extern const char *POINTCLOUD_ATTR_RADIUS; void *BKE_pointcloud_add(struct Main *bmain, const char *name); void *BKE_pointcloud_add_default(struct Main *bmain, const char *name); +struct PointCloud *BKE_pointcloud_new_nomain(const int totpoint); struct BoundBox *BKE_pointcloud_boundbox_get(struct Object *ob); +void BKE_pointcloud_minmax(const struct PointCloud *pointcloud, float r_min[3], float r_max[3]); void BKE_pointcloud_update_customdata_pointers(struct PointCloud *pointcloud); bool BKE_pointcloud_customdata_required(struct PointCloud *pointcloud, diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt index c89a45b2a43..1a90d6fadd3 100644 --- a/source/blender/blenkernel/CMakeLists.txt +++ b/source/blender/blenkernel/CMakeLists.txt @@ -78,6 +78,7 @@ set(SRC intern/armature_deform.c intern/armature_update.c intern/attribute.c + intern/attribute_access.cc intern/autoexec.c intern/blender.c intern/blender_copybuffer.c @@ -124,6 +125,7 @@ set(SRC intern/fmodifier.c intern/font.c intern/freestyle.c + intern/geometry_set.cc intern/gpencil.c intern/gpencil_curve.c intern/gpencil_geom.c @@ -266,6 +268,7 @@ set(SRC BKE_appdir.h BKE_armature.h BKE_attribute.h + BKE_attribute_access.hh BKE_autoexec.h BKE_blender.h BKE_blender_copybuffer.h @@ -311,6 +314,8 @@ set(SRC BKE_fluid.h BKE_font.h BKE_freestyle.h + BKE_geometry_set.h + BKE_geometry_set.hh BKE_global.h BKE_gpencil.h BKE_gpencil_curve.h diff --git a/source/blender/blenkernel/intern/attribute.c b/source/blender/blenkernel/intern/attribute.c index 9ad73133f9e..d8fd3a19303 100644 --- a/source/blender/blenkernel/intern/attribute.c +++ b/source/blender/blenkernel/intern/attribute.c @@ -63,8 +63,8 @@ static void get_domains(ID *id, DomainInfo info[ATTR_DOMAIN_NUM]) } case ID_ME: { Mesh *mesh = (Mesh *)id; - info[ATTR_DOMAIN_VERTEX].customdata = &mesh->vdata; - info[ATTR_DOMAIN_VERTEX].length = mesh->totvert; + info[ATTR_DOMAIN_POINT].customdata = &mesh->vdata; + info[ATTR_DOMAIN_POINT].length = mesh->totvert; info[ATTR_DOMAIN_EDGE].customdata = &mesh->edata; info[ATTR_DOMAIN_EDGE].length = mesh->totedge; info[ATTR_DOMAIN_CORNER].customdata = &mesh->ldata; diff --git a/source/blender/blenkernel/intern/attribute_access.cc b/source/blender/blenkernel/intern/attribute_access.cc new file mode 100644 index 00000000000..2345c834be4 --- /dev/null +++ b/source/blender/blenkernel/intern/attribute_access.cc @@ -0,0 +1,1080 @@ +/* + * 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 <utility> + +#include "BKE_attribute_access.hh" +#include "BKE_customdata.h" +#include "BKE_deform.h" +#include "BKE_geometry_set.hh" +#include "BKE_mesh.h" +#include "BKE_pointcloud.h" + +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" +#include "DNA_pointcloud_types.h" + +#include "BLI_color.hh" +#include "BLI_float2.hh" +#include "BLI_span.hh" + +#include "CLG_log.h" + +#include "NOD_node_tree_multi_function.hh" + +static CLG_LogRef LOG = {"bke.attribute_access"}; + +using blender::float3; +using blender::Set; +using blender::StringRef; +using blender::bke::ReadAttributePtr; +using blender::bke::WriteAttributePtr; + +/* Can't include BKE_object_deform.h right now, due to an enum forward declaration. */ +extern "C" MDeformVert *BKE_object_defgroup_data_create(ID *id); + +namespace blender::bke { + +/* -------------------------------------------------------------------- */ +/** \name Attribute Accessor implementations + * \{ */ + +ReadAttribute::~ReadAttribute() +{ + if (array_is_temporary_ && array_buffer_ != nullptr) { + cpp_type_.destruct_n(array_buffer_, size_); + MEM_freeN(array_buffer_); + } +} + +fn::GSpan ReadAttribute::get_span() const +{ + if (size_ == 0) { + return fn::GSpan(cpp_type_); + } + if (array_buffer_ == nullptr) { + std::lock_guard lock{span_mutex_}; + if (array_buffer_ == nullptr) { + this->initialize_span(); + } + } + return fn::GSpan(cpp_type_, array_buffer_, size_); +} + +void ReadAttribute::initialize_span() const +{ + const int element_size = cpp_type_.size(); + array_buffer_ = MEM_mallocN_aligned(size_ * element_size, cpp_type_.alignment(), __func__); + array_is_temporary_ = true; + for (const int i : IndexRange(size_)) { + this->get_internal(i, POINTER_OFFSET(array_buffer_, i * element_size)); + } +} + +WriteAttribute::~WriteAttribute() +{ + if (array_should_be_applied_) { + CLOG_ERROR(&LOG, "Forgot to call apply_span."); + } + if (array_is_temporary_ && array_buffer_ != nullptr) { + cpp_type_.destruct_n(array_buffer_, size_); + MEM_freeN(array_buffer_); + } +} + +/** + * Get a mutable span that can be modified. When all modifications to the attribute are done, + * #apply_span_if_necessary should be called. + */ +fn::GMutableSpan WriteAttribute::get_span() +{ + if (size_ == 0) { + return fn::GMutableSpan(cpp_type_); + } + if (array_buffer_ == nullptr) { + this->initialize_span(); + } + array_should_be_applied_ = true; + return fn::GMutableSpan(cpp_type_, array_buffer_, size_); +} + +void WriteAttribute::initialize_span() +{ + array_buffer_ = MEM_mallocN_aligned(cpp_type_.size() * size_, cpp_type_.alignment(), __func__); + array_is_temporary_ = true; + /* This does nothing for trivial types, but is necessary for general correctness. */ + cpp_type_.construct_default_n(array_buffer_, size_); +} + +void WriteAttribute::apply_span() +{ + this->apply_span_if_necessary(); + array_should_be_applied_ = false; +} + +void WriteAttribute::apply_span_if_necessary() +{ + /* Only works when the span has been initialized beforehand. */ + BLI_assert(array_buffer_ != nullptr); + + const int element_size = cpp_type_.size(); + for (const int i : IndexRange(size_)) { + this->set_internal(i, POINTER_OFFSET(array_buffer_, i * element_size)); + } +} + +class VertexWeightWriteAttribute final : public WriteAttribute { + private: + MDeformVert *dverts_; + const int dvert_index_; + + public: + VertexWeightWriteAttribute(MDeformVert *dverts, const int totvert, const int dvert_index) + : WriteAttribute(ATTR_DOMAIN_POINT, CPPType::get<float>(), totvert), + dverts_(dverts), + dvert_index_(dvert_index) + { + } + + void get_internal(const int64_t index, void *r_value) const override + { + get_internal(dverts_, dvert_index_, index, r_value); + } + + void set_internal(const int64_t index, const void *value) override + { + MDeformWeight *weight = BKE_defvert_ensure_index(&dverts_[index], dvert_index_); + weight->weight = *reinterpret_cast<const float *>(value); + } + + static void get_internal(const MDeformVert *dverts, + const int dvert_index, + const int64_t index, + void *r_value) + { + if (dverts == nullptr) { + *(float *)r_value = 0.0f; + return; + } + const MDeformVert &dvert = dverts[index]; + for (const MDeformWeight &weight : Span(dvert.dw, dvert.totweight)) { + if (weight.def_nr == dvert_index) { + *(float *)r_value = weight.weight; + return; + } + } + *(float *)r_value = 0.0f; + } +}; + +class VertexWeightReadAttribute final : public ReadAttribute { + private: + const MDeformVert *dverts_; + const int dvert_index_; + + public: + VertexWeightReadAttribute(const MDeformVert *dverts, const int totvert, const int dvert_index) + : ReadAttribute(ATTR_DOMAIN_POINT, CPPType::get<float>(), totvert), + dverts_(dverts), + dvert_index_(dvert_index) + { + } + + void get_internal(const int64_t index, void *r_value) const override + { + VertexWeightWriteAttribute::get_internal(dverts_, dvert_index_, index, r_value); + } +}; + +template<typename T> class ArrayWriteAttribute final : public WriteAttribute { + private: + MutableSpan<T> data_; + + public: + ArrayWriteAttribute(AttributeDomain domain, MutableSpan<T> data) + : WriteAttribute(domain, CPPType::get<T>(), data.size()), data_(data) + { + } + + void get_internal(const int64_t index, void *r_value) const override + { + new (r_value) T(data_[index]); + } + + void set_internal(const int64_t index, const void *value) override + { + data_[index] = *reinterpret_cast<const T *>(value); + } + + void initialize_span() override + { + array_buffer_ = data_.data(); + array_is_temporary_ = false; + } + + void apply_span_if_necessary() override + { + /* Do nothing, because the span contains the attribute itself already. */ + } +}; + +template<typename T> class ArrayReadAttribute final : public ReadAttribute { + private: + Span<T> data_; + + public: + ArrayReadAttribute(AttributeDomain domain, Span<T> data) + : ReadAttribute(domain, CPPType::get<T>(), data.size()), data_(data) + { + } + + void get_internal(const int64_t index, void *r_value) const override + { + new (r_value) T(data_[index]); + } + + void initialize_span() const override + { + /* The data will not be modified, so this const_cast is fine. */ + array_buffer_ = const_cast<T *>(data_.data()); + array_is_temporary_ = false; + } +}; + +template<typename StructT, typename ElemT, typename GetFuncT, typename SetFuncT> +class DerivedArrayWriteAttribute final : public WriteAttribute { + private: + MutableSpan<StructT> data_; + GetFuncT get_function_; + SetFuncT set_function_; + + public: + DerivedArrayWriteAttribute(AttributeDomain domain, + MutableSpan<StructT> data, + GetFuncT get_function, + SetFuncT set_function) + : WriteAttribute(domain, CPPType::get<ElemT>(), data.size()), + data_(data), + get_function_(std::move(get_function)), + set_function_(std::move(set_function)) + { + } + + void get_internal(const int64_t index, void *r_value) const override + { + const StructT &struct_value = data_[index]; + const ElemT value = get_function_(struct_value); + new (r_value) ElemT(value); + } + + void set_internal(const int64_t index, const void *value) override + { + StructT &struct_value = data_[index]; + const ElemT &typed_value = *reinterpret_cast<const ElemT *>(value); + set_function_(struct_value, typed_value); + } +}; + +template<typename StructT, typename ElemT, typename GetFuncT> +class DerivedArrayReadAttribute final : public ReadAttribute { + private: + Span<StructT> data_; + GetFuncT get_function_; + + public: + DerivedArrayReadAttribute(AttributeDomain domain, Span<StructT> data, GetFuncT get_function) + : ReadAttribute(domain, CPPType::get<ElemT>(), data.size()), + data_(data), + get_function_(std::move(get_function)) + { + } + + void get_internal(const int64_t index, void *r_value) const override + { + const StructT &struct_value = data_[index]; + const ElemT value = get_function_(struct_value); + new (r_value) ElemT(value); + } +}; + +class ConstantReadAttribute final : public ReadAttribute { + private: + void *value_; + + public: + ConstantReadAttribute(AttributeDomain domain, + const int64_t size, + const CPPType &type, + const void *value) + : ReadAttribute(domain, type, size) + { + value_ = MEM_mallocN_aligned(type.size(), type.alignment(), __func__); + type.copy_to_uninitialized(value, value_); + } + + ~ConstantReadAttribute() + { + this->cpp_type_.destruct(value_); + MEM_freeN(value_); + } + + void get_internal(const int64_t UNUSED(index), void *r_value) const override + { + this->cpp_type_.copy_to_uninitialized(value_, r_value); + } + + void initialize_span() const override + { + const int element_size = cpp_type_.size(); + array_buffer_ = MEM_mallocN_aligned(size_ * element_size, cpp_type_.alignment(), __func__); + array_is_temporary_ = true; + cpp_type_.fill_uninitialized(value_, array_buffer_, size_); + } +}; + +class ConvertedReadAttribute final : public ReadAttribute { + private: + const CPPType &from_type_; + const CPPType &to_type_; + ReadAttributePtr base_attribute_; + const nodes::DataTypeConversions &conversions_; + + static constexpr int MaxValueSize = 64; + static constexpr int MaxValueAlignment = 64; + + public: + ConvertedReadAttribute(ReadAttributePtr base_attribute, const CPPType &to_type) + : ReadAttribute(base_attribute->domain(), to_type, base_attribute->size()), + from_type_(base_attribute->cpp_type()), + to_type_(to_type), + base_attribute_(std::move(base_attribute)), + conversions_(nodes::get_implicit_type_conversions()) + { + if (from_type_.size() > MaxValueSize || from_type_.alignment() > MaxValueAlignment) { + throw std::runtime_error( + "type is larger than expected, the buffer size has to be increased"); + } + } + + void get_internal(const int64_t index, void *r_value) const override + { + AlignedBuffer<MaxValueSize, MaxValueAlignment> buffer; + base_attribute_->get(index, buffer.ptr()); + conversions_.convert(from_type_, to_type_, buffer.ptr(), r_value); + } +}; + +/** \} */ + +const blender::fn::CPPType *custom_data_type_to_cpp_type(const CustomDataType type) +{ + switch (type) { + case CD_PROP_FLOAT: + return &CPPType::get<float>(); + case CD_PROP_FLOAT2: + return &CPPType::get<float2>(); + case CD_PROP_FLOAT3: + return &CPPType::get<float3>(); + case CD_PROP_INT32: + return &CPPType::get<int>(); + case CD_PROP_COLOR: + return &CPPType::get<Color4f>(); + default: + return nullptr; + } + return nullptr; +} + +CustomDataType cpp_type_to_custom_data_type(const blender::fn::CPPType &type) +{ + if (type.is<float>()) { + return CD_PROP_FLOAT; + } + if (type.is<float2>()) { + return CD_PROP_FLOAT2; + } + if (type.is<float3>()) { + return CD_PROP_FLOAT3; + } + if (type.is<int>()) { + return CD_PROP_INT32; + } + if (type.is<Color4f>()) { + return CD_PROP_COLOR; + } + return static_cast<CustomDataType>(-1); +} + +} // namespace blender::bke + +/* -------------------------------------------------------------------- */ +/** \name Utilities for Accessing Attributes + * \{ */ + +static ReadAttributePtr read_attribute_from_custom_data(const CustomData &custom_data, + const int size, + const StringRef attribute_name, + const AttributeDomain domain) +{ + using namespace blender; + using namespace blender::bke; + for (const CustomDataLayer &layer : Span(custom_data.layers, custom_data.totlayer)) { + if (layer.name != nullptr && layer.name == attribute_name) { + switch (layer.type) { + case CD_PROP_FLOAT: + return std::make_unique<ArrayReadAttribute<float>>( + domain, Span(static_cast<float *>(layer.data), size)); + case CD_PROP_FLOAT2: + return std::make_unique<ArrayReadAttribute<float2>>( + domain, Span(static_cast<float2 *>(layer.data), size)); + case CD_PROP_FLOAT3: + return std::make_unique<ArrayReadAttribute<float3>>( + domain, Span(static_cast<float3 *>(layer.data), size)); + case CD_PROP_INT32: + return std::make_unique<ArrayReadAttribute<int>>( + domain, Span(static_cast<int *>(layer.data), size)); + case CD_PROP_COLOR: + return std::make_unique<ArrayReadAttribute<Color4f>>( + domain, Span(static_cast<Color4f *>(layer.data), size)); + } + } + } + return {}; +} + +static WriteAttributePtr write_attribute_from_custom_data( + CustomData &custom_data, + const int size, + const StringRef attribute_name, + const AttributeDomain domain, + const std::function<void()> &update_customdata_pointers) +{ + + using namespace blender; + using namespace blender::bke; + for (const CustomDataLayer &layer : Span(custom_data.layers, custom_data.totlayer)) { + if (layer.name != nullptr && layer.name == attribute_name) { + const void *data_before = layer.data; + /* The data layer might be shared with someone else. Since the caller wants to modify it, we + * copy it first. */ + CustomData_duplicate_referenced_layer_named(&custom_data, layer.type, layer.name, size); + if (data_before != layer.data) { + update_customdata_pointers(); + } + switch (layer.type) { + case CD_PROP_FLOAT: + return std::make_unique<ArrayWriteAttribute<float>>( + domain, MutableSpan(static_cast<float *>(layer.data), size)); + case CD_PROP_FLOAT2: + return std::make_unique<ArrayWriteAttribute<float2>>( + domain, MutableSpan(static_cast<float2 *>(layer.data), size)); + case CD_PROP_FLOAT3: + return std::make_unique<ArrayWriteAttribute<float3>>( + domain, MutableSpan(static_cast<float3 *>(layer.data), size)); + case CD_PROP_INT32: + return std::make_unique<ArrayWriteAttribute<int>>( + domain, MutableSpan(static_cast<int *>(layer.data), size)); + case CD_PROP_COLOR: + return std::make_unique<ArrayWriteAttribute<Color4f>>( + domain, MutableSpan(static_cast<Color4f *>(layer.data), size)); + } + } + } + return {}; +} + +/* Returns true when the layer was found and is deleted. */ +static bool delete_named_custom_data_layer(CustomData &custom_data, + const StringRef attribute_name, + const int size) +{ + for (const int index : blender::IndexRange(custom_data.totlayer)) { + const CustomDataLayer &layer = custom_data.layers[index]; + if (layer.name == attribute_name) { + CustomData_free_layer(&custom_data, layer.type, size, index); + return true; + } + } + return false; +} + +static void get_custom_data_layer_attribute_names(const CustomData &custom_data, + const GeometryComponent &component, + const AttributeDomain domain, + Set<std::string> &r_names) +{ + for (const CustomDataLayer &layer : blender::Span(custom_data.layers, custom_data.totlayer)) { + if (component.attribute_domain_with_type_supported(domain, + static_cast<CustomDataType>(layer.type))) { + r_names.add(layer.name); + } + } +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Geometry Component + * \{ */ + +bool GeometryComponent::attribute_domain_supported(const AttributeDomain UNUSED(domain)) const +{ + return false; +} + +bool GeometryComponent::attribute_domain_with_type_supported( + const AttributeDomain UNUSED(domain), const CustomDataType UNUSED(data_type)) const +{ + return false; +} + +int GeometryComponent::attribute_domain_size(const AttributeDomain UNUSED(domain)) const +{ + BLI_assert(false); + return 0; +} + +bool GeometryComponent::attribute_is_builtin(const StringRef UNUSED(attribute_name)) const +{ + return true; +} + +ReadAttributePtr GeometryComponent::attribute_try_get_for_read( + const StringRef UNUSED(attribute_name)) const +{ + return {}; +} + +ReadAttributePtr GeometryComponent::attribute_try_adapt_domain(ReadAttributePtr attribute, + const AttributeDomain domain) const +{ + if (attribute && attribute->domain() == domain) { + return attribute; + } + return {}; +} + +WriteAttributePtr GeometryComponent::attribute_try_get_for_write( + const StringRef UNUSED(attribute_name)) +{ + return {}; +} + +bool GeometryComponent::attribute_try_delete(const StringRef UNUSED(attribute_name)) +{ + return false; +} + +bool GeometryComponent::attribute_try_create(const StringRef UNUSED(attribute_name), + const AttributeDomain UNUSED(domain), + const CustomDataType UNUSED(data_type)) +{ + return false; +} + +Set<std::string> GeometryComponent::attribute_names() const +{ + return {}; +} + +static ReadAttributePtr try_adapt_data_type(ReadAttributePtr attribute, + const blender::fn::CPPType &to_type) +{ + const blender::fn::CPPType &from_type = attribute->cpp_type(); + if (from_type == to_type) { + return attribute; + } + + const blender::nodes::DataTypeConversions &conversions = + blender::nodes::get_implicit_type_conversions(); + if (!conversions.is_convertible(from_type, to_type)) { + return {}; + } + + return std::make_unique<blender::bke::ConvertedReadAttribute>(std::move(attribute), to_type); +} + +ReadAttributePtr GeometryComponent::attribute_try_get_for_read( + const StringRef attribute_name, + const AttributeDomain domain, + const CustomDataType data_type) const +{ + if (!this->attribute_domain_with_type_supported(domain, data_type)) { + return {}; + } + + ReadAttributePtr attribute = this->attribute_try_get_for_read(attribute_name); + if (!attribute) { + return {}; + } + + if (attribute->domain() != domain) { + attribute = this->attribute_try_adapt_domain(std::move(attribute), domain); + if (!attribute) { + return {}; + } + } + + const blender::fn::CPPType *cpp_type = blender::bke::custom_data_type_to_cpp_type(data_type); + BLI_assert(cpp_type != nullptr); + if (attribute->cpp_type() != *cpp_type) { + attribute = try_adapt_data_type(std::move(attribute), *cpp_type); + if (!attribute) { + return {}; + } + } + + return attribute; +} + +ReadAttributePtr GeometryComponent::attribute_get_for_read(const StringRef attribute_name, + const AttributeDomain domain, + const CustomDataType data_type, + const void *default_value) const +{ + BLI_assert(this->attribute_domain_with_type_supported(domain, data_type)); + + ReadAttributePtr attribute = this->attribute_try_get_for_read(attribute_name, domain, data_type); + if (attribute) { + return attribute; + } + return this->attribute_get_constant_for_read(domain, data_type, default_value); +} + +blender::bke::ReadAttributePtr GeometryComponent::attribute_get_constant_for_read( + const AttributeDomain domain, const CustomDataType data_type, const void *value) const +{ + BLI_assert(this->attribute_domain_supported(domain)); + const blender::fn::CPPType *cpp_type = blender::bke::custom_data_type_to_cpp_type(data_type); + BLI_assert(cpp_type != nullptr); + if (value == nullptr) { + value = cpp_type->default_value(); + } + const int domain_size = this->attribute_domain_size(domain); + return std::make_unique<blender::bke::ConstantReadAttribute>( + domain, domain_size, *cpp_type, value); +} + +WriteAttributePtr GeometryComponent::attribute_try_ensure_for_write(const StringRef attribute_name, + const AttributeDomain domain, + const CustomDataType data_type) +{ + const blender::fn::CPPType *cpp_type = blender::bke::custom_data_type_to_cpp_type(data_type); + BLI_assert(cpp_type != nullptr); + + WriteAttributePtr attribute = this->attribute_try_get_for_write(attribute_name); + if (attribute && attribute->domain() == domain && attribute->cpp_type() == *cpp_type) { + return attribute; + } + + if (attribute) { + if (!this->attribute_try_delete(attribute_name)) { + return {}; + } + } + if (!this->attribute_domain_with_type_supported(domain, data_type)) { + return {}; + } + if (!this->attribute_try_create(attribute_name, domain, data_type)) { + return {}; + } + return this->attribute_try_get_for_write(attribute_name); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Point Cloud Component + * \{ */ + +bool PointCloudComponent::attribute_domain_supported(const AttributeDomain domain) const +{ + return domain == ATTR_DOMAIN_POINT; +} + +bool PointCloudComponent::attribute_domain_with_type_supported( + const AttributeDomain domain, const CustomDataType data_type) const +{ + return domain == ATTR_DOMAIN_POINT && ELEM(data_type, + CD_PROP_FLOAT, + CD_PROP_FLOAT2, + CD_PROP_FLOAT3, + CD_PROP_INT32, + CD_PROP_COLOR); +} + +int PointCloudComponent::attribute_domain_size(const AttributeDomain domain) const +{ + BLI_assert(domain == ATTR_DOMAIN_POINT); + UNUSED_VARS_NDEBUG(domain); + if (pointcloud_ == nullptr) { + return 0; + } + return pointcloud_->totpoint; +} + +bool PointCloudComponent::attribute_is_builtin(const StringRef attribute_name) const +{ + return attribute_name == "position"; +} + +ReadAttributePtr PointCloudComponent::attribute_try_get_for_read( + const StringRef attribute_name) const +{ + if (pointcloud_ == nullptr) { + return {}; + } + + return read_attribute_from_custom_data( + pointcloud_->pdata, pointcloud_->totpoint, attribute_name, ATTR_DOMAIN_POINT); +} + +WriteAttributePtr PointCloudComponent::attribute_try_get_for_write(const StringRef attribute_name) +{ + PointCloud *pointcloud = this->get_for_write(); + if (pointcloud == nullptr) { + return {}; + } + + return write_attribute_from_custom_data( + pointcloud->pdata, pointcloud->totpoint, attribute_name, ATTR_DOMAIN_POINT, [&]() { + BKE_pointcloud_update_customdata_pointers(pointcloud); + }); +} + +bool PointCloudComponent::attribute_try_delete(const StringRef attribute_name) +{ + if (this->attribute_is_builtin(attribute_name)) { + return false; + } + PointCloud *pointcloud = this->get_for_write(); + if (pointcloud == nullptr) { + return false; + } + delete_named_custom_data_layer(pointcloud->pdata, attribute_name, pointcloud->totpoint); + return true; +} + +static bool custom_data_has_layer_with_name(const CustomData &custom_data, const StringRef name) +{ + for (const CustomDataLayer &layer : blender::Span(custom_data.layers, custom_data.totlayer)) { + if (layer.name == name) { + return true; + } + } + return false; +} + +bool PointCloudComponent::attribute_try_create(const StringRef attribute_name, + const AttributeDomain domain, + const CustomDataType data_type) +{ + if (this->attribute_is_builtin(attribute_name)) { + return false; + } + if (!this->attribute_domain_with_type_supported(domain, data_type)) { + return false; + } + PointCloud *pointcloud = this->get_for_write(); + if (pointcloud == nullptr) { + return false; + } + if (custom_data_has_layer_with_name(pointcloud->pdata, attribute_name)) { + return false; + } + + char attribute_name_c[MAX_NAME]; + attribute_name.copy(attribute_name_c); + CustomData_add_layer_named( + &pointcloud->pdata, data_type, CD_DEFAULT, nullptr, pointcloud_->totpoint, attribute_name_c); + return true; +} + +Set<std::string> PointCloudComponent::attribute_names() const +{ + if (pointcloud_ == nullptr) { + return {}; + } + + Set<std::string> names; + get_custom_data_layer_attribute_names(pointcloud_->pdata, *this, ATTR_DOMAIN_POINT, names); + return names; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Mesh Component + * \{ */ + +bool MeshComponent::attribute_domain_supported(const AttributeDomain domain) const +{ + return ELEM( + domain, ATTR_DOMAIN_CORNER, ATTR_DOMAIN_POINT, ATTR_DOMAIN_EDGE, ATTR_DOMAIN_POLYGON); +} + +bool MeshComponent::attribute_domain_with_type_supported(const AttributeDomain domain, + const CustomDataType data_type) const +{ + if (!this->attribute_domain_supported(domain)) { + return false; + } + return ELEM( + data_type, CD_PROP_FLOAT, CD_PROP_FLOAT2, CD_PROP_FLOAT3, CD_PROP_INT32, CD_PROP_COLOR); +} + +int MeshComponent::attribute_domain_size(const AttributeDomain domain) const +{ + BLI_assert(this->attribute_domain_supported(domain)); + if (mesh_ == nullptr) { + return 0; + } + switch (domain) { + case ATTR_DOMAIN_CORNER: + return mesh_->totloop; + case ATTR_DOMAIN_POINT: + return mesh_->totvert; + case ATTR_DOMAIN_EDGE: + return mesh_->totedge; + case ATTR_DOMAIN_POLYGON: + return mesh_->totpoly; + default: + BLI_assert(false); + break; + } + return 0; +} + +bool MeshComponent::attribute_is_builtin(const StringRef attribute_name) const +{ + return attribute_name == "position"; +} + +ReadAttributePtr MeshComponent::attribute_try_get_for_read(const StringRef attribute_name) const +{ + if (mesh_ == nullptr) { + return {}; + } + + if (attribute_name == "position") { + auto get_vertex_position = [](const MVert &vert) { return float3(vert.co); }; + return std::make_unique< + blender::bke::DerivedArrayReadAttribute<MVert, float3, decltype(get_vertex_position)>>( + ATTR_DOMAIN_POINT, blender::Span(mesh_->mvert, mesh_->totvert), get_vertex_position); + } + + ReadAttributePtr corner_attribute = read_attribute_from_custom_data( + mesh_->ldata, mesh_->totloop, attribute_name, ATTR_DOMAIN_CORNER); + if (corner_attribute) { + return corner_attribute; + } + + const int vertex_group_index = vertex_group_names_.lookup_default(attribute_name, -1); + if (vertex_group_index >= 0) { + return std::make_unique<blender::bke::VertexWeightReadAttribute>( + mesh_->dvert, mesh_->totvert, vertex_group_index); + } + + ReadAttributePtr vertex_attribute = read_attribute_from_custom_data( + mesh_->vdata, mesh_->totvert, attribute_name, ATTR_DOMAIN_POINT); + if (vertex_attribute) { + return vertex_attribute; + } + + ReadAttributePtr edge_attribute = read_attribute_from_custom_data( + mesh_->edata, mesh_->totedge, attribute_name, ATTR_DOMAIN_EDGE); + if (edge_attribute) { + return edge_attribute; + } + + ReadAttributePtr polygon_attribute = read_attribute_from_custom_data( + mesh_->pdata, mesh_->totpoly, attribute_name, ATTR_DOMAIN_POLYGON); + if (polygon_attribute) { + return polygon_attribute; + } + + return {}; +} + +WriteAttributePtr MeshComponent::attribute_try_get_for_write(const StringRef attribute_name) +{ + Mesh *mesh = this->get_for_write(); + if (mesh == nullptr) { + return {}; + } + + const std::function<void()> update_mesh_pointers = [&]() { + BKE_mesh_update_customdata_pointers(mesh, false); + }; + + if (attribute_name == "position") { + CustomData_duplicate_referenced_layer(&mesh->vdata, CD_MVERT, mesh->totvert); + update_mesh_pointers(); + + auto get_vertex_position = [](const MVert &vert) { return float3(vert.co); }; + auto set_vertex_position = [](MVert &vert, const float3 &co) { copy_v3_v3(vert.co, co); }; + return std::make_unique< + blender::bke::DerivedArrayWriteAttribute<MVert, + float3, + decltype(get_vertex_position), + decltype(set_vertex_position)>>( + ATTR_DOMAIN_POINT, + blender::MutableSpan(mesh_->mvert, mesh_->totvert), + get_vertex_position, + set_vertex_position); + } + + WriteAttributePtr corner_attribute = write_attribute_from_custom_data( + mesh_->ldata, mesh_->totloop, attribute_name, ATTR_DOMAIN_CORNER, update_mesh_pointers); + if (corner_attribute) { + return corner_attribute; + } + + const int vertex_group_index = vertex_group_names_.lookup_default_as(attribute_name, -1); + if (vertex_group_index >= 0) { + if (mesh_->dvert == nullptr) { + BKE_object_defgroup_data_create(&mesh_->id); + } + return std::make_unique<blender::bke::VertexWeightWriteAttribute>( + mesh_->dvert, mesh_->totvert, vertex_group_index); + } + + WriteAttributePtr vertex_attribute = write_attribute_from_custom_data( + mesh_->vdata, mesh_->totvert, attribute_name, ATTR_DOMAIN_POINT, update_mesh_pointers); + if (vertex_attribute) { + return vertex_attribute; + } + + WriteAttributePtr edge_attribute = write_attribute_from_custom_data( + mesh_->edata, mesh_->totedge, attribute_name, ATTR_DOMAIN_EDGE, update_mesh_pointers); + if (edge_attribute) { + return edge_attribute; + } + + WriteAttributePtr polygon_attribute = write_attribute_from_custom_data( + mesh_->pdata, mesh_->totpoly, attribute_name, ATTR_DOMAIN_POLYGON, update_mesh_pointers); + if (polygon_attribute) { + return polygon_attribute; + } + + return {}; +} + +bool MeshComponent::attribute_try_delete(const StringRef attribute_name) +{ + if (this->attribute_is_builtin(attribute_name)) { + return false; + } + Mesh *mesh = this->get_for_write(); + if (mesh == nullptr) { + return false; + } + + delete_named_custom_data_layer(mesh_->ldata, attribute_name, mesh_->totloop); + delete_named_custom_data_layer(mesh_->vdata, attribute_name, mesh_->totvert); + delete_named_custom_data_layer(mesh_->edata, attribute_name, mesh_->totedge); + delete_named_custom_data_layer(mesh_->pdata, attribute_name, mesh_->totpoly); + + const int vertex_group_index = vertex_group_names_.lookup_default_as(attribute_name, -1); + if (vertex_group_index != -1) { + for (MDeformVert &dvert : blender::MutableSpan(mesh_->dvert, mesh_->totvert)) { + MDeformWeight *weight = BKE_defvert_find_index(&dvert, vertex_group_index); + BKE_defvert_remove_group(&dvert, weight); + } + vertex_group_names_.remove_as(attribute_name); + } + + return true; +} + +bool MeshComponent::attribute_try_create(const StringRef attribute_name, + const AttributeDomain domain, + const CustomDataType data_type) +{ + if (this->attribute_is_builtin(attribute_name)) { + return false; + } + if (!this->attribute_domain_with_type_supported(domain, data_type)) { + return false; + } + Mesh *mesh = this->get_for_write(); + if (mesh == nullptr) { + return false; + } + + char attribute_name_c[MAX_NAME]; + attribute_name.copy(attribute_name_c); + + switch (domain) { + case ATTR_DOMAIN_CORNER: { + if (custom_data_has_layer_with_name(mesh->ldata, attribute_name)) { + return false; + } + CustomData_add_layer_named( + &mesh->ldata, data_type, CD_DEFAULT, nullptr, mesh->totloop, attribute_name_c); + return true; + } + case ATTR_DOMAIN_POINT: { + if (custom_data_has_layer_with_name(mesh->vdata, attribute_name)) { + return false; + } + if (vertex_group_names_.contains_as(attribute_name)) { + return false; + } + CustomData_add_layer_named( + &mesh->vdata, data_type, CD_DEFAULT, nullptr, mesh->totvert, attribute_name_c); + return true; + } + case ATTR_DOMAIN_EDGE: { + if (custom_data_has_layer_with_name(mesh->edata, attribute_name)) { + return false; + } + CustomData_add_layer_named( + &mesh->edata, data_type, CD_DEFAULT, nullptr, mesh->totedge, attribute_name_c); + return true; + } + case ATTR_DOMAIN_POLYGON: { + if (custom_data_has_layer_with_name(mesh->pdata, attribute_name)) { + return false; + } + CustomData_add_layer_named( + &mesh->pdata, data_type, CD_DEFAULT, nullptr, mesh->totpoly, attribute_name_c); + return true; + } + default: + return false; + } +} + +Set<std::string> MeshComponent::attribute_names() const +{ + if (mesh_ == nullptr) { + return {}; + } + + Set<std::string> names; + names.add("position"); + for (StringRef name : vertex_group_names_.keys()) { + names.add(name); + } + get_custom_data_layer_attribute_names(mesh_->pdata, *this, ATTR_DOMAIN_CORNER, names); + get_custom_data_layer_attribute_names(mesh_->vdata, *this, ATTR_DOMAIN_POINT, names); + get_custom_data_layer_attribute_names(mesh_->edata, *this, ATTR_DOMAIN_EDGE, names); + get_custom_data_layer_attribute_names(mesh_->pdata, *this, ATTR_DOMAIN_POLYGON, names); + return names; +} + +/** \} */ diff --git a/source/blender/blenkernel/intern/fluid.c b/source/blender/blenkernel/intern/fluid.c index 434456a922e..73ca490c395 100644 --- a/source/blender/blenkernel/intern/fluid.c +++ b/source/blender/blenkernel/intern/fluid.c @@ -4559,7 +4559,7 @@ void BKE_fluid_particle_system_destroy(struct Object *ob, const int particle_typ if (psys->part->type == particle_type) { /* clear modifier */ pfmd = psys_get_modifier(ob, psys); - BLI_remlink(&ob->modifiers, pfmd); + BLI_remlink(&ob->modifiers, (ModifierData *)pfmd); BKE_modifier_free((ModifierData *)pfmd); /* clear particle system */ diff --git a/source/blender/blenkernel/intern/geometry_set.cc b/source/blender/blenkernel/intern/geometry_set.cc new file mode 100644 index 00000000000..28695d769d3 --- /dev/null +++ b/source/blender/blenkernel/intern/geometry_set.cc @@ -0,0 +1,554 @@ +/* + * 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 "BKE_geometry_set.hh" +#include "BKE_lib_id.h" +#include "BKE_mesh.h" +#include "BKE_mesh_wrapper.h" +#include "BKE_pointcloud.h" + +#include "DNA_object_types.h" + +#include "MEM_guardedalloc.h" + +using blender::float3; +using blender::MutableSpan; +using blender::Span; +using blender::StringRef; +using blender::Vector; + +/* -------------------------------------------------------------------- */ +/** \name Geometry Component + * \{ */ + +GeometryComponent::GeometryComponent(GeometryComponentType type) : type_(type) +{ +} + +GeometryComponent ::~GeometryComponent() +{ +} + +GeometryComponent *GeometryComponent::create(GeometryComponentType component_type) +{ + switch (component_type) { + case GeometryComponentType::Mesh: + return new MeshComponent(); + case GeometryComponentType::PointCloud: + return new PointCloudComponent(); + case GeometryComponentType::Instances: + return new InstancesComponent(); + } + BLI_assert(false); + return nullptr; +} + +void GeometryComponent::user_add() const +{ + users_.fetch_add(1); +} + +void GeometryComponent::user_remove() const +{ + const int new_users = users_.fetch_sub(1) - 1; + if (new_users == 0) { + delete this; + } +} + +bool GeometryComponent::is_mutable() const +{ + /* If the item is shared, it is read-only. */ + /* The user count can be 0, when this is called from the destructor. */ + return users_ <= 1; +} + +GeometryComponentType GeometryComponent::type() const +{ + return type_; +} + +bool GeometryComponent::is_empty() const +{ + return false; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Geometry Set + * \{ */ + +/* This method can only be used when the geometry set is mutable. It returns a mutable geometry + * component of the given type. + */ +GeometryComponent &GeometrySet::get_component_for_write(GeometryComponentType component_type) +{ + return components_.add_or_modify( + component_type, + [&](GeometryComponentPtr *value_ptr) -> GeometryComponent & { + /* If the component did not exist before, create a new one. */ + new (value_ptr) GeometryComponentPtr(GeometryComponent::create(component_type)); + return **value_ptr; + }, + [&](GeometryComponentPtr *value_ptr) -> GeometryComponent & { + GeometryComponentPtr &value = *value_ptr; + if (value->is_mutable()) { + /* If the referenced component is already mutable, return it directly. */ + return *value; + } + /* If the referenced component is shared, make a copy. The copy is not shared and is + * therefore mutable. */ + GeometryComponent *copied_component = value->copy(); + value = GeometryComponentPtr{copied_component}; + return *copied_component; + }); +} + +/* Get the component of the given type. Might return null if the component does not exist yet. */ +const GeometryComponent *GeometrySet::get_component_for_read( + GeometryComponentType component_type) const +{ + const GeometryComponentPtr *component = components_.lookup_ptr(component_type); + if (component != nullptr) { + return component->get(); + } + return nullptr; +} + +bool GeometrySet::has(const GeometryComponentType component_type) const +{ + return components_.contains(component_type); +} + +void GeometrySet::remove(const GeometryComponentType component_type) +{ + components_.remove(component_type); +} + +void GeometrySet::add(const GeometryComponent &component) +{ + BLI_assert(!components_.contains(component.type())); + component.user_add(); + GeometryComponentPtr component_ptr{const_cast<GeometryComponent *>(&component)}; + components_.add_new(component.type(), std::move(component_ptr)); +} + +void GeometrySet::compute_boundbox_without_instances(float3 *r_min, float3 *r_max) const +{ + const PointCloud *pointcloud = this->get_pointcloud_for_read(); + if (pointcloud != nullptr) { + BKE_pointcloud_minmax(pointcloud, *r_min, *r_max); + } + const Mesh *mesh = this->get_mesh_for_read(); + if (mesh != nullptr) { + BKE_mesh_wrapper_minmax(mesh, *r_min, *r_max); + } +} + +std::ostream &operator<<(std::ostream &stream, const GeometrySet &geometry_set) +{ + stream << "<GeometrySet at " << &geometry_set << ", " << geometry_set.components_.size() + << " components>"; + return stream; +} + +/* This generally should not be used. It is necessary currently, so that GeometrySet can by used by + * the CPPType system. */ +bool operator==(const GeometrySet &UNUSED(a), const GeometrySet &UNUSED(b)) +{ + return false; +} + +/* This generally should not be used. It is necessary currently, so that GeometrySet can by used by + * the CPPType system. */ +uint64_t GeometrySet::hash() const +{ + return reinterpret_cast<uint64_t>(this); +} + +/* Returns a read-only mesh or null. */ +const Mesh *GeometrySet::get_mesh_for_read() const +{ + const MeshComponent *component = this->get_component_for_read<MeshComponent>(); + return (component == nullptr) ? nullptr : component->get_for_read(); +} + +/* Returns true when the geometry set has a mesh component that has a mesh. */ +bool GeometrySet::has_mesh() const +{ + const MeshComponent *component = this->get_component_for_read<MeshComponent>(); + return component != nullptr && component->has_mesh(); +} + +/* Returns a read-only point cloud of null. */ +const PointCloud *GeometrySet::get_pointcloud_for_read() const +{ + const PointCloudComponent *component = this->get_component_for_read<PointCloudComponent>(); + return (component == nullptr) ? nullptr : component->get_for_read(); +} + +/* Returns true when the geometry set has a point cloud component that has a point cloud. */ +bool GeometrySet::has_pointcloud() const +{ + const PointCloudComponent *component = this->get_component_for_read<PointCloudComponent>(); + return component != nullptr && component->has_pointcloud(); +} + +/* Returns true when the geometry set has an instances component that has at least one instance. */ +bool GeometrySet::has_instances() const +{ + const InstancesComponent *component = this->get_component_for_read<InstancesComponent>(); + return component != nullptr && component->instances_amount() >= 1; +} + +/* Create a new geometry set that only contains the given mesh. */ +GeometrySet GeometrySet::create_with_mesh(Mesh *mesh, GeometryOwnershipType ownership) +{ + GeometrySet geometry_set; + MeshComponent &component = geometry_set.get_component_for_write<MeshComponent>(); + component.replace(mesh, ownership); + return geometry_set; +} + +/* Create a new geometry set that only contains the given point cloud. */ +GeometrySet GeometrySet::create_with_pointcloud(PointCloud *pointcloud, + GeometryOwnershipType ownership) +{ + GeometrySet geometry_set; + PointCloudComponent &component = geometry_set.get_component_for_write<PointCloudComponent>(); + component.replace(pointcloud, ownership); + return geometry_set; +} + +/* Clear the existing mesh and replace it with the given one. */ +void GeometrySet::replace_mesh(Mesh *mesh, GeometryOwnershipType ownership) +{ + MeshComponent &component = this->get_component_for_write<MeshComponent>(); + component.replace(mesh, ownership); +} + +/* Clear the existing point cloud and replace with the given one. */ +void GeometrySet::replace_pointcloud(PointCloud *pointcloud, GeometryOwnershipType ownership) +{ + PointCloudComponent &pointcloud_component = this->get_component_for_write<PointCloudComponent>(); + pointcloud_component.replace(pointcloud, ownership); +} + +/* Returns a mutable mesh or null. No ownership is transferred. */ +Mesh *GeometrySet::get_mesh_for_write() +{ + MeshComponent &component = this->get_component_for_write<MeshComponent>(); + return component.get_for_write(); +} + +/* Returns a mutable point cloud or null. No ownership is transferred. */ +PointCloud *GeometrySet::get_pointcloud_for_write() +{ + PointCloudComponent &component = this->get_component_for_write<PointCloudComponent>(); + return component.get_for_write(); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Mesh Component + * \{ */ + +MeshComponent::MeshComponent() : GeometryComponent(GeometryComponentType::Mesh) +{ +} + +MeshComponent::~MeshComponent() +{ + this->clear(); +} + +GeometryComponent *MeshComponent::copy() const +{ + MeshComponent *new_component = new MeshComponent(); + if (mesh_ != nullptr) { + new_component->mesh_ = BKE_mesh_copy_for_eval(mesh_, false); + new_component->ownership_ = GeometryOwnershipType::Owned; + } + return new_component; +} + +void MeshComponent::clear() +{ + BLI_assert(this->is_mutable()); + if (mesh_ != nullptr) { + if (ownership_ == GeometryOwnershipType::Owned) { + BKE_id_free(nullptr, mesh_); + } + mesh_ = nullptr; + } + vertex_group_names_.clear(); +} + +bool MeshComponent::has_mesh() const +{ + return mesh_ != nullptr; +} + +/* Clear the component and replace it with the new mesh. */ +void MeshComponent::replace(Mesh *mesh, GeometryOwnershipType ownership) +{ + BLI_assert(this->is_mutable()); + this->clear(); + mesh_ = mesh; + ownership_ = ownership; +} + +/* Return the mesh and clear the component. The caller takes over responsibility for freeing the + * mesh (if the component was responsible before). */ +Mesh *MeshComponent::release() +{ + BLI_assert(this->is_mutable()); + Mesh *mesh = mesh_; + mesh_ = nullptr; + return mesh; +} + +void MeshComponent::copy_vertex_group_names_from_object(const Object &object) +{ + BLI_assert(this->is_mutable()); + vertex_group_names_.clear(); + int index = 0; + LISTBASE_FOREACH (const bDeformGroup *, group, &object.defbase) { + vertex_group_names_.add(group->name, index); + index++; + } +} + +/* Get the mesh from this component. This method can be used by multiple threads at the same + * time. Therefore, the returned mesh should not be modified. No ownership is transferred. */ +const Mesh *MeshComponent::get_for_read() const +{ + return mesh_; +} + +/* Get the mesh from this component. This method can only be used when the component is mutable, + * i.e. it is not shared. The returned mesh can be modified. No ownership is transferred. */ +Mesh *MeshComponent::get_for_write() +{ + BLI_assert(this->is_mutable()); + if (ownership_ == GeometryOwnershipType::ReadOnly) { + mesh_ = BKE_mesh_copy_for_eval(mesh_, false); + ownership_ = GeometryOwnershipType::Owned; + } + return mesh_; +} + +bool MeshComponent::is_empty() const +{ + return mesh_ == nullptr; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Pointcloud Component + * \{ */ + +PointCloudComponent::PointCloudComponent() : GeometryComponent(GeometryComponentType::PointCloud) +{ +} + +PointCloudComponent::~PointCloudComponent() +{ + this->clear(); +} + +GeometryComponent *PointCloudComponent::copy() const +{ + PointCloudComponent *new_component = new PointCloudComponent(); + if (pointcloud_ != nullptr) { + new_component->pointcloud_ = BKE_pointcloud_copy_for_eval(pointcloud_, false); + new_component->ownership_ = GeometryOwnershipType::Owned; + } + return new_component; +} + +void PointCloudComponent::clear() +{ + BLI_assert(this->is_mutable()); + if (pointcloud_ != nullptr) { + if (ownership_ == GeometryOwnershipType::Owned) { + BKE_id_free(nullptr, pointcloud_); + } + pointcloud_ = nullptr; + } +} + +bool PointCloudComponent::has_pointcloud() const +{ + return pointcloud_ != nullptr; +} + +/* Clear the component and replace it with the new point cloud. */ +void PointCloudComponent::replace(PointCloud *pointcloud, GeometryOwnershipType ownership) +{ + BLI_assert(this->is_mutable()); + this->clear(); + pointcloud_ = pointcloud; + ownership_ = ownership; +} + +/* Return the point cloud and clear the component. The caller takes over responsibility for freeing + * the point cloud (if the component was responsible before). */ +PointCloud *PointCloudComponent::release() +{ + BLI_assert(this->is_mutable()); + PointCloud *pointcloud = pointcloud_; + pointcloud_ = nullptr; + return pointcloud; +} + +/* Get the point cloud from this component. This method can be used by multiple threads at the same + * time. Therefore, the returned point cloud should not be modified. No ownership is transferred. + */ +const PointCloud *PointCloudComponent::get_for_read() const +{ + return pointcloud_; +} + +/* Get the point cloud from this component. This method can only be used when the component is + * mutable, i.e. it is not shared. The returned point cloud can be modified. No ownership is + * transferred. */ +PointCloud *PointCloudComponent::get_for_write() +{ + BLI_assert(this->is_mutable()); + if (ownership_ == GeometryOwnershipType::ReadOnly) { + pointcloud_ = BKE_pointcloud_copy_for_eval(pointcloud_, false); + ownership_ = GeometryOwnershipType::Owned; + } + return pointcloud_; +} + +bool PointCloudComponent::is_empty() const +{ + return pointcloud_ == nullptr; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Instances Component + * \{ */ + +InstancesComponent::InstancesComponent() : GeometryComponent(GeometryComponentType::Instances) +{ +} + +GeometryComponent *InstancesComponent::copy() const +{ + InstancesComponent *new_component = new InstancesComponent(); + new_component->positions_ = positions_; + new_component->rotations_ = rotations_; + new_component->scales_ = scales_; + new_component->objects_ = objects_; + return new_component; +} + +void InstancesComponent::clear() +{ + objects_.clear(); + positions_.clear(); + rotations_.clear(); + scales_.clear(); +} +void InstancesComponent::add_instance(const Object *object, + blender::float3 position, + blender::float3 rotation, + blender::float3 scale) +{ + objects_.append(object); + positions_.append(position); + rotations_.append(rotation); + scales_.append(scale); +} + +Span<const Object *> InstancesComponent::objects() const +{ + return objects_; +} + +Span<float3> InstancesComponent::positions() const +{ + return positions_; +} + +blender::Span<blender::float3> InstancesComponent::rotations() const +{ + return rotations_; +} + +blender::Span<blender::float3> InstancesComponent::scales() const +{ + return scales_; +} + +MutableSpan<float3> InstancesComponent::positions() +{ + return positions_; +} + +int InstancesComponent::instances_amount() const +{ + BLI_assert(positions_.size() == objects_.size()); + return objects_.size(); +} + +bool InstancesComponent::is_empty() const +{ + return positions_.size() == 0; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name C API + * \{ */ + +void BKE_geometry_set_free(GeometrySet *geometry_set) +{ + delete geometry_set; +} + +bool BKE_geometry_set_has_instances(const GeometrySet *geometry_set) +{ + return geometry_set->get_component_for_read<InstancesComponent>() != nullptr; +} + +int BKE_geometry_set_instances(const GeometrySet *geometry_set, + float (**r_positions)[3], + float (**r_rotations)[3], + float (**r_scales)[3], + Object ***r_objects) +{ + const InstancesComponent *component = geometry_set->get_component_for_read<InstancesComponent>(); + if (component == nullptr) { + return 0; + } + *r_positions = (float(*)[3])component->positions().data(); + *r_rotations = (float(*)[3])component->rotations().data(); + *r_scales = (float(*)[3])component->scales().data(); + *r_objects = (Object **)component->objects().data(); + return component->instances_amount(); +} + +/** \} */ diff --git a/source/blender/blenkernel/intern/hair.c b/source/blender/blenkernel/intern/hair.c index 554919ad1a0..a44b054e366 100644 --- a/source/blender/blenkernel/intern/hair.c +++ b/source/blender/blenkernel/intern/hair.c @@ -49,8 +49,8 @@ #include "BLO_read_write.h" -static const char *HAIR_ATTR_POSITION = "Position"; -static const char *HAIR_ATTR_RADIUS = "Radius"; +static const char *HAIR_ATTR_POSITION = "position"; +static const char *HAIR_ATTR_RADIUS = "radius"; /* Hair datablock */ diff --git a/source/blender/blenkernel/intern/lib_remap.c b/source/blender/blenkernel/intern/lib_remap.c index f0031d4191d..9e3189afee9 100644 --- a/source/blender/blenkernel/intern/lib_remap.c +++ b/source/blender/blenkernel/intern/lib_remap.c @@ -342,7 +342,7 @@ static void libblock_remap_data_postprocess_obdata_relink(Main *bmain, Object *o static void libblock_remap_data_postprocess_nodetree_update(Main *bmain, ID *new_id) { /* Update all group nodes using a node group. */ - ntreeUpdateAllUsers(bmain, new_id); + ntreeUpdateAllUsers(bmain, (bNodeTree *)new_id); } /** diff --git a/source/blender/blenkernel/intern/node.c b/source/blender/blenkernel/intern/node.c index e639bb319a3..b564a4c468b 100644 --- a/source/blender/blenkernel/intern/node.c +++ b/source/blender/blenkernel/intern/node.c @@ -39,6 +39,7 @@ #include "DNA_light_types.h" #include "DNA_linestyle_types.h" #include "DNA_material_types.h" +#include "DNA_modifier_types.h" #include "DNA_node_types.h" #include "DNA_scene_types.h" #include "DNA_simulation_types.h" @@ -65,7 +66,6 @@ #include "BKE_lib_query.h" #include "BKE_main.h" #include "BKE_node.h" -#include "BKE_simulation.h" #include "BLI_ghash.h" #include "BLI_threads.h" @@ -75,8 +75,8 @@ #include "NOD_common.h" #include "NOD_composite.h" #include "NOD_function.h" +#include "NOD_geometry.h" #include "NOD_shader.h" -#include "NOD_simulation.h" #include "NOD_socket.h" #include "NOD_texture.h" @@ -85,6 +85,8 @@ #include "BLO_read_write.h" +#include "MOD_nodes.h" + #define NODE_DEFAULT_MAX_WIDTH 700 /* Fallback types for undefined tree, nodes, sockets */ @@ -3958,14 +3960,18 @@ void ntreeUpdateAllNew(Main *main) FOREACH_NODETREE_END; } -void ntreeUpdateAllUsers(Main *main, ID *ngroup) +void ntreeUpdateAllUsers(Main *main, bNodeTree *ngroup) { + if (ngroup == NULL) { + return; + } + /* Update all users of ngroup, to add/remove sockets as needed. */ FOREACH_NODETREE_BEGIN (main, ntree, owner_id) { bool need_update = false; LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { - if (node->id == ngroup) { + if (node->id == &ngroup->id) { if (node->typeinfo->group_update_func) { node->typeinfo->group_update_func(ntree, node); } @@ -3979,6 +3985,19 @@ void ntreeUpdateAllUsers(Main *main, ID *ngroup) } } FOREACH_NODETREE_END; + + if (ngroup->type == NTREE_GEOMETRY) { + LISTBASE_FOREACH (Object *, object, &main->objects) { + LISTBASE_FOREACH (ModifierData *, md, &object->modifiers) { + if (md->type == eModifierType_Nodes) { + NodesModifierData *nmd = (NodesModifierData *)md; + if (nmd->node_group == ngroup) { + MOD_nodes_update_interface(object, nmd); + } + } + } + } + } } void ntreeUpdateTree(Main *bmain, bNodeTree *ntree) @@ -4022,7 +4041,7 @@ void ntreeUpdateTree(Main *bmain, bNodeTree *ntree) } if (bmain) { - ntreeUpdateAllUsers(bmain, &ntree->id); + ntreeUpdateAllUsers(bmain, ntree); } if (ntree->update & (NTREE_UPDATE_LINKS | NTREE_UPDATE_NODES)) { @@ -4659,9 +4678,21 @@ static void registerTextureNodes(void) register_node_type_tex_proc_distnoise(); } -static void registerSimulationNodes(void) +static void registerGeometryNodes(void) { - register_node_type_sim_group(); + register_node_type_geo_group(); + + register_node_type_geo_triangulate(); + register_node_type_geo_edge_split(); + register_node_type_geo_transform(); + register_node_type_geo_subdivision_surface(); + register_node_type_geo_boolean(); + register_node_type_geo_point_distribute(); + register_node_type_geo_point_instance(); + register_node_type_geo_object_info(); + register_node_type_geo_random_attribute(); + register_node_type_geo_attribute_math(); + register_node_type_geo_join_geometry(); } static void registerFunctionNodes(void) @@ -4688,7 +4719,7 @@ void BKE_node_system_init(void) register_node_tree_type_cmp(); register_node_tree_type_sh(); register_node_tree_type_tex(); - register_node_tree_type_sim(); + register_node_tree_type_geo(); register_node_type_frame(); register_node_type_reroute(); @@ -4698,7 +4729,7 @@ void BKE_node_system_init(void) registerCompositNodes(); registerShaderNodes(); registerTextureNodes(); - registerSimulationNodes(); + registerGeometryNodes(); registerFunctionNodes(); } diff --git a/source/blender/blenkernel/intern/object.c b/source/blender/blenkernel/intern/object.c index 242c0edd5a4..31d2e3738f4 100644 --- a/source/blender/blenkernel/intern/object.c +++ b/source/blender/blenkernel/intern/object.c @@ -94,6 +94,7 @@ #include "BKE_fcurve.h" #include "BKE_fcurve_driver.h" #include "BKE_font.h" +#include "BKE_geometry_set.h" #include "BKE_global.h" #include "BKE_gpencil.h" #include "BKE_gpencil_geom.h" @@ -1284,8 +1285,7 @@ bool BKE_object_support_modifier_type_check(const Object *ob, int modifier_type) return (mti->modifyHair != NULL) || (mti->flags & eModifierTypeFlag_AcceptsVertexCosOnly); } if (ob->type == OB_POINTCLOUD) { - return (mti->modifyPointCloud != NULL) || - (mti->flags & eModifierTypeFlag_AcceptsVertexCosOnly); + return (mti->modifyPointCloud != NULL); } if (ob->type == OB_VOLUME) { return (mti->modifyVolume != NULL); @@ -1507,6 +1507,9 @@ void BKE_object_eval_assign_data(Object *object_eval, ID *data_eval, bool is_own object_eval->data = data_eval; } } + + /* Is set separately currently. */ + object_eval->runtime.geometry_set_eval = NULL; } /** @@ -1551,6 +1554,11 @@ void BKE_object_free_derived_caches(Object *ob) BKE_gpencil_eval_delete(ob->runtime.gpd_eval); ob->runtime.gpd_eval = NULL; } + + if (ob->runtime.geometry_set_eval != NULL) { + BKE_geometry_set_free(ob->runtime.geometry_set_eval); + ob->runtime.geometry_set_eval = NULL; + } } void BKE_object_free_caches(Object *object) @@ -1771,6 +1779,10 @@ int BKE_object_visibility(const Object *ob, const int dag_eval_mode) visibility |= OB_VISIBLE_INSTANCES; } + if (ob->runtime.geometry_set_eval != NULL) { + visibility |= OB_VISIBLE_INSTANCES; + } + /* Optional hiding of self if there are particles or instancers. */ if (visibility & (OB_VISIBLE_PARTICLES | OB_VISIBLE_INSTANCES)) { switch ((eEvaluationMode)dag_eval_mode) { @@ -4880,6 +4892,7 @@ void BKE_object_runtime_reset_on_copy(Object *object, const int UNUSED(flag)) runtime->mesh_deform_eval = NULL; runtime->curve_cache = NULL; runtime->object_as_temp_mesh = NULL; + runtime->geometry_set_eval = NULL; } /** diff --git a/source/blender/blenkernel/intern/object_dupli.c b/source/blender/blenkernel/intern/object_dupli.c index a1b01bce6c9..9a8c560f116 100644 --- a/source/blender/blenkernel/intern/object_dupli.c +++ b/source/blender/blenkernel/intern/object_dupli.c @@ -47,6 +47,7 @@ #include "BKE_editmesh.h" #include "BKE_editmesh_cache.h" #include "BKE_font.h" +#include "BKE_geometry_set.h" #include "BKE_global.h" #include "BKE_idprop.h" #include "BKE_lattice.h" @@ -807,6 +808,45 @@ static const DupliGenerator gen_dupli_verts_pointcloud = { /** \} */ /* -------------------------------------------------------------------- */ +/** \name Instances Geometry Component Implementation + * \{ */ + +static void make_duplis_instances_component(const DupliContext *ctx) +{ + float(*positions)[3]; + float(*rotations)[3]; + float(*scales)[3]; + Object **objects; + const int amount = BKE_geometry_set_instances( + ctx->object->runtime.geometry_set_eval, &positions, &rotations, &scales, &objects); + + for (int i = 0; i < amount; i++) { + Object *object = objects[i]; + if (object == NULL) { + continue; + } + float scale_matrix[4][4]; + size_to_mat4(scale_matrix, scales[i]); + float rotation_matrix[4][4]; + eul_to_mat4(rotation_matrix, rotations[i]); + float matrix[4][4]; + mul_m4_m4m4(matrix, rotation_matrix, scale_matrix); + copy_v3_v3(matrix[3], positions[i]); + mul_m4_m4_pre(matrix, ctx->object->obmat); + + make_dupli(ctx, object, matrix, i); + make_recursive_duplis(ctx, object, matrix, i); + } +} + +static const DupliGenerator gen_dupli_instances_component = { + 0, + make_duplis_instances_component, +}; + +/** \} */ + +/* -------------------------------------------------------------------- */ /** \name Dupli-Faces Implementation (#OB_DUPLIFACES) * \{ */ @@ -1473,7 +1513,7 @@ static const DupliGenerator *get_dupli_generator(const DupliContext *ctx) int transflag = ctx->object->transflag; int restrictflag = ctx->object->restrictflag; - if ((transflag & OB_DUPLI) == 0) { + if ((transflag & OB_DUPLI) == 0 && ctx->object->runtime.geometry_set_eval == NULL) { return NULL; } @@ -1483,6 +1523,12 @@ static const DupliGenerator *get_dupli_generator(const DupliContext *ctx) return NULL; } + if (ctx->object->runtime.geometry_set_eval != NULL) { + if (BKE_geometry_set_has_instances(ctx->object->runtime.geometry_set_eval)) { + return &gen_dupli_instances_component; + } + } + if (transflag & OB_DUPLIPARTS) { return &gen_dupli_particles; } diff --git a/source/blender/blenkernel/intern/pointcache.c b/source/blender/blenkernel/intern/pointcache.c index 72115db4fb8..14e049384ef 100644 --- a/source/blender/blenkernel/intern/pointcache.c +++ b/source/blender/blenkernel/intern/pointcache.c @@ -1198,9 +1198,6 @@ static bool foreach_object_modifier_ptcache(Object *object, } } } - else if (md->type == eModifierType_Simulation) { - /* TODO(jacques): */ - } } return true; } diff --git a/source/blender/blenkernel/intern/pointcloud.cc b/source/blender/blenkernel/intern/pointcloud.cc index cbfb1086e8d..5f6685817b9 100644 --- a/source/blender/blenkernel/intern/pointcloud.cc +++ b/source/blender/blenkernel/intern/pointcloud.cc @@ -33,12 +33,14 @@ #include "BKE_anim_data.h" #include "BKE_customdata.h" +#include "BKE_geometry_set.hh" #include "BKE_global.h" #include "BKE_idtype.h" #include "BKE_lib_id.h" #include "BKE_lib_query.h" #include "BKE_lib_remap.h" #include "BKE_main.h" +#include "BKE_mesh_wrapper.h" #include "BKE_modifier.h" #include "BKE_object.h" #include "BKE_pointcloud.h" @@ -53,8 +55,8 @@ static void pointcloud_random(PointCloud *pointcloud); -const char *POINTCLOUD_ATTR_POSITION = "Position"; -const char *POINTCLOUD_ATTR_RADIUS = "Radius"; +const char *POINTCLOUD_ATTR_POSITION = "position"; +const char *POINTCLOUD_ATTR_RADIUS = "radius"; static void pointcloud_init_data(ID *id) { @@ -86,6 +88,8 @@ static void pointcloud_copy_data(Main *UNUSED(bmain), ID *id_dst, const ID *id_s alloc_type, pointcloud_dst->totpoint); BKE_pointcloud_update_customdata_pointers(pointcloud_dst); + + pointcloud_dst->batch_cache = nullptr; } static void pointcloud_free_data(ID *id) @@ -230,10 +234,46 @@ void *BKE_pointcloud_add_default(Main *bmain, const char *name) return pointcloud; } +PointCloud *BKE_pointcloud_new_nomain(const int totpoint) +{ + PointCloud *pointcloud = static_cast<PointCloud *>(BKE_libblock_alloc( + nullptr, ID_PT, BKE_idtype_idcode_to_name(ID_PT), LIB_ID_CREATE_LOCALIZE)); + + pointcloud_init_data(&pointcloud->id); + + pointcloud->totpoint = totpoint; + + CustomData_add_layer_named(&pointcloud->pdata, + CD_PROP_FLOAT, + CD_CALLOC, + nullptr, + pointcloud->totpoint, + POINTCLOUD_ATTR_RADIUS); + + pointcloud->totpoint = totpoint; + CustomData_realloc(&pointcloud->pdata, pointcloud->totpoint); + BKE_pointcloud_update_customdata_pointers(pointcloud); + + return pointcloud; +} + +void BKE_pointcloud_minmax(const struct PointCloud *pointcloud, float r_min[3], float r_max[3]) +{ + float(*pointcloud_co)[3] = pointcloud->co; + float *pointcloud_radius = pointcloud->radius; + for (int a = 0; a < pointcloud->totpoint; a++) { + float *co = pointcloud_co[a]; + float radius = (pointcloud_radius) ? pointcloud_radius[a] : 0.0f; + const float co_min[3] = {co[0] - radius, co[1] - radius, co[2] - radius}; + const float co_max[3] = {co[0] + radius, co[1] + radius, co[2] + radius}; + DO_MIN(co_min, r_min); + DO_MAX(co_max, r_max); + } +} + BoundBox *BKE_pointcloud_boundbox_get(Object *ob) { BLI_assert(ob->type == OB_POINTCLOUD); - PointCloud *pointcloud = static_cast<PointCloud *>(ob->data); if (ob->runtime.bb != nullptr && (ob->runtime.bb->flag & BOUNDBOX_DIRTY) == 0) { return ob->runtime.bb; @@ -241,23 +281,18 @@ BoundBox *BKE_pointcloud_boundbox_get(Object *ob) if (ob->runtime.bb == nullptr) { ob->runtime.bb = static_cast<BoundBox *>(MEM_callocN(sizeof(BoundBox), "pointcloud boundbox")); + } - float min[3], max[3]; - INIT_MINMAX(min, max); - - float(*pointcloud_co)[3] = pointcloud->co; - float *pointcloud_radius = pointcloud->radius; - for (int a = 0; a < pointcloud->totpoint; a++) { - float *co = pointcloud_co[a]; - float radius = (pointcloud_radius) ? pointcloud_radius[a] : 0.0f; - const float co_min[3] = {co[0] - radius, co[1] - radius, co[2] - radius}; - const float co_max[3] = {co[0] + radius, co[1] + radius, co[2] + radius}; - DO_MIN(co_min, min); - DO_MAX(co_max, max); - } - - BKE_boundbox_init_from_minmax(ob->runtime.bb, min, max); + blender::float3 min, max; + INIT_MINMAX(min, max); + if (ob->runtime.geometry_set_eval != nullptr) { + ob->runtime.geometry_set_eval->compute_boundbox_without_instances(&min, &max); + } + else { + const PointCloud *pointcloud = static_cast<PointCloud *>(ob->data); + BKE_pointcloud_minmax(pointcloud, min, max); } + BKE_boundbox_init_from_minmax(ob->runtime.bb, min, max); return ob->runtime.bb; } @@ -306,13 +341,11 @@ PointCloud *BKE_pointcloud_copy_for_eval(struct PointCloud *pointcloud_src, bool return result; } -static PointCloud *pointcloud_evaluate_modifiers(struct Depsgraph *depsgraph, - struct Scene *scene, - Object *object, - PointCloud *pointcloud_input) +static void pointcloud_evaluate_modifiers(struct Depsgraph *depsgraph, + struct Scene *scene, + Object *object, + GeometrySet &geometry_set) { - PointCloud *pointcloud = pointcloud_input; - /* Modifier evaluation modes. */ const bool use_render = (DEG_get_mode(depsgraph) == DAG_EVAL_RENDER); const int required_mode = use_render ? eModifierMode_Render : eModifierMode_Realtime; @@ -332,40 +365,10 @@ static PointCloud *pointcloud_evaluate_modifiers(struct Depsgraph *depsgraph, continue; } - if ((mti->type == eModifierTypeType_OnlyDeform) && - (mti->flags & eModifierTypeFlag_AcceptsVertexCosOnly)) { - /* Ensure we are not modifying the input. */ - if (pointcloud == pointcloud_input) { - pointcloud = BKE_pointcloud_copy_for_eval(pointcloud, true); - } - - /* Ensure we are not overwriting referenced data. */ - CustomData_duplicate_referenced_layer_named( - &pointcloud->pdata, CD_PROP_FLOAT3, POINTCLOUD_ATTR_POSITION, pointcloud->totpoint); - BKE_pointcloud_update_customdata_pointers(pointcloud); - - /* Created deformed coordinates array on demand. */ - mti->deformVerts(md, &mectx, nullptr, pointcloud->co, pointcloud->totpoint); - } - else if (mti->modifyPointCloud) { - /* Ensure we are not modifying the input. */ - if (pointcloud == pointcloud_input) { - pointcloud = BKE_pointcloud_copy_for_eval(pointcloud, true); - } - - PointCloud *pointcloud_next = mti->modifyPointCloud(md, &mectx, pointcloud); - - if (pointcloud_next && pointcloud_next != pointcloud) { - /* If the modifier returned a new pointcloud, release the old one. */ - if (pointcloud != pointcloud_input) { - BKE_id_free(nullptr, pointcloud); - } - pointcloud = pointcloud_next; - } + if (mti->modifyPointCloud) { + mti->modifyPointCloud(md, &mectx, &geometry_set); } } - - return pointcloud; } void BKE_pointcloud_data_update(struct Depsgraph *depsgraph, struct Scene *scene, Object *object) @@ -375,12 +378,14 @@ void BKE_pointcloud_data_update(struct Depsgraph *depsgraph, struct Scene *scene /* Evaluate modifiers. */ PointCloud *pointcloud = static_cast<PointCloud *>(object->data); - PointCloud *pointcloud_eval = pointcloud_evaluate_modifiers( - depsgraph, scene, object, pointcloud); + GeometrySet geometry_set = GeometrySet::create_with_pointcloud(pointcloud, + GeometryOwnershipType::ReadOnly); + pointcloud_evaluate_modifiers(depsgraph, scene, object, geometry_set); /* Assign evaluated object. */ - const bool is_owned = (pointcloud != pointcloud_eval); - BKE_object_eval_assign_data(object, &pointcloud_eval->id, is_owned); + PointCloud *dummy_pointcloud = BKE_pointcloud_new_nomain(0); + BKE_object_eval_assign_data(object, &dummy_pointcloud->id, true); + object->runtime.geometry_set_eval = new GeometrySet(geometry_set); } /* Draw Cache */ diff --git a/source/blender/blenkernel/intern/simulation.cc b/source/blender/blenkernel/intern/simulation.cc index 861779ec700..14e6ce63023 100644 --- a/source/blender/blenkernel/intern/simulation.cc +++ b/source/blender/blenkernel/intern/simulation.cc @@ -48,8 +48,8 @@ #include "BKE_pointcache.h" #include "BKE_simulation.h" +#include "NOD_geometry.h" #include "NOD_node_tree_multi_function.hh" -#include "NOD_simulation.h" #include "BLI_map.hh" #include "BLT_translation.h" @@ -70,7 +70,7 @@ static void simulation_init_data(ID *id) MEMCPY_STRUCT_AFTER(simulation, DNA_struct_default_get(Simulation), id); - bNodeTree *ntree = ntreeAddTree(nullptr, "Simulation Nodetree", ntreeType_Simulation->idname); + bNodeTree *ntree = ntreeAddTree(nullptr, "Geometry Nodetree", ntreeType_Geometry->idname); simulation->nodetree = ntree; } |