| 
									
										
										
										
											2020-12-02 13:25:25 +01:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * 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; | 
					
						
							| 
									
										
										
										
											2021-01-14 15:52:08 +01:00
										 |  |  | using blender::StringRefNull; | 
					
						
							| 
									
										
										
										
											2020-12-02 13:25:25 +01:00
										 |  |  | using blender::bke::ReadAttributePtr; | 
					
						
							|  |  |  | using blender::bke::WriteAttributePtr; | 
					
						
							| 
									
										
										
										
											2021-01-14 15:52:08 +01:00
										 |  |  | using blender::fn::GMutableSpan; | 
					
						
							| 
									
										
										
										
											2020-12-02 13:25:25 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | /* 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, | 
					
						
							| 
									
										
										
										
											2021-01-12 12:49:34 +01:00
										 |  |  |  * #apply_span should be called. */ | 
					
						
							| 
									
										
										
										
											2020-12-02 13:25:25 +01:00
										 |  |  | fn::GMutableSpan WriteAttribute::get_span() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   if (size_ == 0) { | 
					
						
							|  |  |  |     return fn::GMutableSpan(cpp_type_); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   if (array_buffer_ == nullptr) { | 
					
						
							| 
									
										
										
										
											2021-01-12 12:49:34 +01:00
										 |  |  |     this->initialize_span(false); | 
					
						
							| 
									
										
										
										
											2020-12-02 13:25:25 +01:00
										 |  |  |   } | 
					
						
							|  |  |  |   array_should_be_applied_ = true; | 
					
						
							|  |  |  |   return fn::GMutableSpan(cpp_type_, array_buffer_, size_); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-01-12 12:49:34 +01:00
										 |  |  | fn::GMutableSpan WriteAttribute::get_span_for_write_only() | 
					
						
							| 
									
										
										
										
											2020-12-02 13:25:25 +01:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2021-01-12 12:49:34 +01:00
										 |  |  |   if (size_ == 0) { | 
					
						
							|  |  |  |     return fn::GMutableSpan(cpp_type_); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   if (array_buffer_ == nullptr) { | 
					
						
							|  |  |  |     this->initialize_span(true); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   array_should_be_applied_ = true; | 
					
						
							|  |  |  |   return fn::GMutableSpan(cpp_type_, array_buffer_, size_); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void WriteAttribute::initialize_span(const bool write_only) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   const int element_size = cpp_type_.size(); | 
					
						
							|  |  |  |   array_buffer_ = MEM_mallocN_aligned(element_size * size_, cpp_type_.alignment(), __func__); | 
					
						
							| 
									
										
										
										
											2020-12-02 13:25:25 +01:00
										 |  |  |   array_is_temporary_ = true; | 
					
						
							| 
									
										
										
										
											2021-01-12 12:49:34 +01:00
										 |  |  |   if (write_only) { | 
					
						
							|  |  |  |     /* This does nothing for trivial types, but is necessary for general correctness. */ | 
					
						
							|  |  |  |     cpp_type_.construct_default_n(array_buffer_, size_); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   else { | 
					
						
							|  |  |  |     for (const int i : IndexRange(size_)) { | 
					
						
							|  |  |  |       this->get(i, POINTER_OFFSET(array_buffer_, i * element_size)); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2020-12-02 13:25:25 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 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); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-01-12 12:49:34 +01:00
										 |  |  |   void initialize_span(const bool UNUSED(write_only)) override | 
					
						
							| 
									
										
										
										
											2020-12-02 13:25:25 +01:00
										 |  |  |   { | 
					
						
							|  |  |  |     array_buffer_ = data_.data(); | 
					
						
							|  |  |  |     array_is_temporary_ = false; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   void apply_span_if_necessary() override | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |     /* Do nothing, because the span contains the attribute itself already. */ | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-01-14 15:52:08 +01:00
										 |  |  | /* This is used by the #OutputAttributePtr class. */ | 
					
						
							|  |  |  | class TemporaryWriteAttribute final : public WriteAttribute { | 
					
						
							|  |  |  |  public: | 
					
						
							|  |  |  |   GMutableSpan data; | 
					
						
							|  |  |  |   GeometryComponent &component; | 
					
						
							|  |  |  |   std::string final_name; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   TemporaryWriteAttribute(AttributeDomain domain, | 
					
						
							|  |  |  |                           GMutableSpan data, | 
					
						
							|  |  |  |                           GeometryComponent &component, | 
					
						
							|  |  |  |                           std::string final_name) | 
					
						
							|  |  |  |       : WriteAttribute(domain, data.type(), data.size()), | 
					
						
							|  |  |  |         data(data), | 
					
						
							|  |  |  |         component(component), | 
					
						
							|  |  |  |         final_name(std::move(final_name)) | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   ~TemporaryWriteAttribute() override | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |     if (data.data() != nullptr) { | 
					
						
							|  |  |  |       cpp_type_.destruct_n(data.data(), data.size()); | 
					
						
							|  |  |  |       MEM_freeN(data.data()); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   void get_internal(const int64_t index, void *r_value) const override | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |     data.type().copy_to_uninitialized(data[index], r_value); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   void set_internal(const int64_t index, const void *value) override | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |     data.type().copy_to_initialized(value, data[index]); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   void initialize_span(const bool UNUSED(write_only)) 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. */ | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-02 13:25:25 +01:00
										 |  |  | 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_); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-04 12:02:52 +01:00
										 |  |  |   ~ConstantReadAttribute() override | 
					
						
							| 
									
										
										
										
											2020-12-02 13:25:25 +01:00
										 |  |  |   { | 
					
						
							|  |  |  |     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>(); | 
					
						
							| 
									
										
										
										
											2020-12-16 12:33:13 -06:00
										 |  |  |     case CD_PROP_BOOL: | 
					
						
							|  |  |  |       return &CPPType::get<bool>(); | 
					
						
							| 
									
										
										
										
											2020-12-02 13:25:25 +01:00
										 |  |  |     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; | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2020-12-16 12:33:13 -06:00
										 |  |  |   if (type.is<bool>()) { | 
					
						
							|  |  |  |     return CD_PROP_BOOL; | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2020-12-02 13:25:25 +01:00
										 |  |  |   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)); | 
					
						
							| 
									
										
										
										
											2020-12-16 12:33:13 -06:00
										 |  |  |         case CD_PROP_BOOL: | 
					
						
							|  |  |  |           return std::make_unique<ArrayReadAttribute<bool>>( | 
					
						
							|  |  |  |               domain, Span(static_cast<bool *>(layer.data), size)); | 
					
						
							| 
									
										
										
										
											2021-01-14 18:11:44 +01:00
										 |  |  |         case CD_MLOOPUV: | 
					
						
							|  |  |  |           auto get_uv = [](const MLoopUV &uv) { return float2(uv.uv); }; | 
					
						
							|  |  |  |           return std::make_unique<DerivedArrayReadAttribute<MLoopUV, float2, decltype(get_uv)>>( | 
					
						
							|  |  |  |               domain, Span(static_cast<MLoopUV *>(layer.data), size), get_uv); | 
					
						
							| 
									
										
										
										
											2020-12-02 13:25:25 +01:00
										 |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   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)); | 
					
						
							| 
									
										
										
										
											2020-12-16 12:33:13 -06:00
										 |  |  |         case CD_PROP_BOOL: | 
					
						
							|  |  |  |           return std::make_unique<ArrayWriteAttribute<bool>>( | 
					
						
							|  |  |  |               domain, MutableSpan(static_cast<bool *>(layer.data), size)); | 
					
						
							| 
									
										
										
										
											2021-01-14 18:11:44 +01:00
										 |  |  |         case CD_MLOOPUV: | 
					
						
							|  |  |  |           auto get_uv = [](const MLoopUV &uv) { return float2(uv.uv); }; | 
					
						
							|  |  |  |           auto set_uv = [](MLoopUV &uv, const float2 value) { copy_v2_v2(uv.uv, value); }; | 
					
						
							|  |  |  |           return std::make_unique< | 
					
						
							|  |  |  |               DerivedArrayWriteAttribute<MLoopUV, float2, decltype(get_uv), decltype(set_uv)>>( | 
					
						
							|  |  |  |               domain, MutableSpan(static_cast<MLoopUV *>(layer.data), size), get_uv, set_uv); | 
					
						
							| 
									
										
										
										
											2020-12-02 13:25:25 +01:00
										 |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   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)) { | 
					
						
							| 
									
										
										
										
											2021-01-14 18:11:44 +01:00
										 |  |  |     const CustomDataType data_type = static_cast<CustomDataType>(layer.type); | 
					
						
							|  |  |  |     if (component.attribute_domain_with_type_supported(domain, data_type) || | 
					
						
							|  |  |  |         ELEM(data_type, CD_MLOOPUV)) { | 
					
						
							| 
									
										
										
										
											2020-12-02 13:25:25 +01:00
										 |  |  |       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 {}; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-10 10:50:37 -06:00
										 |  |  | bool GeometryComponent::attribute_exists(const blender::StringRef attribute_name) const | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   ReadAttributePtr attribute = this->attribute_try_get_for_read(attribute_name); | 
					
						
							|  |  |  |   if (attribute) { | 
					
						
							|  |  |  |     return true; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   return false; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-02 13:25:25 +01:00
										 |  |  | 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; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-17 07:43:31 -06:00
										 |  |  | ReadAttributePtr GeometryComponent::attribute_try_get_for_read(const StringRef attribute_name, | 
					
						
							|  |  |  |                                                                const AttributeDomain domain) const | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   if (!this->attribute_domain_supported(domain)) { | 
					
						
							|  |  |  |     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 {}; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return attribute; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-02 13:25:25 +01:00
										 |  |  | 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); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-09 16:20:48 +01:00
										 |  |  | blender::bke::ReadAttributePtr GeometryComponent::attribute_get_constant_for_read_converted( | 
					
						
							|  |  |  |     const AttributeDomain domain, | 
					
						
							|  |  |  |     const CustomDataType in_data_type, | 
					
						
							|  |  |  |     const CustomDataType out_data_type, | 
					
						
							|  |  |  |     const void *value) const | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   BLI_assert(this->attribute_domain_supported(domain)); | 
					
						
							|  |  |  |   if (value == nullptr || in_data_type == out_data_type) { | 
					
						
							|  |  |  |     return this->attribute_get_constant_for_read(domain, out_data_type, value); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   const blender::fn::CPPType *in_cpp_type = blender::bke::custom_data_type_to_cpp_type( | 
					
						
							|  |  |  |       in_data_type); | 
					
						
							|  |  |  |   const blender::fn::CPPType *out_cpp_type = blender::bke::custom_data_type_to_cpp_type( | 
					
						
							|  |  |  |       out_data_type); | 
					
						
							|  |  |  |   BLI_assert(in_cpp_type != nullptr); | 
					
						
							|  |  |  |   BLI_assert(out_cpp_type != nullptr); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   const blender::nodes::DataTypeConversions &conversions = | 
					
						
							|  |  |  |       blender::nodes::get_implicit_type_conversions(); | 
					
						
							|  |  |  |   BLI_assert(conversions.is_convertible(*in_cpp_type, *out_cpp_type)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   void *out_value = alloca(out_cpp_type->size()); | 
					
						
							|  |  |  |   conversions.convert(*in_cpp_type, *out_cpp_type, value, out_value); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   const int domain_size = this->attribute_domain_size(domain); | 
					
						
							|  |  |  |   blender::bke::ReadAttributePtr attribute = std::make_unique<blender::bke::ConstantReadAttribute>( | 
					
						
							|  |  |  |       domain, domain_size, *out_cpp_type, out_value); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   out_cpp_type->destruct(out_value); | 
					
						
							|  |  |  |   return attribute; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-01-14 15:52:08 +01:00
										 |  |  | OutputAttributePtr GeometryComponent::attribute_try_get_for_output(const StringRef attribute_name, | 
					
						
							|  |  |  |                                                                    const AttributeDomain domain, | 
					
						
							| 
									
										
										
										
											2021-01-15 11:04:32 -06:00
										 |  |  |                                                                    const CustomDataType data_type, | 
					
						
							|  |  |  |                                                                    const void *default_value) | 
					
						
							| 
									
										
										
										
											2020-12-02 13:25:25 +01:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2021-01-14 15:52:08 +01:00
										 |  |  |   BLI_assert(this->attribute_domain_with_type_supported(domain, data_type)); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-02 13:25:25 +01:00
										 |  |  |   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); | 
					
						
							| 
									
										
										
										
											2021-01-14 15:52:08 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |   /* If the attribute doesn't exist, make a new one with the correct type. */ | 
					
						
							|  |  |  |   if (!attribute) { | 
					
						
							|  |  |  |     this->attribute_try_create(attribute_name, domain, data_type); | 
					
						
							|  |  |  |     attribute = this->attribute_try_get_for_write(attribute_name); | 
					
						
							| 
									
										
										
										
											2021-01-15 11:04:32 -06:00
										 |  |  |     if (default_value != nullptr) { | 
					
						
							|  |  |  |       void *data = attribute->get_span_for_write_only().data(); | 
					
						
							|  |  |  |       cpp_type->fill_initialized(default_value, data, attribute->size()); | 
					
						
							|  |  |  |       attribute->apply_span(); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2021-01-14 15:52:08 +01:00
										 |  |  |     return OutputAttributePtr(std::move(attribute)); | 
					
						
							| 
									
										
										
										
											2020-12-02 13:25:25 +01:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-01-14 15:52:08 +01:00
										 |  |  |   /* If an existing attribute has a matching domain and type, just use that. */ | 
					
						
							|  |  |  |   if (attribute->domain() == domain && attribute->cpp_type() == *cpp_type) { | 
					
						
							|  |  |  |     return OutputAttributePtr(std::move(attribute)); | 
					
						
							| 
									
										
										
										
											2020-12-02 13:25:25 +01:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2021-01-14 15:52:08 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |   /* Otherwise create a temporary buffer to use before saving the new attribute. */ | 
					
						
							|  |  |  |   return OutputAttributePtr(*this, domain, attribute_name, data_type); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* Construct from an attribute that already exists in the geometry component. */ | 
					
						
							|  |  |  | OutputAttributePtr::OutputAttributePtr(WriteAttributePtr attribute) | 
					
						
							|  |  |  |     : attribute_(std::move(attribute)) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* Construct a temporary attribute that has to replace an existing one later on. */ | 
					
						
							|  |  |  | OutputAttributePtr::OutputAttributePtr(GeometryComponent &component, | 
					
						
							|  |  |  |                                        AttributeDomain domain, | 
					
						
							|  |  |  |                                        std::string final_name, | 
					
						
							|  |  |  |                                        CustomDataType data_type) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   const blender::fn::CPPType *cpp_type = blender::bke::custom_data_type_to_cpp_type(data_type); | 
					
						
							|  |  |  |   BLI_assert(cpp_type != nullptr); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   const int domain_size = component.attribute_domain_size(domain); | 
					
						
							|  |  |  |   void *buffer = MEM_malloc_arrayN(domain_size, cpp_type->size(), __func__); | 
					
						
							|  |  |  |   cpp_type->construct_default_n(buffer, domain_size); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   attribute_ = std::make_unique<blender::bke::TemporaryWriteAttribute>( | 
					
						
							|  |  |  |       domain, GMutableSpan{*cpp_type, buffer, domain_size}, component, std::move(final_name)); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* Store the computed attribute. If it was stored from the beginning already, nothing is done. This
 | 
					
						
							|  |  |  |  * might delete another attribute with the same name. */ | 
					
						
							|  |  |  | void OutputAttributePtr::save() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   if (!attribute_) { | 
					
						
							|  |  |  |     CLOG_WARN(&LOG, "Trying to save an attribute that does not exist anymore."); | 
					
						
							|  |  |  |     return; | 
					
						
							| 
									
										
										
										
											2020-12-02 13:25:25 +01:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2021-01-14 15:52:08 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |   blender::bke::TemporaryWriteAttribute *attribute = | 
					
						
							|  |  |  |       dynamic_cast<blender::bke::TemporaryWriteAttribute *>(attribute_.get()); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (attribute == nullptr) { | 
					
						
							|  |  |  |     /* The attribute is saved already. */ | 
					
						
							|  |  |  |     attribute_.reset(); | 
					
						
							|  |  |  |     return; | 
					
						
							| 
									
										
										
										
											2020-12-02 13:25:25 +01:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2021-01-14 15:52:08 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |   StringRefNull name = attribute->final_name; | 
					
						
							|  |  |  |   const blender::fn::CPPType &cpp_type = attribute->cpp_type(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /* Delete an existing attribute with the same name if necessary. */ | 
					
						
							|  |  |  |   attribute->component.attribute_try_delete(name); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (!attribute->component.attribute_try_create( | 
					
						
							|  |  |  |           name, attribute_->domain(), attribute_->custom_data_type())) { | 
					
						
							|  |  |  |     /* Cannot create the target attribute for some reason. */ | 
					
						
							|  |  |  |     CLOG_WARN(&LOG, | 
					
						
							|  |  |  |               "Creating the '%s' attribute with type '%s' failed.", | 
					
						
							|  |  |  |               name.c_str(), | 
					
						
							|  |  |  |               cpp_type.name().c_str()); | 
					
						
							|  |  |  |     attribute_.reset(); | 
					
						
							|  |  |  |     return; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   WriteAttributePtr new_attribute = attribute->component.attribute_try_get_for_write(name); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   GMutableSpan temp_span = attribute->data; | 
					
						
							|  |  |  |   GMutableSpan new_span = new_attribute->get_span_for_write_only(); | 
					
						
							|  |  |  |   BLI_assert(temp_span.size() == new_span.size()); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /* Currently we copy over the attribute. In the future we want to reuse the buffer. */ | 
					
						
							|  |  |  |   cpp_type.move_to_initialized_n(temp_span.data(), new_span.data(), new_span.size()); | 
					
						
							|  |  |  |   new_attribute->apply_span(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   attribute_.reset(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | OutputAttributePtr::~OutputAttributePtr() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   if (attribute_) { | 
					
						
							|  |  |  |     CLOG_ERROR(&LOG, "Forgot to call #save or #apply_span_and_save."); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* Utility function to call #apply_span and #save in the right order. */ | 
					
						
							|  |  |  | void OutputAttributePtr::apply_span_and_save() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   BLI_assert(attribute_); | 
					
						
							|  |  |  |   attribute_->apply_span(); | 
					
						
							|  |  |  |   this->save(); | 
					
						
							| 
									
										
										
										
											2020-12-02 13:25:25 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /** \} */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* -------------------------------------------------------------------- */ | 
					
						
							|  |  |  | /** \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, | 
					
						
							| 
									
										
										
										
											2020-12-16 12:33:13 -06:00
										 |  |  |                                              CD_PROP_BOOL, | 
					
						
							| 
									
										
										
										
											2020-12-02 13:25:25 +01:00
										 |  |  |                                              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; | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2020-12-16 12:33:13 -06:00
										 |  |  |   return ELEM(data_type, | 
					
						
							|  |  |  |               CD_PROP_BOOL, | 
					
						
							|  |  |  |               CD_PROP_FLOAT, | 
					
						
							|  |  |  |               CD_PROP_FLOAT2, | 
					
						
							|  |  |  |               CD_PROP_FLOAT3, | 
					
						
							|  |  |  |               CD_PROP_INT32, | 
					
						
							|  |  |  |               CD_PROP_COLOR); | 
					
						
							| 
									
										
										
										
											2020-12-02 13:25:25 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 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); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2021-01-16 15:24:22 +01:00
										 |  |  |     else { | 
					
						
							|  |  |  |       /* Copy the data layer if it is shared with some other mesh. */ | 
					
						
							|  |  |  |       mesh_->dvert = (MDeformVert *)CustomData_duplicate_referenced_layer( | 
					
						
							|  |  |  |           &mesh_->vdata, CD_MDEFORMVERT, mesh_->totvert); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2020-12-02 13:25:25 +01:00
										 |  |  |     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); | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2021-01-14 18:11:44 +01:00
										 |  |  |   get_custom_data_layer_attribute_names(mesh_->ldata, *this, ATTR_DOMAIN_CORNER, names); | 
					
						
							| 
									
										
										
										
											2020-12-02 13:25:25 +01:00
										 |  |  |   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; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /** \} */ |