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"
|
2021-02-09 11:44:58 +01:00
|
|
|
#include "BKE_attribute_math.hh"
|
2020-12-02 13:25:25 +01:00
|
|
|
#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"
|
2021-03-05 15:16:25 -06:00
|
|
|
#include "BLI_threads.h"
|
2020-12-02 13:25:25 +01:00
|
|
|
|
|
|
|
#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;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2021-02-09 11:44:58 +01:00
|
|
|
template<typename T> class OwnedArrayReadAttribute final : public ReadAttribute {
|
|
|
|
private:
|
|
|
|
Array<T> data_;
|
|
|
|
|
|
|
|
public:
|
|
|
|
OwnedArrayReadAttribute(AttributeDomain domain, Array<T> data)
|
|
|
|
: ReadAttribute(domain, CPPType::get<T>(), data.size()), data_(std::move(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;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2021-02-09 11:24:28 +01:00
|
|
|
template<typename StructT,
|
|
|
|
typename ElemT,
|
|
|
|
ElemT (*GetFunc)(const StructT &),
|
|
|
|
void (*SetFunc)(StructT &, const ElemT &)>
|
2020-12-02 13:25:25 +01:00
|
|
|
class DerivedArrayWriteAttribute final : public WriteAttribute {
|
|
|
|
private:
|
|
|
|
MutableSpan<StructT> data_;
|
|
|
|
|
|
|
|
public:
|
2021-02-09 11:24:28 +01:00
|
|
|
DerivedArrayWriteAttribute(AttributeDomain domain, MutableSpan<StructT> data)
|
|
|
|
: WriteAttribute(domain, CPPType::get<ElemT>(), data.size()), data_(data)
|
2020-12-02 13:25:25 +01:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
void get_internal(const int64_t index, void *r_value) const override
|
|
|
|
{
|
|
|
|
const StructT &struct_value = data_[index];
|
2021-02-09 11:24:28 +01:00
|
|
|
const ElemT value = GetFunc(struct_value);
|
2020-12-02 13:25:25 +01:00
|
|
|
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);
|
2021-02-09 11:24:28 +01:00
|
|
|
SetFunc(struct_value, typed_value);
|
2020-12-02 13:25:25 +01:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2021-02-09 11:24:28 +01:00
|
|
|
template<typename StructT, typename ElemT, ElemT (*GetFunc)(const StructT &)>
|
2020-12-02 13:25:25 +01:00
|
|
|
class DerivedArrayReadAttribute final : public ReadAttribute {
|
|
|
|
private:
|
|
|
|
Span<StructT> data_;
|
|
|
|
|
|
|
|
public:
|
2021-02-09 11:24:28 +01:00
|
|
|
DerivedArrayReadAttribute(AttributeDomain domain, Span<StructT> data)
|
|
|
|
: ReadAttribute(domain, CPPType::get<ElemT>(), data.size()), data_(data)
|
2020-12-02 13:25:25 +01:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
void get_internal(const int64_t index, void *r_value) const override
|
|
|
|
{
|
|
|
|
const StructT &struct_value = data_[index];
|
2021-02-09 11:24:28 +01:00
|
|
|
const ElemT value = GetFunc(struct_value);
|
2020-12-02 13:25:25 +01:00
|
|
|
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_;
|
|
|
|
|
|
|
|
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())
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
void get_internal(const int64_t index, void *r_value) const override
|
|
|
|
{
|
2021-03-07 17:51:56 +01:00
|
|
|
BUFFER_FOR_CPP_TYPE_VALUE(from_type_, buffer);
|
|
|
|
base_attribute_->get(index, buffer);
|
|
|
|
conversions_.convert(from_type_, to_type_, buffer, r_value);
|
2020-12-02 13:25:25 +01:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
/** \} */
|
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2021-02-16 11:55:00 +01:00
|
|
|
static int attribute_data_type_complexity(const CustomDataType data_type)
|
|
|
|
{
|
|
|
|
switch (data_type) {
|
|
|
|
case CD_PROP_BOOL:
|
|
|
|
return 0;
|
|
|
|
case CD_PROP_INT32:
|
|
|
|
return 1;
|
|
|
|
case CD_PROP_FLOAT:
|
|
|
|
return 2;
|
|
|
|
case CD_PROP_FLOAT2:
|
|
|
|
return 3;
|
|
|
|
case CD_PROP_FLOAT3:
|
|
|
|
return 4;
|
|
|
|
case CD_PROP_COLOR:
|
|
|
|
return 5;
|
|
|
|
#if 0 /* These attribute types are not supported yet. */
|
|
|
|
case CD_MLOOPCOL:
|
|
|
|
return 3;
|
|
|
|
case CD_PROP_STRING:
|
|
|
|
return 6;
|
|
|
|
#endif
|
|
|
|
default:
|
|
|
|
/* Only accept "generic" custom data types used by the attribute system. */
|
|
|
|
BLI_assert(false);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
CustomDataType attribute_data_type_highest_complexity(Span<CustomDataType> data_types)
|
|
|
|
{
|
|
|
|
int highest_complexity = INT_MIN;
|
|
|
|
CustomDataType most_complex_type = CD_PROP_COLOR;
|
|
|
|
|
|
|
|
for (const CustomDataType data_type : data_types) {
|
|
|
|
const int complexity = attribute_data_type_complexity(data_type);
|
|
|
|
if (complexity > highest_complexity) {
|
|
|
|
highest_complexity = complexity;
|
|
|
|
most_complex_type = data_type;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return most_complex_type;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* \note Generally the order should mirror the order of the domains
|
|
|
|
* established in each component's ComponentAttributeProviders.
|
|
|
|
*/
|
|
|
|
static int attribute_domain_priority(const AttributeDomain domain)
|
|
|
|
{
|
|
|
|
switch (domain) {
|
|
|
|
#if 0
|
|
|
|
case ATTR_DOMAIN_CURVE:
|
|
|
|
return 0;
|
|
|
|
#endif
|
|
|
|
case ATTR_DOMAIN_POLYGON:
|
|
|
|
return 1;
|
|
|
|
case ATTR_DOMAIN_EDGE:
|
|
|
|
return 2;
|
|
|
|
case ATTR_DOMAIN_POINT:
|
|
|
|
return 3;
|
|
|
|
case ATTR_DOMAIN_CORNER:
|
|
|
|
return 4;
|
|
|
|
default:
|
|
|
|
/* Domain not supported in nodes yet. */
|
|
|
|
BLI_assert(false);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Domains with a higher "information density" have a higher priority, in order
|
|
|
|
* to choose a domain that will not lose data through domain conversion.
|
|
|
|
*/
|
|
|
|
AttributeDomain attribute_domain_highest_priority(Span<AttributeDomain> domains)
|
|
|
|
{
|
|
|
|
int highest_priority = INT_MIN;
|
|
|
|
AttributeDomain highest_priority_domain = ATTR_DOMAIN_CORNER;
|
|
|
|
|
|
|
|
for (const AttributeDomain domain : domains) {
|
|
|
|
const int priority = attribute_domain_priority(domain);
|
|
|
|
if (priority > highest_priority) {
|
|
|
|
highest_priority = priority;
|
|
|
|
highest_priority_domain = domain;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return highest_priority_domain;
|
|
|
|
}
|
|
|
|
|
2021-02-09 11:24:28 +01:00
|
|
|
/**
|
|
|
|
* A #BuiltinAttributeProvider is responsible for exactly one attribute on a geometry component.
|
|
|
|
* The attribute is identified by its name and has a fixed domain and type. Builtin attributes do
|
|
|
|
* not follow the same loose rules as other attributes, because they are mapped to internal
|
|
|
|
* "legacy" data structures. For example, some builtin attributes cannot be deleted. */
|
|
|
|
class BuiltinAttributeProvider {
|
|
|
|
public:
|
2021-02-10 07:57:52 +11:00
|
|
|
/* Some utility enums to avoid hard to read booleans in function calls. */
|
2021-02-09 11:24:28 +01:00
|
|
|
enum CreatableEnum {
|
|
|
|
Creatable,
|
|
|
|
NonCreatable,
|
|
|
|
};
|
|
|
|
enum WritableEnum {
|
|
|
|
Writable,
|
|
|
|
Readonly,
|
|
|
|
};
|
|
|
|
enum DeletableEnum {
|
|
|
|
Deletable,
|
|
|
|
NonDeletable,
|
|
|
|
};
|
2020-12-02 13:25:25 +01:00
|
|
|
|
2021-02-09 11:24:28 +01:00
|
|
|
protected:
|
|
|
|
const std::string name_;
|
|
|
|
const AttributeDomain domain_;
|
|
|
|
const CustomDataType data_type_;
|
|
|
|
const CreatableEnum createable_;
|
|
|
|
const WritableEnum writable_;
|
|
|
|
const DeletableEnum deletable_;
|
2020-12-02 13:25:25 +01:00
|
|
|
|
2021-02-09 11:24:28 +01:00
|
|
|
public:
|
|
|
|
BuiltinAttributeProvider(std::string name,
|
|
|
|
const AttributeDomain domain,
|
|
|
|
const CustomDataType data_type,
|
|
|
|
const CreatableEnum createable,
|
|
|
|
const WritableEnum writable,
|
|
|
|
const DeletableEnum deletable)
|
|
|
|
: name_(std::move(name)),
|
|
|
|
domain_(domain),
|
|
|
|
data_type_(data_type),
|
|
|
|
createable_(createable),
|
|
|
|
writable_(writable),
|
|
|
|
deletable_(deletable)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual ReadAttributePtr try_get_for_read(const GeometryComponent &component) const = 0;
|
|
|
|
virtual WriteAttributePtr try_get_for_write(GeometryComponent &component) const = 0;
|
|
|
|
virtual bool try_delete(GeometryComponent &component) const = 0;
|
|
|
|
virtual bool try_create(GeometryComponent &UNUSED(component)) const = 0;
|
|
|
|
virtual bool exists(const GeometryComponent &component) const = 0;
|
|
|
|
|
|
|
|
StringRefNull name() const
|
|
|
|
{
|
|
|
|
return name_;
|
|
|
|
}
|
|
|
|
|
|
|
|
AttributeDomain domain() const
|
|
|
|
{
|
|
|
|
return domain_;
|
|
|
|
}
|
|
|
|
|
|
|
|
CustomDataType data_type() const
|
|
|
|
{
|
|
|
|
return data_type_;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* A #DynamicAttributesProvider manages a set of named attributes on a geometry component. Each
|
|
|
|
* attribute has a name, domain and type.
|
|
|
|
*/
|
|
|
|
class DynamicAttributesProvider {
|
|
|
|
public:
|
|
|
|
virtual ReadAttributePtr try_get_for_read(const GeometryComponent &component,
|
|
|
|
const StringRef attribute_name) const = 0;
|
|
|
|
virtual WriteAttributePtr try_get_for_write(GeometryComponent &component,
|
|
|
|
const StringRef attribute_name) const = 0;
|
|
|
|
virtual bool try_delete(GeometryComponent &component, const StringRef attribute_name) const = 0;
|
|
|
|
virtual bool try_create(GeometryComponent &UNUSED(component),
|
|
|
|
const StringRef UNUSED(attribute_name),
|
|
|
|
const AttributeDomain UNUSED(domain),
|
|
|
|
const CustomDataType UNUSED(data_type)) const
|
|
|
|
{
|
|
|
|
/* Some providers should not create new attributes. */
|
|
|
|
return false;
|
|
|
|
};
|
|
|
|
|
2021-02-23 15:15:23 +01:00
|
|
|
virtual bool foreach_attribute(const GeometryComponent &component,
|
|
|
|
const AttributeForeachCallback callback) const = 0;
|
2021-02-09 11:24:28 +01:00
|
|
|
virtual void supported_domains(Vector<AttributeDomain> &r_domains) const = 0;
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Utility to group together multiple functions that are used to access custom data on geometry
|
|
|
|
* components in a generic way.
|
|
|
|
*/
|
|
|
|
struct CustomDataAccessInfo {
|
|
|
|
using CustomDataGetter = CustomData *(*)(GeometryComponent &component);
|
|
|
|
using ConstCustomDataGetter = const CustomData *(*)(const GeometryComponent &component);
|
|
|
|
using UpdateCustomDataPointers = void (*)(GeometryComponent &component);
|
|
|
|
|
|
|
|
CustomDataGetter get_custom_data;
|
|
|
|
ConstCustomDataGetter get_const_custom_data;
|
|
|
|
UpdateCustomDataPointers update_custom_data_pointers;
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* This provider is used to provide access to builtin attributes. It supports making internal types
|
|
|
|
* available as different types. For example, the vertex position attribute is stored as part of
|
|
|
|
* the #MVert struct, but is exposed as float3 attribute.
|
|
|
|
*/
|
|
|
|
class BuiltinCustomDataLayerProvider final : public BuiltinAttributeProvider {
|
|
|
|
using AsReadAttribute = ReadAttributePtr (*)(const void *data, const int domain_size);
|
|
|
|
using AsWriteAttribute = WriteAttributePtr (*)(void *data, const int domain_size);
|
2021-03-05 15:16:25 -06:00
|
|
|
using UpdateOnRead = void (*)(const GeometryComponent &component);
|
2021-02-12 11:31:15 +01:00
|
|
|
using UpdateOnWrite = void (*)(GeometryComponent &component);
|
2021-02-09 11:24:28 +01:00
|
|
|
const CustomDataType stored_type_;
|
|
|
|
const CustomDataAccessInfo custom_data_access_;
|
|
|
|
const AsReadAttribute as_read_attribute_;
|
|
|
|
const AsWriteAttribute as_write_attribute_;
|
2021-03-05 15:16:25 -06:00
|
|
|
const UpdateOnRead update_on_read_;
|
2021-02-12 11:31:15 +01:00
|
|
|
const UpdateOnWrite update_on_write_;
|
2021-02-09 11:24:28 +01:00
|
|
|
|
|
|
|
public:
|
|
|
|
BuiltinCustomDataLayerProvider(std::string attribute_name,
|
|
|
|
const AttributeDomain domain,
|
|
|
|
const CustomDataType attribute_type,
|
|
|
|
const CustomDataType stored_type,
|
|
|
|
const CreatableEnum creatable,
|
|
|
|
const WritableEnum writable,
|
|
|
|
const DeletableEnum deletable,
|
|
|
|
const CustomDataAccessInfo custom_data_access,
|
|
|
|
const AsReadAttribute as_read_attribute,
|
2021-02-12 11:31:15 +01:00
|
|
|
const AsWriteAttribute as_write_attribute,
|
2021-03-05 15:16:25 -06:00
|
|
|
const UpdateOnRead update_on_read,
|
2021-02-12 11:31:15 +01:00
|
|
|
const UpdateOnWrite update_on_write)
|
2021-02-09 11:24:28 +01:00
|
|
|
: BuiltinAttributeProvider(
|
|
|
|
std::move(attribute_name), domain, attribute_type, creatable, writable, deletable),
|
|
|
|
stored_type_(stored_type),
|
|
|
|
custom_data_access_(custom_data_access),
|
|
|
|
as_read_attribute_(as_read_attribute),
|
2021-02-12 11:31:15 +01:00
|
|
|
as_write_attribute_(as_write_attribute),
|
2021-03-05 15:16:25 -06:00
|
|
|
update_on_read_(update_on_read),
|
2021-02-12 11:31:15 +01:00
|
|
|
update_on_write_(update_on_write)
|
2021-02-09 11:24:28 +01:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
ReadAttributePtr try_get_for_read(const GeometryComponent &component) const final
|
|
|
|
{
|
|
|
|
const CustomData *custom_data = custom_data_access_.get_const_custom_data(component);
|
|
|
|
if (custom_data == nullptr) {
|
|
|
|
return {};
|
|
|
|
}
|
2021-03-05 15:16:25 -06:00
|
|
|
|
|
|
|
if (update_on_read_ != nullptr) {
|
|
|
|
update_on_read_(component);
|
|
|
|
}
|
|
|
|
|
2021-02-09 11:24:28 +01:00
|
|
|
const int domain_size = component.attribute_domain_size(domain_);
|
|
|
|
const void *data = CustomData_get_layer(custom_data, stored_type_);
|
|
|
|
if (data == nullptr) {
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
return as_read_attribute_(data, domain_size);
|
|
|
|
}
|
|
|
|
|
|
|
|
WriteAttributePtr try_get_for_write(GeometryComponent &component) const final
|
|
|
|
{
|
|
|
|
if (writable_ != Writable) {
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
CustomData *custom_data = custom_data_access_.get_custom_data(component);
|
|
|
|
if (custom_data == nullptr) {
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
const int domain_size = component.attribute_domain_size(domain_);
|
|
|
|
void *data = CustomData_get_layer(custom_data, stored_type_);
|
|
|
|
if (data == nullptr) {
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
void *new_data = CustomData_duplicate_referenced_layer(custom_data, stored_type_, domain_size);
|
|
|
|
if (data != new_data) {
|
|
|
|
custom_data_access_.update_custom_data_pointers(component);
|
|
|
|
data = new_data;
|
|
|
|
}
|
2021-02-12 11:31:15 +01:00
|
|
|
if (update_on_write_ != nullptr) {
|
|
|
|
update_on_write_(component);
|
|
|
|
}
|
2021-02-09 11:24:28 +01:00
|
|
|
return as_write_attribute_(data, domain_size);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool try_delete(GeometryComponent &component) const final
|
|
|
|
{
|
|
|
|
if (deletable_ != Deletable) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
CustomData *custom_data = custom_data_access_.get_custom_data(component);
|
|
|
|
if (custom_data == nullptr) {
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
|
|
|
const int domain_size = component.attribute_domain_size(domain_);
|
|
|
|
const int layer_index = CustomData_get_layer_index(custom_data, stored_type_);
|
|
|
|
const bool delete_success = CustomData_free_layer(
|
|
|
|
custom_data, stored_type_, domain_size, layer_index);
|
|
|
|
if (delete_success) {
|
|
|
|
custom_data_access_.update_custom_data_pointers(component);
|
|
|
|
}
|
|
|
|
return delete_success;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool try_create(GeometryComponent &component) const final
|
|
|
|
{
|
|
|
|
if (createable_ != Creatable) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
CustomData *custom_data = custom_data_access_.get_custom_data(component);
|
|
|
|
if (custom_data == nullptr) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (CustomData_get_layer(custom_data, stored_type_) != nullptr) {
|
|
|
|
/* Exists already. */
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
const int domain_size = component.attribute_domain_size(domain_);
|
|
|
|
const void *data = CustomData_add_layer(
|
|
|
|
custom_data, stored_type_, CD_DEFAULT, nullptr, domain_size);
|
|
|
|
const bool success = data != nullptr;
|
|
|
|
if (success) {
|
|
|
|
custom_data_access_.update_custom_data_pointers(component);
|
|
|
|
}
|
|
|
|
return success;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool exists(const GeometryComponent &component) const final
|
|
|
|
{
|
|
|
|
const CustomData *custom_data = custom_data_access_.get_const_custom_data(component);
|
|
|
|
if (custom_data == nullptr) {
|
|
|
|
return false;
|
|
|
|
}
|
2021-02-09 22:55:59 -06:00
|
|
|
const void *data = CustomData_get_layer(custom_data, stored_type_);
|
2021-02-09 11:24:28 +01:00
|
|
|
return data != nullptr;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* This is the attribute provider for most user generated attributes.
|
|
|
|
*/
|
|
|
|
class CustomDataAttributeProvider final : public DynamicAttributesProvider {
|
|
|
|
private:
|
|
|
|
static constexpr uint64_t supported_types_mask = CD_MASK_PROP_FLOAT | CD_MASK_PROP_FLOAT2 |
|
|
|
|
CD_MASK_PROP_FLOAT3 | CD_MASK_PROP_INT32 |
|
|
|
|
CD_MASK_PROP_COLOR | CD_MASK_PROP_BOOL;
|
|
|
|
const AttributeDomain domain_;
|
|
|
|
const CustomDataAccessInfo custom_data_access_;
|
|
|
|
|
|
|
|
public:
|
|
|
|
CustomDataAttributeProvider(const AttributeDomain domain,
|
|
|
|
const CustomDataAccessInfo custom_data_access)
|
|
|
|
: domain_(domain), custom_data_access_(custom_data_access)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
ReadAttributePtr try_get_for_read(const GeometryComponent &component,
|
|
|
|
const StringRef attribute_name) const final
|
|
|
|
{
|
|
|
|
const CustomData *custom_data = custom_data_access_.get_const_custom_data(component);
|
|
|
|
if (custom_data == nullptr) {
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
const int domain_size = component.attribute_domain_size(domain_);
|
|
|
|
for (const CustomDataLayer &layer : Span(custom_data->layers, custom_data->totlayer)) {
|
|
|
|
if (layer.name != attribute_name) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
const CustomDataType data_type = (CustomDataType)layer.type;
|
|
|
|
switch (data_type) {
|
2020-12-02 13:25:25 +01:00
|
|
|
case CD_PROP_FLOAT:
|
2021-02-09 11:24:28 +01:00
|
|
|
return this->layer_to_read_attribute<float>(layer, domain_size);
|
2020-12-02 13:25:25 +01:00
|
|
|
case CD_PROP_FLOAT2:
|
2021-02-09 11:24:28 +01:00
|
|
|
return this->layer_to_read_attribute<float2>(layer, domain_size);
|
2020-12-02 13:25:25 +01:00
|
|
|
case CD_PROP_FLOAT3:
|
2021-02-09 11:24:28 +01:00
|
|
|
return this->layer_to_read_attribute<float3>(layer, domain_size);
|
2020-12-02 13:25:25 +01:00
|
|
|
case CD_PROP_INT32:
|
2021-02-09 11:24:28 +01:00
|
|
|
return this->layer_to_read_attribute<int>(layer, domain_size);
|
2020-12-02 13:25:25 +01:00
|
|
|
case CD_PROP_COLOR:
|
2021-02-09 11:24:28 +01:00
|
|
|
return this->layer_to_read_attribute<Color4f>(layer, domain_size);
|
2020-12-16 12:33:13 -06:00
|
|
|
case CD_PROP_BOOL:
|
2021-02-09 11:24:28 +01:00
|
|
|
return this->layer_to_read_attribute<bool>(layer, domain_size);
|
|
|
|
default:
|
|
|
|
break;
|
2020-12-02 13:25:25 +01:00
|
|
|
}
|
|
|
|
}
|
2021-02-09 11:24:28 +01:00
|
|
|
return {};
|
2020-12-02 13:25:25 +01:00
|
|
|
}
|
2021-02-09 11:24:28 +01:00
|
|
|
|
|
|
|
WriteAttributePtr try_get_for_write(GeometryComponent &component,
|
|
|
|
const StringRef attribute_name) const final
|
|
|
|
{
|
|
|
|
CustomData *custom_data = custom_data_access_.get_custom_data(component);
|
|
|
|
if (custom_data == nullptr) {
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
const int domain_size = component.attribute_domain_size(domain_);
|
|
|
|
for (CustomDataLayer &layer : MutableSpan(custom_data->layers, custom_data->totlayer)) {
|
|
|
|
if (layer.name != attribute_name) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
CustomData_duplicate_referenced_layer_named(
|
|
|
|
custom_data, layer.type, layer.name, domain_size);
|
|
|
|
const CustomDataType data_type = (CustomDataType)layer.type;
|
|
|
|
switch (data_type) {
|
|
|
|
case CD_PROP_FLOAT:
|
|
|
|
return this->layer_to_write_attribute<float>(layer, domain_size);
|
|
|
|
case CD_PROP_FLOAT2:
|
|
|
|
return this->layer_to_write_attribute<float2>(layer, domain_size);
|
|
|
|
case CD_PROP_FLOAT3:
|
|
|
|
return this->layer_to_write_attribute<float3>(layer, domain_size);
|
|
|
|
case CD_PROP_INT32:
|
|
|
|
return this->layer_to_write_attribute<int>(layer, domain_size);
|
|
|
|
case CD_PROP_COLOR:
|
|
|
|
return this->layer_to_write_attribute<Color4f>(layer, domain_size);
|
|
|
|
case CD_PROP_BOOL:
|
|
|
|
return this->layer_to_write_attribute<bool>(layer, domain_size);
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
|
|
|
bool try_delete(GeometryComponent &component, const StringRef attribute_name) const final
|
|
|
|
{
|
|
|
|
CustomData *custom_data = custom_data_access_.get_custom_data(component);
|
|
|
|
if (custom_data == nullptr) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
const int domain_size = component.attribute_domain_size(domain_);
|
|
|
|
for (const int i : IndexRange(custom_data->totlayer)) {
|
|
|
|
const CustomDataLayer &layer = custom_data->layers[i];
|
|
|
|
if (this->type_is_supported((CustomDataType)layer.type) && layer.name == attribute_name) {
|
|
|
|
CustomData_free_layer(custom_data, layer.type, domain_size, i);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool try_create(GeometryComponent &component,
|
|
|
|
const StringRef attribute_name,
|
|
|
|
const AttributeDomain domain,
|
|
|
|
const CustomDataType data_type) const final
|
|
|
|
{
|
|
|
|
if (domain_ != domain) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (!this->type_is_supported(data_type)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
CustomData *custom_data = custom_data_access_.get_custom_data(component);
|
|
|
|
if (custom_data == nullptr) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
for (const CustomDataLayer &layer : Span(custom_data->layers, custom_data->totlayer)) {
|
|
|
|
if (layer.name == attribute_name) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
const int domain_size = component.attribute_domain_size(domain_);
|
|
|
|
char attribute_name_c[MAX_NAME];
|
|
|
|
attribute_name.copy(attribute_name_c);
|
|
|
|
CustomData_add_layer_named(
|
|
|
|
custom_data, data_type, CD_DEFAULT, nullptr, domain_size, attribute_name_c);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2021-02-23 15:15:23 +01:00
|
|
|
bool foreach_attribute(const GeometryComponent &component,
|
|
|
|
const AttributeForeachCallback callback) const final
|
2021-02-09 11:24:28 +01:00
|
|
|
{
|
|
|
|
const CustomData *custom_data = custom_data_access_.get_const_custom_data(component);
|
|
|
|
if (custom_data == nullptr) {
|
2021-02-23 15:15:23 +01:00
|
|
|
return true;
|
2021-02-09 11:24:28 +01:00
|
|
|
}
|
|
|
|
for (const CustomDataLayer &layer : Span(custom_data->layers, custom_data->totlayer)) {
|
2021-02-23 15:15:23 +01:00
|
|
|
const CustomDataType data_type = (CustomDataType)layer.type;
|
|
|
|
if (this->type_is_supported(data_type)) {
|
|
|
|
AttributeMetaData meta_data{domain_, data_type};
|
|
|
|
if (!callback(layer.name, meta_data)) {
|
|
|
|
return false;
|
|
|
|
}
|
2021-02-09 11:24:28 +01:00
|
|
|
}
|
|
|
|
}
|
2021-02-23 15:15:23 +01:00
|
|
|
return true;
|
2021-02-09 11:24:28 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void supported_domains(Vector<AttributeDomain> &r_domains) const final
|
|
|
|
{
|
|
|
|
r_domains.append_non_duplicates(domain_);
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
template<typename T>
|
|
|
|
ReadAttributePtr layer_to_read_attribute(const CustomDataLayer &layer,
|
|
|
|
const int domain_size) const
|
|
|
|
{
|
|
|
|
return std::make_unique<ArrayReadAttribute<T>>(
|
|
|
|
domain_, Span(static_cast<const T *>(layer.data), domain_size));
|
|
|
|
}
|
|
|
|
|
|
|
|
template<typename T>
|
|
|
|
WriteAttributePtr layer_to_write_attribute(CustomDataLayer &layer, const int domain_size) const
|
|
|
|
{
|
|
|
|
return std::make_unique<ArrayWriteAttribute<T>>(
|
|
|
|
domain_, MutableSpan(static_cast<T *>(layer.data), domain_size));
|
|
|
|
}
|
|
|
|
|
|
|
|
bool type_is_supported(CustomDataType data_type) const
|
|
|
|
{
|
|
|
|
return ((1ULL << data_type) & supported_types_mask) != 0;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
static Mesh *get_mesh_from_component_for_write(GeometryComponent &component)
|
|
|
|
{
|
|
|
|
BLI_assert(component.type() == GeometryComponentType::Mesh);
|
|
|
|
MeshComponent &mesh_component = static_cast<MeshComponent &>(component);
|
|
|
|
return mesh_component.get_for_write();
|
|
|
|
}
|
|
|
|
|
|
|
|
static const Mesh *get_mesh_from_component_for_read(const GeometryComponent &component)
|
|
|
|
{
|
|
|
|
BLI_assert(component.type() == GeometryComponentType::Mesh);
|
|
|
|
const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component);
|
|
|
|
return mesh_component.get_for_read();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2021-02-18 10:24:02 +01:00
|
|
|
* This attribute provider is used for uv maps and vertex colors.
|
2021-02-09 11:24:28 +01:00
|
|
|
*/
|
2021-02-18 10:24:02 +01:00
|
|
|
class NamedLegacyCustomDataProvider final : public DynamicAttributesProvider {
|
|
|
|
private:
|
|
|
|
using AsReadAttribute = ReadAttributePtr (*)(const void *data, const int domain_size);
|
|
|
|
using AsWriteAttribute = WriteAttributePtr (*)(void *data, const int domain_size);
|
|
|
|
const AttributeDomain domain_;
|
2021-02-23 15:15:23 +01:00
|
|
|
const CustomDataType attribute_type_;
|
2021-02-18 10:24:02 +01:00
|
|
|
const CustomDataType stored_type_;
|
|
|
|
const CustomDataAccessInfo custom_data_access_;
|
|
|
|
const AsReadAttribute as_read_attribute_;
|
|
|
|
const AsWriteAttribute as_write_attribute_;
|
|
|
|
|
2021-02-09 11:24:28 +01:00
|
|
|
public:
|
2021-02-18 10:24:02 +01:00
|
|
|
NamedLegacyCustomDataProvider(const AttributeDomain domain,
|
2021-02-23 15:15:23 +01:00
|
|
|
const CustomDataType attribute_type,
|
2021-02-18 10:24:02 +01:00
|
|
|
const CustomDataType stored_type,
|
|
|
|
const CustomDataAccessInfo custom_data_access,
|
|
|
|
const AsReadAttribute as_read_attribute,
|
|
|
|
const AsWriteAttribute as_write_attribute)
|
|
|
|
: domain_(domain),
|
2021-02-23 15:15:23 +01:00
|
|
|
attribute_type_(attribute_type),
|
2021-02-18 10:24:02 +01:00
|
|
|
stored_type_(stored_type),
|
|
|
|
custom_data_access_(custom_data_access),
|
|
|
|
as_read_attribute_(as_read_attribute),
|
|
|
|
as_write_attribute_(as_write_attribute)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2021-02-09 11:24:28 +01:00
|
|
|
ReadAttributePtr try_get_for_read(const GeometryComponent &component,
|
|
|
|
const StringRef attribute_name) const final
|
|
|
|
{
|
2021-02-18 10:24:02 +01:00
|
|
|
const CustomData *custom_data = custom_data_access_.get_const_custom_data(component);
|
|
|
|
if (custom_data == nullptr) {
|
2021-02-09 11:24:28 +01:00
|
|
|
return {};
|
|
|
|
}
|
2021-02-18 10:24:02 +01:00
|
|
|
for (const CustomDataLayer &layer : Span(custom_data->layers, custom_data->totlayer)) {
|
|
|
|
if (layer.type == stored_type_) {
|
2021-02-09 11:24:28 +01:00
|
|
|
if (layer.name == attribute_name) {
|
2021-02-18 10:24:02 +01:00
|
|
|
const int domain_size = component.attribute_domain_size(domain_);
|
|
|
|
return as_read_attribute_(layer.data, domain_size);
|
2021-02-09 11:24:28 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
|
|
|
WriteAttributePtr try_get_for_write(GeometryComponent &component,
|
|
|
|
const StringRef attribute_name) const final
|
|
|
|
{
|
2021-02-18 10:24:02 +01:00
|
|
|
CustomData *custom_data = custom_data_access_.get_custom_data(component);
|
|
|
|
if (custom_data == nullptr) {
|
2021-02-09 11:24:28 +01:00
|
|
|
return {};
|
|
|
|
}
|
2021-02-18 10:24:02 +01:00
|
|
|
for (CustomDataLayer &layer : MutableSpan(custom_data->layers, custom_data->totlayer)) {
|
|
|
|
if (layer.type == stored_type_) {
|
2021-02-09 11:24:28 +01:00
|
|
|
if (layer.name == attribute_name) {
|
2021-02-18 10:24:02 +01:00
|
|
|
const int domain_size = component.attribute_domain_size(domain_);
|
2021-02-09 11:24:28 +01:00
|
|
|
void *data_old = layer.data;
|
|
|
|
void *data_new = CustomData_duplicate_referenced_layer_named(
|
2021-02-18 10:24:02 +01:00
|
|
|
custom_data, stored_type_, layer.name, domain_size);
|
2021-02-09 11:24:28 +01:00
|
|
|
if (data_old != data_new) {
|
2021-02-18 10:24:02 +01:00
|
|
|
custom_data_access_.update_custom_data_pointers(component);
|
2021-02-09 11:24:28 +01:00
|
|
|
}
|
2021-02-18 10:24:02 +01:00
|
|
|
return as_write_attribute_(layer.data, domain_size);
|
2021-02-09 11:24:28 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
|
|
|
bool try_delete(GeometryComponent &component, const StringRef attribute_name) const final
|
|
|
|
{
|
2021-02-18 10:24:02 +01:00
|
|
|
CustomData *custom_data = custom_data_access_.get_custom_data(component);
|
|
|
|
if (custom_data == nullptr) {
|
2021-02-09 11:24:28 +01:00
|
|
|
return false;
|
|
|
|
}
|
2021-02-18 10:24:02 +01:00
|
|
|
for (const int i : IndexRange(custom_data->totlayer)) {
|
|
|
|
const CustomDataLayer &layer = custom_data->layers[i];
|
|
|
|
if (layer.type == stored_type_) {
|
|
|
|
if (layer.name == attribute_name) {
|
|
|
|
const int domain_size = component.attribute_domain_size(domain_);
|
|
|
|
CustomData_free_layer(custom_data, stored_type_, domain_size, i);
|
|
|
|
custom_data_access_.update_custom_data_pointers(component);
|
|
|
|
return true;
|
|
|
|
}
|
2021-02-09 11:24:28 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2021-02-23 15:15:23 +01:00
|
|
|
bool foreach_attribute(const GeometryComponent &component,
|
|
|
|
const AttributeForeachCallback callback) const final
|
2021-02-09 11:24:28 +01:00
|
|
|
{
|
2021-02-18 10:24:02 +01:00
|
|
|
const CustomData *custom_data = custom_data_access_.get_const_custom_data(component);
|
|
|
|
if (custom_data == nullptr) {
|
2021-02-23 15:15:23 +01:00
|
|
|
return true;
|
2021-02-09 11:24:28 +01:00
|
|
|
}
|
2021-02-18 10:24:02 +01:00
|
|
|
for (const CustomDataLayer &layer : Span(custom_data->layers, custom_data->totlayer)) {
|
|
|
|
if (layer.type == stored_type_) {
|
2021-02-23 15:15:23 +01:00
|
|
|
AttributeMetaData meta_data{domain_, attribute_type_};
|
|
|
|
if (!callback(layer.name, meta_data)) {
|
|
|
|
return false;
|
|
|
|
}
|
2021-02-09 11:24:28 +01:00
|
|
|
}
|
|
|
|
}
|
2021-02-23 15:15:23 +01:00
|
|
|
return true;
|
2021-02-09 11:24:28 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void supported_domains(Vector<AttributeDomain> &r_domains) const final
|
|
|
|
{
|
2021-02-18 10:24:02 +01:00
|
|
|
r_domains.append_non_duplicates(domain_);
|
2021-02-09 11:24:28 +01:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* This provider makes vertex groups available as float attributes.
|
|
|
|
*/
|
|
|
|
class VertexGroupsAttributeProvider final : public DynamicAttributesProvider {
|
|
|
|
public:
|
|
|
|
ReadAttributePtr try_get_for_read(const GeometryComponent &component,
|
|
|
|
const StringRef attribute_name) const final
|
|
|
|
{
|
|
|
|
BLI_assert(component.type() == GeometryComponentType::Mesh);
|
|
|
|
const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component);
|
|
|
|
const Mesh *mesh = mesh_component.get_for_read();
|
|
|
|
const int vertex_group_index = mesh_component.vertex_group_names().lookup_default_as(
|
|
|
|
attribute_name, -1);
|
|
|
|
if (vertex_group_index < 0) {
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
if (mesh == nullptr || mesh->dvert == nullptr) {
|
|
|
|
static const float default_value = 0.0f;
|
|
|
|
return std::make_unique<ConstantReadAttribute>(
|
|
|
|
ATTR_DOMAIN_POINT, mesh->totvert, CPPType::get<float>(), &default_value);
|
|
|
|
}
|
|
|
|
return std::make_unique<VertexWeightReadAttribute>(
|
|
|
|
mesh->dvert, mesh->totvert, vertex_group_index);
|
|
|
|
}
|
|
|
|
|
|
|
|
WriteAttributePtr try_get_for_write(GeometryComponent &component,
|
|
|
|
const StringRef attribute_name) const final
|
|
|
|
{
|
|
|
|
BLI_assert(component.type() == GeometryComponentType::Mesh);
|
|
|
|
MeshComponent &mesh_component = static_cast<MeshComponent &>(component);
|
|
|
|
Mesh *mesh = mesh_component.get_for_write();
|
|
|
|
if (mesh == nullptr) {
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
const int vertex_group_index = mesh_component.vertex_group_names().lookup_default_as(
|
|
|
|
attribute_name, -1);
|
|
|
|
if (vertex_group_index < 0) {
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
if (mesh->dvert == nullptr) {
|
|
|
|
BKE_object_defgroup_data_create(&mesh->id);
|
|
|
|
}
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
return std::make_unique<blender::bke::VertexWeightWriteAttribute>(
|
|
|
|
mesh->dvert, mesh->totvert, vertex_group_index);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool try_delete(GeometryComponent &component, const StringRef attribute_name) const final
|
|
|
|
{
|
|
|
|
BLI_assert(component.type() == GeometryComponentType::Mesh);
|
|
|
|
MeshComponent &mesh_component = static_cast<MeshComponent &>(component);
|
|
|
|
|
|
|
|
const int vertex_group_index = mesh_component.vertex_group_names().pop_default_as(
|
|
|
|
attribute_name, -1);
|
|
|
|
if (vertex_group_index < 0) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
Mesh *mesh = mesh_component.get_for_write();
|
|
|
|
if (mesh == nullptr) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
if (mesh->dvert == nullptr) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
for (MDeformVert &dvert : MutableSpan(mesh->dvert, mesh->totvert)) {
|
|
|
|
MDeformWeight *weight = BKE_defvert_find_index(&dvert, vertex_group_index);
|
|
|
|
BKE_defvert_remove_group(&dvert, weight);
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2021-02-23 15:15:23 +01:00
|
|
|
bool foreach_attribute(const GeometryComponent &component,
|
|
|
|
const AttributeForeachCallback callback) const final
|
2021-02-09 11:24:28 +01:00
|
|
|
{
|
|
|
|
BLI_assert(component.type() == GeometryComponentType::Mesh);
|
|
|
|
const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component);
|
2021-02-25 16:55:08 +01:00
|
|
|
for (const auto item : mesh_component.vertex_group_names().items()) {
|
2021-02-23 15:15:23 +01:00
|
|
|
const StringRefNull name = item.key;
|
|
|
|
const int vertex_group_index = item.value;
|
|
|
|
if (vertex_group_index >= 0) {
|
|
|
|
AttributeMetaData meta_data{ATTR_DOMAIN_POINT, CD_PROP_FLOAT};
|
|
|
|
if (!callback(name, meta_data)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
2021-02-09 11:24:28 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void supported_domains(Vector<AttributeDomain> &r_domains) const final
|
|
|
|
{
|
|
|
|
r_domains.append_non_duplicates(ATTR_DOMAIN_POINT);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* This is a container for multiple attribute providers that are used by one geometry component
|
|
|
|
* type (e.g. there is a set of attribute providers for mesh components).
|
|
|
|
*/
|
|
|
|
class ComponentAttributeProviders {
|
|
|
|
private:
|
|
|
|
/**
|
|
|
|
* Builtin attribute providers are identified by their name. Attribute names that are in this
|
|
|
|
* map will only be accessed using builtin attribute providers. Therefore, these providers have
|
|
|
|
* higher priority when an attribute name is looked up. Usually, that means that builtin
|
|
|
|
* providers are checked before dynamic ones.
|
|
|
|
*/
|
|
|
|
Map<std::string, const BuiltinAttributeProvider *> builtin_attribute_providers_;
|
|
|
|
/**
|
|
|
|
* An ordered list of dynamic attribute providers. The order is important because that is order
|
|
|
|
* in which they are checked when an attribute is looked up.
|
|
|
|
*/
|
|
|
|
Vector<const DynamicAttributesProvider *> dynamic_attribute_providers_;
|
|
|
|
/**
|
|
|
|
* All the domains that are supported by at least one of the providers above.
|
|
|
|
*/
|
|
|
|
Vector<AttributeDomain> supported_domains_;
|
|
|
|
|
|
|
|
public:
|
|
|
|
ComponentAttributeProviders(Span<const BuiltinAttributeProvider *> builtin_attribute_providers,
|
|
|
|
Span<const DynamicAttributesProvider *> dynamic_attribute_providers)
|
|
|
|
: dynamic_attribute_providers_(dynamic_attribute_providers)
|
|
|
|
{
|
|
|
|
Set<AttributeDomain> domains;
|
|
|
|
for (const BuiltinAttributeProvider *provider : builtin_attribute_providers) {
|
|
|
|
/* Use #add_new to make sure that no two builtin attributes have the same name. */
|
|
|
|
builtin_attribute_providers_.add_new(provider->name(), provider);
|
|
|
|
supported_domains_.append_non_duplicates(provider->domain());
|
|
|
|
}
|
|
|
|
for (const DynamicAttributesProvider *provider : dynamic_attribute_providers) {
|
|
|
|
provider->supported_domains(supported_domains_);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const Map<std::string, const BuiltinAttributeProvider *> &builtin_attribute_providers() const
|
|
|
|
{
|
|
|
|
return builtin_attribute_providers_;
|
|
|
|
}
|
|
|
|
|
|
|
|
Span<const DynamicAttributesProvider *> dynamic_attribute_providers() const
|
|
|
|
{
|
|
|
|
return dynamic_attribute_providers_;
|
|
|
|
}
|
|
|
|
|
|
|
|
Span<AttributeDomain> supported_domains() const
|
|
|
|
{
|
|
|
|
return supported_domains_;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
static float3 get_vertex_position(const MVert &vert)
|
|
|
|
{
|
|
|
|
return float3(vert.co);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void set_vertex_position(MVert &vert, const float3 &position)
|
|
|
|
{
|
|
|
|
copy_v3_v3(vert.co, position);
|
2020-12-02 13:25:25 +01:00
|
|
|
}
|
|
|
|
|
2021-02-09 11:24:28 +01:00
|
|
|
static ReadAttributePtr make_vertex_position_read_attribute(const void *data,
|
|
|
|
const int domain_size)
|
2020-12-02 13:25:25 +01:00
|
|
|
{
|
2021-02-09 11:24:28 +01:00
|
|
|
return std::make_unique<DerivedArrayReadAttribute<MVert, float3, get_vertex_position>>(
|
|
|
|
ATTR_DOMAIN_POINT, Span<MVert>((const MVert *)data, domain_size));
|
|
|
|
}
|
2020-12-02 13:25:25 +01:00
|
|
|
|
2021-02-09 11:24:28 +01:00
|
|
|
static WriteAttributePtr make_vertex_position_write_attribute(void *data, const int domain_size)
|
|
|
|
{
|
|
|
|
return std::make_unique<
|
|
|
|
DerivedArrayWriteAttribute<MVert, float3, get_vertex_position, set_vertex_position>>(
|
|
|
|
ATTR_DOMAIN_POINT, MutableSpan<MVert>((MVert *)data, domain_size));
|
2020-12-02 13:25:25 +01:00
|
|
|
}
|
|
|
|
|
2021-02-12 11:31:15 +01:00
|
|
|
static void tag_normals_dirty_when_writing_position(GeometryComponent &component)
|
|
|
|
{
|
|
|
|
Mesh *mesh = get_mesh_from_component_for_write(component);
|
|
|
|
if (mesh != nullptr) {
|
|
|
|
mesh->runtime.cd_dirty_vert |= CD_MASK_NORMAL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-02-17 08:30:15 -06:00
|
|
|
static int get_material_index(const MPoly &mpoly)
|
|
|
|
{
|
|
|
|
return static_cast<int>(mpoly.mat_nr);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void set_material_index(MPoly &mpoly, const int &index)
|
|
|
|
{
|
|
|
|
mpoly.mat_nr = static_cast<short>(std::clamp(index, 0, SHRT_MAX));
|
|
|
|
}
|
|
|
|
|
|
|
|
static ReadAttributePtr make_material_index_read_attribute(const void *data, const int domain_size)
|
|
|
|
{
|
|
|
|
return std::make_unique<DerivedArrayReadAttribute<MPoly, int, get_material_index>>(
|
|
|
|
ATTR_DOMAIN_POLYGON, Span<MPoly>((const MPoly *)data, domain_size));
|
|
|
|
}
|
|
|
|
|
|
|
|
static WriteAttributePtr make_material_index_write_attribute(void *data, const int domain_size)
|
|
|
|
{
|
|
|
|
return std::make_unique<
|
|
|
|
DerivedArrayWriteAttribute<MPoly, int, get_material_index, set_material_index>>(
|
|
|
|
ATTR_DOMAIN_POLYGON, MutableSpan<MPoly>((MPoly *)data, domain_size));
|
|
|
|
}
|
|
|
|
|
2021-03-05 15:16:25 -06:00
|
|
|
static float3 get_vertex_normal(const MVert &vert)
|
|
|
|
{
|
|
|
|
float3 result;
|
|
|
|
normal_short_to_float_v3(result, vert.no);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
static ReadAttributePtr make_vertex_normal_read_attribute(const void *data, const int domain_size)
|
|
|
|
{
|
|
|
|
return std::make_unique<DerivedArrayReadAttribute<MVert, float3, get_vertex_normal>>(
|
|
|
|
ATTR_DOMAIN_POINT, Span<MVert>((const MVert *)data, domain_size));
|
|
|
|
}
|
|
|
|
|
|
|
|
static void update_vertex_normals_when_dirty(const GeometryComponent &component)
|
|
|
|
{
|
|
|
|
const Mesh *mesh = get_mesh_from_component_for_read(component);
|
|
|
|
if (mesh == nullptr) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2021-03-06 13:03:30 +11:00
|
|
|
/* Since normals are derived data, `const` write access to them is okay. However, ensure that
|
2021-03-05 15:16:25 -06:00
|
|
|
* two threads don't use write normals to a mesh at the same time. Note that this relies on
|
2021-03-06 13:03:30 +11:00
|
|
|
* the idempotence of the operation; calculating the normals just fills the #MVert struct
|
2021-03-05 15:16:25 -06:00
|
|
|
* rather than allocating new memory. */
|
|
|
|
if (mesh->runtime.cd_dirty_vert & CD_MASK_NORMAL) {
|
|
|
|
ThreadMutex *mesh_eval_mutex = (ThreadMutex *)mesh->runtime.eval_mutex;
|
|
|
|
BLI_mutex_lock(mesh_eval_mutex);
|
|
|
|
|
|
|
|
/* Check again to avoid a second thread needlessly recalculating the same normals. */
|
|
|
|
if (mesh->runtime.cd_dirty_vert & CD_MASK_NORMAL) {
|
|
|
|
BKE_mesh_calc_normals(const_cast<Mesh *>(mesh));
|
|
|
|
}
|
|
|
|
|
|
|
|
BLI_mutex_unlock(mesh_eval_mutex);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-02-18 10:24:02 +01:00
|
|
|
static float2 get_loop_uv(const MLoopUV &uv)
|
|
|
|
{
|
|
|
|
return float2(uv.uv);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void set_loop_uv(MLoopUV &uv, const float2 &co)
|
|
|
|
{
|
|
|
|
copy_v2_v2(uv.uv, co);
|
|
|
|
}
|
|
|
|
|
|
|
|
static ReadAttributePtr make_uvs_read_attribute(const void *data, const int domain_size)
|
|
|
|
{
|
|
|
|
return std::make_unique<DerivedArrayReadAttribute<MLoopUV, float2, get_loop_uv>>(
|
|
|
|
ATTR_DOMAIN_CORNER, Span((const MLoopUV *)data, domain_size));
|
|
|
|
}
|
|
|
|
|
|
|
|
static WriteAttributePtr make_uvs_write_attribute(void *data, const int domain_size)
|
|
|
|
{
|
|
|
|
return std::make_unique<DerivedArrayWriteAttribute<MLoopUV, float2, get_loop_uv, set_loop_uv>>(
|
|
|
|
ATTR_DOMAIN_CORNER, MutableSpan((MLoopUV *)data, domain_size));
|
|
|
|
}
|
|
|
|
|
|
|
|
static Color4f get_loop_color(const MLoopCol &col)
|
|
|
|
{
|
|
|
|
Color4f value;
|
|
|
|
rgba_uchar_to_float(value, &col.r);
|
|
|
|
return value;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void set_loop_color(MLoopCol &col, const Color4f &value)
|
|
|
|
{
|
|
|
|
rgba_float_to_uchar(&col.r, value);
|
|
|
|
}
|
|
|
|
|
|
|
|
static ReadAttributePtr make_vertex_color_read_attribute(const void *data, const int domain_size)
|
|
|
|
{
|
|
|
|
return std::make_unique<DerivedArrayReadAttribute<MLoopCol, Color4f, get_loop_color>>(
|
|
|
|
ATTR_DOMAIN_CORNER, Span((const MLoopCol *)data, domain_size));
|
|
|
|
}
|
|
|
|
|
|
|
|
static WriteAttributePtr make_vertex_color_write_attribute(void *data, const int domain_size)
|
|
|
|
{
|
|
|
|
return std::make_unique<
|
|
|
|
DerivedArrayWriteAttribute<MLoopCol, Color4f, get_loop_color, set_loop_color>>(
|
|
|
|
ATTR_DOMAIN_CORNER, MutableSpan((MLoopCol *)data, domain_size));
|
|
|
|
}
|
|
|
|
|
2021-02-09 11:24:28 +01:00
|
|
|
template<typename T, AttributeDomain Domain>
|
|
|
|
static ReadAttributePtr make_array_read_attribute(const void *data, const int domain_size)
|
2020-12-02 13:25:25 +01:00
|
|
|
{
|
2021-02-09 11:24:28 +01:00
|
|
|
return std::make_unique<ArrayReadAttribute<T>>(Domain, Span<T>((const T *)data, domain_size));
|
|
|
|
}
|
|
|
|
|
|
|
|
template<typename T, AttributeDomain Domain>
|
|
|
|
static WriteAttributePtr make_array_write_attribute(void *data, const int domain_size)
|
|
|
|
{
|
|
|
|
return std::make_unique<ArrayWriteAttribute<T>>(Domain, MutableSpan<T>((T *)data, domain_size));
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* In this function all the attribute providers for a mesh component are created. Most data in this
|
|
|
|
* function is statically allocated, because it does not change over time.
|
|
|
|
*/
|
|
|
|
static ComponentAttributeProviders create_attribute_providers_for_mesh()
|
|
|
|
{
|
|
|
|
static auto update_custom_data_pointers = [](GeometryComponent &component) {
|
|
|
|
Mesh *mesh = get_mesh_from_component_for_write(component);
|
|
|
|
if (mesh != nullptr) {
|
|
|
|
BKE_mesh_update_customdata_pointers(mesh, false);
|
2020-12-02 13:25:25 +01:00
|
|
|
}
|
2021-02-09 11:24:28 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
#define MAKE_MUTABLE_CUSTOM_DATA_GETTER(NAME) \
|
|
|
|
[](GeometryComponent &component) -> CustomData * { \
|
|
|
|
Mesh *mesh = get_mesh_from_component_for_write(component); \
|
|
|
|
return mesh ? &mesh->NAME : nullptr; \
|
|
|
|
}
|
|
|
|
#define MAKE_CONST_CUSTOM_DATA_GETTER(NAME) \
|
|
|
|
[](const GeometryComponent &component) -> const CustomData * { \
|
|
|
|
const Mesh *mesh = get_mesh_from_component_for_read(component); \
|
|
|
|
return mesh ? &mesh->NAME : nullptr; \
|
|
|
|
}
|
|
|
|
|
|
|
|
static CustomDataAccessInfo corner_access = {MAKE_MUTABLE_CUSTOM_DATA_GETTER(ldata),
|
|
|
|
MAKE_CONST_CUSTOM_DATA_GETTER(ldata),
|
|
|
|
update_custom_data_pointers};
|
|
|
|
static CustomDataAccessInfo point_access = {MAKE_MUTABLE_CUSTOM_DATA_GETTER(vdata),
|
|
|
|
MAKE_CONST_CUSTOM_DATA_GETTER(vdata),
|
|
|
|
update_custom_data_pointers};
|
|
|
|
static CustomDataAccessInfo edge_access = {MAKE_MUTABLE_CUSTOM_DATA_GETTER(edata),
|
|
|
|
MAKE_CONST_CUSTOM_DATA_GETTER(edata),
|
|
|
|
update_custom_data_pointers};
|
|
|
|
static CustomDataAccessInfo polygon_access = {MAKE_MUTABLE_CUSTOM_DATA_GETTER(pdata),
|
|
|
|
MAKE_CONST_CUSTOM_DATA_GETTER(pdata),
|
|
|
|
update_custom_data_pointers};
|
|
|
|
|
|
|
|
#undef MAKE_CONST_CUSTOM_DATA_GETTER
|
|
|
|
#undef MAKE_MUTABLE_CUSTOM_DATA_GETTER
|
|
|
|
|
|
|
|
static BuiltinCustomDataLayerProvider position("position",
|
|
|
|
ATTR_DOMAIN_POINT,
|
|
|
|
CD_PROP_FLOAT3,
|
|
|
|
CD_MVERT,
|
|
|
|
BuiltinAttributeProvider::NonCreatable,
|
|
|
|
BuiltinAttributeProvider::Writable,
|
|
|
|
BuiltinAttributeProvider::NonDeletable,
|
|
|
|
point_access,
|
|
|
|
make_vertex_position_read_attribute,
|
2021-02-12 11:31:15 +01:00
|
|
|
make_vertex_position_write_attribute,
|
2021-03-05 15:16:25 -06:00
|
|
|
nullptr,
|
2021-02-12 11:31:15 +01:00
|
|
|
tag_normals_dirty_when_writing_position);
|
2021-02-17 08:30:15 -06:00
|
|
|
|
|
|
|
static BuiltinCustomDataLayerProvider material_index("material_index",
|
|
|
|
ATTR_DOMAIN_POLYGON,
|
|
|
|
CD_PROP_INT32,
|
|
|
|
CD_MPOLY,
|
|
|
|
BuiltinAttributeProvider::NonCreatable,
|
|
|
|
BuiltinAttributeProvider::Writable,
|
|
|
|
BuiltinAttributeProvider::NonDeletable,
|
|
|
|
polygon_access,
|
|
|
|
make_material_index_read_attribute,
|
|
|
|
make_material_index_write_attribute,
|
2021-03-05 15:16:25 -06:00
|
|
|
nullptr,
|
2021-02-17 08:30:15 -06:00
|
|
|
nullptr);
|
|
|
|
|
2021-03-05 15:16:25 -06:00
|
|
|
static BuiltinCustomDataLayerProvider vertex_normal("vertex_normal",
|
|
|
|
ATTR_DOMAIN_POINT,
|
|
|
|
CD_PROP_FLOAT3,
|
|
|
|
CD_MVERT,
|
|
|
|
BuiltinAttributeProvider::NonCreatable,
|
|
|
|
BuiltinAttributeProvider::Readonly,
|
|
|
|
BuiltinAttributeProvider::NonDeletable,
|
|
|
|
point_access,
|
|
|
|
make_vertex_normal_read_attribute,
|
|
|
|
nullptr,
|
|
|
|
update_vertex_normals_when_dirty,
|
|
|
|
nullptr);
|
|
|
|
|
2021-02-18 10:24:02 +01:00
|
|
|
static NamedLegacyCustomDataProvider uvs(ATTR_DOMAIN_CORNER,
|
2021-02-23 15:15:23 +01:00
|
|
|
CD_PROP_FLOAT2,
|
2021-02-18 10:24:02 +01:00
|
|
|
CD_MLOOPUV,
|
|
|
|
corner_access,
|
|
|
|
make_uvs_read_attribute,
|
|
|
|
make_uvs_write_attribute);
|
|
|
|
|
|
|
|
static NamedLegacyCustomDataProvider vertex_colors(ATTR_DOMAIN_CORNER,
|
2021-02-23 15:15:23 +01:00
|
|
|
CD_PROP_COLOR,
|
2021-02-18 10:24:02 +01:00
|
|
|
CD_MLOOPCOL,
|
|
|
|
corner_access,
|
|
|
|
make_vertex_color_read_attribute,
|
|
|
|
make_vertex_color_write_attribute);
|
|
|
|
|
2021-02-09 11:24:28 +01:00
|
|
|
static VertexGroupsAttributeProvider vertex_groups;
|
|
|
|
static CustomDataAttributeProvider corner_custom_data(ATTR_DOMAIN_CORNER, corner_access);
|
|
|
|
static CustomDataAttributeProvider point_custom_data(ATTR_DOMAIN_POINT, point_access);
|
|
|
|
static CustomDataAttributeProvider edge_custom_data(ATTR_DOMAIN_EDGE, edge_access);
|
|
|
|
static CustomDataAttributeProvider polygon_custom_data(ATTR_DOMAIN_POLYGON, polygon_access);
|
|
|
|
|
2021-03-05 15:16:25 -06:00
|
|
|
return ComponentAttributeProviders({&position, &material_index, &vertex_normal},
|
2021-02-09 11:24:28 +01:00
|
|
|
{&uvs,
|
2021-02-18 10:24:02 +01:00
|
|
|
&vertex_colors,
|
2021-02-09 11:24:28 +01:00
|
|
|
&corner_custom_data,
|
|
|
|
&vertex_groups,
|
|
|
|
&point_custom_data,
|
|
|
|
&edge_custom_data,
|
|
|
|
&polygon_custom_data});
|
2020-12-02 13:25:25 +01:00
|
|
|
}
|
|
|
|
|
2021-02-09 11:24:28 +01:00
|
|
|
/**
|
|
|
|
* In this function all the attribute providers for a point cloud component are created. Most data
|
|
|
|
* in this function is statically allocated, because it does not change over time.
|
|
|
|
*/
|
|
|
|
static ComponentAttributeProviders create_attribute_providers_for_point_cloud()
|
2020-12-02 13:25:25 +01:00
|
|
|
{
|
2021-02-09 11:24:28 +01:00
|
|
|
static auto update_custom_data_pointers = [](GeometryComponent &component) {
|
|
|
|
PointCloudComponent &pointcloud_component = static_cast<PointCloudComponent &>(component);
|
|
|
|
PointCloud *pointcloud = pointcloud_component.get_for_write();
|
|
|
|
if (pointcloud != nullptr) {
|
|
|
|
BKE_pointcloud_update_customdata_pointers(pointcloud);
|
2020-12-02 13:25:25 +01:00
|
|
|
}
|
2021-02-09 11:24:28 +01:00
|
|
|
};
|
|
|
|
static CustomDataAccessInfo point_access = {
|
|
|
|
[](GeometryComponent &component) -> CustomData * {
|
|
|
|
PointCloudComponent &pointcloud_component = static_cast<PointCloudComponent &>(component);
|
|
|
|
PointCloud *pointcloud = pointcloud_component.get_for_write();
|
|
|
|
return pointcloud ? &pointcloud->pdata : nullptr;
|
|
|
|
},
|
|
|
|
[](const GeometryComponent &component) -> const CustomData * {
|
|
|
|
const PointCloudComponent &pointcloud_component = static_cast<const PointCloudComponent &>(
|
|
|
|
component);
|
|
|
|
const PointCloud *pointcloud = pointcloud_component.get_for_read();
|
|
|
|
return pointcloud ? &pointcloud->pdata : nullptr;
|
|
|
|
},
|
|
|
|
update_custom_data_pointers};
|
|
|
|
|
|
|
|
static BuiltinCustomDataLayerProvider position(
|
|
|
|
"position",
|
|
|
|
ATTR_DOMAIN_POINT,
|
|
|
|
CD_PROP_FLOAT3,
|
|
|
|
CD_PROP_FLOAT3,
|
|
|
|
BuiltinAttributeProvider::NonCreatable,
|
|
|
|
BuiltinAttributeProvider::Writable,
|
|
|
|
BuiltinAttributeProvider::NonDeletable,
|
|
|
|
point_access,
|
|
|
|
make_array_read_attribute<float3, ATTR_DOMAIN_POINT>,
|
2021-02-12 11:31:15 +01:00
|
|
|
make_array_write_attribute<float3, ATTR_DOMAIN_POINT>,
|
2021-03-05 15:16:25 -06:00
|
|
|
nullptr,
|
2021-02-12 11:31:15 +01:00
|
|
|
nullptr);
|
2021-02-09 11:24:28 +01:00
|
|
|
static BuiltinCustomDataLayerProvider radius(
|
|
|
|
"radius",
|
|
|
|
ATTR_DOMAIN_POINT,
|
|
|
|
CD_PROP_FLOAT,
|
|
|
|
CD_PROP_FLOAT,
|
|
|
|
BuiltinAttributeProvider::Creatable,
|
|
|
|
BuiltinAttributeProvider::Writable,
|
|
|
|
BuiltinAttributeProvider::Deletable,
|
|
|
|
point_access,
|
|
|
|
make_array_read_attribute<float, ATTR_DOMAIN_POINT>,
|
2021-02-12 11:31:15 +01:00
|
|
|
make_array_write_attribute<float, ATTR_DOMAIN_POINT>,
|
2021-03-05 15:16:25 -06:00
|
|
|
nullptr,
|
2021-02-12 11:31:15 +01:00
|
|
|
nullptr);
|
2021-02-09 11:24:28 +01:00
|
|
|
static CustomDataAttributeProvider point_custom_data(ATTR_DOMAIN_POINT, point_access);
|
|
|
|
return ComponentAttributeProviders({&position, &radius}, {&point_custom_data});
|
2020-12-02 13:25:25 +01:00
|
|
|
}
|
|
|
|
|
2021-02-09 11:24:28 +01:00
|
|
|
} // namespace blender::bke
|
2020-12-02 13:25:25 +01:00
|
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
/** \name Geometry Component
|
|
|
|
* \{ */
|
|
|
|
|
2021-02-09 11:24:28 +01:00
|
|
|
const blender::bke::ComponentAttributeProviders *GeometryComponent::get_attribute_providers() const
|
2020-12-02 13:25:25 +01:00
|
|
|
{
|
2021-02-09 11:24:28 +01:00
|
|
|
return nullptr;
|
2020-12-02 13:25:25 +01:00
|
|
|
}
|
|
|
|
|
2021-02-09 11:24:28 +01:00
|
|
|
bool GeometryComponent::attribute_domain_supported(const AttributeDomain domain) const
|
2020-12-02 13:25:25 +01:00
|
|
|
{
|
2021-02-09 11:24:28 +01:00
|
|
|
using namespace blender::bke;
|
|
|
|
const ComponentAttributeProviders *providers = this->get_attribute_providers();
|
|
|
|
if (providers == nullptr) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return providers->supported_domains().contains(domain);
|
2020-12-02 13:25:25 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
int GeometryComponent::attribute_domain_size(const AttributeDomain UNUSED(domain)) const
|
|
|
|
{
|
|
|
|
BLI_assert(false);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
ReadAttributePtr GeometryComponent::attribute_try_get_for_read(
|
2021-02-09 11:24:28 +01:00
|
|
|
const StringRef attribute_name) const
|
2020-12-02 13:25:25 +01:00
|
|
|
{
|
2021-02-09 11:24:28 +01:00
|
|
|
using namespace blender::bke;
|
|
|
|
const ComponentAttributeProviders *providers = this->get_attribute_providers();
|
|
|
|
if (providers == nullptr) {
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
const BuiltinAttributeProvider *builtin_provider =
|
|
|
|
providers->builtin_attribute_providers().lookup_default_as(attribute_name, nullptr);
|
|
|
|
if (builtin_provider != nullptr) {
|
|
|
|
return builtin_provider->try_get_for_read(*this);
|
|
|
|
}
|
|
|
|
for (const DynamicAttributesProvider *dynamic_provider :
|
|
|
|
providers->dynamic_attribute_providers()) {
|
|
|
|
ReadAttributePtr attribute = dynamic_provider->try_get_for_read(*this, attribute_name);
|
|
|
|
if (attribute) {
|
|
|
|
return attribute;
|
|
|
|
}
|
|
|
|
}
|
2020-12-02 13:25:25 +01:00
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
2021-02-09 11:44:58 +01:00
|
|
|
ReadAttributePtr GeometryComponent::attribute_try_adapt_domain(
|
|
|
|
ReadAttributePtr attribute, const AttributeDomain new_domain) const
|
2020-12-02 13:25:25 +01:00
|
|
|
{
|
2021-02-09 11:44:58 +01:00
|
|
|
if (attribute && attribute->domain() == new_domain) {
|
2020-12-02 13:25:25 +01:00
|
|
|
return attribute;
|
|
|
|
}
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
2021-02-09 11:24:28 +01:00
|
|
|
WriteAttributePtr GeometryComponent::attribute_try_get_for_write(const StringRef attribute_name)
|
2020-12-02 13:25:25 +01:00
|
|
|
{
|
2021-02-09 11:24:28 +01:00
|
|
|
using namespace blender::bke;
|
|
|
|
const ComponentAttributeProviders *providers = this->get_attribute_providers();
|
|
|
|
if (providers == nullptr) {
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
const BuiltinAttributeProvider *builtin_provider =
|
|
|
|
providers->builtin_attribute_providers().lookup_default_as(attribute_name, nullptr);
|
|
|
|
if (builtin_provider != nullptr) {
|
|
|
|
return builtin_provider->try_get_for_write(*this);
|
|
|
|
}
|
|
|
|
for (const DynamicAttributesProvider *dynamic_provider :
|
|
|
|
providers->dynamic_attribute_providers()) {
|
|
|
|
WriteAttributePtr attribute = dynamic_provider->try_get_for_write(*this, attribute_name);
|
|
|
|
if (attribute) {
|
|
|
|
return attribute;
|
|
|
|
}
|
|
|
|
}
|
2020-12-02 13:25:25 +01:00
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
2021-02-09 11:24:28 +01:00
|
|
|
bool GeometryComponent::attribute_try_delete(const StringRef attribute_name)
|
2020-12-02 13:25:25 +01:00
|
|
|
{
|
2021-02-09 11:24:28 +01:00
|
|
|
using namespace blender::bke;
|
|
|
|
const ComponentAttributeProviders *providers = this->get_attribute_providers();
|
|
|
|
if (providers == nullptr) {
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
const BuiltinAttributeProvider *builtin_provider =
|
|
|
|
providers->builtin_attribute_providers().lookup_default_as(attribute_name, nullptr);
|
|
|
|
if (builtin_provider != nullptr) {
|
|
|
|
return builtin_provider->try_delete(*this);
|
|
|
|
}
|
|
|
|
bool success = false;
|
|
|
|
for (const DynamicAttributesProvider *dynamic_provider :
|
|
|
|
providers->dynamic_attribute_providers()) {
|
|
|
|
success = dynamic_provider->try_delete(*this, attribute_name) || success;
|
|
|
|
}
|
|
|
|
return success;
|
2020-12-02 13:25:25 +01:00
|
|
|
}
|
|
|
|
|
2021-02-09 11:24:28 +01:00
|
|
|
bool GeometryComponent::attribute_try_create(const StringRef attribute_name,
|
|
|
|
const AttributeDomain domain,
|
|
|
|
const CustomDataType data_type)
|
2020-12-02 13:25:25 +01:00
|
|
|
{
|
2021-02-09 11:24:28 +01:00
|
|
|
using namespace blender::bke;
|
2021-02-19 18:08:40 +01:00
|
|
|
if (attribute_name.is_empty()) {
|
|
|
|
return false;
|
|
|
|
}
|
2021-02-09 11:24:28 +01:00
|
|
|
const ComponentAttributeProviders *providers = this->get_attribute_providers();
|
|
|
|
if (providers == nullptr) {
|
2021-02-19 18:08:40 +01:00
|
|
|
return false;
|
2021-02-09 11:24:28 +01:00
|
|
|
}
|
|
|
|
const BuiltinAttributeProvider *builtin_provider =
|
|
|
|
providers->builtin_attribute_providers().lookup_default_as(attribute_name, nullptr);
|
|
|
|
if (builtin_provider != nullptr) {
|
|
|
|
if (builtin_provider->domain() != domain) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (builtin_provider->data_type() != data_type) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return builtin_provider->try_create(*this);
|
|
|
|
}
|
|
|
|
for (const DynamicAttributesProvider *dynamic_provider :
|
|
|
|
providers->dynamic_attribute_providers()) {
|
|
|
|
if (dynamic_provider->try_create(*this, attribute_name, domain, data_type)) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
2020-12-02 13:25:25 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
Set<std::string> GeometryComponent::attribute_names() const
|
2021-02-23 15:15:23 +01:00
|
|
|
{
|
|
|
|
Set<std::string> attributes;
|
|
|
|
this->attribute_foreach([&](StringRefNull name, const AttributeMetaData &UNUSED(meta_data)) {
|
|
|
|
attributes.add(name);
|
|
|
|
return true;
|
|
|
|
});
|
|
|
|
return attributes;
|
|
|
|
}
|
|
|
|
|
|
|
|
void GeometryComponent::attribute_foreach(const AttributeForeachCallback callback) const
|
2020-12-02 13:25:25 +01:00
|
|
|
{
|
2021-02-09 11:24:28 +01:00
|
|
|
using namespace blender::bke;
|
|
|
|
const ComponentAttributeProviders *providers = this->get_attribute_providers();
|
|
|
|
if (providers == nullptr) {
|
2021-02-23 15:15:23 +01:00
|
|
|
return;
|
2021-02-09 11:24:28 +01:00
|
|
|
}
|
2021-02-23 15:15:23 +01:00
|
|
|
|
|
|
|
/* Keep track handled attribute names to make sure that we do not return the same name twice. */
|
|
|
|
Set<std::string> handled_attribute_names;
|
|
|
|
|
2021-02-09 11:24:28 +01:00
|
|
|
for (const BuiltinAttributeProvider *provider :
|
|
|
|
providers->builtin_attribute_providers().values()) {
|
|
|
|
if (provider->exists(*this)) {
|
2021-02-23 15:15:23 +01:00
|
|
|
AttributeMetaData meta_data{provider->domain(), provider->data_type()};
|
|
|
|
if (!callback(provider->name(), meta_data)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
handled_attribute_names.add_new(provider->name());
|
2021-02-09 11:24:28 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
for (const DynamicAttributesProvider *provider : providers->dynamic_attribute_providers()) {
|
2021-02-23 15:15:23 +01:00
|
|
|
const bool continue_loop = provider->foreach_attribute(
|
|
|
|
*this, [&](StringRefNull name, const AttributeMetaData &meta_data) {
|
|
|
|
if (handled_attribute_names.add(name)) {
|
|
|
|
return callback(name, meta_data);
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
});
|
|
|
|
if (!continue_loop) {
|
|
|
|
return;
|
|
|
|
}
|
2021-02-09 11:24:28 +01:00
|
|
|
}
|
2020-12-02 13:25:25 +01:00
|
|
|
}
|
|
|
|
|
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
|
|
|
|
{
|
|
|
|
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
|
|
|
|
{
|
|
|
|
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
|
|
|
{
|
|
|
|
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-03-02 13:29:01 +01:00
|
|
|
if (attribute && default_value != nullptr) {
|
2021-01-15 11:04:32 -06:00
|
|
|
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__);
|
2021-01-21 16:55:44 +01:00
|
|
|
GMutableSpan new_span{*cpp_type, buffer, domain_size};
|
|
|
|
|
|
|
|
/* Copy converted values from conflicting attribute, in case the value is read.
|
|
|
|
* TODO: An optimization could be to not do this, when the caller says that the attribute will
|
|
|
|
* only be written. */
|
|
|
|
ReadAttributePtr src_attribute = component.attribute_get_for_read(
|
|
|
|
final_name, domain, data_type, nullptr);
|
|
|
|
for (const int i : blender::IndexRange(domain_size)) {
|
|
|
|
src_attribute->get(i, new_span[i]);
|
|
|
|
}
|
2021-01-14 15:52:08 +01:00
|
|
|
|
|
|
|
attribute_ = std::make_unique<blender::bke::TemporaryWriteAttribute>(
|
2021-01-21 16:55:44 +01:00
|
|
|
domain, new_span, component, std::move(final_name));
|
2021-01-14 15:52:08 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* 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
|
|
|
|
* \{ */
|
|
|
|
|
2021-02-09 11:24:28 +01:00
|
|
|
const blender::bke::ComponentAttributeProviders *PointCloudComponent::get_attribute_providers()
|
|
|
|
const
|
2020-12-02 13:25:25 +01:00
|
|
|
{
|
2021-02-09 11:24:28 +01:00
|
|
|
static blender::bke::ComponentAttributeProviders providers =
|
|
|
|
blender::bke::create_attribute_providers_for_point_cloud();
|
|
|
|
return &providers;
|
2020-12-02 13:25:25 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** \} */
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
/** \name Mesh Component
|
|
|
|
* \{ */
|
|
|
|
|
2021-02-09 11:24:28 +01:00
|
|
|
const blender::bke::ComponentAttributeProviders *MeshComponent::get_attribute_providers() const
|
2020-12-02 13:25:25 +01:00
|
|
|
{
|
2021-02-09 11:24:28 +01:00
|
|
|
static blender::bke::ComponentAttributeProviders providers =
|
|
|
|
blender::bke::create_attribute_providers_for_mesh();
|
|
|
|
return &providers;
|
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;
|
|
|
|
}
|
|
|
|
|
2021-02-09 11:44:58 +01:00
|
|
|
namespace blender::bke {
|
|
|
|
|
|
|
|
template<typename T>
|
2021-02-18 11:03:38 +01:00
|
|
|
static void adapt_mesh_domain_corner_to_point_impl(const Mesh &mesh,
|
|
|
|
const TypedReadAttribute<T> &attribute,
|
|
|
|
MutableSpan<T> r_values)
|
2021-02-09 11:44:58 +01:00
|
|
|
{
|
|
|
|
BLI_assert(r_values.size() == mesh.totvert);
|
|
|
|
attribute_math::DefaultMixer<T> mixer(r_values);
|
|
|
|
|
|
|
|
for (const int loop_index : IndexRange(mesh.totloop)) {
|
|
|
|
const T value = attribute[loop_index];
|
|
|
|
const MLoop &loop = mesh.mloop[loop_index];
|
|
|
|
const int point_index = loop.v;
|
|
|
|
mixer.mix_in(point_index, value);
|
|
|
|
}
|
|
|
|
mixer.finalize();
|
|
|
|
}
|
|
|
|
|
|
|
|
static ReadAttributePtr adapt_mesh_domain_corner_to_point(const Mesh &mesh,
|
|
|
|
ReadAttributePtr attribute)
|
|
|
|
{
|
|
|
|
ReadAttributePtr new_attribute;
|
|
|
|
const CustomDataType data_type = attribute->custom_data_type();
|
|
|
|
attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
|
|
|
|
using T = decltype(dummy);
|
|
|
|
if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) {
|
|
|
|
/* We compute all interpolated values at once, because for this interpolation, one has to
|
|
|
|
* iterate over all loops anyway. */
|
|
|
|
Array<T> values(mesh.totvert);
|
|
|
|
adapt_mesh_domain_corner_to_point_impl<T>(mesh, *attribute, values);
|
|
|
|
new_attribute = std::make_unique<OwnedArrayReadAttribute<T>>(ATTR_DOMAIN_POINT,
|
|
|
|
std::move(values));
|
|
|
|
}
|
|
|
|
});
|
|
|
|
return new_attribute;
|
|
|
|
}
|
|
|
|
|
2021-02-18 11:03:38 +01:00
|
|
|
template<typename T>
|
|
|
|
static void adapt_mesh_domain_point_to_corner_impl(const Mesh &mesh,
|
|
|
|
const TypedReadAttribute<T> &attribute,
|
|
|
|
MutableSpan<T> r_values)
|
|
|
|
{
|
|
|
|
BLI_assert(r_values.size() == mesh.totloop);
|
|
|
|
|
|
|
|
for (const int loop_index : IndexRange(mesh.totloop)) {
|
|
|
|
const int vertex_index = mesh.mloop[loop_index].v;
|
|
|
|
r_values[loop_index] = attribute[vertex_index];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static ReadAttributePtr adapt_mesh_domain_point_to_corner(const Mesh &mesh,
|
|
|
|
ReadAttributePtr attribute)
|
|
|
|
{
|
|
|
|
ReadAttributePtr new_attribute;
|
|
|
|
const CustomDataType data_type = attribute->custom_data_type();
|
|
|
|
attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
|
|
|
|
using T = decltype(dummy);
|
|
|
|
/* It is not strictly necessary to compute the value for all corners here. Instead one could
|
|
|
|
* lazily lookup the mesh topology when a specific index accessed. This can be more efficient
|
|
|
|
* when an algorithm only accesses very few of the corner values. However, for the algorithms
|
|
|
|
* we currently have, precomputing the array is fine. Also, it is easier to implement. */
|
|
|
|
Array<T> values(mesh.totloop);
|
|
|
|
adapt_mesh_domain_point_to_corner_impl<T>(mesh, *attribute, values);
|
|
|
|
new_attribute = std::make_unique<OwnedArrayReadAttribute<T>>(ATTR_DOMAIN_CORNER,
|
|
|
|
std::move(values));
|
|
|
|
});
|
|
|
|
return new_attribute;
|
|
|
|
}
|
|
|
|
|
2021-02-09 11:44:58 +01:00
|
|
|
} // namespace blender::bke
|
|
|
|
|
|
|
|
ReadAttributePtr MeshComponent::attribute_try_adapt_domain(ReadAttributePtr attribute,
|
|
|
|
const AttributeDomain new_domain) const
|
|
|
|
{
|
|
|
|
if (!attribute) {
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
if (attribute->size() == 0) {
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
const AttributeDomain old_domain = attribute->domain();
|
|
|
|
if (old_domain == new_domain) {
|
|
|
|
return attribute;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (old_domain) {
|
|
|
|
case ATTR_DOMAIN_CORNER: {
|
|
|
|
switch (new_domain) {
|
|
|
|
case ATTR_DOMAIN_POINT:
|
|
|
|
return blender::bke::adapt_mesh_domain_corner_to_point(*mesh_, std::move(attribute));
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
2021-02-18 11:03:38 +01:00
|
|
|
case ATTR_DOMAIN_POINT: {
|
|
|
|
switch (new_domain) {
|
|
|
|
case ATTR_DOMAIN_CORNER:
|
|
|
|
return blender::bke::adapt_mesh_domain_point_to_corner(*mesh_, std::move(attribute));
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2021-02-09 11:44:58 +01:00
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
2020-12-02 13:25:25 +01:00
|
|
|
/** \} */
|