Geometry Nodes: new geometry attribute API
Currently, there are two attribute API. The first, defined in `BKE_attribute.h` is accessible from RNA and C code. The second is implemented with `GeometryComponent` and is only accessible in C++ code. The second is widely used, but only being accessible through the `GeometrySet` API makes it awkward to use, and even impossible for types that don't correspond directly to a geometry component like `CurvesGeometry`. This patch adds a new attribute API, designed to replace the `GeometryComponent` attribute API now, and to eventually replace or be the basis of the other one. The basic idea is that there is an `AttributeAccessor` class that allows code to interact with a set of attributes owned by some geometry. The accessor itself has no ownership. `AttributeAccessor` is a simple type that can be passed around by value. That makes it easy to return it from functions and to store it in containers. For const-correctness, there is also a `MutableAttributeAccessor` that allows changing individual and can add or remove attributes. Currently, `AttributeAccessor` is composed of two pointers. The first is a pointer to the owner of the attribute data. The second is a pointer to a struct with function pointers, that is similar to a virtual function table. The functions know how to access attributes on the owner. The actual attribute access for geometries is still implemented with the `AttributeProvider` pattern, which makes it easy to support different sources of attributes on a geometry and simplifies dealing with built-in attributes. There are different ways to get an attribute accessor for a geometry: * `GeometryComponent.attributes()` * `CurvesGeometry.attributes()` * `bke::mesh_attributes(const Mesh &)` * `bke::pointcloud_attributes(const PointCloud &)` All of these also have a `_for_write` variant that returns a `MutabelAttributeAccessor`. Differential Revision: https://developer.blender.org/D15280
This commit is contained in:
814
source/blender/blenkernel/BKE_attribute.hh
Normal file
814
source/blender/blenkernel/BKE_attribute.hh
Normal file
@@ -0,0 +1,814 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BLI_color.hh"
|
||||
#include "BLI_function_ref.hh"
|
||||
#include "BLI_generic_span.hh"
|
||||
#include "BLI_generic_virtual_array.hh"
|
||||
#include "BLI_math_vec_types.hh"
|
||||
#include "BLI_set.hh"
|
||||
|
||||
#include "BKE_anonymous_attribute.hh"
|
||||
#include "BKE_attribute.h"
|
||||
|
||||
struct Mesh;
|
||||
struct PointCloud;
|
||||
|
||||
namespace blender::bke {
|
||||
|
||||
/**
|
||||
* Identifies an attribute that is either named or anonymous.
|
||||
* It does not own the identifier, so it is just a reference.
|
||||
*/
|
||||
class AttributeIDRef {
|
||||
private:
|
||||
StringRef name_;
|
||||
const AnonymousAttributeID *anonymous_id_ = nullptr;
|
||||
|
||||
public:
|
||||
AttributeIDRef();
|
||||
AttributeIDRef(StringRef name);
|
||||
AttributeIDRef(StringRefNull name);
|
||||
AttributeIDRef(const char *name);
|
||||
AttributeIDRef(const std::string &name);
|
||||
AttributeIDRef(const AnonymousAttributeID *anonymous_id);
|
||||
|
||||
operator bool() const;
|
||||
uint64_t hash() const;
|
||||
bool is_named() const;
|
||||
bool is_anonymous() const;
|
||||
StringRef name() const;
|
||||
const AnonymousAttributeID &anonymous_id() const;
|
||||
bool should_be_kept() const;
|
||||
|
||||
friend bool operator==(const AttributeIDRef &a, const AttributeIDRef &b);
|
||||
friend std::ostream &operator<<(std::ostream &stream, const AttributeIDRef &attribute_id);
|
||||
};
|
||||
|
||||
/**
|
||||
* Contains information about an attribute in a geometry component.
|
||||
* More information can be added in the future. E.g. whether the attribute is builtin and how it is
|
||||
* stored (uv map, vertex group, ...).
|
||||
*/
|
||||
struct AttributeMetaData {
|
||||
eAttrDomain domain;
|
||||
eCustomDataType data_type;
|
||||
|
||||
constexpr friend bool operator==(AttributeMetaData a, AttributeMetaData b)
|
||||
{
|
||||
return (a.domain == b.domain) && (a.data_type == b.data_type);
|
||||
}
|
||||
};
|
||||
|
||||
struct AttributeKind {
|
||||
eAttrDomain domain;
|
||||
eCustomDataType data_type;
|
||||
};
|
||||
|
||||
/**
|
||||
* Base class for the attribute initializer types described below.
|
||||
*/
|
||||
struct AttributeInit {
|
||||
enum class Type {
|
||||
Default,
|
||||
VArray,
|
||||
MoveArray,
|
||||
};
|
||||
Type type;
|
||||
AttributeInit(const Type type) : type(type)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Create an attribute using the default value for the data type.
|
||||
* The default values may depend on the attribute provider implementation.
|
||||
*/
|
||||
struct AttributeInitDefault : public AttributeInit {
|
||||
AttributeInitDefault() : AttributeInit(Type::Default)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Create an attribute by copying data from an existing virtual array. The virtual array
|
||||
* must have the same type as the newly created attribute.
|
||||
*
|
||||
* Note that this can be used to fill the new attribute with the default
|
||||
*/
|
||||
struct AttributeInitVArray : public AttributeInit {
|
||||
blender::GVArray varray;
|
||||
|
||||
AttributeInitVArray(blender::GVArray varray)
|
||||
: AttributeInit(Type::VArray), varray(std::move(varray))
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Create an attribute with a by passing ownership of a pre-allocated contiguous array of data.
|
||||
* Sometimes data is created before a geometry component is available. In that case, it's
|
||||
* preferable to move data directly to the created attribute to avoid a new allocation and a copy.
|
||||
*
|
||||
* Note that this will only have a benefit for attributes that are stored directly as contiguous
|
||||
* arrays, so not for some built-in attributes.
|
||||
*
|
||||
* The array must be allocated with MEM_*, since `attribute_try_create` will free the array if it
|
||||
* can't be used directly, and that is generally how Blender expects custom data to be allocated.
|
||||
*/
|
||||
struct AttributeInitMove : public AttributeInit {
|
||||
void *data = nullptr;
|
||||
|
||||
AttributeInitMove(void *data) : AttributeInit(Type::MoveArray), data(data)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
/* Returns false when the iteration should be stopped. */
|
||||
using AttributeForeachCallback =
|
||||
FunctionRef<bool(const AttributeIDRef &attribute_id, const AttributeMetaData &meta_data)>;
|
||||
|
||||
/**
|
||||
* Result when looking up an attribute from some geometry with the intention of only reading from
|
||||
* it.
|
||||
*/
|
||||
template<typename T> struct AttributeReader {
|
||||
/**
|
||||
* Virtual array that provides access to the attribute data. This may be empty.
|
||||
*/
|
||||
VArray<T> varray;
|
||||
/**
|
||||
* Domain where the attribute is stored. This also determines the size of the virtual array.
|
||||
*/
|
||||
eAttrDomain domain;
|
||||
|
||||
operator bool() const
|
||||
{
|
||||
return this->varray;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Result when looking up an attribute from some geometry with read an write access. After writing
|
||||
* to the attribute, the #finish method has to be called. This may invalidate caches based on this
|
||||
* attribute.
|
||||
*/
|
||||
template<typename T> struct AttributeWriter {
|
||||
/**
|
||||
* Virtual array giving read and write access to the attribute. This may be empty.
|
||||
* Consider using #SpanAttributeWriter when you want to access the virtual array as a span.
|
||||
*/
|
||||
VMutableArray<T> varray;
|
||||
/**
|
||||
* Domain where the attribute is stored on the geometry. Also determines the size of the virtual
|
||||
* array.
|
||||
*/
|
||||
eAttrDomain domain;
|
||||
/**
|
||||
* A function that has to be called after the attribute has been edited. This may be empty.
|
||||
*/
|
||||
std::function<void()> tag_modified_fn;
|
||||
|
||||
operator bool() const
|
||||
{
|
||||
return this->varray;
|
||||
}
|
||||
|
||||
/**
|
||||
* Has to be called after the attribute has been modified.
|
||||
*/
|
||||
void finish()
|
||||
{
|
||||
if (this->tag_modified_fn) {
|
||||
this->tag_modified_fn();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* A version of #AttributeWriter for the common case when the user of the attribute wants to write
|
||||
* to a span instead of a virtual array. Since most attributes are spans internally, this can
|
||||
* result in better performance and also simplifies code.
|
||||
*/
|
||||
template<typename T> struct SpanAttributeWriter {
|
||||
/**
|
||||
* A span based on the virtual array that contains the attribute data. This may be empty.
|
||||
*/
|
||||
MutableVArraySpan<T> span;
|
||||
/**
|
||||
* Domain of the attribute. Also determines the size of the span.
|
||||
*/
|
||||
eAttrDomain domain;
|
||||
/**
|
||||
* Has to be called after writing to the span.
|
||||
*/
|
||||
std::function<void()> tag_modified_fn;
|
||||
|
||||
SpanAttributeWriter() = default;
|
||||
|
||||
SpanAttributeWriter(AttributeWriter<T> &&other, const bool copy_values_to_span)
|
||||
: span(std::move(other.varray), copy_values_to_span),
|
||||
domain(other.domain),
|
||||
tag_modified_fn(std::move(other.tag_modified_fn))
|
||||
{
|
||||
}
|
||||
|
||||
operator bool() const
|
||||
{
|
||||
return span.varray();
|
||||
}
|
||||
|
||||
/**
|
||||
* Has to be called when done writing to the attribute. This makes sure that the data is copied
|
||||
* to the underlying attribute if it was not stored as an array. Furthermore, this may invalidate
|
||||
* other data depending on the modified attribute.
|
||||
*/
|
||||
void finish()
|
||||
{
|
||||
this->span.save();
|
||||
if (this->tag_modified_fn) {
|
||||
this->tag_modified_fn();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* A generic version of #AttributeReader.
|
||||
*/
|
||||
struct GAttributeReader {
|
||||
GVArray varray;
|
||||
eAttrDomain domain;
|
||||
|
||||
operator bool() const
|
||||
{
|
||||
return this->varray;
|
||||
}
|
||||
|
||||
template<typename T> AttributeReader<T> typed() const
|
||||
{
|
||||
return {varray.typed<T>(), domain};
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* A generic version of #AttributeWriter.
|
||||
*/
|
||||
struct GAttributeWriter {
|
||||
GVMutableArray varray;
|
||||
eAttrDomain domain;
|
||||
std::function<void()> tag_modified_fn;
|
||||
|
||||
operator bool() const
|
||||
{
|
||||
return this->varray;
|
||||
}
|
||||
|
||||
void finish()
|
||||
{
|
||||
if (this->tag_modified_fn) {
|
||||
this->tag_modified_fn();
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T> AttributeWriter<T> typed() const
|
||||
{
|
||||
return {varray.typed<T>(), domain, tag_modified_fn};
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* A generic version of #SpanAttributeWriter.
|
||||
*/
|
||||
struct GSpanAttributeWriter {
|
||||
GMutableVArraySpan span;
|
||||
eAttrDomain domain;
|
||||
std::function<void()> tag_modified_fn;
|
||||
|
||||
GSpanAttributeWriter() = default;
|
||||
|
||||
GSpanAttributeWriter(GAttributeWriter &&other, const bool copy_values_to_span)
|
||||
: span(std::move(other.varray), copy_values_to_span),
|
||||
domain(other.domain),
|
||||
tag_modified_fn(std::move(other.tag_modified_fn))
|
||||
{
|
||||
}
|
||||
|
||||
operator bool() const
|
||||
{
|
||||
return span.varray();
|
||||
}
|
||||
|
||||
void finish()
|
||||
{
|
||||
this->span.save();
|
||||
if (this->tag_modified_fn) {
|
||||
this->tag_modified_fn();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Core functions which make up the attribute API. They should not be called directly, but through
|
||||
* #AttributesAccessor or #MutableAttributesAccessor.
|
||||
*
|
||||
* This is similar to a virtual function table. A struct of function pointers is used instead,
|
||||
* because this way the attribute accessors can be trivial and can be passed around by value. This
|
||||
* makes it easy to return the attribute accessor for a geometry from a function.
|
||||
*/
|
||||
struct AttributeAccessorFunctions {
|
||||
bool (*contains)(const void *owner, const AttributeIDRef &attribute_id);
|
||||
std::optional<AttributeMetaData> (*lookup_meta_data)(const void *owner,
|
||||
const AttributeIDRef &attribute_id);
|
||||
bool (*domain_supported)(const void *owner, eAttrDomain domain);
|
||||
int (*domain_size)(const void *owner, eAttrDomain domain);
|
||||
bool (*is_builtin)(const void *owner, const AttributeIDRef &attribute_id);
|
||||
GAttributeReader (*lookup)(const void *owner, const AttributeIDRef &attribute_id);
|
||||
GVArray (*adapt_domain)(const void *owner,
|
||||
const GVArray &varray,
|
||||
eAttrDomain from_domain,
|
||||
eAttrDomain to_domain);
|
||||
bool (*for_all)(const void *owner,
|
||||
FunctionRef<bool(const AttributeIDRef &, const AttributeMetaData &)> fn);
|
||||
|
||||
GAttributeWriter (*lookup_for_write)(void *owner, const AttributeIDRef &attribute_id);
|
||||
bool (*remove)(void *owner, const AttributeIDRef &attribute_id);
|
||||
bool (*add)(void *owner,
|
||||
const AttributeIDRef &attribute_id,
|
||||
eAttrDomain domain,
|
||||
eCustomDataType data_type,
|
||||
const AttributeInit &initializer);
|
||||
};
|
||||
|
||||
/**
|
||||
* Provides read-only access to the set of attributes on some geometry.
|
||||
*
|
||||
* Note, this does not own the attributes. When the owner is freed, it is invalid to access its
|
||||
* attributes.
|
||||
*/
|
||||
class AttributeAccessor {
|
||||
protected:
|
||||
/**
|
||||
* The data that actually owns the attributes, for example, a pointer to a #Mesh or #PointCloud
|
||||
* Most commonly this is a pointer to a #Mesh or #PointCloud.
|
||||
* Under some circumstances this can be null. In that case most methods can't be used. Just e.g.
|
||||
* the #domain_size method works and returns 0 for every domain.
|
||||
*
|
||||
* \note This class cannot modify the owner's attributes, but the pointer is still non-const, so
|
||||
* this class can be a base class for the mutable version.
|
||||
*/
|
||||
void *owner_;
|
||||
/**
|
||||
* Functions that know how to access the attributes stored in the owner above.
|
||||
*/
|
||||
const AttributeAccessorFunctions *fn_;
|
||||
|
||||
public:
|
||||
AttributeAccessor(const void *owner, const AttributeAccessorFunctions &fn)
|
||||
: owner_(const_cast<void *>(owner)), fn_(&fn)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* \return True, when the attribute is available.
|
||||
*/
|
||||
bool contains(const AttributeIDRef &attribute_id) const
|
||||
{
|
||||
return fn_->contains(owner_, attribute_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* \return Information about the attribute if it exists.
|
||||
*/
|
||||
std::optional<AttributeMetaData> lookup_meta_data(const AttributeIDRef &attribute_id) const
|
||||
{
|
||||
return fn_->lookup_meta_data(owner_, attribute_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* \return True, when attributes can exist on that domain.
|
||||
*/
|
||||
bool domain_supported(const eAttrDomain domain) const
|
||||
{
|
||||
return fn_->domain_supported(owner_, domain);
|
||||
}
|
||||
|
||||
/**
|
||||
* \return Number of elements in the given domain.
|
||||
*/
|
||||
int domain_size(const eAttrDomain domain) const
|
||||
{
|
||||
return fn_->domain_size(owner_, domain);
|
||||
}
|
||||
|
||||
/**
|
||||
* \return True, when the attribute has a special meaning for Blender and can't be used for
|
||||
* arbitrary things.
|
||||
*/
|
||||
bool is_builtin(const AttributeIDRef &attribute_id) const
|
||||
{
|
||||
return fn_->is_builtin(owner_, attribute_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get read-only access to the attribute. If the attribute does not exist, the return value is
|
||||
* empty.
|
||||
*/
|
||||
GAttributeReader lookup(const AttributeIDRef &attribute_id) const
|
||||
{
|
||||
return fn_->lookup(owner_, attribute_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get read-only access to the attribute. If necessary, the attribute is interpolated to the
|
||||
* given domain, and converted to the given type, in that order. The result may be empty.
|
||||
*/
|
||||
GVArray lookup(const AttributeIDRef &attribute_id,
|
||||
const std::optional<eAttrDomain> domain,
|
||||
const std::optional<eCustomDataType> data_type) const;
|
||||
|
||||
/**
|
||||
* Get read-only access to the attribute whereby the attribute is interpolated to the given
|
||||
* domain. The result may be empty.
|
||||
*/
|
||||
GVArray lookup(const AttributeIDRef &attribute_id, const eAttrDomain domain) const
|
||||
{
|
||||
return this->lookup(attribute_id, domain, std::nullopt);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get read-only access to the attribute whereby the attribute is converted to the given type.
|
||||
* The result may be empty.
|
||||
*/
|
||||
GVArray lookup(const AttributeIDRef &attribute_id, const eCustomDataType data_type) const
|
||||
{
|
||||
return this->lookup(attribute_id, std::nullopt, data_type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get read-only access to the attribute. If necessary, the attribute is interpolated to the
|
||||
* given domain and then converted to the given type, in that order. The result may be empty.
|
||||
*/
|
||||
template<typename T>
|
||||
VArray<T> lookup(const AttributeIDRef &attribute_id,
|
||||
const std::optional<eAttrDomain> domain = std::nullopt) const
|
||||
{
|
||||
const CPPType &cpp_type = CPPType::get<T>();
|
||||
const eCustomDataType data_type = cpp_type_to_custom_data_type(cpp_type);
|
||||
return this->lookup(attribute_id, domain, data_type).typed<T>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get read-only access to the attribute. If necessary, the attribute is interpolated to the
|
||||
* given domain and then converted to the given data type, in that order.
|
||||
* If the attribute does not exist, a virtual array with the given default value is returned.
|
||||
* If the passed in default value is null, the default value of the type is used (generally 0).
|
||||
*/
|
||||
GVArray lookup_or_default(const AttributeIDRef &attribute_id,
|
||||
const eAttrDomain domain,
|
||||
const eCustomDataType data_type,
|
||||
const void *default_value = nullptr) const;
|
||||
|
||||
/**
|
||||
* Same as the generic version above, but should be used when the type is known at compile time.
|
||||
*/
|
||||
template<typename T>
|
||||
VArray<T> lookup_or_default(const AttributeIDRef &attribute_id,
|
||||
const eAttrDomain domain,
|
||||
const T &default_value) const
|
||||
{
|
||||
if (VArray<T> varray = this->lookup<T>(attribute_id, domain)) {
|
||||
return varray;
|
||||
}
|
||||
return VArray<T>::ForSingle(default_value, this->domain_size(domain));
|
||||
}
|
||||
|
||||
/**
|
||||
* Interpolate data from one domain to another.
|
||||
*/
|
||||
GVArray adapt_domain(const GVArray &varray,
|
||||
const eAttrDomain from_domain,
|
||||
const eAttrDomain to_domain) const
|
||||
{
|
||||
return fn_->adapt_domain(owner_, varray, from_domain, to_domain);
|
||||
}
|
||||
|
||||
/**
|
||||
* Interpolate data from one domain to another.
|
||||
*/
|
||||
template<typename T>
|
||||
VArray<T> adapt_domain(const VArray<T> &varray,
|
||||
const eAttrDomain from_domain,
|
||||
const eAttrDomain to_domain) const
|
||||
{
|
||||
return this->adapt_domain(GVArray(varray), from_domain, to_domain).typed<T>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Run the provided function for every attribute.
|
||||
*/
|
||||
bool for_all(const AttributeForeachCallback fn) const
|
||||
{
|
||||
return fn_->for_all(owner_, fn);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a set of all attributes.
|
||||
*/
|
||||
Set<AttributeIDRef> all_ids() const;
|
||||
};
|
||||
|
||||
/**
|
||||
* Extends #AttributeAccessor with methods that allow modifying individual attributes as well as
|
||||
* the set of attributes.
|
||||
*/
|
||||
class MutableAttributeAccessor : public AttributeAccessor {
|
||||
public:
|
||||
MutableAttributeAccessor(void *owner, const AttributeAccessorFunctions &fn)
|
||||
: AttributeAccessor(owner, fn)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a writable attribute or none if it does not exist.
|
||||
* Make sure to call #finish after changes are done.
|
||||
*/
|
||||
GAttributeWriter lookup_for_write(const AttributeIDRef &attribute_id)
|
||||
{
|
||||
return fn_->lookup_for_write(owner_, attribute_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a writable attribute or non if it does not exist.
|
||||
* Make sure to call #finish after changes are done.
|
||||
*/
|
||||
template<typename T> AttributeWriter<T> lookup_for_write(const AttributeIDRef &attribute_id)
|
||||
{
|
||||
GAttributeWriter attribute = this->lookup_for_write(attribute_id);
|
||||
if (!attribute) {
|
||||
return {};
|
||||
}
|
||||
if (!attribute.varray.type().is<T>()) {
|
||||
return {};
|
||||
}
|
||||
return attribute.typed<T>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new attribute.
|
||||
* \return True, when a new attribute has been created. False, when it's not possible to create
|
||||
* this attribute or there is already an attribute with that id.
|
||||
*/
|
||||
bool add(const AttributeIDRef &attribute_id,
|
||||
const eAttrDomain domain,
|
||||
const eCustomDataType data_type,
|
||||
const AttributeInit &initializer)
|
||||
{
|
||||
return fn_->add(owner_, attribute_id, domain, data_type, initializer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find an attribute with the given id, domain and data type. If it does not exist, create a new
|
||||
* attribute. If the attribute does not exist and can't be created (e.g. because it already
|
||||
* exists on a different domain or with a different type), none is returned.
|
||||
*/
|
||||
GAttributeWriter lookup_or_add_for_write(
|
||||
const AttributeIDRef &attribute_id,
|
||||
const eAttrDomain domain,
|
||||
const eCustomDataType data_type,
|
||||
const AttributeInit &initializer = AttributeInitDefault());
|
||||
|
||||
/**
|
||||
* Same as above, but returns a type that makes it easier to work with the attribute as a span.
|
||||
* If the caller newly initializes the attribute, it's better to use
|
||||
* #lookup_or_add_for_write_only_span.
|
||||
*/
|
||||
GSpanAttributeWriter lookup_or_add_for_write_span(
|
||||
const AttributeIDRef &attribute_id,
|
||||
const eAttrDomain domain,
|
||||
const eCustomDataType data_type,
|
||||
const AttributeInit &initializer = AttributeInitDefault());
|
||||
|
||||
/**
|
||||
* Same as above, but should be used when the type is known at compile time.
|
||||
*/
|
||||
template<typename T>
|
||||
AttributeWriter<T> lookup_or_add_for_write(
|
||||
const AttributeIDRef &attribute_id,
|
||||
const eAttrDomain domain,
|
||||
const AttributeInit &initializer = AttributeInitDefault())
|
||||
{
|
||||
const CPPType &cpp_type = CPPType::get<T>();
|
||||
const eCustomDataType data_type = cpp_type_to_custom_data_type(cpp_type);
|
||||
return this->lookup_or_add_for_write(attribute_id, domain, data_type, initializer).typed<T>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as above, but should be used when the type is known at compile time.
|
||||
*/
|
||||
template<typename T>
|
||||
SpanAttributeWriter<T> lookup_or_add_for_write_span(
|
||||
const AttributeIDRef &attribute_id,
|
||||
const eAttrDomain domain,
|
||||
const AttributeInit &initializer = AttributeInitDefault())
|
||||
{
|
||||
AttributeWriter<T> attribute = this->lookup_or_add_for_write<T>(
|
||||
attribute_id, domain, initializer);
|
||||
if (attribute) {
|
||||
return SpanAttributeWriter<T>{std::move(attribute), true};
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
/**
|
||||
* Find an attribute with the given id, domain and data type. If it does not exist, create a new
|
||||
* attribute. If the attribute does not exist and can't be created, none is returned.
|
||||
*
|
||||
* The "only" in the name indicates that the caller should not read existing values from the
|
||||
* span. If the attribute is not stored as span internally, the existing values won't be copied
|
||||
* over to the span.
|
||||
*/
|
||||
GSpanAttributeWriter lookup_or_add_for_write_only_span(const AttributeIDRef &attribute_id,
|
||||
const eAttrDomain domain,
|
||||
const eCustomDataType data_type);
|
||||
|
||||
/**
|
||||
* Same as above, but should be used when the type is known at compile time.
|
||||
*/
|
||||
template<typename T>
|
||||
SpanAttributeWriter<T> lookup_or_add_for_write_only_span(const AttributeIDRef &attribute_id,
|
||||
const eAttrDomain domain)
|
||||
{
|
||||
AttributeWriter<T> attribute = this->lookup_or_add_for_write<T>(attribute_id, domain);
|
||||
if (attribute) {
|
||||
return SpanAttributeWriter<T>{std::move(attribute), false};
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove an attribute.
|
||||
* \return True, when the attribute has been deleted. False, when it's not possible to delete
|
||||
* this attribute or if there is no attribute with that id.
|
||||
*/
|
||||
bool remove(const AttributeIDRef &attribute_id)
|
||||
{
|
||||
return fn_->remove(owner_, attribute_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove all anonymous attributes.
|
||||
*/
|
||||
void remove_anonymous();
|
||||
};
|
||||
|
||||
bool allow_procedural_attribute_access(StringRef attribute_name);
|
||||
extern const char *no_procedural_access_message;
|
||||
|
||||
eCustomDataType attribute_data_type_highest_complexity(Span<eCustomDataType> data_types);
|
||||
/**
|
||||
* Domains with a higher "information density" have a higher priority,
|
||||
* in order to choose a domain that will not lose data through domain conversion.
|
||||
*/
|
||||
eAttrDomain attribute_domain_highest_priority(Span<eAttrDomain> domains);
|
||||
|
||||
/**
|
||||
* A basic container around DNA CustomData so that its users
|
||||
* don't have to implement special copy and move constructors.
|
||||
*/
|
||||
class CustomDataAttributes {
|
||||
/**
|
||||
* #CustomData needs a size to be freed, and unfortunately it isn't stored in the struct
|
||||
* itself, so keep track of the size here so this class can implement its own destructor.
|
||||
* If the implementation of the attribute storage changes, this could be removed.
|
||||
*/
|
||||
int size_;
|
||||
|
||||
public:
|
||||
CustomData data;
|
||||
|
||||
CustomDataAttributes();
|
||||
~CustomDataAttributes();
|
||||
CustomDataAttributes(const CustomDataAttributes &other);
|
||||
CustomDataAttributes(CustomDataAttributes &&other);
|
||||
CustomDataAttributes &operator=(const CustomDataAttributes &other);
|
||||
|
||||
void reallocate(int size);
|
||||
|
||||
void clear();
|
||||
|
||||
std::optional<blender::GSpan> get_for_read(const AttributeIDRef &attribute_id) const;
|
||||
|
||||
/**
|
||||
* Return a virtual array for a stored attribute, or a single value virtual array with the
|
||||
* default value if the attribute doesn't exist. If no default value is provided, the default
|
||||
* value for the type will be used.
|
||||
*/
|
||||
blender::GVArray get_for_read(const AttributeIDRef &attribute_id,
|
||||
eCustomDataType data_type,
|
||||
const void *default_value) const;
|
||||
|
||||
template<typename T>
|
||||
blender::VArray<T> get_for_read(const AttributeIDRef &attribute_id, const T &default_value) const
|
||||
{
|
||||
const blender::CPPType &cpp_type = blender::CPPType::get<T>();
|
||||
const eCustomDataType type = blender::bke::cpp_type_to_custom_data_type(cpp_type);
|
||||
GVArray varray = this->get_for_read(attribute_id, type, &default_value);
|
||||
return varray.typed<T>();
|
||||
}
|
||||
|
||||
std::optional<blender::GMutableSpan> get_for_write(const AttributeIDRef &attribute_id);
|
||||
bool create(const AttributeIDRef &attribute_id, eCustomDataType data_type);
|
||||
bool create_by_move(const AttributeIDRef &attribute_id, eCustomDataType data_type, void *buffer);
|
||||
bool remove(const AttributeIDRef &attribute_id);
|
||||
|
||||
/**
|
||||
* Change the order of the attributes to match the order of IDs in the argument.
|
||||
*/
|
||||
void reorder(Span<AttributeIDRef> new_order);
|
||||
|
||||
bool foreach_attribute(const AttributeForeachCallback callback, eAttrDomain domain) const;
|
||||
};
|
||||
|
||||
AttributeAccessor mesh_attributes(const Mesh &mesh);
|
||||
MutableAttributeAccessor mesh_attributes_for_write(Mesh &mesh);
|
||||
|
||||
AttributeAccessor pointcloud_attributes(const PointCloud &pointcloud);
|
||||
MutableAttributeAccessor pointcloud_attributes_for_write(PointCloud &pointcloud);
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name #AttributeIDRef Inline Methods
|
||||
* \{ */
|
||||
|
||||
inline AttributeIDRef::AttributeIDRef() = default;
|
||||
|
||||
inline AttributeIDRef::AttributeIDRef(StringRef name) : name_(name)
|
||||
{
|
||||
}
|
||||
|
||||
inline AttributeIDRef::AttributeIDRef(StringRefNull name) : name_(name)
|
||||
{
|
||||
}
|
||||
|
||||
inline AttributeIDRef::AttributeIDRef(const char *name) : name_(name)
|
||||
{
|
||||
}
|
||||
|
||||
inline AttributeIDRef::AttributeIDRef(const std::string &name) : name_(name)
|
||||
{
|
||||
}
|
||||
|
||||
/* The anonymous id is only borrowed, the caller has to keep a reference to it. */
|
||||
inline AttributeIDRef::AttributeIDRef(const AnonymousAttributeID *anonymous_id)
|
||||
: anonymous_id_(anonymous_id)
|
||||
{
|
||||
}
|
||||
|
||||
inline bool operator==(const AttributeIDRef &a, const AttributeIDRef &b)
|
||||
{
|
||||
return a.anonymous_id_ == b.anonymous_id_ && a.name_ == b.name_;
|
||||
}
|
||||
|
||||
inline AttributeIDRef::operator bool() const
|
||||
{
|
||||
return this->is_named() || this->is_anonymous();
|
||||
}
|
||||
|
||||
inline uint64_t AttributeIDRef::hash() const
|
||||
{
|
||||
return get_default_hash_2(name_, anonymous_id_);
|
||||
}
|
||||
|
||||
inline bool AttributeIDRef::is_named() const
|
||||
{
|
||||
return !name_.is_empty();
|
||||
}
|
||||
|
||||
inline bool AttributeIDRef::is_anonymous() const
|
||||
{
|
||||
return anonymous_id_ != nullptr;
|
||||
}
|
||||
|
||||
inline StringRef AttributeIDRef::name() const
|
||||
{
|
||||
BLI_assert(this->is_named());
|
||||
return name_;
|
||||
}
|
||||
|
||||
inline const AnonymousAttributeID &AttributeIDRef::anonymous_id() const
|
||||
{
|
||||
BLI_assert(this->is_anonymous());
|
||||
return *anonymous_id_;
|
||||
}
|
||||
|
||||
/**
|
||||
* \return True if the attribute should not be removed automatically as an optimization during
|
||||
* processing or copying. Anonymous attributes can be removed when they no longer have any
|
||||
* references.
|
||||
*/
|
||||
inline bool AttributeIDRef::should_be_kept() const
|
||||
{
|
||||
return this->is_named() || BKE_anonymous_attribute_id_has_strong_references(anonymous_id_);
|
||||
}
|
||||
|
||||
} // namespace blender::bke
|
||||
@@ -1,541 +0,0 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <mutex>
|
||||
|
||||
#include "BKE_anonymous_attribute.hh"
|
||||
#include "BKE_attribute.h"
|
||||
|
||||
#include "BLI_color.hh"
|
||||
#include "BLI_function_ref.hh"
|
||||
#include "BLI_generic_span.hh"
|
||||
#include "BLI_generic_virtual_array.hh"
|
||||
#include "BLI_math_vec_types.hh"
|
||||
|
||||
/**
|
||||
* This file defines classes that help to provide access to attribute data on a #GeometryComponent.
|
||||
* The API for retrieving attributes is defined in `BKE_geometry_set.hh`, but this comment has some
|
||||
* general comments about the system.
|
||||
*
|
||||
* Attributes are stored in geometry data, though they can also be stored in instances. Their
|
||||
* storage is often tied to `CustomData`, which is a system to store "layers" of data with specific
|
||||
* types and names. However, since `CustomData` was added to Blender before attributes were
|
||||
* conceptualized, it combines the "legacy" style of task-specific attribute types with generic
|
||||
* types like "Float". The attribute API here only provides access to generic types.
|
||||
*
|
||||
* Attributes are retrieved from geometry components by providing an "id" (#AttributeIDRef). This
|
||||
* is most commonly just an attribute name. The attribute API on geometry components has some more
|
||||
* advanced capabilities:
|
||||
* 1. Read-only access: With a `const` geometry component, an attribute on the geometry cannot be
|
||||
* modified, so the `for_write` and `for_output` versions of the API are not available. This is
|
||||
* extremely important for writing coherent bug-free code. When an attribute is retrieved with
|
||||
* write access, via #WriteAttributeLookup or #OutputAttribute, the geometry component must be
|
||||
* tagged to clear caches that depend on the changed data.
|
||||
* 2. Domain interpolation: When retrieving an attribute, a domain (#eAttrDomain) can be
|
||||
* provided. If the attribute is stored on a different domain and conversion is possible, a
|
||||
* version of the data interpolated to the requested domain will be provided. These conversions
|
||||
* are implemented in each #GeometryComponent by `attribute_try_adapt_domain_impl`.
|
||||
* 3. Implicit type conversion: In addition to interpolating domains, attribute types can be
|
||||
* converted, using the conversions in `BKE_type_conversions.hh`. The #VArray / #GVArray system
|
||||
* makes it possible to only convert necessary indices on-demand.
|
||||
* 4. Anonymous attributes: The "id" used to look up an attribute can also be an anonymous
|
||||
* attribute reference. Currently anonymous attributes are only used in geometry nodes.
|
||||
* 5. Abstracted storage: Since the data returned from the API is usually a virtual array,
|
||||
* it doesn't have to be stored contiguously (even though that is generally preferred). This
|
||||
* allows accessing "legacy" attributes like `material_index`, which is stored in `MPoly`.
|
||||
*/
|
||||
|
||||
namespace blender::bke {
|
||||
|
||||
/**
|
||||
* Identifies an attribute that is either named or anonymous.
|
||||
* It does not own the identifier, so it is just a reference.
|
||||
*/
|
||||
class AttributeIDRef {
|
||||
private:
|
||||
StringRef name_;
|
||||
const AnonymousAttributeID *anonymous_id_ = nullptr;
|
||||
|
||||
public:
|
||||
AttributeIDRef();
|
||||
AttributeIDRef(StringRef name);
|
||||
AttributeIDRef(StringRefNull name);
|
||||
AttributeIDRef(const char *name);
|
||||
AttributeIDRef(const std::string &name);
|
||||
AttributeIDRef(const AnonymousAttributeID *anonymous_id);
|
||||
|
||||
operator bool() const;
|
||||
uint64_t hash() const;
|
||||
bool is_named() const;
|
||||
bool is_anonymous() const;
|
||||
StringRef name() const;
|
||||
const AnonymousAttributeID &anonymous_id() const;
|
||||
bool should_be_kept() const;
|
||||
|
||||
friend bool operator==(const AttributeIDRef &a, const AttributeIDRef &b);
|
||||
friend std::ostream &operator<<(std::ostream &stream, const AttributeIDRef &attribute_id);
|
||||
};
|
||||
|
||||
bool allow_procedural_attribute_access(StringRef attribute_name);
|
||||
extern const char *no_procedural_access_message;
|
||||
|
||||
} // namespace blender::bke
|
||||
|
||||
/**
|
||||
* Contains information about an attribute in a geometry component.
|
||||
* More information can be added in the future. E.g. whether the attribute is builtin and how it is
|
||||
* stored (uv map, vertex group, ...).
|
||||
*/
|
||||
struct AttributeMetaData {
|
||||
eAttrDomain domain;
|
||||
eCustomDataType data_type;
|
||||
|
||||
constexpr friend bool operator==(AttributeMetaData a, AttributeMetaData b)
|
||||
{
|
||||
return (a.domain == b.domain) && (a.data_type == b.data_type);
|
||||
}
|
||||
};
|
||||
|
||||
struct AttributeKind {
|
||||
eAttrDomain domain;
|
||||
eCustomDataType data_type;
|
||||
};
|
||||
|
||||
/**
|
||||
* Base class for the attribute initializer types described below.
|
||||
*/
|
||||
struct AttributeInit {
|
||||
enum class Type {
|
||||
Default,
|
||||
VArray,
|
||||
MoveArray,
|
||||
};
|
||||
Type type;
|
||||
AttributeInit(const Type type) : type(type)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Create an attribute using the default value for the data type.
|
||||
* The default values may depend on the attribute provider implementation.
|
||||
*/
|
||||
struct AttributeInitDefault : public AttributeInit {
|
||||
AttributeInitDefault() : AttributeInit(Type::Default)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Create an attribute by copying data from an existing virtual array. The virtual array
|
||||
* must have the same type as the newly created attribute.
|
||||
*
|
||||
* Note that this can be used to fill the new attribute with the default
|
||||
*/
|
||||
struct AttributeInitVArray : public AttributeInit {
|
||||
blender::GVArray varray;
|
||||
|
||||
AttributeInitVArray(blender::GVArray varray)
|
||||
: AttributeInit(Type::VArray), varray(std::move(varray))
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Create an attribute with a by passing ownership of a pre-allocated contiguous array of data.
|
||||
* Sometimes data is created before a geometry component is available. In that case, it's
|
||||
* preferable to move data directly to the created attribute to avoid a new allocation and a copy.
|
||||
*
|
||||
* Note that this will only have a benefit for attributes that are stored directly as contiguous
|
||||
* arrays, so not for some built-in attributes.
|
||||
*
|
||||
* The array must be allocated with MEM_*, since `attribute_try_create` will free the array if it
|
||||
* can't be used directly, and that is generally how Blender expects custom data to be allocated.
|
||||
*/
|
||||
struct AttributeInitMove : public AttributeInit {
|
||||
void *data = nullptr;
|
||||
|
||||
AttributeInitMove(void *data) : AttributeInit(Type::MoveArray), data(data)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
/* Returns false when the iteration should be stopped. */
|
||||
using AttributeForeachCallback = blender::FunctionRef<bool(
|
||||
const blender::bke::AttributeIDRef &attribute_id, const AttributeMetaData &meta_data)>;
|
||||
|
||||
namespace blender::bke {
|
||||
|
||||
eCustomDataType attribute_data_type_highest_complexity(Span<eCustomDataType> data_types);
|
||||
/**
|
||||
* Domains with a higher "information density" have a higher priority,
|
||||
* in order to choose a domain that will not lose data through domain conversion.
|
||||
*/
|
||||
eAttrDomain attribute_domain_highest_priority(Span<eAttrDomain> domains);
|
||||
|
||||
/**
|
||||
* Used when looking up a "plain attribute" based on a name for reading from it.
|
||||
*/
|
||||
struct ReadAttributeLookup {
|
||||
/* The virtual array that is used to read from this attribute. */
|
||||
GVArray varray;
|
||||
/* Domain the attribute lives on in the geometry. */
|
||||
eAttrDomain domain;
|
||||
|
||||
/* Convenience function to check if the attribute has been found. */
|
||||
operator bool() const
|
||||
{
|
||||
return this->varray;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Used when looking up a "plain attribute" based on a name for reading from it and writing to it.
|
||||
*/
|
||||
struct WriteAttributeLookup {
|
||||
/** The virtual array that is used to read from and write to the attribute. */
|
||||
GVMutableArray varray;
|
||||
/** Domain the attributes lives on in the geometry component. */
|
||||
eAttrDomain domain;
|
||||
/**
|
||||
* Call this after changing the attribute to invalidate caches that depend on this attribute.
|
||||
* \note Do not call this after the component the attribute is from has been destructed.
|
||||
*/
|
||||
std::function<void()> tag_modified_fn;
|
||||
|
||||
/* Convenience function to check if the attribute has been found. */
|
||||
operator bool() const
|
||||
{
|
||||
return this->varray;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* An output attribute allows writing to an attribute (and optionally reading as well). It adds
|
||||
* some convenience features on top of `GVMutableArray` that are very commonly used.
|
||||
*
|
||||
* Supported convenience features:
|
||||
* - Implicit type conversion when writing to builtin attributes.
|
||||
* - Supports simple access to a span containing the attribute values (that avoids the use of
|
||||
* MutableVArraySpan in many cases).
|
||||
* - An output attribute can live side by side with an existing attribute with a different domain
|
||||
* or data type. The old attribute will only be overwritten when the #save function is called.
|
||||
*
|
||||
* \note The lifetime of an output attribute should not be longer than the lifetime of the
|
||||
* geometry component it comes from, since it can keep a reference to the component for use in
|
||||
* the #save method.
|
||||
*/
|
||||
class OutputAttribute {
|
||||
public:
|
||||
using SaveFn = std::function<void(OutputAttribute &)>;
|
||||
|
||||
private:
|
||||
GVMutableArray varray_;
|
||||
eAttrDomain domain_ = ATTR_DOMAIN_AUTO;
|
||||
SaveFn save_;
|
||||
std::unique_ptr<GMutableVArraySpan> optional_span_varray_;
|
||||
bool ignore_old_values_ = false;
|
||||
bool save_has_been_called_ = false;
|
||||
|
||||
public:
|
||||
OutputAttribute();
|
||||
OutputAttribute(OutputAttribute &&other);
|
||||
OutputAttribute(GVMutableArray varray, eAttrDomain domain, SaveFn save, bool ignore_old_values);
|
||||
|
||||
~OutputAttribute();
|
||||
|
||||
operator bool() const;
|
||||
|
||||
GVMutableArray &operator*();
|
||||
GVMutableArray *operator->();
|
||||
GVMutableArray &varray();
|
||||
eAttrDomain domain() const;
|
||||
const CPPType &cpp_type() const;
|
||||
eCustomDataType custom_data_type() const;
|
||||
|
||||
GMutableSpan as_span();
|
||||
template<typename T> MutableSpan<T> as_span();
|
||||
|
||||
void save();
|
||||
};
|
||||
|
||||
/**
|
||||
* Same as OutputAttribute, but should be used when the data type is known at compile time.
|
||||
*/
|
||||
template<typename T> class OutputAttribute_Typed {
|
||||
private:
|
||||
OutputAttribute attribute_;
|
||||
VMutableArray<T> varray_;
|
||||
|
||||
public:
|
||||
OutputAttribute_Typed();
|
||||
OutputAttribute_Typed(OutputAttribute attribute) : attribute_(std::move(attribute))
|
||||
{
|
||||
if (attribute_) {
|
||||
varray_ = attribute_.varray().template typed<T>();
|
||||
}
|
||||
}
|
||||
|
||||
OutputAttribute_Typed(OutputAttribute_Typed &&other);
|
||||
~OutputAttribute_Typed();
|
||||
|
||||
OutputAttribute_Typed &operator=(OutputAttribute_Typed &&other)
|
||||
{
|
||||
if (this == &other) {
|
||||
return *this;
|
||||
}
|
||||
this->~OutputAttribute_Typed();
|
||||
new (this) OutputAttribute_Typed(std::move(other));
|
||||
return *this;
|
||||
}
|
||||
|
||||
operator bool() const
|
||||
{
|
||||
return varray_;
|
||||
}
|
||||
|
||||
VMutableArray<T> &operator*()
|
||||
{
|
||||
return varray_;
|
||||
}
|
||||
|
||||
VMutableArray<T> *operator->()
|
||||
{
|
||||
return &varray_;
|
||||
}
|
||||
|
||||
VMutableArray<T> &varray()
|
||||
{
|
||||
return varray_;
|
||||
}
|
||||
|
||||
eAttrDomain domain() const
|
||||
{
|
||||
return attribute_.domain();
|
||||
}
|
||||
|
||||
const CPPType &cpp_type() const
|
||||
{
|
||||
return CPPType::get<T>();
|
||||
}
|
||||
|
||||
eCustomDataType custom_data_type() const
|
||||
{
|
||||
return cpp_type_to_custom_data_type(this->cpp_type());
|
||||
}
|
||||
|
||||
MutableSpan<T> as_span()
|
||||
{
|
||||
return attribute_.as_span<T>();
|
||||
}
|
||||
|
||||
void save()
|
||||
{
|
||||
attribute_.save();
|
||||
}
|
||||
};
|
||||
|
||||
/* These are not defined in the class directly, because when defining them there, the external
|
||||
* template instantiation does not work, resulting in longer compile times. */
|
||||
template<typename T> inline OutputAttribute_Typed<T>::OutputAttribute_Typed() = default;
|
||||
template<typename T>
|
||||
inline OutputAttribute_Typed<T>::OutputAttribute_Typed(OutputAttribute_Typed &&other) = default;
|
||||
template<typename T> inline OutputAttribute_Typed<T>::~OutputAttribute_Typed() = default;
|
||||
|
||||
/**
|
||||
* A basic container around DNA CustomData so that its users
|
||||
* don't have to implement special copy and move constructors.
|
||||
*/
|
||||
class CustomDataAttributes {
|
||||
/**
|
||||
* #CustomData needs a size to be freed, and unfortunately it isn't stored in the struct
|
||||
* itself, so keep track of the size here so this class can implement its own destructor.
|
||||
* If the implementation of the attribute storage changes, this could be removed.
|
||||
*/
|
||||
int size_;
|
||||
|
||||
public:
|
||||
CustomData data;
|
||||
|
||||
CustomDataAttributes();
|
||||
~CustomDataAttributes();
|
||||
CustomDataAttributes(const CustomDataAttributes &other);
|
||||
CustomDataAttributes(CustomDataAttributes &&other);
|
||||
CustomDataAttributes &operator=(const CustomDataAttributes &other);
|
||||
|
||||
void reallocate(int size);
|
||||
|
||||
void clear();
|
||||
|
||||
std::optional<blender::GSpan> get_for_read(const AttributeIDRef &attribute_id) const;
|
||||
|
||||
/**
|
||||
* Return a virtual array for a stored attribute, or a single value virtual array with the
|
||||
* default value if the attribute doesn't exist. If no default value is provided, the default
|
||||
* value for the type will be used.
|
||||
*/
|
||||
blender::GVArray get_for_read(const AttributeIDRef &attribute_id,
|
||||
eCustomDataType data_type,
|
||||
const void *default_value) const;
|
||||
|
||||
template<typename T>
|
||||
blender::VArray<T> get_for_read(const AttributeIDRef &attribute_id, const T &default_value) const
|
||||
{
|
||||
const blender::CPPType &cpp_type = blender::CPPType::get<T>();
|
||||
const eCustomDataType type = blender::bke::cpp_type_to_custom_data_type(cpp_type);
|
||||
GVArray varray = this->get_for_read(attribute_id, type, &default_value);
|
||||
return varray.typed<T>();
|
||||
}
|
||||
|
||||
std::optional<blender::GMutableSpan> get_for_write(const AttributeIDRef &attribute_id);
|
||||
bool create(const AttributeIDRef &attribute_id, eCustomDataType data_type);
|
||||
bool create_by_move(const AttributeIDRef &attribute_id, eCustomDataType data_type, void *buffer);
|
||||
bool remove(const AttributeIDRef &attribute_id);
|
||||
|
||||
/**
|
||||
* Change the order of the attributes to match the order of IDs in the argument.
|
||||
*/
|
||||
void reorder(Span<AttributeIDRef> new_order);
|
||||
|
||||
bool foreach_attribute(const AttributeForeachCallback callback, eAttrDomain domain) const;
|
||||
};
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name #AttributeIDRef Inline Methods
|
||||
* \{ */
|
||||
|
||||
inline AttributeIDRef::AttributeIDRef() = default;
|
||||
|
||||
inline AttributeIDRef::AttributeIDRef(StringRef name) : name_(name)
|
||||
{
|
||||
}
|
||||
|
||||
inline AttributeIDRef::AttributeIDRef(StringRefNull name) : name_(name)
|
||||
{
|
||||
}
|
||||
|
||||
inline AttributeIDRef::AttributeIDRef(const char *name) : name_(name)
|
||||
{
|
||||
}
|
||||
|
||||
inline AttributeIDRef::AttributeIDRef(const std::string &name) : name_(name)
|
||||
{
|
||||
}
|
||||
|
||||
/* The anonymous id is only borrowed, the caller has to keep a reference to it. */
|
||||
inline AttributeIDRef::AttributeIDRef(const AnonymousAttributeID *anonymous_id)
|
||||
: anonymous_id_(anonymous_id)
|
||||
{
|
||||
}
|
||||
|
||||
inline bool operator==(const AttributeIDRef &a, const AttributeIDRef &b)
|
||||
{
|
||||
return a.anonymous_id_ == b.anonymous_id_ && a.name_ == b.name_;
|
||||
}
|
||||
|
||||
inline AttributeIDRef::operator bool() const
|
||||
{
|
||||
return this->is_named() || this->is_anonymous();
|
||||
}
|
||||
|
||||
inline uint64_t AttributeIDRef::hash() const
|
||||
{
|
||||
return get_default_hash_2(name_, anonymous_id_);
|
||||
}
|
||||
|
||||
inline bool AttributeIDRef::is_named() const
|
||||
{
|
||||
return !name_.is_empty();
|
||||
}
|
||||
|
||||
inline bool AttributeIDRef::is_anonymous() const
|
||||
{
|
||||
return anonymous_id_ != nullptr;
|
||||
}
|
||||
|
||||
inline StringRef AttributeIDRef::name() const
|
||||
{
|
||||
BLI_assert(this->is_named());
|
||||
return name_;
|
||||
}
|
||||
|
||||
inline const AnonymousAttributeID &AttributeIDRef::anonymous_id() const
|
||||
{
|
||||
BLI_assert(this->is_anonymous());
|
||||
return *anonymous_id_;
|
||||
}
|
||||
|
||||
/**
|
||||
* \return True if the attribute should not be removed automatically as an optimization during
|
||||
* processing or copying. Anonymous attributes can be removed when they no longer have any
|
||||
* references.
|
||||
*/
|
||||
inline bool AttributeIDRef::should_be_kept() const
|
||||
{
|
||||
return this->is_named() || BKE_anonymous_attribute_id_has_strong_references(anonymous_id_);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name #OutputAttribute Inline Methods
|
||||
* \{ */
|
||||
|
||||
inline OutputAttribute::OutputAttribute() = default;
|
||||
inline OutputAttribute::OutputAttribute(OutputAttribute &&other) = default;
|
||||
|
||||
inline OutputAttribute::OutputAttribute(GVMutableArray varray,
|
||||
eAttrDomain domain,
|
||||
SaveFn save,
|
||||
const bool ignore_old_values)
|
||||
: varray_(std::move(varray)),
|
||||
domain_(domain),
|
||||
save_(std::move(save)),
|
||||
ignore_old_values_(ignore_old_values)
|
||||
{
|
||||
}
|
||||
|
||||
inline OutputAttribute::operator bool() const
|
||||
{
|
||||
return varray_;
|
||||
}
|
||||
|
||||
inline GVMutableArray &OutputAttribute::operator*()
|
||||
{
|
||||
return varray_;
|
||||
}
|
||||
|
||||
inline GVMutableArray *OutputAttribute::operator->()
|
||||
{
|
||||
return &varray_;
|
||||
}
|
||||
|
||||
inline GVMutableArray &OutputAttribute::varray()
|
||||
{
|
||||
return varray_;
|
||||
}
|
||||
|
||||
inline eAttrDomain OutputAttribute::domain() const
|
||||
{
|
||||
return domain_;
|
||||
}
|
||||
|
||||
inline const CPPType &OutputAttribute::cpp_type() const
|
||||
{
|
||||
return varray_.type();
|
||||
}
|
||||
|
||||
inline eCustomDataType OutputAttribute::custom_data_type() const
|
||||
{
|
||||
return cpp_type_to_custom_data_type(this->cpp_type());
|
||||
}
|
||||
|
||||
template<typename T> inline MutableSpan<T> OutputAttribute::as_span()
|
||||
{
|
||||
return this->as_span().typed<T>();
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender::bke
|
||||
@@ -20,7 +20,7 @@
|
||||
#include "BLI_vector.hh"
|
||||
#include "BLI_virtual_array.hh"
|
||||
|
||||
#include "BKE_attribute_access.hh"
|
||||
#include "BKE_attribute.hh"
|
||||
|
||||
namespace blender::bke {
|
||||
|
||||
@@ -401,6 +401,9 @@ class CurvesGeometry : public ::CurvesGeometry {
|
||||
*/
|
||||
void remove_attributes_based_on_types();
|
||||
|
||||
AttributeAccessor attributes() const;
|
||||
MutableAttributeAccessor attributes_for_write();
|
||||
|
||||
/* --------------------------------------------------------------------
|
||||
* Attributes.
|
||||
*/
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
#include <atomic>
|
||||
#include <iostream>
|
||||
#include <mutex>
|
||||
|
||||
#include "BLI_float4x4.hh"
|
||||
#include "BLI_function_ref.hh"
|
||||
@@ -19,7 +20,7 @@
|
||||
#include "BLI_vector_set.hh"
|
||||
|
||||
#include "BKE_anonymous_attribute.hh"
|
||||
#include "BKE_attribute_access.hh"
|
||||
#include "BKE_attribute.hh"
|
||||
#include "BKE_geometry_set.h"
|
||||
|
||||
struct Curves;
|
||||
@@ -63,6 +64,15 @@ class GeometryComponent {
|
||||
virtual ~GeometryComponent() = default;
|
||||
static GeometryComponent *create(GeometryComponentType component_type);
|
||||
|
||||
int attribute_domain_size(eAttrDomain domain) const;
|
||||
|
||||
/**
|
||||
* Get access to the attributes in this geometry component. May return none if the geometry does
|
||||
* not support the attribute system.
|
||||
*/
|
||||
virtual std::optional<blender::bke::AttributeAccessor> attributes() const;
|
||||
virtual std::optional<blender::bke::MutableAttributeAccessor> attributes_for_write();
|
||||
|
||||
/* The returned component should be of the same type as the type this is called on. */
|
||||
virtual GeometryComponent *copy() const = 0;
|
||||
|
||||
@@ -78,203 +88,7 @@ class GeometryComponent {
|
||||
|
||||
GeometryComponentType type() const;
|
||||
|
||||
/**
|
||||
* Return true when any attribute with this name exists, including built in attributes.
|
||||
*/
|
||||
bool attribute_exists(const blender::bke::AttributeIDRef &attribute_id) const;
|
||||
|
||||
/**
|
||||
* Return the data type and domain of an attribute with the given name if it exists.
|
||||
*/
|
||||
std::optional<AttributeMetaData> attribute_get_meta_data(
|
||||
const blender::bke::AttributeIDRef &attribute_id) const;
|
||||
|
||||
/**
|
||||
* Return true when the geometry component supports this attribute domain.
|
||||
* \note Conceptually this function is static, the result is always the same for different
|
||||
* instances of the same geometry component type.
|
||||
*/
|
||||
bool attribute_domain_supported(eAttrDomain domain) const;
|
||||
/**
|
||||
* Return the length of a specific domain, or 0 if the domain is not supported.
|
||||
*/
|
||||
virtual int attribute_domain_num(eAttrDomain domain) const;
|
||||
|
||||
/**
|
||||
* Return true if the attribute name corresponds to a built-in attribute with a hardcoded domain
|
||||
* and data type.
|
||||
*/
|
||||
bool attribute_is_builtin(const blender::StringRef attribute_name) const;
|
||||
bool attribute_is_builtin(const blender::bke::AttributeIDRef &attribute_id) const;
|
||||
|
||||
/**
|
||||
* Get read-only access to an attribute with the given name or id, on the highest priority domain
|
||||
* if there is a name collision.
|
||||
* \return null if the attribute does not exist.
|
||||
*/
|
||||
blender::bke::ReadAttributeLookup attribute_try_get_for_read(
|
||||
const blender::bke::AttributeIDRef &attribute_id) const;
|
||||
|
||||
/**
|
||||
* Get read and write access to an attribute with the given name or id, on the highest priority
|
||||
* domain if there is a name collision.
|
||||
* \note #WriteAttributeLookup.tag_modified_fn must be called after modifying data.
|
||||
* \return null if the attribute does not exist
|
||||
*/
|
||||
blender::bke::WriteAttributeLookup attribute_try_get_for_write(
|
||||
const blender::bke::AttributeIDRef &attribute_id);
|
||||
|
||||
/**
|
||||
* Get a read-only attribute for the domain based on the given attribute. This can be used to
|
||||
* interpolate from one domain to another.
|
||||
* \return null if the interpolation is not implemented.
|
||||
*/
|
||||
blender::GVArray attribute_try_adapt_domain(const blender::GVArray &varray,
|
||||
const eAttrDomain from_domain,
|
||||
const eAttrDomain to_domain) const
|
||||
{
|
||||
return this->attribute_try_adapt_domain_impl(varray, from_domain, to_domain);
|
||||
}
|
||||
/* Use instead of the method above when the type is known at compile time for type safety. */
|
||||
template<typename T>
|
||||
blender::VArray<T> attribute_try_adapt_domain(const blender::VArray<T> &varray,
|
||||
const eAttrDomain from_domain,
|
||||
const eAttrDomain to_domain) const
|
||||
{
|
||||
return this->attribute_try_adapt_domain_impl(varray, from_domain, to_domain)
|
||||
.template typed<T>();
|
||||
}
|
||||
|
||||
/** Returns true when the attribute has been deleted. */
|
||||
bool attribute_try_delete(const blender::bke::AttributeIDRef &attribute_id);
|
||||
|
||||
/**
|
||||
* Remove any anonymous attributes on the geometry (they generally shouldn't exist on original
|
||||
* geometry).
|
||||
*/
|
||||
void attributes_remove_anonymous();
|
||||
|
||||
/** Returns true when the attribute has been created. */
|
||||
bool attribute_try_create(const blender::bke::AttributeIDRef &attribute_id,
|
||||
eAttrDomain domain,
|
||||
eCustomDataType data_type,
|
||||
const AttributeInit &initializer);
|
||||
|
||||
/**
|
||||
* Try to create the builtin attribute with the given name. No data type or domain has to be
|
||||
* provided, because those are fixed for builtin attributes.
|
||||
*/
|
||||
bool attribute_try_create_builtin(const blender::StringRef attribute_name,
|
||||
const AttributeInit &initializer);
|
||||
|
||||
blender::Set<blender::bke::AttributeIDRef> attribute_ids() const;
|
||||
/**
|
||||
* \return False if the callback explicitly returned false at any point, otherwise true,
|
||||
* meaning the callback made it all the way through.
|
||||
*/
|
||||
bool attribute_foreach(const AttributeForeachCallback callback) const;
|
||||
|
||||
virtual bool is_empty() const;
|
||||
|
||||
/**
|
||||
* Get a virtual array that refers to the data of an attribute, interpolated to the given domain
|
||||
* and converted to the data type. Returns null when the attribute does not exist or cannot be
|
||||
* interpolated or converted.
|
||||
*/
|
||||
blender::GVArray attribute_try_get_for_read(const blender::bke::AttributeIDRef &attribute_id,
|
||||
eAttrDomain domain,
|
||||
eCustomDataType data_type) const;
|
||||
|
||||
/**
|
||||
* Get a virtual array that refers to the data of an attribute, interpolated to the given domain.
|
||||
* The data type is left unchanged. Returns null when the attribute does not exist or cannot be
|
||||
* interpolated.
|
||||
*/
|
||||
blender::GVArray attribute_try_get_for_read(const blender::bke::AttributeIDRef &attribute_id,
|
||||
eAttrDomain domain) const;
|
||||
|
||||
/**
|
||||
* Get a virtual array that refers to the data of an attribute converted to the given data type.
|
||||
* The attribute's domain is left unchanged. Returns null when the attribute does not exist or
|
||||
* cannot be converted.
|
||||
*/
|
||||
blender::bke::ReadAttributeLookup attribute_try_get_for_read(
|
||||
const blender::bke::AttributeIDRef &attribute_id, eCustomDataType data_type) const;
|
||||
|
||||
/**
|
||||
* Get a virtual array that refers to the data of an attribute, interpolated to the given domain
|
||||
* and converted to the data type. If that is not possible, the returned virtual array will
|
||||
* contain a default value. This never returns null.
|
||||
*/
|
||||
blender::GVArray attribute_get_for_read(const blender::bke::AttributeIDRef &attribute_id,
|
||||
eAttrDomain domain,
|
||||
eCustomDataType data_type,
|
||||
const void *default_value = nullptr) const;
|
||||
/* Use instead of the method above when the type is known at compile time for type safety. */
|
||||
template<typename T>
|
||||
blender::VArray<T> attribute_get_for_read(const blender::bke::AttributeIDRef &attribute_id,
|
||||
const eAttrDomain domain,
|
||||
const T &default_value) const
|
||||
{
|
||||
const blender::CPPType &cpp_type = blender::CPPType::get<T>();
|
||||
const eCustomDataType type = blender::bke::cpp_type_to_custom_data_type(cpp_type);
|
||||
return this->attribute_get_for_read(attribute_id, domain, type, &default_value)
|
||||
.template typed<T>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an "output attribute", which is essentially a mutable virtual array with some commonly
|
||||
* used convince features. The returned output attribute might be empty if requested attribute
|
||||
* cannot exist on the geometry.
|
||||
*
|
||||
* The included convenience features are:
|
||||
* - Implicit type conversion when writing to builtin attributes.
|
||||
* - If the attribute name exists already, but has a different type/domain, a temporary attribute
|
||||
* is created that will overwrite the existing attribute in the end.
|
||||
*/
|
||||
blender::bke::OutputAttribute attribute_try_get_for_output(
|
||||
const blender::bke::AttributeIDRef &attribute_id,
|
||||
eAttrDomain domain,
|
||||
eCustomDataType data_type,
|
||||
const void *default_value = nullptr);
|
||||
/* Use instead of the method above when the type is known at compile time for type safety. */
|
||||
template<typename T>
|
||||
blender::bke::OutputAttribute_Typed<T> attribute_try_get_for_output(
|
||||
const blender::bke::AttributeIDRef &attribute_id,
|
||||
const eAttrDomain domain,
|
||||
const T default_value)
|
||||
{
|
||||
const blender::CPPType &cpp_type = blender::CPPType::get<T>();
|
||||
const eCustomDataType data_type = blender::bke::cpp_type_to_custom_data_type(cpp_type);
|
||||
return this->attribute_try_get_for_output(attribute_id, domain, data_type, &default_value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as #attribute_try_get_for_output, but should be used when the original values in the
|
||||
* attributes are not read, i.e. the attribute is used only for output. The can be faster because
|
||||
* it can avoid interpolation and conversion of existing values. Since values are not read from
|
||||
* this attribute, no default value is necessary.
|
||||
*/
|
||||
blender::bke::OutputAttribute attribute_try_get_for_output_only(
|
||||
const blender::bke::AttributeIDRef &attribute_id,
|
||||
eAttrDomain domain,
|
||||
eCustomDataType data_type);
|
||||
/* Use instead of the method above when the type is known at compile time for type safety. */
|
||||
template<typename T>
|
||||
blender::bke::OutputAttribute_Typed<T> attribute_try_get_for_output_only(
|
||||
const blender::bke::AttributeIDRef &attribute_id, const eAttrDomain domain)
|
||||
{
|
||||
const blender::CPPType &cpp_type = blender::CPPType::get<T>();
|
||||
const eCustomDataType data_type = blender::bke::cpp_type_to_custom_data_type(cpp_type);
|
||||
return this->attribute_try_get_for_output_only(attribute_id, domain, data_type);
|
||||
}
|
||||
|
||||
private:
|
||||
virtual const blender::bke::ComponentAttributeProviders *get_attribute_providers() const;
|
||||
|
||||
virtual blender::GVArray attribute_try_adapt_domain_impl(const blender::GVArray &varray,
|
||||
eAttrDomain from_domain,
|
||||
eAttrDomain to_domain) const;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
@@ -381,7 +195,7 @@ struct GeometrySet {
|
||||
|
||||
using AttributeForeachCallback =
|
||||
blender::FunctionRef<void(const blender::bke::AttributeIDRef &attribute_id,
|
||||
const AttributeMetaData &meta_data,
|
||||
const blender::bke::AttributeMetaData &meta_data,
|
||||
const GeometryComponent &component)>;
|
||||
|
||||
void attribute_foreach(blender::Span<GeometryComponentType> component_types,
|
||||
@@ -392,7 +206,7 @@ struct GeometrySet {
|
||||
blender::Span<GeometryComponentType> component_types,
|
||||
GeometryComponentType dst_component_type,
|
||||
bool include_instances,
|
||||
blender::Map<blender::bke::AttributeIDRef, AttributeKind> &r_attributes) const;
|
||||
blender::Map<blender::bke::AttributeIDRef, blender::bke::AttributeKind> &r_attributes) const;
|
||||
|
||||
blender::Vector<GeometryComponentType> gather_component_types(bool include_instances,
|
||||
bool ignore_empty) const;
|
||||
@@ -565,8 +379,6 @@ class MeshComponent : public GeometryComponent {
|
||||
*/
|
||||
Mesh *get_for_write();
|
||||
|
||||
int attribute_domain_num(eAttrDomain domain) const final;
|
||||
|
||||
bool is_empty() const final;
|
||||
|
||||
bool owns_direct_data() const override;
|
||||
@@ -574,12 +386,8 @@ class MeshComponent : public GeometryComponent {
|
||||
|
||||
static constexpr inline GeometryComponentType static_type = GEO_COMPONENT_TYPE_MESH;
|
||||
|
||||
private:
|
||||
const blender::bke::ComponentAttributeProviders *get_attribute_providers() const final;
|
||||
|
||||
blender::GVArray attribute_try_adapt_domain_impl(const blender::GVArray &varray,
|
||||
eAttrDomain from_domain,
|
||||
eAttrDomain to_domain) const final;
|
||||
std::optional<blender::bke::AttributeAccessor> attributes() const final;
|
||||
std::optional<blender::bke::MutableAttributeAccessor> attributes_for_write() final;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -628,17 +436,17 @@ class PointCloudComponent : public GeometryComponent {
|
||||
*/
|
||||
PointCloud *get_for_write();
|
||||
|
||||
int attribute_domain_num(eAttrDomain domain) const final;
|
||||
|
||||
bool is_empty() const final;
|
||||
|
||||
bool owns_direct_data() const override;
|
||||
void ensure_owns_direct_data() override;
|
||||
|
||||
std::optional<blender::bke::AttributeAccessor> attributes() const final;
|
||||
std::optional<blender::bke::MutableAttributeAccessor> attributes_for_write() final;
|
||||
|
||||
static constexpr inline GeometryComponentType static_type = GEO_COMPONENT_TYPE_POINT_CLOUD;
|
||||
|
||||
private:
|
||||
const blender::bke::ComponentAttributeProviders *get_attribute_providers() const final;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -669,21 +477,15 @@ class CurveComponentLegacy : public GeometryComponent {
|
||||
const CurveEval *get_for_read() const;
|
||||
CurveEval *get_for_write();
|
||||
|
||||
int attribute_domain_num(eAttrDomain domain) const final;
|
||||
|
||||
bool is_empty() const final;
|
||||
|
||||
bool owns_direct_data() const override;
|
||||
void ensure_owns_direct_data() override;
|
||||
|
||||
std::optional<blender::bke::AttributeAccessor> attributes() const final;
|
||||
std::optional<blender::bke::MutableAttributeAccessor> attributes_for_write() final;
|
||||
|
||||
static constexpr inline GeometryComponentType static_type = GEO_COMPONENT_TYPE_CURVE;
|
||||
|
||||
private:
|
||||
const blender::bke::ComponentAttributeProviders *get_attribute_providers() const final;
|
||||
|
||||
blender::GVArray attribute_try_adapt_domain_impl(const blender::GVArray &varray,
|
||||
eAttrDomain from_domain,
|
||||
eAttrDomain to_domain) const final;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -721,8 +523,6 @@ class CurveComponent : public GeometryComponent {
|
||||
const Curves *get_for_read() const;
|
||||
Curves *get_for_write();
|
||||
|
||||
int attribute_domain_num(eAttrDomain domain) const final;
|
||||
|
||||
bool is_empty() const final;
|
||||
|
||||
bool owns_direct_data() const override;
|
||||
@@ -734,14 +534,10 @@ class CurveComponent : public GeometryComponent {
|
||||
*/
|
||||
const Curve *get_curve_for_render() const;
|
||||
|
||||
std::optional<blender::bke::AttributeAccessor> attributes() const final;
|
||||
std::optional<blender::bke::MutableAttributeAccessor> attributes_for_write() final;
|
||||
|
||||
static constexpr inline GeometryComponentType static_type = GEO_COMPONENT_TYPE_CURVE;
|
||||
|
||||
private:
|
||||
const blender::bke::ComponentAttributeProviders *get_attribute_providers() const final;
|
||||
|
||||
blender::GVArray attribute_try_adapt_domain_impl(const blender::GVArray &varray,
|
||||
eAttrDomain from_domain,
|
||||
eAttrDomain to_domain) const final;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -966,10 +762,11 @@ class InstancesComponent : public GeometryComponent {
|
||||
|
||||
blender::Span<int> almost_unique_ids() const;
|
||||
|
||||
blender::bke::CustomDataAttributes &attributes();
|
||||
const blender::bke::CustomDataAttributes &attributes() const;
|
||||
blender::bke::CustomDataAttributes &instance_attributes();
|
||||
const blender::bke::CustomDataAttributes &instance_attributes() const;
|
||||
|
||||
int attribute_domain_num(eAttrDomain domain) const final;
|
||||
std::optional<blender::bke::AttributeAccessor> attributes() const final;
|
||||
std::optional<blender::bke::MutableAttributeAccessor> attributes_for_write() final;
|
||||
|
||||
void foreach_referenced_geometry(
|
||||
blender::FunctionRef<void(const GeometrySet &geometry_set)> callback) const;
|
||||
@@ -982,7 +779,6 @@ class InstancesComponent : public GeometryComponent {
|
||||
static constexpr inline GeometryComponentType static_type = GEO_COMPONENT_TYPE_INSTANCES;
|
||||
|
||||
private:
|
||||
const blender::bke::ComponentAttributeProviders *get_attribute_providers() const final;
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include "DNA_meshdata_types.h"
|
||||
|
||||
#include "BKE_attribute.h"
|
||||
#include "BKE_attribute.hh"
|
||||
|
||||
struct Mesh;
|
||||
struct BVHTreeFromMesh;
|
||||
@@ -21,11 +22,6 @@ namespace blender {
|
||||
class RandomNumberGenerator;
|
||||
}
|
||||
|
||||
namespace blender::bke {
|
||||
struct ReadAttributeLookup;
|
||||
class OutputAttribute;
|
||||
} // namespace blender::bke
|
||||
|
||||
namespace blender::bke::mesh_surface_sample {
|
||||
|
||||
void sample_point_attribute(const Mesh &mesh,
|
||||
@@ -81,8 +77,8 @@ class MeshAttributeInterpolator {
|
||||
eAttributeMapMode mode,
|
||||
const GMutableSpan dst);
|
||||
|
||||
void sample_attribute(const ReadAttributeLookup &src_attribute,
|
||||
OutputAttribute &dst_attribute,
|
||||
void sample_attribute(const GAttributeReader &src_attribute,
|
||||
GSpanAttributeWriter &dst_attribute,
|
||||
eAttributeMapMode mode);
|
||||
|
||||
protected:
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
#include "BLI_math_vec_types.hh"
|
||||
#include "BLI_vector.hh"
|
||||
|
||||
#include "BKE_attribute_access.hh"
|
||||
#include "BKE_attribute.hh"
|
||||
#include "BKE_attribute_math.hh"
|
||||
|
||||
struct Curve;
|
||||
@@ -646,6 +646,8 @@ struct CurveEval {
|
||||
void transform(const blender::float4x4 &matrix);
|
||||
bool bounds_min_max(blender::float3 &min, blender::float3 &max, bool use_evaluated) const;
|
||||
|
||||
blender::bke::MutableAttributeAccessor attributes_for_write();
|
||||
|
||||
/**
|
||||
* Return the start indices for each of the curve spline's control points, if they were part
|
||||
* of a flattened array. This can be used to facilitate parallelism by avoiding the need to
|
||||
|
||||
@@ -325,7 +325,7 @@ set(SRC
|
||||
BKE_asset_library.h
|
||||
BKE_asset_library.hh
|
||||
BKE_attribute.h
|
||||
BKE_attribute_access.hh
|
||||
BKE_attribute.hh
|
||||
BKE_attribute_math.hh
|
||||
BKE_autoexec.h
|
||||
BKE_blender.h
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
#include "BLI_string_utils.h"
|
||||
|
||||
#include "BKE_attribute.h"
|
||||
#include "BKE_attribute_access.hh"
|
||||
#include "BKE_attribute.hh"
|
||||
#include "BKE_curves.h"
|
||||
#include "BKE_customdata.h"
|
||||
#include "BKE_editmesh.h"
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -15,12 +15,14 @@ namespace blender::bke {
|
||||
* components in a generic way.
|
||||
*/
|
||||
struct CustomDataAccessInfo {
|
||||
using CustomDataGetter = CustomData *(*)(GeometryComponent &component);
|
||||
using ConstCustomDataGetter = const CustomData *(*)(const GeometryComponent &component);
|
||||
using UpdateCustomDataPointers = void (*)(GeometryComponent &component);
|
||||
using CustomDataGetter = CustomData *(*)(void *owner);
|
||||
using ConstCustomDataGetter = const CustomData *(*)(const void *owner);
|
||||
using GetElementNum = int (*)(const void *owner);
|
||||
using UpdateCustomDataPointers = void (*)(void *owner);
|
||||
|
||||
CustomDataGetter get_custom_data;
|
||||
ConstCustomDataGetter get_const_custom_data;
|
||||
GetElementNum get_element_num;
|
||||
UpdateCustomDataPointers update_custom_data_pointers;
|
||||
};
|
||||
|
||||
@@ -69,12 +71,11 @@ class BuiltinAttributeProvider {
|
||||
{
|
||||
}
|
||||
|
||||
virtual GVArray try_get_for_read(const GeometryComponent &component) const = 0;
|
||||
virtual WriteAttributeLookup try_get_for_write(GeometryComponent &component) const = 0;
|
||||
virtual bool try_delete(GeometryComponent &component) const = 0;
|
||||
virtual bool try_create(GeometryComponent &UNUSED(component),
|
||||
const AttributeInit &UNUSED(initializer)) const = 0;
|
||||
virtual bool exists(const GeometryComponent &component) const = 0;
|
||||
virtual GVArray try_get_for_read(const void *owner) const = 0;
|
||||
virtual GAttributeWriter try_get_for_write(void *owner) const = 0;
|
||||
virtual bool try_delete(void *owner) const = 0;
|
||||
virtual bool try_create(void *onwer, const AttributeInit &initializer) const = 0;
|
||||
virtual bool exists(const void *owner) const = 0;
|
||||
|
||||
StringRefNull name() const
|
||||
{
|
||||
@@ -98,23 +99,23 @@ class BuiltinAttributeProvider {
|
||||
*/
|
||||
class DynamicAttributesProvider {
|
||||
public:
|
||||
virtual ReadAttributeLookup try_get_for_read(const GeometryComponent &component,
|
||||
const AttributeIDRef &attribute_id) const = 0;
|
||||
virtual WriteAttributeLookup try_get_for_write(GeometryComponent &component,
|
||||
const AttributeIDRef &attribute_id) const = 0;
|
||||
virtual bool try_delete(GeometryComponent &component,
|
||||
const AttributeIDRef &attribute_id) const = 0;
|
||||
virtual bool try_create(GeometryComponent &UNUSED(component),
|
||||
const AttributeIDRef &UNUSED(attribute_id),
|
||||
const eAttrDomain UNUSED(domain),
|
||||
const eCustomDataType UNUSED(data_type),
|
||||
const AttributeInit &UNUSED(initializer)) const
|
||||
virtual GAttributeReader try_get_for_read(const void *owner,
|
||||
const AttributeIDRef &attribute_id) const = 0;
|
||||
virtual GAttributeWriter try_get_for_write(void *owner,
|
||||
const AttributeIDRef &attribute_id) const = 0;
|
||||
virtual bool try_delete(void *owner, const AttributeIDRef &attribute_id) const = 0;
|
||||
virtual bool try_create(void *owner,
|
||||
const AttributeIDRef &attribute_id,
|
||||
const eAttrDomain domain,
|
||||
const eCustomDataType data_type,
|
||||
const AttributeInit &initializer) const
|
||||
{
|
||||
UNUSED_VARS(owner, attribute_id, domain, data_type, initializer);
|
||||
/* Some providers should not create new attributes. */
|
||||
return false;
|
||||
};
|
||||
|
||||
virtual bool foreach_attribute(const GeometryComponent &component,
|
||||
virtual bool foreach_attribute(const void *owner,
|
||||
const AttributeForeachCallback callback) const = 0;
|
||||
virtual void foreach_domain(const FunctionRef<void(eAttrDomain)> callback) const = 0;
|
||||
};
|
||||
@@ -138,22 +139,20 @@ class CustomDataAttributeProvider final : public DynamicAttributesProvider {
|
||||
{
|
||||
}
|
||||
|
||||
ReadAttributeLookup try_get_for_read(const GeometryComponent &component,
|
||||
const AttributeIDRef &attribute_id) const final;
|
||||
GAttributeReader try_get_for_read(const void *owner,
|
||||
const AttributeIDRef &attribute_id) const final;
|
||||
|
||||
WriteAttributeLookup try_get_for_write(GeometryComponent &component,
|
||||
const AttributeIDRef &attribute_id) const final;
|
||||
GAttributeWriter try_get_for_write(void *owner, const AttributeIDRef &attribute_id) const final;
|
||||
|
||||
bool try_delete(GeometryComponent &component, const AttributeIDRef &attribute_id) const final;
|
||||
bool try_delete(void *owner, const AttributeIDRef &attribute_id) const final;
|
||||
|
||||
bool try_create(GeometryComponent &component,
|
||||
bool try_create(void *owner,
|
||||
const AttributeIDRef &attribute_id,
|
||||
eAttrDomain domain,
|
||||
const eCustomDataType data_type,
|
||||
const AttributeInit &initializer) const final;
|
||||
|
||||
bool foreach_attribute(const GeometryComponent &component,
|
||||
const AttributeForeachCallback callback) const final;
|
||||
bool foreach_attribute(const void *owner, const AttributeForeachCallback callback) const final;
|
||||
|
||||
void foreach_domain(const FunctionRef<void(eAttrDomain)> callback) const final
|
||||
{
|
||||
@@ -197,13 +196,11 @@ class NamedLegacyCustomDataProvider final : public DynamicAttributesProvider {
|
||||
{
|
||||
}
|
||||
|
||||
ReadAttributeLookup try_get_for_read(const GeometryComponent &component,
|
||||
const AttributeIDRef &attribute_id) const final;
|
||||
WriteAttributeLookup try_get_for_write(GeometryComponent &component,
|
||||
const AttributeIDRef &attribute_id) const final;
|
||||
bool try_delete(GeometryComponent &component, const AttributeIDRef &attribute_id) const final;
|
||||
bool foreach_attribute(const GeometryComponent &component,
|
||||
const AttributeForeachCallback callback) const final;
|
||||
GAttributeReader try_get_for_read(const void *owner,
|
||||
const AttributeIDRef &attribute_id) const final;
|
||||
GAttributeWriter try_get_for_write(void *owner, const AttributeIDRef &attribute_id) const final;
|
||||
bool try_delete(void *owner, const AttributeIDRef &attribute_id) const final;
|
||||
bool foreach_attribute(const void *owner, const AttributeForeachCallback callback) const final;
|
||||
void foreach_domain(const FunctionRef<void(eAttrDomain)> callback) const final;
|
||||
};
|
||||
|
||||
@@ -226,10 +223,10 @@ template<typename T> GVMutableArray make_array_write_attribute(void *data, const
|
||||
* if the stored type is the same as the attribute type.
|
||||
*/
|
||||
class BuiltinCustomDataLayerProvider final : public BuiltinAttributeProvider {
|
||||
using AsReadAttribute = GVArray (*)(const void *data, int domain_num);
|
||||
using AsWriteAttribute = GVMutableArray (*)(void *data, int domain_num);
|
||||
using UpdateOnRead = void (*)(const GeometryComponent &component);
|
||||
using UpdateOnWrite = void (*)(GeometryComponent &component);
|
||||
using AsReadAttribute = GVArray (*)(const void *data, int element_num);
|
||||
using AsWriteAttribute = GVMutableArray (*)(void *data, int element_num);
|
||||
using UpdateOnRead = void (*)(const void *owner);
|
||||
using UpdateOnWrite = void (*)(void *owner);
|
||||
const eCustomDataType stored_type_;
|
||||
const CustomDataAccessInfo custom_data_access_;
|
||||
const AsReadAttribute as_read_attribute_;
|
||||
@@ -260,11 +257,11 @@ class BuiltinCustomDataLayerProvider final : public BuiltinAttributeProvider {
|
||||
{
|
||||
}
|
||||
|
||||
GVArray try_get_for_read(const GeometryComponent &component) const final;
|
||||
WriteAttributeLookup try_get_for_write(GeometryComponent &component) const final;
|
||||
bool try_delete(GeometryComponent &component) const final;
|
||||
bool try_create(GeometryComponent &component, const AttributeInit &initializer) const final;
|
||||
bool exists(const GeometryComponent &component) const final;
|
||||
GVArray try_get_for_read(const void *owner) const final;
|
||||
GAttributeWriter try_get_for_write(void *owner) const final;
|
||||
bool try_delete(void *owner) const final;
|
||||
bool try_create(void *owner, const AttributeInit &initializer) const final;
|
||||
bool exists(const void *owner) const final;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -321,4 +318,183 @@ class ComponentAttributeProviders {
|
||||
}
|
||||
};
|
||||
|
||||
namespace attribute_accessor_functions {
|
||||
|
||||
template<const ComponentAttributeProviders &providers>
|
||||
inline bool is_builtin(const void *UNUSED(owner), const AttributeIDRef &attribute_id)
|
||||
{
|
||||
if (!attribute_id.is_named()) {
|
||||
return false;
|
||||
}
|
||||
const StringRef name = attribute_id.name();
|
||||
return providers.builtin_attribute_providers().contains_as(name);
|
||||
}
|
||||
|
||||
template<const ComponentAttributeProviders &providers>
|
||||
inline GAttributeReader lookup(const void *owner, const AttributeIDRef &attribute_id)
|
||||
{
|
||||
if (attribute_id.is_named()) {
|
||||
const StringRef name = attribute_id.name();
|
||||
if (const BuiltinAttributeProvider *provider =
|
||||
providers.builtin_attribute_providers().lookup_default_as(name, nullptr)) {
|
||||
return {provider->try_get_for_read(owner), provider->domain()};
|
||||
}
|
||||
}
|
||||
for (const DynamicAttributesProvider *provider : providers.dynamic_attribute_providers()) {
|
||||
GAttributeReader attribute = provider->try_get_for_read(owner, attribute_id);
|
||||
if (attribute) {
|
||||
return attribute;
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
template<const ComponentAttributeProviders &providers>
|
||||
inline bool for_all(const void *owner,
|
||||
FunctionRef<bool(const AttributeIDRef &, const AttributeMetaData &)> fn)
|
||||
{
|
||||
Set<AttributeIDRef> handled_attribute_ids;
|
||||
for (const BuiltinAttributeProvider *provider :
|
||||
providers.builtin_attribute_providers().values()) {
|
||||
if (provider->exists(owner)) {
|
||||
AttributeMetaData meta_data{provider->domain(), provider->data_type()};
|
||||
if (!fn(provider->name(), meta_data)) {
|
||||
return false;
|
||||
}
|
||||
handled_attribute_ids.add_new(provider->name());
|
||||
}
|
||||
}
|
||||
for (const DynamicAttributesProvider *provider : providers.dynamic_attribute_providers()) {
|
||||
const bool continue_loop = provider->foreach_attribute(
|
||||
owner, [&](const AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) {
|
||||
if (handled_attribute_ids.add(attribute_id)) {
|
||||
return fn(attribute_id, meta_data);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
if (!continue_loop) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template<const ComponentAttributeProviders &providers>
|
||||
inline bool contains(const void *owner, const blender::bke::AttributeIDRef &attribute_id)
|
||||
{
|
||||
bool found = false;
|
||||
for_all<providers>(
|
||||
owner,
|
||||
[&](const AttributeIDRef &other_attribute_id, const AttributeMetaData & /* meta_data */) {
|
||||
if (attribute_id == other_attribute_id) {
|
||||
found = true;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
return found;
|
||||
}
|
||||
|
||||
template<const ComponentAttributeProviders &providers>
|
||||
inline std::optional<AttributeMetaData> lookup_meta_data(const void *owner,
|
||||
const AttributeIDRef &attribute_id)
|
||||
{
|
||||
std::optional<AttributeMetaData> meta_data;
|
||||
for_all<providers>(
|
||||
owner,
|
||||
[&](const AttributeIDRef &other_attribute_id, const AttributeMetaData &other_meta_data) {
|
||||
if (attribute_id == other_attribute_id) {
|
||||
meta_data = other_meta_data;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
return meta_data;
|
||||
}
|
||||
|
||||
template<const ComponentAttributeProviders &providers>
|
||||
inline GAttributeWriter lookup_for_write(void *owner, const AttributeIDRef &attribute_id)
|
||||
{
|
||||
if (attribute_id.is_named()) {
|
||||
const StringRef name = attribute_id.name();
|
||||
if (const BuiltinAttributeProvider *provider =
|
||||
providers.builtin_attribute_providers().lookup_default_as(name, nullptr)) {
|
||||
return provider->try_get_for_write(owner);
|
||||
}
|
||||
}
|
||||
for (const DynamicAttributesProvider *provider : providers.dynamic_attribute_providers()) {
|
||||
GAttributeWriter attribute = provider->try_get_for_write(owner, attribute_id);
|
||||
if (attribute) {
|
||||
return attribute;
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
template<const ComponentAttributeProviders &providers>
|
||||
inline bool remove(void *owner, const AttributeIDRef &attribute_id)
|
||||
{
|
||||
if (attribute_id.is_named()) {
|
||||
const StringRef name = attribute_id.name();
|
||||
if (const BuiltinAttributeProvider *provider =
|
||||
providers.builtin_attribute_providers().lookup_default_as(name, nullptr)) {
|
||||
return provider->try_delete(owner);
|
||||
}
|
||||
}
|
||||
bool success = false;
|
||||
for (const DynamicAttributesProvider *provider : providers.dynamic_attribute_providers()) {
|
||||
success = provider->try_delete(owner, attribute_id) || success;
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
template<const ComponentAttributeProviders &providers>
|
||||
inline bool add(void *owner,
|
||||
const AttributeIDRef &attribute_id,
|
||||
eAttrDomain domain,
|
||||
eCustomDataType data_type,
|
||||
const AttributeInit &initializer)
|
||||
{
|
||||
if (contains<providers>(owner, attribute_id)) {
|
||||
return false;
|
||||
}
|
||||
if (attribute_id.is_named()) {
|
||||
const StringRef name = attribute_id.name();
|
||||
if (const BuiltinAttributeProvider *provider =
|
||||
providers.builtin_attribute_providers().lookup_default_as(name, nullptr)) {
|
||||
if (provider->domain() != domain) {
|
||||
return false;
|
||||
}
|
||||
if (provider->data_type() != data_type) {
|
||||
return false;
|
||||
}
|
||||
return provider->try_create(owner, initializer);
|
||||
}
|
||||
}
|
||||
for (const DynamicAttributesProvider *provider : providers.dynamic_attribute_providers()) {
|
||||
if (provider->try_create(owner, attribute_id, domain, data_type, initializer)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template<const ComponentAttributeProviders &providers>
|
||||
inline AttributeAccessorFunctions accessor_functions_for_providers()
|
||||
{
|
||||
return AttributeAccessorFunctions{contains<providers>,
|
||||
lookup_meta_data<providers>,
|
||||
nullptr,
|
||||
nullptr,
|
||||
is_builtin<providers>,
|
||||
lookup<providers>,
|
||||
nullptr,
|
||||
for_all<providers>,
|
||||
lookup_for_write<providers>,
|
||||
remove<providers>,
|
||||
add<providers>};
|
||||
}
|
||||
|
||||
} // namespace attribute_accessor_functions
|
||||
|
||||
} // namespace blender::bke
|
||||
|
||||
@@ -31,9 +31,7 @@ using blender::VArray;
|
||||
using blender::VArraySpan;
|
||||
using blender::Vector;
|
||||
using blender::bke::AttributeIDRef;
|
||||
using blender::bke::OutputAttribute;
|
||||
using blender::bke::OutputAttribute_Typed;
|
||||
using blender::bke::ReadAttributeLookup;
|
||||
using blender::bke::AttributeMetaData;
|
||||
|
||||
blender::Span<SplinePtr> CurveEval::splines() const
|
||||
{
|
||||
@@ -345,32 +343,31 @@ std::unique_ptr<CurveEval> curve_eval_from_dna_curve(const Curve &dna_curve)
|
||||
return curve_eval_from_dna_curve(dna_curve, *BKE_curve_nurbs_get_for_read(&dna_curve));
|
||||
}
|
||||
|
||||
static void copy_attributes_between_components(const GeometryComponent &src_component,
|
||||
GeometryComponent &dst_component,
|
||||
Span<std::string> skip)
|
||||
static void copy_attributes_between_components(
|
||||
const blender::bke::AttributeAccessor &src_attributes,
|
||||
blender::bke::MutableAttributeAccessor &dst_attributes,
|
||||
Span<std::string> skip)
|
||||
{
|
||||
src_component.attribute_foreach(
|
||||
[&](const AttributeIDRef &id, const AttributeMetaData meta_data) {
|
||||
if (id.is_named() && skip.contains(id.name())) {
|
||||
return true;
|
||||
}
|
||||
src_attributes.for_all([&](const AttributeIDRef &id, const AttributeMetaData meta_data) {
|
||||
if (id.is_named() && skip.contains(id.name())) {
|
||||
return true;
|
||||
}
|
||||
|
||||
GVArray src_attribute = src_component.attribute_try_get_for_read(
|
||||
id, meta_data.domain, meta_data.data_type);
|
||||
if (!src_attribute) {
|
||||
return true;
|
||||
}
|
||||
GVArraySpan src_attribute_data{src_attribute};
|
||||
GVArray src_attribute = src_attributes.lookup(id, meta_data.domain, meta_data.data_type);
|
||||
if (!src_attribute) {
|
||||
return true;
|
||||
}
|
||||
GVArraySpan src_attribute_data{src_attribute};
|
||||
|
||||
OutputAttribute dst_attribute = dst_component.attribute_try_get_for_output_only(
|
||||
id, meta_data.domain, meta_data.data_type);
|
||||
if (!dst_attribute) {
|
||||
return true;
|
||||
}
|
||||
dst_attribute.varray().set_all(src_attribute_data.data());
|
||||
dst_attribute.save();
|
||||
return true;
|
||||
});
|
||||
blender::bke::GAttributeWriter dst_attribute = dst_attributes.lookup_or_add_for_write(
|
||||
id, meta_data.domain, meta_data.data_type);
|
||||
if (!dst_attribute) {
|
||||
return true;
|
||||
}
|
||||
dst_attribute.varray.set_all(src_attribute_data.data());
|
||||
dst_attribute.finish();
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
std::unique_ptr<CurveEval> curves_to_curve_eval(const Curves &curves_id)
|
||||
@@ -379,21 +376,22 @@ std::unique_ptr<CurveEval> curves_to_curve_eval(const Curves &curves_id)
|
||||
src_component.replace(&const_cast<Curves &>(curves_id), GeometryOwnershipType::ReadOnly);
|
||||
const blender::bke::CurvesGeometry &curves = blender::bke::CurvesGeometry::wrap(
|
||||
curves_id.geometry);
|
||||
const blender::bke::AttributeAccessor src_attributes = curves.attributes();
|
||||
|
||||
VArray<int> resolution = curves.resolution();
|
||||
VArray<int8_t> normal_mode = curves.normal_mode();
|
||||
|
||||
VArraySpan<float> nurbs_weights{
|
||||
src_component.attribute_get_for_read<float>("nurbs_weight", ATTR_DOMAIN_POINT, 0.0f)};
|
||||
src_attributes.lookup_or_default<float>("nurbs_weight", ATTR_DOMAIN_POINT, 0.0f)};
|
||||
VArraySpan<int8_t> nurbs_orders{
|
||||
src_component.attribute_get_for_read<int8_t>("nurbs_order", ATTR_DOMAIN_CURVE, 4)};
|
||||
src_attributes.lookup_or_default<int8_t>("nurbs_order", ATTR_DOMAIN_CURVE, 4)};
|
||||
VArraySpan<int8_t> nurbs_knots_modes{
|
||||
src_component.attribute_get_for_read<int8_t>("knots_mode", ATTR_DOMAIN_CURVE, 0)};
|
||||
src_attributes.lookup_or_default<int8_t>("knots_mode", ATTR_DOMAIN_CURVE, 0)};
|
||||
|
||||
VArraySpan<int8_t> handle_types_right{
|
||||
src_component.attribute_get_for_read<int8_t>("handle_type_right", ATTR_DOMAIN_POINT, 0)};
|
||||
src_attributes.lookup_or_default<int8_t>("handle_type_right", ATTR_DOMAIN_POINT, 0)};
|
||||
VArraySpan<int8_t> handle_types_left{
|
||||
src_component.attribute_get_for_read<int8_t>("handle_type_left", ATTR_DOMAIN_POINT, 0)};
|
||||
src_attributes.lookup_or_default<int8_t>("handle_type_left", ATTR_DOMAIN_POINT, 0)};
|
||||
|
||||
/* Create splines with the correct size and type. */
|
||||
VArray<int8_t> curve_types = curves.curve_types();
|
||||
@@ -446,9 +444,10 @@ std::unique_ptr<CurveEval> curves_to_curve_eval(const Curves &curves_id)
|
||||
|
||||
CurveComponentLegacy dst_component;
|
||||
dst_component.replace(curve_eval.get(), GeometryOwnershipType::Editable);
|
||||
blender::bke::MutableAttributeAccessor dst_attributes = *dst_component.attributes_for_write();
|
||||
|
||||
copy_attributes_between_components(src_component,
|
||||
dst_component,
|
||||
copy_attributes_between_components(src_attributes,
|
||||
dst_attributes,
|
||||
{"curve_type",
|
||||
"resolution",
|
||||
"normal_mode",
|
||||
@@ -467,37 +466,38 @@ Curves *curve_eval_to_curves(const CurveEval &curve_eval)
|
||||
curve_eval.splines().size());
|
||||
CurveComponent dst_component;
|
||||
dst_component.replace(curves_id, GeometryOwnershipType::Editable);
|
||||
blender::bke::MutableAttributeAccessor dst_attributes = *dst_component.attributes_for_write();
|
||||
|
||||
blender::bke::CurvesGeometry &curves = blender::bke::CurvesGeometry::wrap(curves_id->geometry);
|
||||
curves.offsets_for_write().copy_from(curve_eval.control_point_offsets());
|
||||
MutableSpan<int8_t> curve_types = curves.curve_types_for_write();
|
||||
|
||||
OutputAttribute_Typed<int8_t> normal_mode =
|
||||
dst_component.attribute_try_get_for_output_only<int8_t>("normal_mode", ATTR_DOMAIN_CURVE);
|
||||
OutputAttribute_Typed<float> nurbs_weight;
|
||||
OutputAttribute_Typed<int> nurbs_order;
|
||||
OutputAttribute_Typed<int8_t> nurbs_knots_mode;
|
||||
blender::bke::SpanAttributeWriter<int8_t> normal_mode =
|
||||
dst_attributes.lookup_or_add_for_write_only_span<int8_t>("normal_mode", ATTR_DOMAIN_CURVE);
|
||||
blender::bke::SpanAttributeWriter<float> nurbs_weight;
|
||||
blender::bke::SpanAttributeWriter<int> nurbs_order;
|
||||
blender::bke::SpanAttributeWriter<int8_t> nurbs_knots_mode;
|
||||
if (curve_eval.has_spline_with_type(CURVE_TYPE_NURBS)) {
|
||||
nurbs_weight = dst_component.attribute_try_get_for_output_only<float>("nurbs_weight",
|
||||
ATTR_DOMAIN_POINT);
|
||||
nurbs_order = dst_component.attribute_try_get_for_output_only<int>("nurbs_order",
|
||||
ATTR_DOMAIN_CURVE);
|
||||
nurbs_knots_mode = dst_component.attribute_try_get_for_output_only<int8_t>("knots_mode",
|
||||
ATTR_DOMAIN_CURVE);
|
||||
nurbs_weight = dst_attributes.lookup_or_add_for_write_only_span<float>("nurbs_weight",
|
||||
ATTR_DOMAIN_POINT);
|
||||
nurbs_order = dst_attributes.lookup_or_add_for_write_only_span<int>("nurbs_order",
|
||||
ATTR_DOMAIN_CURVE);
|
||||
nurbs_knots_mode = dst_attributes.lookup_or_add_for_write_only_span<int8_t>("knots_mode",
|
||||
ATTR_DOMAIN_CURVE);
|
||||
}
|
||||
OutputAttribute_Typed<int8_t> handle_type_right;
|
||||
OutputAttribute_Typed<int8_t> handle_type_left;
|
||||
blender::bke::SpanAttributeWriter<int8_t> handle_type_right;
|
||||
blender::bke::SpanAttributeWriter<int8_t> handle_type_left;
|
||||
if (curve_eval.has_spline_with_type(CURVE_TYPE_BEZIER)) {
|
||||
handle_type_right = dst_component.attribute_try_get_for_output_only<int8_t>(
|
||||
handle_type_right = dst_attributes.lookup_or_add_for_write_only_span<int8_t>(
|
||||
"handle_type_right", ATTR_DOMAIN_POINT);
|
||||
handle_type_left = dst_component.attribute_try_get_for_output_only<int8_t>("handle_type_left",
|
||||
ATTR_DOMAIN_POINT);
|
||||
handle_type_left = dst_attributes.lookup_or_add_for_write_only_span<int8_t>("handle_type_left",
|
||||
ATTR_DOMAIN_POINT);
|
||||
}
|
||||
|
||||
for (const int curve_index : curve_eval.splines().index_range()) {
|
||||
const Spline &spline = *curve_eval.splines()[curve_index];
|
||||
curve_types[curve_index] = curve_eval.splines()[curve_index]->type();
|
||||
normal_mode.as_span()[curve_index] = curve_eval.splines()[curve_index]->normal_mode;
|
||||
normal_mode.span[curve_index] = curve_eval.splines()[curve_index]->normal_mode;
|
||||
const IndexRange points = curves.points_for_curve(curve_index);
|
||||
|
||||
switch (spline.type()) {
|
||||
@@ -505,15 +505,15 @@ Curves *curve_eval_to_curves(const CurveEval &curve_eval)
|
||||
break;
|
||||
case CURVE_TYPE_BEZIER: {
|
||||
const BezierSpline &src = static_cast<const BezierSpline &>(spline);
|
||||
handle_type_right.as_span().slice(points).copy_from(src.handle_types_right());
|
||||
handle_type_left.as_span().slice(points).copy_from(src.handle_types_left());
|
||||
handle_type_right.span.slice(points).copy_from(src.handle_types_right());
|
||||
handle_type_left.span.slice(points).copy_from(src.handle_types_left());
|
||||
break;
|
||||
}
|
||||
case CURVE_TYPE_NURBS: {
|
||||
const NURBSpline &src = static_cast<const NURBSpline &>(spline);
|
||||
nurbs_knots_mode.as_span()[curve_index] = static_cast<int8_t>(src.knots_mode);
|
||||
nurbs_order.as_span()[curve_index] = src.order();
|
||||
nurbs_weight.as_span().slice(points).copy_from(src.weights());
|
||||
nurbs_knots_mode.span[curve_index] = static_cast<int8_t>(src.knots_mode);
|
||||
nurbs_order.span[curve_index] = src.order();
|
||||
nurbs_weight.span.slice(points).copy_from(src.weights());
|
||||
break;
|
||||
}
|
||||
case CURVE_TYPE_CATMULL_ROM: {
|
||||
@@ -525,17 +525,18 @@ Curves *curve_eval_to_curves(const CurveEval &curve_eval)
|
||||
|
||||
curves.update_curve_types();
|
||||
|
||||
normal_mode.save();
|
||||
nurbs_weight.save();
|
||||
nurbs_order.save();
|
||||
nurbs_knots_mode.save();
|
||||
handle_type_right.save();
|
||||
handle_type_left.save();
|
||||
normal_mode.finish();
|
||||
nurbs_weight.finish();
|
||||
nurbs_order.finish();
|
||||
nurbs_knots_mode.finish();
|
||||
handle_type_right.finish();
|
||||
handle_type_left.finish();
|
||||
|
||||
CurveComponentLegacy src_component;
|
||||
src_component.replace(&const_cast<CurveEval &>(curve_eval), GeometryOwnershipType::ReadOnly);
|
||||
const blender::bke::AttributeAccessor src_attributes = *src_component.attributes();
|
||||
|
||||
copy_attributes_between_components(src_component, dst_component, {});
|
||||
copy_attributes_between_components(src_attributes, dst_attributes, {});
|
||||
|
||||
return curves_id;
|
||||
}
|
||||
|
||||
@@ -83,8 +83,7 @@ Curves *curve_legacy_to_curves(const Curve &curve_legacy, const ListBase &nurbs_
|
||||
|
||||
Curves *curves_id = curves_new_nomain(0, src_curves.size());
|
||||
CurvesGeometry &curves = CurvesGeometry::wrap(curves_id->geometry);
|
||||
CurveComponent component;
|
||||
component.replace(curves_id, GeometryOwnershipType::Editable);
|
||||
MutableAttributeAccessor curves_attributes = curves.attributes_for_write();
|
||||
|
||||
MutableSpan<int8_t> types = curves.curve_types_for_write();
|
||||
MutableSpan<bool> cyclic = curves.cyclic_for_write();
|
||||
@@ -110,9 +109,9 @@ Curves *curve_legacy_to_curves(const Curve &curve_legacy, const ListBase &nurbs_
|
||||
}
|
||||
|
||||
MutableSpan<float3> positions = curves.positions_for_write();
|
||||
OutputAttribute_Typed<float> radius_attribute =
|
||||
component.attribute_try_get_for_output_only<float>("radius", ATTR_DOMAIN_POINT);
|
||||
MutableSpan<float> radii = radius_attribute.as_span();
|
||||
SpanAttributeWriter<float> radius_attribute =
|
||||
curves_attributes.lookup_or_add_for_write_only_span<float>("radius", ATTR_DOMAIN_POINT);
|
||||
MutableSpan<float> radii = radius_attribute.span;
|
||||
MutableSpan<float> tilts = curves.tilt_for_write();
|
||||
|
||||
auto create_poly = [&](IndexMask selection) {
|
||||
@@ -203,7 +202,7 @@ Curves *curve_legacy_to_curves(const Curve &curve_legacy, const ListBase &nurbs_
|
||||
|
||||
curves.normal_mode_for_write().fill(normal_mode_from_legacy(curve_legacy.twist_mode));
|
||||
|
||||
radius_attribute.save();
|
||||
radius_attribute.finish();
|
||||
|
||||
return curves_id;
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
#include "DNA_mesh_types.h"
|
||||
#include "DNA_meshdata_types.h"
|
||||
|
||||
#include "BKE_attribute_access.hh"
|
||||
#include "BKE_attribute_math.hh"
|
||||
#include "BKE_curves.hh"
|
||||
#include "BKE_geometry_set.hh"
|
||||
@@ -315,15 +314,15 @@ static ResultOffsets calculate_result_offsets(const CurvesInfo &info, const bool
|
||||
return result;
|
||||
}
|
||||
|
||||
static eAttrDomain get_attribute_domain_for_mesh(const MeshComponent &mesh,
|
||||
static eAttrDomain get_attribute_domain_for_mesh(const AttributeAccessor &mesh_attributes,
|
||||
const AttributeIDRef &attribute_id)
|
||||
{
|
||||
/* Only use a different domain if it is builtin and must only exist on one domain. */
|
||||
if (!mesh.attribute_is_builtin(attribute_id)) {
|
||||
if (!mesh_attributes.is_builtin(attribute_id)) {
|
||||
return ATTR_DOMAIN_POINT;
|
||||
}
|
||||
|
||||
std::optional<AttributeMetaData> meta_data = mesh.attribute_get_meta_data(attribute_id);
|
||||
std::optional<AttributeMetaData> meta_data = mesh_attributes.lookup_meta_data(attribute_id);
|
||||
if (!meta_data) {
|
||||
return ATTR_DOMAIN_POINT;
|
||||
}
|
||||
@@ -331,16 +330,17 @@ static eAttrDomain get_attribute_domain_for_mesh(const MeshComponent &mesh,
|
||||
return meta_data->domain;
|
||||
}
|
||||
|
||||
static bool should_add_attribute_to_mesh(const CurveComponent &curve_component,
|
||||
const MeshComponent &mesh_component,
|
||||
static bool should_add_attribute_to_mesh(const AttributeAccessor &curve_attributes,
|
||||
const AttributeAccessor &mesh_attributes,
|
||||
const AttributeIDRef &id)
|
||||
{
|
||||
|
||||
/* The position attribute has special non-generic evaluation. */
|
||||
if (id.is_named() && id.name() == "position") {
|
||||
return false;
|
||||
}
|
||||
/* Don't propagate built-in curves attributes that are not built-in on meshes. */
|
||||
if (curve_component.attribute_is_builtin(id) && !mesh_component.attribute_is_builtin(id)) {
|
||||
if (curve_attributes.is_builtin(id) && !mesh_attributes.is_builtin(id)) {
|
||||
return false;
|
||||
}
|
||||
if (!id.should_be_kept()) {
|
||||
@@ -667,20 +667,13 @@ Mesh *curve_to_mesh_sweep(const CurvesGeometry &main,
|
||||
|
||||
Vector<std::byte> eval_buffer;
|
||||
|
||||
Curves main_id = {{nullptr}};
|
||||
main_id.geometry = reinterpret_cast<const ::CurvesGeometry &>(main);
|
||||
CurveComponent main_component;
|
||||
main_component.replace(&main_id, GeometryOwnershipType::Editable);
|
||||
|
||||
Curves profile_id = {{nullptr}};
|
||||
profile_id.geometry = reinterpret_cast<const ::CurvesGeometry &>(profile);
|
||||
CurveComponent profile_component;
|
||||
profile_component.replace(&profile_id, GeometryOwnershipType::Editable);
|
||||
const AttributeAccessor main_attributes = main.attributes();
|
||||
const AttributeAccessor profile_attributes = profile.attributes();
|
||||
|
||||
Span<float> radii = {};
|
||||
if (main_component.attribute_exists("radius")) {
|
||||
if (main_attributes.contains("radius")) {
|
||||
radii = evaluated_attribute_if_necessary(
|
||||
main_component.attribute_get_for_read<float>("radius", ATTR_DOMAIN_POINT, 1.0f),
|
||||
main_attributes.lookup_or_default<float>("radius", ATTR_DOMAIN_POINT, 1.0f),
|
||||
main,
|
||||
main.curve_type_counts(),
|
||||
eval_buffer)
|
||||
@@ -716,24 +709,23 @@ Mesh *curve_to_mesh_sweep(const CurvesGeometry &main,
|
||||
});
|
||||
}
|
||||
|
||||
Set<AttributeIDRef> main_attributes;
|
||||
Set<AttributeIDRef> main_attributes_set;
|
||||
|
||||
MeshComponent mesh_component;
|
||||
mesh_component.replace(mesh, GeometryOwnershipType::Editable);
|
||||
MutableAttributeAccessor mesh_attributes = bke::mesh_attributes_for_write(*mesh);
|
||||
|
||||
main_component.attribute_foreach([&](const AttributeIDRef &id,
|
||||
const AttributeMetaData meta_data) {
|
||||
if (!should_add_attribute_to_mesh(main_component, mesh_component, id)) {
|
||||
main_attributes.for_all([&](const AttributeIDRef &id, const AttributeMetaData meta_data) {
|
||||
if (!should_add_attribute_to_mesh(main_attributes, mesh_attributes, id)) {
|
||||
return true;
|
||||
}
|
||||
main_attributes.add_new(id);
|
||||
main_attributes_set.add_new(id);
|
||||
|
||||
const eAttrDomain src_domain = meta_data.domain;
|
||||
const eCustomDataType type = meta_data.data_type;
|
||||
GVArray src = main_component.attribute_try_get_for_read(id, src_domain, type);
|
||||
GVArray src = main_attributes.lookup(id, src_domain, type);
|
||||
|
||||
const eAttrDomain dst_domain = get_attribute_domain_for_mesh(mesh_component, id);
|
||||
OutputAttribute dst = mesh_component.attribute_try_get_for_output_only(id, dst_domain, type);
|
||||
const eAttrDomain dst_domain = get_attribute_domain_for_mesh(mesh_attributes, id);
|
||||
GSpanAttributeWriter dst = mesh_attributes.lookup_or_add_for_write_only_span(
|
||||
id, dst_domain, type);
|
||||
if (!dst) {
|
||||
return true;
|
||||
}
|
||||
@@ -744,31 +736,31 @@ Mesh *curve_to_mesh_sweep(const CurvesGeometry &main,
|
||||
offsets,
|
||||
dst_domain,
|
||||
evaluated_attribute_if_necessary(src, main, main.curve_type_counts(), eval_buffer),
|
||||
dst.as_span());
|
||||
dst.span);
|
||||
}
|
||||
else if (src_domain == ATTR_DOMAIN_CURVE) {
|
||||
copy_curve_domain_attribute_to_mesh(
|
||||
offsets, offsets.main_indices, dst_domain, src, dst.as_span());
|
||||
offsets, offsets.main_indices, dst_domain, src, dst.span);
|
||||
}
|
||||
|
||||
dst.save();
|
||||
dst.finish();
|
||||
return true;
|
||||
});
|
||||
|
||||
profile_component.attribute_foreach([&](const AttributeIDRef &id,
|
||||
const AttributeMetaData meta_data) {
|
||||
profile_attributes.for_all([&](const AttributeIDRef &id, const AttributeMetaData meta_data) {
|
||||
if (main_attributes.contains(id)) {
|
||||
return true;
|
||||
}
|
||||
if (!should_add_attribute_to_mesh(profile_component, mesh_component, id)) {
|
||||
if (!should_add_attribute_to_mesh(profile_attributes, mesh_attributes, id)) {
|
||||
return true;
|
||||
}
|
||||
const eAttrDomain src_domain = meta_data.domain;
|
||||
const eCustomDataType type = meta_data.data_type;
|
||||
GVArray src = profile_component.attribute_try_get_for_read(id, src_domain, type);
|
||||
GVArray src = profile_attributes.lookup(id, src_domain, type);
|
||||
|
||||
const eAttrDomain dst_domain = get_attribute_domain_for_mesh(mesh_component, id);
|
||||
OutputAttribute dst = mesh_component.attribute_try_get_for_output_only(id, dst_domain, type);
|
||||
const eAttrDomain dst_domain = get_attribute_domain_for_mesh(mesh_attributes, id);
|
||||
GSpanAttributeWriter dst = mesh_attributes.lookup_or_add_for_write_only_span(
|
||||
id, dst_domain, type);
|
||||
if (!dst) {
|
||||
return true;
|
||||
}
|
||||
@@ -779,14 +771,14 @@ Mesh *curve_to_mesh_sweep(const CurvesGeometry &main,
|
||||
offsets,
|
||||
dst_domain,
|
||||
evaluated_attribute_if_necessary(src, profile, profile.curve_type_counts(), eval_buffer),
|
||||
dst.as_span());
|
||||
dst.span);
|
||||
}
|
||||
else if (src_domain == ATTR_DOMAIN_CURVE) {
|
||||
copy_curve_domain_attribute_to_mesh(
|
||||
offsets, offsets.profile_indices, dst_domain, src, dst.as_span());
|
||||
offsets, offsets.profile_indices, dst_domain, src, dst.span);
|
||||
}
|
||||
|
||||
dst.save();
|
||||
dst.finish();
|
||||
return true;
|
||||
});
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
#include "DNA_ID_enums.h"
|
||||
#include "DNA_curve_types.h"
|
||||
|
||||
#include "BKE_attribute_access.hh"
|
||||
#include "BKE_attribute_math.hh"
|
||||
#include "BKE_curve.h"
|
||||
#include "BKE_geometry_set.hh"
|
||||
@@ -114,24 +113,6 @@ void CurveComponentLegacy::ensure_owns_direct_data()
|
||||
/** \name Attribute Access Helper Functions
|
||||
* \{ */
|
||||
|
||||
int CurveComponentLegacy::attribute_domain_num(const eAttrDomain domain) const
|
||||
{
|
||||
if (curve_ == nullptr) {
|
||||
return 0;
|
||||
}
|
||||
if (domain == ATTR_DOMAIN_POINT) {
|
||||
int total = 0;
|
||||
for (const SplinePtr &spline : curve_->splines()) {
|
||||
total += spline->size();
|
||||
}
|
||||
return total;
|
||||
}
|
||||
if (domain == ATTR_DOMAIN_CURVE) {
|
||||
return curve_->splines().size();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
namespace blender::bke {
|
||||
|
||||
namespace {
|
||||
@@ -308,9 +289,10 @@ static GVArray adapt_curve_domain_spline_to_point(const CurveEval &curve, GVArra
|
||||
|
||||
} // namespace blender::bke
|
||||
|
||||
GVArray CurveComponentLegacy::attribute_try_adapt_domain_impl(const GVArray &varray,
|
||||
const eAttrDomain from_domain,
|
||||
const eAttrDomain to_domain) const
|
||||
static GVArray adapt_curve_attribute_domain(const CurveEval &curve,
|
||||
const GVArray &varray,
|
||||
const eAttrDomain from_domain,
|
||||
const eAttrDomain to_domain)
|
||||
{
|
||||
if (!varray) {
|
||||
return {};
|
||||
@@ -323,30 +305,15 @@ GVArray CurveComponentLegacy::attribute_try_adapt_domain_impl(const GVArray &var
|
||||
}
|
||||
|
||||
if (from_domain == ATTR_DOMAIN_POINT && to_domain == ATTR_DOMAIN_CURVE) {
|
||||
return blender::bke::adapt_curve_domain_point_to_spline(*curve_, std::move(varray));
|
||||
return blender::bke::adapt_curve_domain_point_to_spline(curve, std::move(varray));
|
||||
}
|
||||
if (from_domain == ATTR_DOMAIN_CURVE && to_domain == ATTR_DOMAIN_POINT) {
|
||||
return blender::bke::adapt_curve_domain_spline_to_point(*curve_, std::move(varray));
|
||||
return blender::bke::adapt_curve_domain_spline_to_point(curve, std::move(varray));
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
static CurveEval *get_curve_from_component_for_write(GeometryComponent &component)
|
||||
{
|
||||
BLI_assert(component.type() == GEO_COMPONENT_TYPE_CURVE);
|
||||
CurveComponentLegacy &curve_component = static_cast<CurveComponentLegacy &>(component);
|
||||
return curve_component.get_for_write();
|
||||
}
|
||||
|
||||
static const CurveEval *get_curve_from_component_for_read(const GeometryComponent &component)
|
||||
{
|
||||
BLI_assert(component.type() == GEO_COMPONENT_TYPE_CURVE);
|
||||
const CurveComponentLegacy &curve_component = static_cast<const CurveComponentLegacy &>(
|
||||
component);
|
||||
return curve_component.get_for_read();
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
namespace blender::bke {
|
||||
@@ -380,41 +347,41 @@ class BuiltinSplineAttributeProvider final : public BuiltinAttributeProvider {
|
||||
{
|
||||
}
|
||||
|
||||
GVArray try_get_for_read(const GeometryComponent &component) const final
|
||||
GVArray try_get_for_read(const void *owner) const final
|
||||
{
|
||||
const CurveEval *curve = get_curve_from_component_for_read(component);
|
||||
const CurveEval *curve = static_cast<const CurveEval *>(owner);
|
||||
if (curve == nullptr) {
|
||||
return {};
|
||||
}
|
||||
return as_read_attribute_(*curve);
|
||||
}
|
||||
|
||||
WriteAttributeLookup try_get_for_write(GeometryComponent &component) const final
|
||||
GAttributeWriter try_get_for_write(void *owner) const final
|
||||
{
|
||||
if (writable_ != Writable) {
|
||||
return {};
|
||||
}
|
||||
CurveEval *curve = get_curve_from_component_for_write(component);
|
||||
CurveEval *curve = static_cast<CurveEval *>(owner);
|
||||
if (curve == nullptr) {
|
||||
return {};
|
||||
}
|
||||
return {as_write_attribute_(*curve), domain_};
|
||||
}
|
||||
|
||||
bool try_delete(GeometryComponent &UNUSED(component)) const final
|
||||
bool try_delete(void *UNUSED(owner)) const final
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool try_create(GeometryComponent &UNUSED(component),
|
||||
const AttributeInit &UNUSED(initializer)) const final
|
||||
bool try_create(void *UNUSED(owner), const AttributeInit &UNUSED(initializer)) const final
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool exists(const GeometryComponent &component) const final
|
||||
bool exists(const void *owner) const final
|
||||
{
|
||||
return component.attribute_domain_num(ATTR_DOMAIN_CURVE) != 0;
|
||||
const CurveEval *curve = static_cast<const CurveEval *>(owner);
|
||||
return !curve->splines().is_empty();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -600,12 +567,11 @@ static GVArray varray_from_initializer(const AttributeInit &initializer,
|
||||
return {};
|
||||
}
|
||||
|
||||
static bool create_point_attribute(GeometryComponent &component,
|
||||
static bool create_point_attribute(CurveEval *curve,
|
||||
const AttributeIDRef &attribute_id,
|
||||
const AttributeInit &initializer,
|
||||
const eCustomDataType data_type)
|
||||
{
|
||||
CurveEval *curve = get_curve_from_component_for_write(component);
|
||||
if (curve == nullptr || curve->splines().size() == 0) {
|
||||
return false;
|
||||
}
|
||||
@@ -638,7 +604,7 @@ static bool create_point_attribute(GeometryComponent &component,
|
||||
return true;
|
||||
}
|
||||
|
||||
WriteAttributeLookup write_attribute = component.attribute_try_get_for_write(attribute_id);
|
||||
GAttributeWriter write_attribute = curve->attributes_for_write().lookup_for_write(attribute_id);
|
||||
/* We just created the attribute, it should exist. */
|
||||
BLI_assert(write_attribute);
|
||||
|
||||
@@ -647,6 +613,7 @@ static bool create_point_attribute(GeometryComponent &component,
|
||||
* this theoretically unnecessary materialize step could be removed. */
|
||||
GVArraySpan source_VArraySpan{source_varray};
|
||||
write_attribute.varray.set_all(source_VArraySpan.data());
|
||||
write_attribute.finish();
|
||||
|
||||
if (initializer.type == AttributeInit::Type::MoveArray) {
|
||||
MEM_freeN(static_cast<const AttributeInitMove &>(initializer).data);
|
||||
@@ -655,10 +622,8 @@ static bool create_point_attribute(GeometryComponent &component,
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool remove_point_attribute(GeometryComponent &component,
|
||||
const AttributeIDRef &attribute_id)
|
||||
static bool remove_point_attribute(CurveEval *curve, const AttributeIDRef &attribute_id)
|
||||
{
|
||||
CurveEval *curve = get_curve_from_component_for_write(component);
|
||||
if (curve == nullptr) {
|
||||
return false;
|
||||
}
|
||||
@@ -934,14 +899,14 @@ template<typename T> class BuiltinPointAttributeProvider : public BuiltinAttribu
|
||||
{
|
||||
}
|
||||
|
||||
GVArray try_get_for_read(const GeometryComponent &component) const override
|
||||
GVArray try_get_for_read(const void *owner) const override
|
||||
{
|
||||
const CurveEval *curve = get_curve_from_component_for_read(component);
|
||||
const CurveEval *curve = static_cast<const CurveEval *>(owner);
|
||||
if (curve == nullptr) {
|
||||
return {};
|
||||
}
|
||||
|
||||
if (!this->exists(component)) {
|
||||
if (!this->exists(owner)) {
|
||||
return {};
|
||||
}
|
||||
|
||||
@@ -962,14 +927,14 @@ template<typename T> class BuiltinPointAttributeProvider : public BuiltinAttribu
|
||||
return point_data_varray(spans, offsets);
|
||||
}
|
||||
|
||||
WriteAttributeLookup try_get_for_write(GeometryComponent &component) const override
|
||||
GAttributeWriter try_get_for_write(void *owner) const override
|
||||
{
|
||||
CurveEval *curve = get_curve_from_component_for_write(component);
|
||||
CurveEval *curve = static_cast<CurveEval *>(owner);
|
||||
if (curve == nullptr) {
|
||||
return {};
|
||||
}
|
||||
|
||||
if (!this->exists(component)) {
|
||||
if (!this->exists(owner)) {
|
||||
return {};
|
||||
}
|
||||
|
||||
@@ -998,25 +963,27 @@ template<typename T> class BuiltinPointAttributeProvider : public BuiltinAttribu
|
||||
return {point_data_varray_mutable(spans, offsets), domain_, tag_modified_fn};
|
||||
}
|
||||
|
||||
bool try_delete(GeometryComponent &component) const final
|
||||
bool try_delete(void *owner) const final
|
||||
{
|
||||
if (deletable_ == DeletableEnum::NonDeletable) {
|
||||
return false;
|
||||
}
|
||||
return remove_point_attribute(component, name_);
|
||||
CurveEval *curve = static_cast<CurveEval *>(owner);
|
||||
return remove_point_attribute(curve, name_);
|
||||
}
|
||||
|
||||
bool try_create(GeometryComponent &component, const AttributeInit &initializer) const final
|
||||
bool try_create(void *owner, const AttributeInit &initializer) const final
|
||||
{
|
||||
if (createable_ == CreatableEnum::NonCreatable) {
|
||||
return false;
|
||||
}
|
||||
return create_point_attribute(component, name_, initializer, CD_PROP_INT32);
|
||||
CurveEval *curve = static_cast<CurveEval *>(owner);
|
||||
return create_point_attribute(curve, name_, initializer, CD_PROP_INT32);
|
||||
}
|
||||
|
||||
bool exists(const GeometryComponent &component) const final
|
||||
bool exists(const void *owner) const final
|
||||
{
|
||||
const CurveEval *curve = get_curve_from_component_for_read(component);
|
||||
const CurveEval *curve = static_cast<const CurveEval *>(owner);
|
||||
if (curve == nullptr) {
|
||||
return false;
|
||||
}
|
||||
@@ -1067,9 +1034,9 @@ class PositionAttributeProvider final : public BuiltinPointAttributeProvider<flo
|
||||
{
|
||||
}
|
||||
|
||||
WriteAttributeLookup try_get_for_write(GeometryComponent &component) const final
|
||||
GAttributeWriter try_get_for_write(void *owner) const final
|
||||
{
|
||||
CurveEval *curve = get_curve_from_component_for_write(component);
|
||||
CurveEval *curve = static_cast<CurveEval *>(owner);
|
||||
if (curve == nullptr) {
|
||||
return {};
|
||||
}
|
||||
@@ -1077,7 +1044,7 @@ class PositionAttributeProvider final : public BuiltinPointAttributeProvider<flo
|
||||
/* Use the regular position virtual array when there aren't any Bezier splines
|
||||
* to avoid the overhead of checking the spline type for every point. */
|
||||
if (!curve->has_spline_with_type(CURVE_TYPE_BEZIER)) {
|
||||
return BuiltinPointAttributeProvider<float3>::try_get_for_write(component);
|
||||
return BuiltinPointAttributeProvider<float3>::try_get_for_write(owner);
|
||||
}
|
||||
|
||||
auto tag_modified_fn = [curve]() {
|
||||
@@ -1110,9 +1077,9 @@ class BezierHandleAttributeProvider : public BuiltinAttributeProvider {
|
||||
{
|
||||
}
|
||||
|
||||
GVArray try_get_for_read(const GeometryComponent &component) const override
|
||||
GVArray try_get_for_read(const void *owner) const override
|
||||
{
|
||||
const CurveEval *curve = get_curve_from_component_for_read(component);
|
||||
const CurveEval *curve = static_cast<const CurveEval *>(owner);
|
||||
if (curve == nullptr) {
|
||||
return {};
|
||||
}
|
||||
@@ -1128,9 +1095,9 @@ class BezierHandleAttributeProvider : public BuiltinAttributeProvider {
|
||||
const_cast<CurveEval *>(curve)->splines(), std::move(offsets), is_right_);
|
||||
}
|
||||
|
||||
WriteAttributeLookup try_get_for_write(GeometryComponent &component) const override
|
||||
GAttributeWriter try_get_for_write(void *owner) const override
|
||||
{
|
||||
CurveEval *curve = get_curve_from_component_for_write(component);
|
||||
CurveEval *curve = static_cast<CurveEval *>(owner);
|
||||
if (curve == nullptr) {
|
||||
return {};
|
||||
}
|
||||
@@ -1148,26 +1115,27 @@ class BezierHandleAttributeProvider : public BuiltinAttributeProvider {
|
||||
tag_modified_fn};
|
||||
}
|
||||
|
||||
bool try_delete(GeometryComponent &UNUSED(component)) const final
|
||||
bool try_delete(void *UNUSED(owner)) const final
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool try_create(GeometryComponent &UNUSED(component),
|
||||
const AttributeInit &UNUSED(initializer)) const final
|
||||
bool try_create(void *UNUSED(owner), const AttributeInit &UNUSED(initializer)) const final
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool exists(const GeometryComponent &component) const final
|
||||
bool exists(const void *owner) const final
|
||||
{
|
||||
const CurveEval *curve = get_curve_from_component_for_read(component);
|
||||
const CurveEval *curve = static_cast<const CurveEval *>(owner);
|
||||
if (curve == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return curve->has_spline_with_type(CURVE_TYPE_BEZIER) &&
|
||||
component.attribute_domain_num(ATTR_DOMAIN_POINT) != 0;
|
||||
CurveComponentLegacy component;
|
||||
component.replace(const_cast<CurveEval *>(curve), GeometryOwnershipType::ReadOnly);
|
||||
|
||||
return curve->has_spline_with_type(CURVE_TYPE_BEZIER) && !curve->splines().is_empty();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1190,10 +1158,10 @@ class DynamicPointAttributeProvider final : public DynamicAttributesProvider {
|
||||
CD_MASK_PROP_INT8;
|
||||
|
||||
public:
|
||||
ReadAttributeLookup try_get_for_read(const GeometryComponent &component,
|
||||
const AttributeIDRef &attribute_id) const final
|
||||
GAttributeReader try_get_for_read(const void *owner,
|
||||
const AttributeIDRef &attribute_id) const final
|
||||
{
|
||||
const CurveEval *curve = get_curve_from_component_for_read(component);
|
||||
const CurveEval *curve = static_cast<const CurveEval *>(owner);
|
||||
if (curve == nullptr || curve->splines().size() == 0) {
|
||||
return {};
|
||||
}
|
||||
@@ -1228,7 +1196,7 @@ class DynamicPointAttributeProvider final : public DynamicAttributesProvider {
|
||||
return {GVArray::ForSpan(spans.first()), ATTR_DOMAIN_POINT};
|
||||
}
|
||||
|
||||
ReadAttributeLookup attribute = {};
|
||||
GAttributeReader attribute = {};
|
||||
Array<int> offsets = curve->control_point_offsets();
|
||||
attribute_math::convert_to_static_type(spans[0].type(), [&](auto dummy) {
|
||||
using T = decltype(dummy);
|
||||
@@ -1246,10 +1214,9 @@ class DynamicPointAttributeProvider final : public DynamicAttributesProvider {
|
||||
}
|
||||
|
||||
/* This function is almost the same as #try_get_for_read, but without const. */
|
||||
WriteAttributeLookup try_get_for_write(GeometryComponent &component,
|
||||
const AttributeIDRef &attribute_id) const final
|
||||
GAttributeWriter try_get_for_write(void *owner, const AttributeIDRef &attribute_id) const final
|
||||
{
|
||||
CurveEval *curve = get_curve_from_component_for_write(component);
|
||||
CurveEval *curve = static_cast<CurveEval *>(owner);
|
||||
if (curve == nullptr || curve->splines().size() == 0) {
|
||||
return {};
|
||||
}
|
||||
@@ -1284,7 +1251,7 @@ class DynamicPointAttributeProvider final : public DynamicAttributesProvider {
|
||||
return {GVMutableArray::ForSpan(spans.first()), ATTR_DOMAIN_POINT};
|
||||
}
|
||||
|
||||
WriteAttributeLookup attribute = {};
|
||||
GAttributeWriter attribute = {};
|
||||
Array<int> offsets = curve->control_point_offsets();
|
||||
attribute_math::convert_to_static_type(spans[0].type(), [&](auto dummy) {
|
||||
using T = decltype(dummy);
|
||||
@@ -1298,12 +1265,13 @@ class DynamicPointAttributeProvider final : public DynamicAttributesProvider {
|
||||
return attribute;
|
||||
}
|
||||
|
||||
bool try_delete(GeometryComponent &component, const AttributeIDRef &attribute_id) const final
|
||||
bool try_delete(void *owner, const AttributeIDRef &attribute_id) const final
|
||||
{
|
||||
return remove_point_attribute(component, attribute_id);
|
||||
CurveEval *curve = static_cast<CurveEval *>(owner);
|
||||
return remove_point_attribute(curve, attribute_id);
|
||||
}
|
||||
|
||||
bool try_create(GeometryComponent &component,
|
||||
bool try_create(void *owner,
|
||||
const AttributeIDRef &attribute_id,
|
||||
const eAttrDomain domain,
|
||||
const eCustomDataType data_type,
|
||||
@@ -1313,13 +1281,13 @@ class DynamicPointAttributeProvider final : public DynamicAttributesProvider {
|
||||
if (domain != ATTR_DOMAIN_POINT) {
|
||||
return false;
|
||||
}
|
||||
return create_point_attribute(component, attribute_id, initializer, data_type);
|
||||
CurveEval *curve = static_cast<CurveEval *>(owner);
|
||||
return create_point_attribute(curve, attribute_id, initializer, data_type);
|
||||
}
|
||||
|
||||
bool foreach_attribute(const GeometryComponent &component,
|
||||
const AttributeForeachCallback callback) const final
|
||||
bool foreach_attribute(const void *owner, const AttributeForeachCallback callback) const final
|
||||
{
|
||||
const CurveEval *curve = get_curve_from_component_for_read(component);
|
||||
const CurveEval *curve = static_cast<const CurveEval *>(owner);
|
||||
if (curve == nullptr || curve->splines().size() == 0) {
|
||||
return false;
|
||||
}
|
||||
@@ -1371,14 +1339,18 @@ static ComponentAttributeProviders create_attribute_providers_for_curve()
|
||||
make_cyclic_write_attribute);
|
||||
|
||||
static CustomDataAccessInfo spline_custom_data_access = {
|
||||
[](GeometryComponent &component) -> CustomData * {
|
||||
CurveEval *curve = get_curve_from_component_for_write(component);
|
||||
[](void *owner) -> CustomData * {
|
||||
CurveEval *curve = static_cast<CurveEval *>(owner);
|
||||
return curve ? &curve->attributes.data : nullptr;
|
||||
},
|
||||
[](const GeometryComponent &component) -> const CustomData * {
|
||||
const CurveEval *curve = get_curve_from_component_for_read(component);
|
||||
[](const void *owner) -> const CustomData * {
|
||||
const CurveEval *curve = static_cast<const CurveEval *>(owner);
|
||||
return curve ? &curve->attributes.data : nullptr;
|
||||
},
|
||||
[](const void *owner) -> int {
|
||||
const CurveEval *curve = static_cast<const CurveEval *>(owner);
|
||||
return curve->splines().size();
|
||||
},
|
||||
nullptr};
|
||||
|
||||
static CustomDataAttributeProvider spline_custom_data(ATTR_DOMAIN_CURVE,
|
||||
@@ -1430,12 +1402,62 @@ static ComponentAttributeProviders create_attribute_providers_for_curve()
|
||||
|
||||
/** \} */
|
||||
|
||||
static AttributeAccessorFunctions get_curve_accessor_functions()
|
||||
{
|
||||
static const ComponentAttributeProviders providers = create_attribute_providers_for_curve();
|
||||
AttributeAccessorFunctions fn =
|
||||
attribute_accessor_functions::accessor_functions_for_providers<providers>();
|
||||
fn.domain_size = [](const void *owner, const eAttrDomain domain) -> int {
|
||||
if (owner == nullptr) {
|
||||
return 0;
|
||||
}
|
||||
const CurveEval &curve_eval = *static_cast<const CurveEval *>(owner);
|
||||
switch (domain) {
|
||||
case ATTR_DOMAIN_POINT:
|
||||
return curve_eval.total_control_point_num();
|
||||
case ATTR_DOMAIN_CURVE:
|
||||
return curve_eval.splines().size();
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
fn.domain_supported = [](const void *UNUSED(owner), const eAttrDomain domain) {
|
||||
return ELEM(domain, ATTR_DOMAIN_POINT, ATTR_DOMAIN_CURVE);
|
||||
};
|
||||
fn.adapt_domain = [](const void *owner,
|
||||
const blender::GVArray &varray,
|
||||
const eAttrDomain from_domain,
|
||||
const eAttrDomain to_domain) -> GVArray {
|
||||
if (owner == nullptr) {
|
||||
return {};
|
||||
}
|
||||
const CurveEval &curve_eval = *static_cast<const CurveEval *>(owner);
|
||||
return adapt_curve_attribute_domain(curve_eval, varray, from_domain, to_domain);
|
||||
};
|
||||
return fn;
|
||||
}
|
||||
|
||||
static const AttributeAccessorFunctions &get_curve_accessor_functions_ref()
|
||||
{
|
||||
static const AttributeAccessorFunctions fn = get_curve_accessor_functions();
|
||||
return fn;
|
||||
}
|
||||
|
||||
} // namespace blender::bke
|
||||
|
||||
const blender::bke::ComponentAttributeProviders *CurveComponentLegacy::get_attribute_providers()
|
||||
const
|
||||
std::optional<blender::bke::AttributeAccessor> CurveComponentLegacy::attributes() const
|
||||
{
|
||||
static blender::bke::ComponentAttributeProviders providers =
|
||||
blender::bke::create_attribute_providers_for_curve();
|
||||
return &providers;
|
||||
return blender::bke::AttributeAccessor(curve_, blender::bke::get_curve_accessor_functions_ref());
|
||||
}
|
||||
|
||||
std::optional<blender::bke::MutableAttributeAccessor> CurveComponentLegacy::attributes_for_write()
|
||||
{
|
||||
return blender::bke::MutableAttributeAccessor(curve_,
|
||||
blender::bke::get_curve_accessor_functions_ref());
|
||||
}
|
||||
|
||||
blender::bke::MutableAttributeAccessor CurveEval::attributes_for_write()
|
||||
{
|
||||
return blender::bke::MutableAttributeAccessor(this,
|
||||
blender::bke::get_curve_accessor_functions_ref());
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
#include "DNA_ID_enums.h"
|
||||
#include "DNA_curve_types.h"
|
||||
|
||||
#include "BKE_attribute_access.hh"
|
||||
#include "BKE_attribute_math.hh"
|
||||
#include "BKE_curve.h"
|
||||
#include "BKE_curves.hh"
|
||||
@@ -218,7 +217,7 @@ VArray<float3> curve_normals_varray(const CurveComponent &component, const eAttr
|
||||
|
||||
const VArray<int8_t> types = curves.curve_types();
|
||||
if (curves.is_single_type(CURVE_TYPE_POLY)) {
|
||||
return component.attribute_try_adapt_domain<float3>(
|
||||
return component.attributes()->adapt_domain<float3>(
|
||||
VArray<float3>::ForSpan(curves.evaluated_normals()), ATTR_DOMAIN_POINT, domain);
|
||||
}
|
||||
|
||||
@@ -229,7 +228,7 @@ VArray<float3> curve_normals_varray(const CurveComponent &component, const eAttr
|
||||
}
|
||||
|
||||
if (domain == ATTR_DOMAIN_CURVE) {
|
||||
return component.attribute_try_adapt_domain<float3>(
|
||||
return component.attributes()->adapt_domain<float3>(
|
||||
VArray<float3>::ForContainer(std::move(normals)), ATTR_DOMAIN_POINT, ATTR_DOMAIN_CURVE);
|
||||
}
|
||||
|
||||
@@ -264,7 +263,7 @@ static VArray<float> construct_curve_length_gvarray(const CurveComponent &compon
|
||||
}
|
||||
|
||||
if (domain == ATTR_DOMAIN_POINT) {
|
||||
return component.attribute_try_adapt_domain<float>(
|
||||
return component.attributes()->adapt_domain<float>(
|
||||
std::move(lengths), ATTR_DOMAIN_CURVE, ATTR_DOMAIN_POINT);
|
||||
}
|
||||
|
||||
@@ -307,75 +306,29 @@ bool CurveLengthFieldInput::is_equal_to(const fn::FieldNode &other) const
|
||||
/** \name Attribute Access Helper Functions
|
||||
* \{ */
|
||||
|
||||
int CurveComponent::attribute_domain_num(const eAttrDomain domain) const
|
||||
static void tag_component_topology_changed(void *owner)
|
||||
{
|
||||
if (curves_ == nullptr) {
|
||||
return 0;
|
||||
}
|
||||
const blender::bke::CurvesGeometry &curves = blender::bke::CurvesGeometry::wrap(
|
||||
curves_->geometry);
|
||||
if (domain == ATTR_DOMAIN_POINT) {
|
||||
return curves.points_num();
|
||||
}
|
||||
if (domain == ATTR_DOMAIN_CURVE) {
|
||||
return curves.curves_num();
|
||||
}
|
||||
return 0;
|
||||
blender::bke::CurvesGeometry &curves = *static_cast<blender::bke::CurvesGeometry *>(owner);
|
||||
curves.tag_topology_changed();
|
||||
}
|
||||
|
||||
GVArray CurveComponent::attribute_try_adapt_domain_impl(const GVArray &varray,
|
||||
const eAttrDomain from_domain,
|
||||
const eAttrDomain to_domain) const
|
||||
static void tag_component_curve_types_changed(void *owner)
|
||||
{
|
||||
return blender::bke::CurvesGeometry::wrap(curves_->geometry)
|
||||
.adapt_domain(varray, from_domain, to_domain);
|
||||
blender::bke::CurvesGeometry &curves = *static_cast<blender::bke::CurvesGeometry *>(owner);
|
||||
curves.update_curve_types();
|
||||
curves.tag_topology_changed();
|
||||
}
|
||||
|
||||
static Curves *get_curves_from_component_for_write(GeometryComponent &component)
|
||||
static void tag_component_positions_changed(void *owner)
|
||||
{
|
||||
BLI_assert(component.type() == GEO_COMPONENT_TYPE_CURVE);
|
||||
CurveComponent &curve_component = static_cast<CurveComponent &>(component);
|
||||
return curve_component.get_for_write();
|
||||
blender::bke::CurvesGeometry &curves = *static_cast<blender::bke::CurvesGeometry *>(owner);
|
||||
curves.tag_positions_changed();
|
||||
}
|
||||
|
||||
static const Curves *get_curves_from_component_for_read(const GeometryComponent &component)
|
||||
static void tag_component_normals_changed(void *owner)
|
||||
{
|
||||
BLI_assert(component.type() == GEO_COMPONENT_TYPE_CURVE);
|
||||
const CurveComponent &curve_component = static_cast<const CurveComponent &>(component);
|
||||
return curve_component.get_for_read();
|
||||
}
|
||||
|
||||
static void tag_component_topology_changed(GeometryComponent &component)
|
||||
{
|
||||
Curves *curves = get_curves_from_component_for_write(component);
|
||||
if (curves) {
|
||||
blender::bke::CurvesGeometry::wrap(curves->geometry).tag_topology_changed();
|
||||
}
|
||||
}
|
||||
|
||||
static void tag_component_curve_types_changed(GeometryComponent &component)
|
||||
{
|
||||
Curves *curves = get_curves_from_component_for_write(component);
|
||||
if (curves) {
|
||||
blender::bke::CurvesGeometry::wrap(curves->geometry).update_curve_types();
|
||||
blender::bke::CurvesGeometry::wrap(curves->geometry).tag_topology_changed();
|
||||
}
|
||||
}
|
||||
|
||||
static void tag_component_positions_changed(GeometryComponent &component)
|
||||
{
|
||||
Curves *curves = get_curves_from_component_for_write(component);
|
||||
if (curves) {
|
||||
blender::bke::CurvesGeometry::wrap(curves->geometry).tag_positions_changed();
|
||||
}
|
||||
}
|
||||
|
||||
static void tag_component_normals_changed(GeometryComponent &component)
|
||||
{
|
||||
Curves *curves = get_curves_from_component_for_write(component);
|
||||
if (curves) {
|
||||
blender::bke::CurvesGeometry::wrap(curves->geometry).tag_normals_changed();
|
||||
}
|
||||
blender::bke::CurvesGeometry &curves = *static_cast<blender::bke::CurvesGeometry *>(owner);
|
||||
curves.tag_normals_changed();
|
||||
}
|
||||
|
||||
/** \} */
|
||||
@@ -393,34 +346,38 @@ namespace blender::bke {
|
||||
static ComponentAttributeProviders create_attribute_providers_for_curve()
|
||||
{
|
||||
static CustomDataAccessInfo curve_access = {
|
||||
[](GeometryComponent &component) -> CustomData * {
|
||||
Curves *curves = get_curves_from_component_for_write(component);
|
||||
return curves ? &curves->geometry.curve_data : nullptr;
|
||||
[](void *owner) -> CustomData * {
|
||||
CurvesGeometry &curves = *static_cast<CurvesGeometry *>(owner);
|
||||
return &curves.curve_data;
|
||||
},
|
||||
[](const GeometryComponent &component) -> const CustomData * {
|
||||
const Curves *curves = get_curves_from_component_for_read(component);
|
||||
return curves ? &curves->geometry.curve_data : nullptr;
|
||||
[](const void *owner) -> const CustomData * {
|
||||
const CurvesGeometry &curves = *static_cast<const CurvesGeometry *>(owner);
|
||||
return &curves.curve_data;
|
||||
},
|
||||
[](GeometryComponent &component) {
|
||||
Curves *curves = get_curves_from_component_for_write(component);
|
||||
if (curves) {
|
||||
blender::bke::CurvesGeometry::wrap(curves->geometry).update_customdata_pointers();
|
||||
}
|
||||
[](const void *owner) -> int {
|
||||
const CurvesGeometry &curves = *static_cast<const CurvesGeometry *>(owner);
|
||||
return curves.curves_num();
|
||||
},
|
||||
[](void *owner) {
|
||||
CurvesGeometry &curves = *static_cast<CurvesGeometry *>(owner);
|
||||
curves.update_customdata_pointers();
|
||||
}};
|
||||
static CustomDataAccessInfo point_access = {
|
||||
[](GeometryComponent &component) -> CustomData * {
|
||||
Curves *curves = get_curves_from_component_for_write(component);
|
||||
return curves ? &curves->geometry.point_data : nullptr;
|
||||
[](void *owner) -> CustomData * {
|
||||
CurvesGeometry &curves = *static_cast<CurvesGeometry *>(owner);
|
||||
return &curves.point_data;
|
||||
},
|
||||
[](const GeometryComponent &component) -> const CustomData * {
|
||||
const Curves *curves = get_curves_from_component_for_read(component);
|
||||
return curves ? &curves->geometry.point_data : nullptr;
|
||||
[](const void *owner) -> const CustomData * {
|
||||
const CurvesGeometry &curves = *static_cast<const CurvesGeometry *>(owner);
|
||||
return &curves.point_data;
|
||||
},
|
||||
[](GeometryComponent &component) {
|
||||
Curves *curves = get_curves_from_component_for_write(component);
|
||||
if (curves) {
|
||||
blender::bke::CurvesGeometry::wrap(curves->geometry).update_customdata_pointers();
|
||||
}
|
||||
[](const void *owner) -> int {
|
||||
const CurvesGeometry &curves = *static_cast<const CurvesGeometry *>(owner);
|
||||
return curves.points_num();
|
||||
},
|
||||
[](void *owner) {
|
||||
CurvesGeometry &curves = *static_cast<CurvesGeometry *>(owner);
|
||||
curves.update_customdata_pointers();
|
||||
}};
|
||||
|
||||
static BuiltinCustomDataLayerProvider position("position",
|
||||
@@ -626,11 +583,67 @@ static ComponentAttributeProviders create_attribute_providers_for_curve()
|
||||
|
||||
/** \} */
|
||||
|
||||
static AttributeAccessorFunctions get_curves_accessor_functions()
|
||||
{
|
||||
static const ComponentAttributeProviders providers = create_attribute_providers_for_curve();
|
||||
AttributeAccessorFunctions fn =
|
||||
attribute_accessor_functions::accessor_functions_for_providers<providers>();
|
||||
fn.domain_size = [](const void *owner, const eAttrDomain domain) {
|
||||
if (owner == nullptr) {
|
||||
return 0;
|
||||
}
|
||||
const CurvesGeometry &curves = *static_cast<const CurvesGeometry *>(owner);
|
||||
switch (domain) {
|
||||
case ATTR_DOMAIN_POINT:
|
||||
return curves.points_num();
|
||||
case ATTR_DOMAIN_CURVE:
|
||||
return curves.curves_num();
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
fn.domain_supported = [](const void *UNUSED(owner), const eAttrDomain domain) {
|
||||
return ELEM(domain, ATTR_DOMAIN_POINT, ATTR_DOMAIN_CURVE);
|
||||
};
|
||||
fn.adapt_domain = [](const void *owner,
|
||||
const blender::GVArray &varray,
|
||||
const eAttrDomain from_domain,
|
||||
const eAttrDomain to_domain) -> GVArray {
|
||||
if (owner == nullptr) {
|
||||
return {};
|
||||
}
|
||||
const CurvesGeometry &curves = *static_cast<const CurvesGeometry *>(owner);
|
||||
return curves.adapt_domain(varray, from_domain, to_domain);
|
||||
};
|
||||
return fn;
|
||||
}
|
||||
|
||||
static const AttributeAccessorFunctions &get_curves_accessor_functions_ref()
|
||||
{
|
||||
static const AttributeAccessorFunctions fn = get_curves_accessor_functions();
|
||||
return fn;
|
||||
}
|
||||
|
||||
AttributeAccessor CurvesGeometry::attributes() const
|
||||
{
|
||||
return AttributeAccessor(this, get_curves_accessor_functions_ref());
|
||||
}
|
||||
|
||||
MutableAttributeAccessor CurvesGeometry::attributes_for_write()
|
||||
{
|
||||
return MutableAttributeAccessor(this, get_curves_accessor_functions_ref());
|
||||
}
|
||||
|
||||
} // namespace blender::bke
|
||||
|
||||
const blender::bke::ComponentAttributeProviders *CurveComponent::get_attribute_providers() const
|
||||
std::optional<blender::bke::AttributeAccessor> CurveComponent::attributes() const
|
||||
{
|
||||
static blender::bke::ComponentAttributeProviders providers =
|
||||
blender::bke::create_attribute_providers_for_curve();
|
||||
return &providers;
|
||||
return blender::bke::AttributeAccessor(curves_ ? &curves_->geometry : nullptr,
|
||||
blender::bke::get_curves_accessor_functions_ref());
|
||||
}
|
||||
|
||||
std::optional<blender::bke::MutableAttributeAccessor> CurveComponent::attributes_for_write()
|
||||
{
|
||||
return blender::bke::MutableAttributeAccessor(curves_ ? &curves_->geometry : nullptr,
|
||||
blender::bke::get_curves_accessor_functions_ref());
|
||||
}
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
|
||||
#include "DNA_collection_types.h"
|
||||
|
||||
#include "BKE_attribute_access.hh"
|
||||
#include "BKE_attribute_math.hh"
|
||||
#include "BKE_geometry_set.hh"
|
||||
#include "BKE_geometry_set_instances.hh"
|
||||
@@ -157,7 +156,7 @@ void InstancesComponent::remove_instances(const IndexMask mask)
|
||||
dst_attributes.reallocate(mask.size());
|
||||
|
||||
src_attributes.foreach_attribute(
|
||||
[&](const bke::AttributeIDRef &id, const AttributeMetaData &meta_data) {
|
||||
[&](const bke::AttributeIDRef &id, const bke::AttributeMetaData &meta_data) {
|
||||
if (!id.should_be_kept()) {
|
||||
return true;
|
||||
}
|
||||
@@ -366,20 +365,12 @@ blender::Span<int> InstancesComponent::almost_unique_ids() const
|
||||
return almost_unique_ids_;
|
||||
}
|
||||
|
||||
int InstancesComponent::attribute_domain_num(const eAttrDomain domain) const
|
||||
{
|
||||
if (domain != ATTR_DOMAIN_INSTANCE) {
|
||||
return 0;
|
||||
}
|
||||
return this->instances_num();
|
||||
}
|
||||
|
||||
blender::bke::CustomDataAttributes &InstancesComponent::attributes()
|
||||
blender::bke::CustomDataAttributes &InstancesComponent::instance_attributes()
|
||||
{
|
||||
return this->attributes_;
|
||||
}
|
||||
|
||||
const blender::bke::CustomDataAttributes &InstancesComponent::attributes() const
|
||||
const blender::bke::CustomDataAttributes &InstancesComponent::instance_attributes() const
|
||||
{
|
||||
return this->attributes_;
|
||||
}
|
||||
@@ -404,17 +395,17 @@ class InstancePositionAttributeProvider final : public BuiltinAttributeProvider
|
||||
{
|
||||
}
|
||||
|
||||
GVArray try_get_for_read(const GeometryComponent &component) const final
|
||||
GVArray try_get_for_read(const void *owner) const final
|
||||
{
|
||||
const InstancesComponent &instances_component = static_cast<const InstancesComponent &>(
|
||||
component);
|
||||
const InstancesComponent &instances_component = *static_cast<const InstancesComponent *>(
|
||||
owner);
|
||||
Span<float4x4> transforms = instances_component.instance_transforms();
|
||||
return VArray<float3>::ForDerivedSpan<float4x4, get_transform_position>(transforms);
|
||||
}
|
||||
|
||||
WriteAttributeLookup try_get_for_write(GeometryComponent &component) const final
|
||||
GAttributeWriter try_get_for_write(void *owner) const final
|
||||
{
|
||||
InstancesComponent &instances_component = static_cast<InstancesComponent &>(component);
|
||||
InstancesComponent &instances_component = *static_cast<InstancesComponent *>(owner);
|
||||
MutableSpan<float4x4> transforms = instances_component.instance_transforms();
|
||||
return {VMutableArray<float3>::ForDerivedSpan<float4x4,
|
||||
get_transform_position,
|
||||
@@ -422,18 +413,17 @@ class InstancePositionAttributeProvider final : public BuiltinAttributeProvider
|
||||
domain_};
|
||||
}
|
||||
|
||||
bool try_delete(GeometryComponent &UNUSED(component)) const final
|
||||
bool try_delete(void *UNUSED(owner)) const final
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool try_create(GeometryComponent &UNUSED(component),
|
||||
const AttributeInit &UNUSED(initializer)) const final
|
||||
bool try_create(void *UNUSED(owner), const AttributeInit &UNUSED(initializer)) const final
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool exists(const GeometryComponent &UNUSED(component)) const final
|
||||
bool exists(const void *UNUSED(owner)) const final
|
||||
{
|
||||
return true;
|
||||
}
|
||||
@@ -443,13 +433,17 @@ static ComponentAttributeProviders create_attribute_providers_for_instances()
|
||||
{
|
||||
static InstancePositionAttributeProvider position;
|
||||
static CustomDataAccessInfo instance_custom_data_access = {
|
||||
[](GeometryComponent &component) -> CustomData * {
|
||||
InstancesComponent &inst = static_cast<InstancesComponent &>(component);
|
||||
return &inst.attributes().data;
|
||||
[](void *owner) -> CustomData * {
|
||||
InstancesComponent &inst = *static_cast<InstancesComponent *>(owner);
|
||||
return &inst.instance_attributes().data;
|
||||
},
|
||||
[](const GeometryComponent &component) -> const CustomData * {
|
||||
const InstancesComponent &inst = static_cast<const InstancesComponent &>(component);
|
||||
return &inst.attributes().data;
|
||||
[](const void *owner) -> const CustomData * {
|
||||
const InstancesComponent &inst = *static_cast<const InstancesComponent *>(owner);
|
||||
return &inst.instance_attributes().data;
|
||||
},
|
||||
[](const void *owner) -> int {
|
||||
const InstancesComponent &inst = *static_cast<const InstancesComponent *>(owner);
|
||||
return inst.instances_num();
|
||||
},
|
||||
nullptr};
|
||||
|
||||
@@ -476,14 +470,57 @@ static ComponentAttributeProviders create_attribute_providers_for_instances()
|
||||
|
||||
return ComponentAttributeProviders({&position, &id}, {&instance_custom_data});
|
||||
}
|
||||
|
||||
static AttributeAccessorFunctions get_instances_accessor_functions()
|
||||
{
|
||||
static const ComponentAttributeProviders providers = create_attribute_providers_for_instances();
|
||||
AttributeAccessorFunctions fn =
|
||||
attribute_accessor_functions::accessor_functions_for_providers<providers>();
|
||||
fn.domain_size = [](const void *owner, const eAttrDomain domain) {
|
||||
if (owner == nullptr) {
|
||||
return 0;
|
||||
}
|
||||
const InstancesComponent &instances = *static_cast<const InstancesComponent *>(owner);
|
||||
switch (domain) {
|
||||
case ATTR_DOMAIN_INSTANCE:
|
||||
return instances.instances_num();
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
fn.domain_supported = [](const void *UNUSED(owner), const eAttrDomain domain) {
|
||||
return domain == ATTR_DOMAIN_INSTANCE;
|
||||
};
|
||||
fn.adapt_domain = [](const void *UNUSED(owner),
|
||||
const blender::GVArray &varray,
|
||||
const eAttrDomain from_domain,
|
||||
const eAttrDomain to_domain) {
|
||||
if (from_domain == to_domain && from_domain == ATTR_DOMAIN_INSTANCE) {
|
||||
return varray;
|
||||
}
|
||||
return blender::GVArray{};
|
||||
};
|
||||
return fn;
|
||||
}
|
||||
|
||||
static const AttributeAccessorFunctions &get_instances_accessor_functions_ref()
|
||||
{
|
||||
static const AttributeAccessorFunctions fn = get_instances_accessor_functions();
|
||||
return fn;
|
||||
}
|
||||
|
||||
} // namespace blender::bke
|
||||
|
||||
const blender::bke::ComponentAttributeProviders *InstancesComponent::get_attribute_providers()
|
||||
const
|
||||
std::optional<blender::bke::AttributeAccessor> InstancesComponent::attributes() const
|
||||
{
|
||||
static blender::bke::ComponentAttributeProviders providers =
|
||||
blender::bke::create_attribute_providers_for_instances();
|
||||
return &providers;
|
||||
return blender::bke::AttributeAccessor(this,
|
||||
blender::bke::get_instances_accessor_functions_ref());
|
||||
}
|
||||
|
||||
std::optional<blender::bke::MutableAttributeAccessor> InstancesComponent::attributes_for_write()
|
||||
{
|
||||
return blender::bke::MutableAttributeAccessor(
|
||||
this, blender::bke::get_instances_accessor_functions_ref());
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
#include "DNA_meshdata_types.h"
|
||||
#include "DNA_object_types.h"
|
||||
|
||||
#include "BKE_attribute_access.hh"
|
||||
#include "BKE_attribute_math.hh"
|
||||
#include "BKE_deform.h"
|
||||
#include "BKE_geometry_fields.hh"
|
||||
@@ -151,7 +150,7 @@ VArray<float3> mesh_normals_varray(const MeshComponent &mesh_component,
|
||||
* array and copy the face normal for each of its corners. In this case using the mesh
|
||||
* component's generic domain interpolation is fine, the data will still be normalized,
|
||||
* since the face normal is just copied to every corner. */
|
||||
return mesh_component.attribute_try_adapt_domain(
|
||||
return mesh_component.attributes()->adapt_domain(
|
||||
VArray<float3>::ForSpan({(float3 *)BKE_mesh_poly_normals_ensure(&mesh), mesh.totpoly}),
|
||||
ATTR_DOMAIN_FACE,
|
||||
ATTR_DOMAIN_CORNER);
|
||||
@@ -169,26 +168,6 @@ VArray<float3> mesh_normals_varray(const MeshComponent &mesh_component,
|
||||
/** \name Attribute Access
|
||||
* \{ */
|
||||
|
||||
int MeshComponent::attribute_domain_num(const eAttrDomain domain) const
|
||||
{
|
||||
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_FACE:
|
||||
return mesh_->totpoly;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
namespace blender::bke {
|
||||
|
||||
template<typename T>
|
||||
@@ -747,9 +726,10 @@ static GVArray adapt_mesh_domain_edge_to_face(const Mesh &mesh, const GVArray &v
|
||||
|
||||
} // namespace blender::bke
|
||||
|
||||
blender::GVArray MeshComponent::attribute_try_adapt_domain_impl(const blender::GVArray &varray,
|
||||
const eAttrDomain from_domain,
|
||||
const eAttrDomain to_domain) const
|
||||
static blender::GVArray adapt_mesh_attribute_domain(const Mesh &mesh,
|
||||
const blender::GVArray &varray,
|
||||
const eAttrDomain from_domain,
|
||||
const eAttrDomain to_domain)
|
||||
{
|
||||
if (!varray) {
|
||||
return {};
|
||||
@@ -765,11 +745,11 @@ blender::GVArray MeshComponent::attribute_try_adapt_domain_impl(const blender::G
|
||||
case ATTR_DOMAIN_CORNER: {
|
||||
switch (to_domain) {
|
||||
case ATTR_DOMAIN_POINT:
|
||||
return blender::bke::adapt_mesh_domain_corner_to_point(*mesh_, varray);
|
||||
return blender::bke::adapt_mesh_domain_corner_to_point(mesh, varray);
|
||||
case ATTR_DOMAIN_FACE:
|
||||
return blender::bke::adapt_mesh_domain_corner_to_face(*mesh_, varray);
|
||||
return blender::bke::adapt_mesh_domain_corner_to_face(mesh, varray);
|
||||
case ATTR_DOMAIN_EDGE:
|
||||
return blender::bke::adapt_mesh_domain_corner_to_edge(*mesh_, varray);
|
||||
return blender::bke::adapt_mesh_domain_corner_to_edge(mesh, varray);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -778,11 +758,11 @@ blender::GVArray MeshComponent::attribute_try_adapt_domain_impl(const blender::G
|
||||
case ATTR_DOMAIN_POINT: {
|
||||
switch (to_domain) {
|
||||
case ATTR_DOMAIN_CORNER:
|
||||
return blender::bke::adapt_mesh_domain_point_to_corner(*mesh_, varray);
|
||||
return blender::bke::adapt_mesh_domain_point_to_corner(mesh, varray);
|
||||
case ATTR_DOMAIN_FACE:
|
||||
return blender::bke::adapt_mesh_domain_point_to_face(*mesh_, varray);
|
||||
return blender::bke::adapt_mesh_domain_point_to_face(mesh, varray);
|
||||
case ATTR_DOMAIN_EDGE:
|
||||
return blender::bke::adapt_mesh_domain_point_to_edge(*mesh_, varray);
|
||||
return blender::bke::adapt_mesh_domain_point_to_edge(mesh, varray);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -791,11 +771,11 @@ blender::GVArray MeshComponent::attribute_try_adapt_domain_impl(const blender::G
|
||||
case ATTR_DOMAIN_FACE: {
|
||||
switch (to_domain) {
|
||||
case ATTR_DOMAIN_POINT:
|
||||
return blender::bke::adapt_mesh_domain_face_to_point(*mesh_, varray);
|
||||
return blender::bke::adapt_mesh_domain_face_to_point(mesh, varray);
|
||||
case ATTR_DOMAIN_CORNER:
|
||||
return blender::bke::adapt_mesh_domain_face_to_corner(*mesh_, varray);
|
||||
return blender::bke::adapt_mesh_domain_face_to_corner(mesh, varray);
|
||||
case ATTR_DOMAIN_EDGE:
|
||||
return blender::bke::adapt_mesh_domain_face_to_edge(*mesh_, varray);
|
||||
return blender::bke::adapt_mesh_domain_face_to_edge(mesh, varray);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -804,11 +784,11 @@ blender::GVArray MeshComponent::attribute_try_adapt_domain_impl(const blender::G
|
||||
case ATTR_DOMAIN_EDGE: {
|
||||
switch (to_domain) {
|
||||
case ATTR_DOMAIN_CORNER:
|
||||
return blender::bke::adapt_mesh_domain_edge_to_corner(*mesh_, varray);
|
||||
return blender::bke::adapt_mesh_domain_edge_to_corner(mesh, varray);
|
||||
case ATTR_DOMAIN_POINT:
|
||||
return blender::bke::adapt_mesh_domain_edge_to_point(*mesh_, varray);
|
||||
return blender::bke::adapt_mesh_domain_edge_to_point(mesh, varray);
|
||||
case ATTR_DOMAIN_FACE:
|
||||
return blender::bke::adapt_mesh_domain_edge_to_face(*mesh_, varray);
|
||||
return blender::bke::adapt_mesh_domain_edge_to_face(mesh, varray);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -821,20 +801,6 @@ blender::GVArray MeshComponent::attribute_try_adapt_domain_impl(const blender::G
|
||||
return {};
|
||||
}
|
||||
|
||||
static Mesh *get_mesh_from_component_for_write(GeometryComponent &component)
|
||||
{
|
||||
BLI_assert(component.type() == GEO_COMPONENT_TYPE_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() == GEO_COMPONENT_TYPE_MESH);
|
||||
const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component);
|
||||
return mesh_component.get_for_read();
|
||||
}
|
||||
|
||||
namespace blender::bke {
|
||||
|
||||
template<typename StructT, typename ElemT, ElemT (*GetFunc)(const StructT &)>
|
||||
@@ -864,9 +830,9 @@ static void set_vertex_position(MVert &vert, float3 position)
|
||||
copy_v3_v3(vert.co, position);
|
||||
}
|
||||
|
||||
static void tag_component_positions_changed(GeometryComponent &component)
|
||||
static void tag_component_positions_changed(void *owner)
|
||||
{
|
||||
Mesh *mesh = get_mesh_from_component_for_write(component);
|
||||
Mesh *mesh = static_cast<Mesh *>(owner);
|
||||
if (mesh != nullptr) {
|
||||
BKE_mesh_tag_coords_changed(mesh);
|
||||
}
|
||||
@@ -1001,15 +967,13 @@ class VArrayImpl_For_VertexWeights final : public VMutableArrayImpl<float> {
|
||||
*/
|
||||
class VertexGroupsAttributeProvider final : public DynamicAttributesProvider {
|
||||
public:
|
||||
ReadAttributeLookup try_get_for_read(const GeometryComponent &component,
|
||||
const AttributeIDRef &attribute_id) const final
|
||||
GAttributeReader try_get_for_read(const void *owner,
|
||||
const AttributeIDRef &attribute_id) const final
|
||||
{
|
||||
BLI_assert(component.type() == GEO_COMPONENT_TYPE_MESH);
|
||||
if (!attribute_id.is_named()) {
|
||||
return {};
|
||||
}
|
||||
const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component);
|
||||
const Mesh *mesh = mesh_component.get_for_read();
|
||||
const Mesh *mesh = static_cast<const Mesh *>(owner);
|
||||
if (mesh == nullptr) {
|
||||
return {};
|
||||
}
|
||||
@@ -1028,15 +992,12 @@ class VertexGroupsAttributeProvider final : public DynamicAttributesProvider {
|
||||
ATTR_DOMAIN_POINT};
|
||||
}
|
||||
|
||||
WriteAttributeLookup try_get_for_write(GeometryComponent &component,
|
||||
const AttributeIDRef &attribute_id) const final
|
||||
GAttributeWriter try_get_for_write(void *owner, const AttributeIDRef &attribute_id) const final
|
||||
{
|
||||
BLI_assert(component.type() == GEO_COMPONENT_TYPE_MESH);
|
||||
if (!attribute_id.is_named()) {
|
||||
return {};
|
||||
}
|
||||
MeshComponent &mesh_component = static_cast<MeshComponent &>(component);
|
||||
Mesh *mesh = mesh_component.get_for_write();
|
||||
Mesh *mesh = static_cast<Mesh *>(owner);
|
||||
if (mesh == nullptr) {
|
||||
return {};
|
||||
}
|
||||
@@ -1060,14 +1021,12 @@ class VertexGroupsAttributeProvider final : public DynamicAttributesProvider {
|
||||
ATTR_DOMAIN_POINT};
|
||||
}
|
||||
|
||||
bool try_delete(GeometryComponent &component, const AttributeIDRef &attribute_id) const final
|
||||
bool try_delete(void *owner, const AttributeIDRef &attribute_id) const final
|
||||
{
|
||||
BLI_assert(component.type() == GEO_COMPONENT_TYPE_MESH);
|
||||
if (!attribute_id.is_named()) {
|
||||
return false;
|
||||
}
|
||||
MeshComponent &mesh_component = static_cast<MeshComponent &>(component);
|
||||
Mesh *mesh = mesh_component.get_for_write();
|
||||
Mesh *mesh = static_cast<Mesh *>(owner);
|
||||
if (mesh == nullptr) {
|
||||
return true;
|
||||
}
|
||||
@@ -1101,12 +1060,9 @@ class VertexGroupsAttributeProvider final : public DynamicAttributesProvider {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool foreach_attribute(const GeometryComponent &component,
|
||||
const AttributeForeachCallback callback) const final
|
||||
bool foreach_attribute(const void *owner, const AttributeForeachCallback callback) const final
|
||||
{
|
||||
BLI_assert(component.type() == GEO_COMPONENT_TYPE_MESH);
|
||||
const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component);
|
||||
const Mesh *mesh = mesh_component.get_for_read();
|
||||
const Mesh *mesh = static_cast<const Mesh *>(owner);
|
||||
if (mesh == nullptr) {
|
||||
return true;
|
||||
}
|
||||
@@ -1136,35 +1092,34 @@ class NormalAttributeProvider final : public BuiltinAttributeProvider {
|
||||
{
|
||||
}
|
||||
|
||||
GVArray try_get_for_read(const GeometryComponent &component) const final
|
||||
GVArray try_get_for_read(const void *owner) const final
|
||||
{
|
||||
const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component);
|
||||
const Mesh *mesh = mesh_component.get_for_read();
|
||||
const Mesh *mesh = static_cast<const Mesh *>(owner);
|
||||
if (mesh == nullptr || mesh->totpoly == 0) {
|
||||
return {};
|
||||
}
|
||||
return VArray<float3>::ForSpan({(float3 *)BKE_mesh_poly_normals_ensure(mesh), mesh->totpoly});
|
||||
}
|
||||
|
||||
WriteAttributeLookup try_get_for_write(GeometryComponent &UNUSED(component)) const final
|
||||
GAttributeWriter try_get_for_write(void *UNUSED(owner)) const final
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
bool try_delete(GeometryComponent &UNUSED(component)) const final
|
||||
bool try_delete(void *UNUSED(owner)) const final
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool try_create(GeometryComponent &UNUSED(component),
|
||||
const AttributeInit &UNUSED(initializer)) const final
|
||||
bool try_create(void *UNUSED(owner), const AttributeInit &UNUSED(initializer)) const final
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool exists(const GeometryComponent &component) const final
|
||||
bool exists(const void *owner) const final
|
||||
{
|
||||
return component.attribute_domain_num(ATTR_DOMAIN_FACE) != 0;
|
||||
const Mesh *mesh = static_cast<const Mesh *>(owner);
|
||||
return mesh->totpoly != 0;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1174,34 +1129,42 @@ class NormalAttributeProvider final : public BuiltinAttributeProvider {
|
||||
*/
|
||||
static ComponentAttributeProviders create_attribute_providers_for_mesh()
|
||||
{
|
||||
static auto update_custom_data_pointers = [](GeometryComponent &component) {
|
||||
if (Mesh *mesh = get_mesh_from_component_for_write(component)) {
|
||||
BKE_mesh_update_customdata_pointers(mesh, false);
|
||||
}
|
||||
static auto update_custom_data_pointers = [](void *owner) {
|
||||
Mesh *mesh = static_cast<Mesh *>(owner);
|
||||
BKE_mesh_update_customdata_pointers(mesh, false);
|
||||
};
|
||||
|
||||
#define MAKE_MUTABLE_CUSTOM_DATA_GETTER(NAME) \
|
||||
[](GeometryComponent &component) -> CustomData * { \
|
||||
Mesh *mesh = get_mesh_from_component_for_write(component); \
|
||||
return mesh ? &mesh->NAME : nullptr; \
|
||||
[](void *owner) -> CustomData * { \
|
||||
Mesh *mesh = static_cast<Mesh *>(owner); \
|
||||
return &mesh->NAME; \
|
||||
}
|
||||
#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; \
|
||||
[](const void *owner) -> const CustomData * { \
|
||||
const Mesh *mesh = static_cast<const Mesh *>(owner); \
|
||||
return &mesh->NAME; \
|
||||
}
|
||||
#define MAKE_GET_ELEMENT_NUM_GETTER(NAME) \
|
||||
[](const void *owner) -> int { \
|
||||
const Mesh *mesh = static_cast<const Mesh *>(owner); \
|
||||
return mesh->NAME; \
|
||||
}
|
||||
|
||||
static CustomDataAccessInfo corner_access = {MAKE_MUTABLE_CUSTOM_DATA_GETTER(ldata),
|
||||
MAKE_CONST_CUSTOM_DATA_GETTER(ldata),
|
||||
MAKE_GET_ELEMENT_NUM_GETTER(totloop),
|
||||
update_custom_data_pointers};
|
||||
static CustomDataAccessInfo point_access = {MAKE_MUTABLE_CUSTOM_DATA_GETTER(vdata),
|
||||
MAKE_CONST_CUSTOM_DATA_GETTER(vdata),
|
||||
MAKE_GET_ELEMENT_NUM_GETTER(totvert),
|
||||
update_custom_data_pointers};
|
||||
static CustomDataAccessInfo edge_access = {MAKE_MUTABLE_CUSTOM_DATA_GETTER(edata),
|
||||
MAKE_CONST_CUSTOM_DATA_GETTER(edata),
|
||||
MAKE_GET_ELEMENT_NUM_GETTER(totedge),
|
||||
update_custom_data_pointers};
|
||||
static CustomDataAccessInfo face_access = {MAKE_MUTABLE_CUSTOM_DATA_GETTER(pdata),
|
||||
MAKE_CONST_CUSTOM_DATA_GETTER(pdata),
|
||||
MAKE_GET_ELEMENT_NUM_GETTER(totpoly),
|
||||
update_custom_data_pointers};
|
||||
|
||||
#undef MAKE_CONST_CUSTOM_DATA_GETTER
|
||||
@@ -1297,13 +1260,72 @@ static ComponentAttributeProviders create_attribute_providers_for_mesh()
|
||||
&face_custom_data});
|
||||
}
|
||||
|
||||
static AttributeAccessorFunctions get_mesh_accessor_functions()
|
||||
{
|
||||
static const ComponentAttributeProviders providers = create_attribute_providers_for_mesh();
|
||||
AttributeAccessorFunctions fn =
|
||||
attribute_accessor_functions::accessor_functions_for_providers<providers>();
|
||||
fn.domain_size = [](const void *owner, const eAttrDomain domain) {
|
||||
if (owner == nullptr) {
|
||||
return 0;
|
||||
}
|
||||
const Mesh &mesh = *static_cast<const Mesh *>(owner);
|
||||
switch (domain) {
|
||||
case ATTR_DOMAIN_POINT:
|
||||
return mesh.totvert;
|
||||
case ATTR_DOMAIN_EDGE:
|
||||
return mesh.totedge;
|
||||
case ATTR_DOMAIN_FACE:
|
||||
return mesh.totpoly;
|
||||
case ATTR_DOMAIN_CORNER:
|
||||
return mesh.totloop;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
fn.domain_supported = [](const void *UNUSED(owner), const eAttrDomain domain) {
|
||||
return ELEM(domain, ATTR_DOMAIN_POINT, ATTR_DOMAIN_EDGE, ATTR_DOMAIN_FACE, ATTR_DOMAIN_CORNER);
|
||||
};
|
||||
fn.adapt_domain = [](const void *owner,
|
||||
const blender::GVArray &varray,
|
||||
const eAttrDomain from_domain,
|
||||
const eAttrDomain to_domain) -> blender::GVArray {
|
||||
if (owner == nullptr) {
|
||||
return {};
|
||||
}
|
||||
const Mesh &mesh = *static_cast<const Mesh *>(owner);
|
||||
return adapt_mesh_attribute_domain(mesh, varray, from_domain, to_domain);
|
||||
};
|
||||
return fn;
|
||||
}
|
||||
|
||||
static const AttributeAccessorFunctions &get_mesh_accessor_functions_ref()
|
||||
{
|
||||
static const AttributeAccessorFunctions fn = get_mesh_accessor_functions();
|
||||
return fn;
|
||||
}
|
||||
|
||||
AttributeAccessor mesh_attributes(const Mesh &mesh)
|
||||
{
|
||||
return AttributeAccessor(&mesh, get_mesh_accessor_functions_ref());
|
||||
}
|
||||
|
||||
MutableAttributeAccessor mesh_attributes_for_write(Mesh &mesh)
|
||||
{
|
||||
return MutableAttributeAccessor(&mesh, get_mesh_accessor_functions_ref());
|
||||
}
|
||||
|
||||
} // namespace blender::bke
|
||||
|
||||
const blender::bke::ComponentAttributeProviders *MeshComponent::get_attribute_providers() const
|
||||
std::optional<blender::bke::AttributeAccessor> MeshComponent::attributes() const
|
||||
{
|
||||
static blender::bke::ComponentAttributeProviders providers =
|
||||
blender::bke::create_attribute_providers_for_mesh();
|
||||
return &providers;
|
||||
return blender::bke::AttributeAccessor(mesh_, blender::bke::get_mesh_accessor_functions_ref());
|
||||
}
|
||||
|
||||
std::optional<blender::bke::MutableAttributeAccessor> MeshComponent::attributes_for_write()
|
||||
{
|
||||
return blender::bke::MutableAttributeAccessor(mesh_,
|
||||
blender::bke::get_mesh_accessor_functions_ref());
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
#include "DNA_pointcloud_types.h"
|
||||
|
||||
#include "BKE_attribute_access.hh"
|
||||
#include "BKE_geometry_set.hh"
|
||||
#include "BKE_lib_id.h"
|
||||
#include "BKE_pointcloud.h"
|
||||
@@ -104,17 +103,6 @@ void PointCloudComponent::ensure_owns_direct_data()
|
||||
/** \name Attribute Access
|
||||
* \{ */
|
||||
|
||||
int PointCloudComponent::attribute_domain_num(const eAttrDomain domain) const
|
||||
{
|
||||
if (pointcloud_ == nullptr) {
|
||||
return 0;
|
||||
}
|
||||
if (domain != ATTR_DOMAIN_POINT) {
|
||||
return 0;
|
||||
}
|
||||
return pointcloud_->totpoint;
|
||||
}
|
||||
|
||||
namespace blender::bke {
|
||||
|
||||
/**
|
||||
@@ -123,23 +111,22 @@ namespace blender::bke {
|
||||
*/
|
||||
static ComponentAttributeProviders create_attribute_providers_for_point_cloud()
|
||||
{
|
||||
static auto update_custom_data_pointers = [](GeometryComponent &component) {
|
||||
PointCloudComponent &pointcloud_component = static_cast<PointCloudComponent &>(component);
|
||||
if (PointCloud *pointcloud = pointcloud_component.get_for_write()) {
|
||||
BKE_pointcloud_update_customdata_pointers(pointcloud);
|
||||
}
|
||||
static auto update_custom_data_pointers = [](void *owner) {
|
||||
PointCloud *pointcloud = static_cast<PointCloud *>(owner);
|
||||
BKE_pointcloud_update_customdata_pointers(pointcloud);
|
||||
};
|
||||
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;
|
||||
[](void *owner) -> CustomData * {
|
||||
PointCloud *pointcloud = static_cast<PointCloud *>(owner);
|
||||
return &pointcloud->pdata;
|
||||
},
|
||||
[](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;
|
||||
[](const void *owner) -> const CustomData * {
|
||||
const PointCloud *pointcloud = static_cast<const PointCloud *>(owner);
|
||||
return &pointcloud->pdata;
|
||||
},
|
||||
[](const void *owner) -> int {
|
||||
const PointCloud *pointcloud = static_cast<const PointCloud *>(owner);
|
||||
return pointcloud->totpoint;
|
||||
},
|
||||
update_custom_data_pointers};
|
||||
|
||||
@@ -180,14 +167,67 @@ static ComponentAttributeProviders create_attribute_providers_for_point_cloud()
|
||||
return ComponentAttributeProviders({&position, &radius, &id}, {&point_custom_data});
|
||||
}
|
||||
|
||||
static AttributeAccessorFunctions get_pointcloud_accessor_functions()
|
||||
{
|
||||
static const ComponentAttributeProviders providers =
|
||||
create_attribute_providers_for_point_cloud();
|
||||
AttributeAccessorFunctions fn =
|
||||
attribute_accessor_functions::accessor_functions_for_providers<providers>();
|
||||
fn.domain_size = [](const void *owner, const eAttrDomain domain) {
|
||||
if (owner == nullptr) {
|
||||
return 0;
|
||||
}
|
||||
const PointCloud &pointcloud = *static_cast<const PointCloud *>(owner);
|
||||
switch (domain) {
|
||||
case ATTR_DOMAIN_POINT:
|
||||
return pointcloud.totpoint;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
fn.domain_supported = [](const void *UNUSED(owner), const eAttrDomain domain) {
|
||||
return domain == ATTR_DOMAIN_POINT;
|
||||
};
|
||||
fn.adapt_domain = [](const void *UNUSED(owner),
|
||||
const blender::GVArray &varray,
|
||||
const eAttrDomain from_domain,
|
||||
const eAttrDomain to_domain) {
|
||||
if (from_domain == to_domain && from_domain == ATTR_DOMAIN_POINT) {
|
||||
return varray;
|
||||
}
|
||||
return blender::GVArray{};
|
||||
};
|
||||
return fn;
|
||||
}
|
||||
|
||||
static const AttributeAccessorFunctions &get_pointcloud_accessor_functions_ref()
|
||||
{
|
||||
static const AttributeAccessorFunctions fn = get_pointcloud_accessor_functions();
|
||||
return fn;
|
||||
}
|
||||
|
||||
AttributeAccessor pointcloud_attributes(const PointCloud &pointcloud)
|
||||
{
|
||||
return AttributeAccessor(&pointcloud, get_pointcloud_accessor_functions_ref());
|
||||
}
|
||||
|
||||
MutableAttributeAccessor pointcloud_attributes_for_write(PointCloud &pointcloud)
|
||||
{
|
||||
return MutableAttributeAccessor(&pointcloud, get_pointcloud_accessor_functions_ref());
|
||||
}
|
||||
|
||||
} // namespace blender::bke
|
||||
|
||||
const blender::bke::ComponentAttributeProviders *PointCloudComponent::get_attribute_providers()
|
||||
const
|
||||
std::optional<blender::bke::AttributeAccessor> PointCloudComponent::attributes() const
|
||||
{
|
||||
static blender::bke::ComponentAttributeProviders providers =
|
||||
blender::bke::create_attribute_providers_for_point_cloud();
|
||||
return &providers;
|
||||
return blender::bke::AttributeAccessor(pointcloud_,
|
||||
blender::bke::get_pointcloud_accessor_functions_ref());
|
||||
}
|
||||
|
||||
std::optional<blender::bke::MutableAttributeAccessor> PointCloudComponent::attributes_for_write()
|
||||
{
|
||||
return blender::bke::MutableAttributeAccessor(
|
||||
pointcloud_, blender::bke::get_pointcloud_accessor_functions_ref());
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
#include "BLT_translation.h"
|
||||
|
||||
#include "BKE_attribute.h"
|
||||
#include "BKE_attribute_access.hh"
|
||||
#include "BKE_curves.hh"
|
||||
#include "BKE_geometry_fields.hh"
|
||||
#include "BKE_geometry_set.hh"
|
||||
@@ -59,6 +58,27 @@ GeometryComponent *GeometryComponent::create(GeometryComponentType component_typ
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
int GeometryComponent::attribute_domain_size(const eAttrDomain domain) const
|
||||
{
|
||||
if (this->is_empty()) {
|
||||
return 0;
|
||||
}
|
||||
const std::optional<blender::bke::AttributeAccessor> attributes = this->attributes();
|
||||
if (attributes.has_value()) {
|
||||
return attributes->domain_size(domain);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::optional<blender::bke::AttributeAccessor> GeometryComponent::attributes() const
|
||||
{
|
||||
return std::nullopt;
|
||||
};
|
||||
std::optional<blender::bke::MutableAttributeAccessor> GeometryComponent::attributes_for_write()
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
void GeometryComponent::user_add() const
|
||||
{
|
||||
users_.fetch_add(1);
|
||||
@@ -444,11 +464,14 @@ void GeometrySet::attribute_foreach(const Span<GeometryComponentType> component_
|
||||
continue;
|
||||
}
|
||||
const GeometryComponent &component = *this->get_component_for_read(component_type);
|
||||
component.attribute_foreach(
|
||||
[&](const AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) {
|
||||
callback(attribute_id, meta_data, component);
|
||||
return true;
|
||||
});
|
||||
const std::optional<AttributeAccessor> attributes = component.attributes();
|
||||
if (attributes.has_value()) {
|
||||
attributes->for_all(
|
||||
[&](const AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) {
|
||||
callback(attribute_id, meta_data, component);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
}
|
||||
if (include_instances && this->has_instances()) {
|
||||
const InstancesComponent &instances = *this->get_component_for_read<InstancesComponent>();
|
||||
@@ -462,7 +485,7 @@ void GeometrySet::gather_attributes_for_propagation(
|
||||
const Span<GeometryComponentType> component_types,
|
||||
const GeometryComponentType dst_component_type,
|
||||
bool include_instances,
|
||||
blender::Map<blender::bke::AttributeIDRef, AttributeKind> &r_attributes) const
|
||||
blender::Map<blender::bke::AttributeIDRef, blender::bke::AttributeKind> &r_attributes) const
|
||||
{
|
||||
using namespace blender;
|
||||
using namespace blender::bke;
|
||||
@@ -475,8 +498,8 @@ void GeometrySet::gather_attributes_for_propagation(
|
||||
[&](const AttributeIDRef &attribute_id,
|
||||
const AttributeMetaData &meta_data,
|
||||
const GeometryComponent &component) {
|
||||
if (component.attribute_is_builtin(attribute_id)) {
|
||||
if (!dummy_component->attribute_is_builtin(attribute_id)) {
|
||||
if (component.attributes()->is_builtin(attribute_id)) {
|
||||
if (!dummy_component->attributes()->is_builtin(attribute_id)) {
|
||||
/* Don't propagate built-in attributes that are not built-in on the destination
|
||||
* component. */
|
||||
return;
|
||||
|
||||
@@ -1209,9 +1209,7 @@ Mesh *BKE_mesh_new_from_object_to_bmain(Main *bmain,
|
||||
BKE_mesh_nomain_to_mesh(mesh, mesh_in_bmain, nullptr, &CD_MASK_MESH, true);
|
||||
|
||||
/* Anonymous attributes shouldn't exist on original data. */
|
||||
MeshComponent component;
|
||||
component.replace(mesh_in_bmain, GeometryOwnershipType::Editable);
|
||||
component.attributes_remove_anonymous();
|
||||
blender::bke::mesh_attributes_for_write(*mesh_in_bmain).remove_anonymous();
|
||||
|
||||
/* User-count is required because so far mesh was in a limbo, where library management does
|
||||
* not perform any user management (i.e. copy of a mesh will not increase users of materials). */
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "BKE_attribute_access.hh"
|
||||
#include "BKE_attribute_math.hh"
|
||||
#include "BKE_bvhutils.h"
|
||||
#include "BKE_mesh_runtime.h"
|
||||
@@ -253,12 +252,12 @@ void MeshAttributeInterpolator::sample_data(const GVArray &src,
|
||||
}
|
||||
}
|
||||
|
||||
void MeshAttributeInterpolator::sample_attribute(const ReadAttributeLookup &src_attribute,
|
||||
OutputAttribute &dst_attribute,
|
||||
void MeshAttributeInterpolator::sample_attribute(const GAttributeReader &src_attribute,
|
||||
GSpanAttributeWriter &dst_attribute,
|
||||
eAttributeMapMode mode)
|
||||
{
|
||||
if (src_attribute && dst_attribute) {
|
||||
this->sample_data(src_attribute.varray, src_attribute.domain, mode, dst_attribute.as_span());
|
||||
this->sample_data(src_attribute.varray, src_attribute.domain, mode, dst_attribute.span);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
#include "BLI_task.hh"
|
||||
#include "BLI_timeit.hh"
|
||||
|
||||
#include "BKE_attribute_access.hh"
|
||||
#include "BKE_attribute_math.hh"
|
||||
#include "BKE_spline.hh"
|
||||
|
||||
@@ -21,6 +20,7 @@ using blender::Span;
|
||||
using blender::VArray;
|
||||
using blender::attribute_math::convert_to_static_type;
|
||||
using blender::bke::AttributeIDRef;
|
||||
using blender::bke::AttributeMetaData;
|
||||
|
||||
CurveType Spline::type() const
|
||||
{
|
||||
|
||||
@@ -357,20 +357,20 @@ static void curves_batch_ensure_attribute(const Curves &curves,
|
||||
request.domain == ATTR_DOMAIN_POINT ? curves.geometry.point_num :
|
||||
curves.geometry.curve_num);
|
||||
|
||||
CurveComponent component;
|
||||
component.replace(const_cast<Curves *>(&curves), GeometryOwnershipType::ReadOnly);
|
||||
const blender::bke::AttributeAccessor attributes =
|
||||
blender::bke::CurvesGeometry::wrap(curves.geometry).attributes();
|
||||
|
||||
/* TODO(@kevindietrich): float4 is used for scalar attributes as the implicit conversion done
|
||||
* by OpenGL to vec4 for a scalar `s` will produce a `vec4(s, 0, 0, 1)`. However, following
|
||||
* the Blender convention, it should be `vec4(s, s, s, 1)`. This could be resolved using a
|
||||
* similar texture state swizzle to map the attribute correctly as for volume attributes, so we
|
||||
* can control the conversion ourselves. */
|
||||
blender::VArray<ColorGeometry4f> attribute = component.attribute_get_for_read<ColorGeometry4f>(
|
||||
blender::VArray<ColorGeometry4f> attribute = attributes.lookup_or_default<ColorGeometry4f>(
|
||||
request.attribute_name, request.domain, {0.0f, 0.0f, 0.0f, 1.0f});
|
||||
|
||||
MutableSpan<ColorGeometry4f> vbo_span{
|
||||
static_cast<ColorGeometry4f *>(GPU_vertbuf_get_data(attr_vbo)),
|
||||
component.attribute_domain_num(request.domain)};
|
||||
attributes.domain_size(request.domain)};
|
||||
|
||||
attribute.materialize(vbo_span);
|
||||
|
||||
@@ -629,9 +629,10 @@ static void request_attribute(Curves &curves, const char *name)
|
||||
|
||||
DRW_Attributes attributes{};
|
||||
|
||||
CurveComponent component;
|
||||
component.replace(&curves, GeometryOwnershipType::ReadOnly);
|
||||
std::optional<AttributeMetaData> meta_data = component.attribute_get_meta_data(name);
|
||||
blender::bke::CurvesGeometry &curves_geometry = blender::bke::CurvesGeometry::wrap(
|
||||
curves.geometry);
|
||||
std::optional<blender::bke::AttributeMetaData> meta_data =
|
||||
curves_geometry.attributes().lookup_meta_data(name);
|
||||
if (!meta_data) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -336,9 +336,7 @@ DRWShadingGroup *DRW_shgroup_curves_create_sub(Object *object,
|
||||
const blender::bke::CurvesGeometry &curves = blender::bke::CurvesGeometry::wrap(
|
||||
curves_id.geometry);
|
||||
if (curves.curves_num() >= 1) {
|
||||
CurveComponent curves_component;
|
||||
curves_component.replace(&curves_id, GeometryOwnershipType::ReadOnly);
|
||||
blender::VArray<float> radii = curves_component.attribute_get_for_read(
|
||||
blender::VArray<float> radii = curves.attributes().lookup_or_default(
|
||||
"radius", ATTR_DOMAIN_POINT, 0.005f);
|
||||
const blender::IndexRange first_curve_points = curves.points_for_curve(0);
|
||||
const float first_radius = radii[first_curve_points.first()];
|
||||
|
||||
@@ -533,9 +533,9 @@ static void snap_curves_to_surface_exec_object(Object &curves_ob,
|
||||
|
||||
VArraySpan<float2> surface_uv_map;
|
||||
if (curves_id.surface_uv_map != nullptr) {
|
||||
surface_uv_map = surface_mesh_component
|
||||
.attribute_try_get_for_read(
|
||||
curves_id.surface_uv_map, ATTR_DOMAIN_CORNER, CD_PROP_FLOAT2)
|
||||
const bke::AttributeAccessor surface_attributes = bke::mesh_attributes(surface_mesh);
|
||||
surface_uv_map = surface_attributes
|
||||
.lookup(curves_id.surface_uv_map, ATTR_DOMAIN_CORNER, CD_PROP_FLOAT2)
|
||||
.typed<float2>();
|
||||
}
|
||||
|
||||
@@ -756,21 +756,20 @@ static int curves_set_selection_domain_exec(bContext *C, wmOperator *op)
|
||||
curves_id->selection_domain = domain;
|
||||
curves_id->flag |= CV_SCULPT_SELECTION_ENABLED;
|
||||
|
||||
CurveComponent component;
|
||||
component.replace(curves_id, GeometryOwnershipType::Editable);
|
||||
CurvesGeometry &curves = CurvesGeometry::wrap(curves_id->geometry);
|
||||
bke::MutableAttributeAccessor attributes = curves.attributes_for_write();
|
||||
|
||||
if (old_domain == ATTR_DOMAIN_POINT && domain == ATTR_DOMAIN_CURVE) {
|
||||
VArray<float> curve_selection = curves.adapt_domain(
|
||||
curves.selection_point_float(), ATTR_DOMAIN_POINT, ATTR_DOMAIN_CURVE);
|
||||
curve_selection.materialize(curves.selection_curve_float_for_write());
|
||||
component.attribute_try_delete(".selection_point_float");
|
||||
attributes.remove(".selection_point_float");
|
||||
}
|
||||
else if (old_domain == ATTR_DOMAIN_CURVE && domain == ATTR_DOMAIN_POINT) {
|
||||
VArray<float> point_selection = curves.adapt_domain(
|
||||
curves.selection_curve_float(), ATTR_DOMAIN_CURVE, ATTR_DOMAIN_POINT);
|
||||
point_selection.materialize(curves.selection_point_float_for_write());
|
||||
component.attribute_try_delete(".selection_curve_float");
|
||||
attributes.remove(".selection_curve_float");
|
||||
}
|
||||
|
||||
/* Use #ID_RECALC_GEOMETRY instead of #ID_RECALC_SELECT because it is handled as a generic
|
||||
@@ -900,15 +899,14 @@ static int select_all_exec(bContext *C, wmOperator *op)
|
||||
}
|
||||
|
||||
for (Curves *curves_id : unique_curves) {
|
||||
CurvesGeometry &curves = CurvesGeometry::wrap(curves_id->geometry);
|
||||
if (action == SEL_SELECT) {
|
||||
/* As an optimization, just remove the selection attributes when everything is selected. */
|
||||
CurveComponent component;
|
||||
component.replace(curves_id, GeometryOwnershipType::Editable);
|
||||
component.attribute_try_delete(".selection_point_float");
|
||||
component.attribute_try_delete(".selection_curve_float");
|
||||
bke::MutableAttributeAccessor attributes = curves.attributes_for_write();
|
||||
attributes.remove(".selection_point_float");
|
||||
attributes.remove(".selection_curve_float");
|
||||
}
|
||||
else {
|
||||
CurvesGeometry &curves = CurvesGeometry::wrap(curves_id->geometry);
|
||||
MutableSpan<float> selection = curves_id->selection_domain == ATTR_DOMAIN_POINT ?
|
||||
curves.selection_point_float_for_write() :
|
||||
curves.selection_curve_float_for_write();
|
||||
|
||||
@@ -282,8 +282,7 @@ static int geometry_attribute_convert_exec(bContext *C, wmOperator *op)
|
||||
RNA_enum_get(op->ptr, "mode"));
|
||||
|
||||
Mesh *mesh = reinterpret_cast<Mesh *>(ob_data);
|
||||
MeshComponent mesh_component;
|
||||
mesh_component.replace(mesh, GeometryOwnershipType::Editable);
|
||||
bke::MutableAttributeAccessor attributes = bke::mesh_attributes_for_write(*mesh);
|
||||
|
||||
/* General conversion steps are always the same:
|
||||
* 1. Convert old data to right domain and data type.
|
||||
@@ -301,33 +300,33 @@ static int geometry_attribute_convert_exec(bContext *C, wmOperator *op)
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
GVArray src_varray = mesh_component.attribute_get_for_read(name, dst_domain, dst_type);
|
||||
GVArray src_varray = attributes.lookup_or_default(name, dst_domain, dst_type);
|
||||
const CPPType &cpp_type = src_varray.type();
|
||||
void *new_data = MEM_malloc_arrayN(src_varray.size(), cpp_type.size(), __func__);
|
||||
src_varray.materialize_to_uninitialized(new_data);
|
||||
mesh_component.attribute_try_delete(name);
|
||||
mesh_component.attribute_try_create(name, dst_domain, dst_type, AttributeInitMove(new_data));
|
||||
attributes.remove(name);
|
||||
attributes.add(name, dst_domain, dst_type, blender::bke::AttributeInitMove(new_data));
|
||||
break;
|
||||
}
|
||||
case ConvertAttributeMode::UVMap: {
|
||||
MLoopUV *dst_uvs = static_cast<MLoopUV *>(
|
||||
MEM_calloc_arrayN(mesh->totloop, sizeof(MLoopUV), __func__));
|
||||
VArray<float2> src_varray = mesh_component.attribute_get_for_read<float2>(
|
||||
VArray<float2> src_varray = attributes.lookup_or_default<float2>(
|
||||
name, ATTR_DOMAIN_CORNER, {0.0f, 0.0f});
|
||||
for (const int i : IndexRange(mesh->totloop)) {
|
||||
copy_v2_v2(dst_uvs[i].uv, src_varray[i]);
|
||||
}
|
||||
mesh_component.attribute_try_delete(name);
|
||||
attributes.remove(name);
|
||||
CustomData_add_layer_named(
|
||||
&mesh->ldata, CD_MLOOPUV, CD_ASSIGN, dst_uvs, mesh->totloop, name.c_str());
|
||||
break;
|
||||
}
|
||||
case ConvertAttributeMode::VertexGroup: {
|
||||
Array<float> src_weights(mesh->totvert);
|
||||
VArray<float> src_varray = mesh_component.attribute_get_for_read<float>(
|
||||
VArray<float> src_varray = attributes.lookup_or_default<float>(
|
||||
name, ATTR_DOMAIN_POINT, 0.0f);
|
||||
src_varray.materialize(src_weights);
|
||||
mesh_component.attribute_try_delete(name);
|
||||
attributes.remove(name);
|
||||
|
||||
bDeformGroup *defgroup = BKE_object_defgroup_new(ob, name.c_str());
|
||||
const int defgroup_index = BLI_findindex(BKE_id_defgroup_list_get(&mesh->id), defgroup);
|
||||
@@ -652,15 +651,16 @@ bool ED_geometry_attribute_convert(Mesh *mesh,
|
||||
return false;
|
||||
}
|
||||
|
||||
MeshComponent mesh_component;
|
||||
mesh_component.replace(mesh, GeometryOwnershipType::Editable);
|
||||
GVArray src_varray = mesh_component.attribute_get_for_read(name, new_domain, new_type);
|
||||
blender::bke::MutableAttributeAccessor attributes = blender::bke::mesh_attributes_for_write(
|
||||
*mesh);
|
||||
|
||||
GVArray src_varray = attributes.lookup_or_default(name, new_domain, new_type);
|
||||
|
||||
const CPPType &cpp_type = src_varray.type();
|
||||
void *new_data = MEM_malloc_arrayN(src_varray.size(), cpp_type.size(), __func__);
|
||||
src_varray.materialize_to_uninitialized(new_data);
|
||||
mesh_component.attribute_try_delete(name);
|
||||
mesh_component.attribute_try_create(name, new_domain, new_type, AttributeInitMove(new_data));
|
||||
attributes.remove(name);
|
||||
attributes.add(name, new_domain, new_type, blender::bke::AttributeInitMove(new_data));
|
||||
|
||||
int *active_index = BKE_id_attributes_active_index_p(&mesh->id);
|
||||
if (*active_index > 0) {
|
||||
|
||||
@@ -3177,9 +3177,7 @@ static int object_convert_exec(bContext *C, wmOperator *op)
|
||||
}
|
||||
|
||||
/* Anonymous attributes shouldn't be available on the applied geometry. */
|
||||
MeshComponent component;
|
||||
component.replace(new_mesh, GeometryOwnershipType::Editable);
|
||||
component.attributes_remove_anonymous();
|
||||
blender::bke::mesh_attributes_for_write(*new_mesh).remove_anonymous();
|
||||
|
||||
BKE_object_free_modifiers(newob, 0); /* after derivedmesh calls! */
|
||||
}
|
||||
|
||||
@@ -757,9 +757,7 @@ static bool modifier_apply_obdata(
|
||||
BKE_mesh_nomain_to_mesh(mesh_applied, me, ob, &CD_MASK_MESH, true);
|
||||
|
||||
/* Anonymous attributes shouldn't be available on the applied geometry. */
|
||||
MeshComponent component;
|
||||
component.replace(me, GeometryOwnershipType::Editable);
|
||||
component.attributes_remove_anonymous();
|
||||
blender::bke::mesh_attributes_for_write(*me).remove_anonymous();
|
||||
|
||||
if (md_eval->type == eModifierType_Multires) {
|
||||
multires_customdata_delete(me);
|
||||
@@ -828,11 +826,12 @@ static bool modifier_apply_obdata(
|
||||
BKE_report(reports, RPT_ERROR, "Evaluated geometry from modifier does not contain curves");
|
||||
return false;
|
||||
}
|
||||
CurveComponent &component = geometry_set.get_component_for_write<CurveComponent>();
|
||||
Curves &curves_eval = *geometry_set.get_curves_for_write();
|
||||
|
||||
/* Anonymous attributes shouldn't be available on the applied geometry. */
|
||||
component.attributes_remove_anonymous();
|
||||
blender::bke::CurvesGeometry::wrap(curves_eval.geometry)
|
||||
.attributes_for_write()
|
||||
.remove_anonymous();
|
||||
|
||||
/* If the modifier's output is a different curves data-block, copy the relevant information to
|
||||
* the original. */
|
||||
|
||||
@@ -176,12 +176,9 @@ struct AddOperationExecutor {
|
||||
/* Find UV map. */
|
||||
VArraySpan<float2> surface_uv_map;
|
||||
if (curves_id_->surface_uv_map != nullptr) {
|
||||
MeshComponent surface_component;
|
||||
surface_component.replace(surface_, GeometryOwnershipType::ReadOnly);
|
||||
surface_uv_map = surface_component
|
||||
.attribute_try_get_for_read(curves_id_->surface_uv_map,
|
||||
ATTR_DOMAIN_CORNER)
|
||||
.typed<float2>();
|
||||
const bke::AttributeAccessor surface_attributes = bke::mesh_attributes(*surface_);
|
||||
surface_uv_map = surface_attributes.lookup<float2>(curves_id_->surface_uv_map,
|
||||
ATTR_DOMAIN_CORNER);
|
||||
}
|
||||
|
||||
/* Find normals. */
|
||||
|
||||
@@ -64,7 +64,6 @@ struct DensityAddOperationExecutor {
|
||||
Mesh *surface_ = nullptr;
|
||||
Span<MLoopTri> surface_looptris_;
|
||||
Span<float3> corner_normals_su_;
|
||||
VArraySpan<float2> surface_uv_map_;
|
||||
|
||||
const CurvesSculpt *curves_sculpt_ = nullptr;
|
||||
const Brush *brush_ = nullptr;
|
||||
@@ -228,12 +227,9 @@ struct DensityAddOperationExecutor {
|
||||
/* Find UV map. */
|
||||
VArraySpan<float2> surface_uv_map;
|
||||
if (curves_id_->surface_uv_map != nullptr) {
|
||||
MeshComponent surface_component;
|
||||
surface_component.replace(surface_, GeometryOwnershipType::ReadOnly);
|
||||
surface_uv_map = surface_component
|
||||
.attribute_try_get_for_read(curves_id_->surface_uv_map,
|
||||
ATTR_DOMAIN_CORNER)
|
||||
.typed<float2>();
|
||||
bke::AttributeAccessor surface_attributes = bke::mesh_attributes(*surface_);
|
||||
surface_uv_map = surface_attributes.lookup<float2>(curves_id_->surface_uv_map,
|
||||
ATTR_DOMAIN_CORNER);
|
||||
}
|
||||
|
||||
/* Find normals. */
|
||||
|
||||
@@ -148,12 +148,9 @@ struct SlideOperationExecutor {
|
||||
BKE_mesh_runtime_looptri_len(surface_)};
|
||||
|
||||
if (curves_id_->surface_uv_map != nullptr) {
|
||||
MeshComponent surface_component;
|
||||
surface_component.replace(surface_, GeometryOwnershipType::ReadOnly);
|
||||
surface_uv_map_ = surface_component
|
||||
.attribute_try_get_for_read(curves_id_->surface_uv_map,
|
||||
ATTR_DOMAIN_CORNER)
|
||||
.typed<float2>();
|
||||
const bke::AttributeAccessor surface_attributes = bke::mesh_attributes(*surface_);
|
||||
surface_uv_map_ = surface_attributes.lookup<float2>(curves_id_->surface_uv_map,
|
||||
ATTR_DOMAIN_CORNER);
|
||||
}
|
||||
|
||||
if (stroke_extension.is_first) {
|
||||
|
||||
@@ -92,11 +92,9 @@ static bool vertex_paint_from_weight(Object *ob)
|
||||
return false;
|
||||
}
|
||||
|
||||
MeshComponent component;
|
||||
component.replace(me, GeometryOwnershipType::Editable);
|
||||
bke::MutableAttributeAccessor attributes = bke::mesh_attributes_for_write(*me);
|
||||
|
||||
bke::WriteAttributeLookup color_attribute = component.attribute_try_get_for_write(
|
||||
active_color_layer->name);
|
||||
bke::GAttributeWriter color_attribute = attributes.lookup_for_write(active_color_layer->name);
|
||||
if (!color_attribute) {
|
||||
BLI_assert_unreachable();
|
||||
return false;
|
||||
@@ -104,7 +102,7 @@ static bool vertex_paint_from_weight(Object *ob)
|
||||
|
||||
/* Retrieve the vertex group with the domain and type of the existing color
|
||||
* attribute, in order to let the attribute API handle both conversions. */
|
||||
const GVArray vertex_group = component.attribute_get_for_read(
|
||||
const GVArray vertex_group = attributes.lookup(
|
||||
deform_group->name,
|
||||
ATTR_DOMAIN_POINT,
|
||||
bke::cpp_type_to_custom_data_type(color_attribute.varray.type()));
|
||||
@@ -113,14 +111,11 @@ static bool vertex_paint_from_weight(Object *ob)
|
||||
return false;
|
||||
}
|
||||
|
||||
GVArraySpan interpolated{component.attribute_try_adapt_domain(
|
||||
vertex_group, ATTR_DOMAIN_POINT, color_attribute.domain)};
|
||||
GVArraySpan interpolated{
|
||||
attributes.adapt_domain(vertex_group, ATTR_DOMAIN_POINT, color_attribute.domain)};
|
||||
|
||||
color_attribute.varray.set_all(interpolated.data());
|
||||
|
||||
if (color_attribute.tag_modified_fn) {
|
||||
color_attribute.tag_modified_fn();
|
||||
}
|
||||
color_attribute.finish();
|
||||
tag_object_after_update(ob);
|
||||
|
||||
return true;
|
||||
@@ -167,29 +162,28 @@ static IndexMask get_selected_indices(const Mesh &mesh,
|
||||
Span<MVert> verts(mesh.mvert, mesh.totvert);
|
||||
Span<MPoly> faces(mesh.mpoly, mesh.totpoly);
|
||||
|
||||
MeshComponent component;
|
||||
component.replace(&const_cast<Mesh &>(mesh), GeometryOwnershipType::ReadOnly);
|
||||
bke::AttributeAccessor attributes = bke::mesh_attributes(mesh);
|
||||
|
||||
if (mesh.editflag & ME_EDIT_PAINT_FACE_SEL) {
|
||||
const VArray<bool> selection = component.attribute_try_adapt_domain(
|
||||
const VArray<bool> selection = attributes.adapt_domain(
|
||||
VArray<bool>::ForFunc(faces.size(),
|
||||
[&](const int i) { return faces[i].flag & ME_FACE_SEL; }),
|
||||
ATTR_DOMAIN_FACE,
|
||||
domain);
|
||||
|
||||
return index_mask_ops::find_indices_from_virtual_array(
|
||||
IndexMask(component.attribute_domain_num(domain)), selection, 4096, indices);
|
||||
IndexMask(attributes.domain_size(domain)), selection, 4096, indices);
|
||||
}
|
||||
if (mesh.editflag & ME_EDIT_PAINT_VERT_SEL) {
|
||||
const VArray<bool> selection = component.attribute_try_adapt_domain(
|
||||
const VArray<bool> selection = attributes.adapt_domain(
|
||||
VArray<bool>::ForFunc(verts.size(), [&](const int i) { return verts[i].flag & SELECT; }),
|
||||
ATTR_DOMAIN_POINT,
|
||||
domain);
|
||||
|
||||
return index_mask_ops::find_indices_from_virtual_array(
|
||||
IndexMask(component.attribute_domain_num(domain)), selection, 4096, indices);
|
||||
IndexMask(attributes.domain_size(domain)), selection, 4096, indices);
|
||||
}
|
||||
return IndexMask(component.attribute_domain_num(domain));
|
||||
return IndexMask(attributes.domain_size(domain));
|
||||
}
|
||||
|
||||
static void face_corner_color_equalize_vertices(Mesh &mesh, const IndexMask selection)
|
||||
@@ -202,17 +196,15 @@ static void face_corner_color_equalize_vertices(Mesh &mesh, const IndexMask sele
|
||||
return;
|
||||
}
|
||||
|
||||
MeshComponent component;
|
||||
component.replace(&mesh, GeometryOwnershipType::Editable);
|
||||
bke::AttributeAccessor attributes = bke::mesh_attributes(mesh);
|
||||
|
||||
if (component.attribute_get_meta_data(active_color_layer->name)->domain == ATTR_DOMAIN_POINT) {
|
||||
if (attributes.lookup_meta_data(active_color_layer->name)->domain == ATTR_DOMAIN_POINT) {
|
||||
return;
|
||||
}
|
||||
|
||||
GVArray color_attribute_point = component.attribute_try_get_for_read(active_color_layer->name,
|
||||
ATTR_DOMAIN_POINT);
|
||||
GVArray color_attribute_point = attributes.lookup(active_color_layer->name, ATTR_DOMAIN_POINT);
|
||||
|
||||
GVArray color_attribute_corner = component.attribute_try_adapt_domain(
|
||||
GVArray color_attribute_corner = attributes.adapt_domain(
|
||||
color_attribute_point, ATTR_DOMAIN_POINT, ATTR_DOMAIN_CORNER);
|
||||
|
||||
color_attribute_corner.materialize(selection, active_color_layer->data);
|
||||
@@ -278,11 +270,9 @@ static bool transform_active_color(Mesh &mesh, const TransformFn &transform_fn)
|
||||
return false;
|
||||
}
|
||||
|
||||
MeshComponent component;
|
||||
component.replace(&mesh, GeometryOwnershipType::Editable);
|
||||
bke::MutableAttributeAccessor attributes = bke::mesh_attributes_for_write(mesh);
|
||||
|
||||
bke::WriteAttributeLookup color_attribute = component.attribute_try_get_for_write(
|
||||
active_color_layer->name);
|
||||
bke::GAttributeWriter color_attribute = attributes.lookup_for_write(active_color_layer->name);
|
||||
if (!color_attribute) {
|
||||
BLI_assert_unreachable();
|
||||
return false;
|
||||
@@ -310,6 +300,8 @@ static bool transform_active_color(Mesh &mesh, const TransformFn &transform_fn)
|
||||
});
|
||||
});
|
||||
|
||||
color_attribute.finish();
|
||||
|
||||
DEG_id_tag_update(&mesh.id, 0);
|
||||
|
||||
return true;
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include "BLI_index_mask_ops.hh"
|
||||
#include "BLI_virtual_array.hh"
|
||||
|
||||
#include "BKE_attribute.hh"
|
||||
#include "BKE_context.h"
|
||||
#include "BKE_editmesh.h"
|
||||
#include "BKE_geometry_fields.hh"
|
||||
@@ -65,7 +66,12 @@ std::unique_ptr<ColumnValues> ExtraColumns::get_column_values(
|
||||
void GeometryDataSource::foreach_default_column_ids(
|
||||
FunctionRef<void(const SpreadsheetColumnID &, bool is_extra)> fn) const
|
||||
{
|
||||
if (component_->attribute_domain_num(domain_) == 0) {
|
||||
if (!component_->attributes().has_value()) {
|
||||
return;
|
||||
}
|
||||
const bke::AttributeAccessor attributes = *component_->attributes();
|
||||
|
||||
if (attributes.domain_size(domain_) == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -74,8 +80,9 @@ void GeometryDataSource::foreach_default_column_ids(
|
||||
}
|
||||
|
||||
extra_columns_.foreach_default_column_ids(fn);
|
||||
component_->attribute_foreach(
|
||||
[&](const bke::AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) {
|
||||
|
||||
attributes.for_all(
|
||||
[&](const bke::AttributeIDRef &attribute_id, const bke::AttributeMetaData &meta_data) {
|
||||
if (meta_data.domain != domain_) {
|
||||
return true;
|
||||
}
|
||||
@@ -114,7 +121,11 @@ void GeometryDataSource::foreach_default_column_ids(
|
||||
std::unique_ptr<ColumnValues> GeometryDataSource::get_column_values(
|
||||
const SpreadsheetColumnID &column_id) const
|
||||
{
|
||||
const int domain_num = component_->attribute_domain_num(domain_);
|
||||
if (!component_->attributes().has_value()) {
|
||||
return {};
|
||||
}
|
||||
const bke::AttributeAccessor attributes = *component_->attributes();
|
||||
const int domain_num = attributes.domain_size(domain_);
|
||||
if (domain_num == 0) {
|
||||
return {};
|
||||
}
|
||||
@@ -200,7 +211,7 @@ std::unique_ptr<ColumnValues> GeometryDataSource::get_column_values(
|
||||
}
|
||||
}
|
||||
|
||||
bke::ReadAttributeLookup attribute = component_->attribute_try_get_for_read(column_id.name);
|
||||
bke::GAttributeReader attribute = attributes.lookup(column_id.name);
|
||||
if (!attribute) {
|
||||
return {};
|
||||
}
|
||||
@@ -214,7 +225,11 @@ std::unique_ptr<ColumnValues> GeometryDataSource::get_column_values(
|
||||
|
||||
int GeometryDataSource::tot_rows() const
|
||||
{
|
||||
return component_->attribute_domain_num(domain_);
|
||||
if (!component_->attributes().has_value()) {
|
||||
return {};
|
||||
}
|
||||
const bke::AttributeAccessor attributes = *component_->attributes();
|
||||
return attributes.domain_size(domain_);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -253,7 +268,7 @@ IndexMask GeometryDataSource::apply_selection_filter(Vector<int64_t> &indices) c
|
||||
const int *orig_indices = (int *)CustomData_get_layer(&mesh_eval->vdata, CD_ORIGINDEX);
|
||||
if (orig_indices != nullptr) {
|
||||
/* Use CD_ORIGINDEX layer if it exists. */
|
||||
VArray<bool> selection = mesh_component->attribute_try_adapt_domain<bool>(
|
||||
VArray<bool> selection = mesh_component->attributes()->adapt_domain<bool>(
|
||||
VArray<bool>::ForFunc(mesh_eval->totvert,
|
||||
[bm, orig_indices](int vertex_index) -> bool {
|
||||
const int i_orig = orig_indices[vertex_index];
|
||||
@@ -273,7 +288,7 @@ IndexMask GeometryDataSource::apply_selection_filter(Vector<int64_t> &indices) c
|
||||
|
||||
if (mesh_eval->totvert == bm->totvert) {
|
||||
/* Use a simple heuristic to match original vertices to evaluated ones. */
|
||||
VArray<bool> selection = mesh_component->attribute_try_adapt_domain<bool>(
|
||||
VArray<bool> selection = mesh_component->attributes()->adapt_domain<bool>(
|
||||
VArray<bool>::ForFunc(mesh_eval->totvert,
|
||||
[bm](int vertex_index) -> bool {
|
||||
BMVert *vert = bm->vtable[vertex_index];
|
||||
@@ -511,7 +526,7 @@ static void add_fields_as_extra_columns(SpaceSpreadsheet *sspreadsheet,
|
||||
std::make_unique<GeometryComponentCacheKey>(component));
|
||||
|
||||
const eAttrDomain domain = (eAttrDomain)sspreadsheet->attribute_domain;
|
||||
const int domain_num = component.attribute_domain_num(domain);
|
||||
const int domain_num = component.attributes()->domain_size(domain);
|
||||
for (const auto item : fields_to_show.items()) {
|
||||
const StringRef name = item.key;
|
||||
const GField &field = item.value;
|
||||
|
||||
@@ -194,7 +194,7 @@ std::optional<int> GeometryDataSetTreeViewItem::count() const
|
||||
}
|
||||
|
||||
if (const GeometryComponent *component = geometry.get_component_for_read(component_type_)) {
|
||||
return component->attribute_domain_num(*domain_);
|
||||
return component->attribute_domain_size(*domain_);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
#include "DNA_mesh_types.h"
|
||||
#include "DNA_meshdata_types.h"
|
||||
|
||||
#include "BKE_attribute_access.hh"
|
||||
#include "BKE_geometry_set.hh"
|
||||
#include "BKE_mesh.h"
|
||||
|
||||
@@ -322,11 +321,10 @@ static void calculate_polys(const CuboidConfig &config,
|
||||
|
||||
static void calculate_uvs(const CuboidConfig &config, Mesh *mesh, const bke::AttributeIDRef &uv_id)
|
||||
{
|
||||
MeshComponent mesh_component;
|
||||
mesh_component.replace(mesh, GeometryOwnershipType::Editable);
|
||||
bke::OutputAttribute_Typed<float2> uv_attribute =
|
||||
mesh_component.attribute_try_get_for_output_only<float2>(uv_id, ATTR_DOMAIN_CORNER);
|
||||
MutableSpan<float2> uvs = uv_attribute.as_span();
|
||||
bke::MutableAttributeAccessor attributes = bke::mesh_attributes_for_write(*mesh);
|
||||
bke::SpanAttributeWriter<float2> uv_attribute =
|
||||
attributes.lookup_or_add_for_write_only_span<float2>(uv_id, ATTR_DOMAIN_CORNER);
|
||||
MutableSpan<float2> uvs = uv_attribute.span;
|
||||
|
||||
int loop_index = 0;
|
||||
|
||||
@@ -394,7 +392,7 @@ static void calculate_uvs(const CuboidConfig &config, Mesh *mesh, const bke::Att
|
||||
}
|
||||
}
|
||||
|
||||
uv_attribute.save();
|
||||
uv_attribute.finish();
|
||||
}
|
||||
|
||||
Mesh *create_cuboid_mesh(const float3 &size,
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
#include "DNA_mesh_types.h"
|
||||
#include "DNA_meshdata_types.h"
|
||||
|
||||
#include "BKE_attribute_access.hh"
|
||||
#include "BKE_attribute.hh"
|
||||
#include "BKE_attribute_math.hh"
|
||||
#include "BKE_curves.hh"
|
||||
#include "BKE_geometry_set.hh"
|
||||
@@ -44,14 +44,13 @@ static Curves *create_curve_from_vert_indices(const MeshComponent &mesh_componen
|
||||
curves.cyclic_for_write().fill(false);
|
||||
curves.cyclic_for_write().slice(cyclic_curves).fill(true);
|
||||
|
||||
Set<bke::AttributeIDRef> source_attribute_ids = mesh_component.attribute_ids();
|
||||
bke::MutableAttributeAccessor curves_attributes = curves.attributes_for_write();
|
||||
const bke::AttributeAccessor mesh_attributes = *mesh_component.attributes();
|
||||
|
||||
CurveComponent curves_component;
|
||||
curves_component.replace(curves_id, GeometryOwnershipType::Editable);
|
||||
Set<bke::AttributeIDRef> source_attribute_ids = mesh_attributes.all_ids();
|
||||
|
||||
for (const bke::AttributeIDRef &attribute_id : source_attribute_ids) {
|
||||
if (mesh_component.attribute_is_builtin(attribute_id) &&
|
||||
!curves_component.attribute_is_builtin(attribute_id)) {
|
||||
if (mesh_attributes.is_builtin(attribute_id) && !curves_attributes.is_builtin(attribute_id)) {
|
||||
/* Don't copy attributes that are built-in on meshes but not on curves. */
|
||||
continue;
|
||||
}
|
||||
@@ -60,8 +59,7 @@ static Curves *create_curve_from_vert_indices(const MeshComponent &mesh_componen
|
||||
continue;
|
||||
}
|
||||
|
||||
const GVArray mesh_attribute = mesh_component.attribute_try_get_for_read(attribute_id,
|
||||
ATTR_DOMAIN_POINT);
|
||||
const GVArray mesh_attribute = mesh_attributes.lookup(attribute_id, ATTR_DOMAIN_POINT);
|
||||
/* Some attributes might not exist if they were builtin attribute on domains that don't
|
||||
* have any elements, i.e. a face attribute on the output of the line primitive node. */
|
||||
if (!mesh_attribute) {
|
||||
@@ -71,10 +69,10 @@ static Curves *create_curve_from_vert_indices(const MeshComponent &mesh_componen
|
||||
/* Copy attribute based on the map for this curve. */
|
||||
attribute_math::convert_to_static_type(mesh_attribute.type(), [&](auto dummy) {
|
||||
using T = decltype(dummy);
|
||||
bke::OutputAttribute_Typed<T> attribute =
|
||||
curves_component.attribute_try_get_for_output_only<T>(attribute_id, ATTR_DOMAIN_POINT);
|
||||
copy_with_map<T>(mesh_attribute.typed<T>(), vert_indices, attribute.as_span());
|
||||
attribute.save();
|
||||
bke::SpanAttributeWriter<T> attribute =
|
||||
curves_attributes.lookup_or_add_for_write_only_span<T>(attribute_id, ATTR_DOMAIN_POINT);
|
||||
copy_with_map<T>(mesh_attribute.typed<T>(), vert_indices, attribute.span);
|
||||
attribute.finish();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -104,24 +104,24 @@ PointCloud *point_merge_by_distance(const PointCloudComponent &src_points,
|
||||
point_merge_counts[dst_index]++;
|
||||
}
|
||||
|
||||
Set<bke::AttributeIDRef> attributes = src_points.attribute_ids();
|
||||
const bke::AttributeAccessor src_attributes = *src_points.attributes();
|
||||
bke::MutableAttributeAccessor dst_attributes = *dst_points.attributes_for_write();
|
||||
Set<bke::AttributeIDRef> attributes = src_attributes.all_ids();
|
||||
|
||||
/* Transfer the ID attribute if it exists, using the ID of the first merged point. */
|
||||
if (attributes.contains("id")) {
|
||||
VArray<int> src = src_points.attribute_get_for_read<int>("id", ATTR_DOMAIN_POINT, 0);
|
||||
bke::OutputAttribute_Typed<int> dst = dst_points.attribute_try_get_for_output_only<int>(
|
||||
VArraySpan<int> src = src_attributes.lookup_or_default<int>("id", ATTR_DOMAIN_POINT, 0);
|
||||
bke::SpanAttributeWriter<int> dst = dst_attributes.lookup_or_add_for_write_only_span<int>(
|
||||
"id", ATTR_DOMAIN_POINT);
|
||||
Span<int> src_ids = src.get_internal_span();
|
||||
MutableSpan<int> dst_ids = dst.as_span();
|
||||
|
||||
threading::parallel_for(IndexRange(dst_size), 1024, [&](IndexRange range) {
|
||||
for (const int i_dst : range) {
|
||||
const IndexRange points(map_offsets[i_dst], map_offsets[i_dst + 1] - map_offsets[i_dst]);
|
||||
dst_ids[i_dst] = src_ids[points.first()];
|
||||
dst.span[i_dst] = src[points.first()];
|
||||
}
|
||||
});
|
||||
|
||||
dst.save();
|
||||
dst.finish();
|
||||
attributes.remove_contained("id");
|
||||
}
|
||||
|
||||
@@ -131,20 +131,19 @@ PointCloud *point_merge_by_distance(const PointCloudComponent &src_points,
|
||||
continue;
|
||||
}
|
||||
|
||||
bke::ReadAttributeLookup src_attribute = src_points.attribute_try_get_for_read(id);
|
||||
bke::GAttributeReader src_attribute = src_attributes.lookup(id);
|
||||
attribute_math::convert_to_static_type(src_attribute.varray.type(), [&](auto dummy) {
|
||||
using T = decltype(dummy);
|
||||
if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) {
|
||||
bke::OutputAttribute_Typed<T> dst_attribute =
|
||||
dst_points.attribute_try_get_for_output_only<T>(id, ATTR_DOMAIN_POINT);
|
||||
Span<T> src = src_attribute.varray.get_internal_span().typed<T>();
|
||||
MutableSpan<T> dst = dst_attribute.as_span();
|
||||
bke::SpanAttributeWriter<T> dst_attribute =
|
||||
dst_attributes.lookup_or_add_for_write_only_span<T>(id, ATTR_DOMAIN_POINT);
|
||||
VArraySpan<T> src = src_attribute.varray.typed<T>();
|
||||
|
||||
threading::parallel_for(IndexRange(dst_size), 1024, [&](IndexRange range) {
|
||||
for (const int i_dst : range) {
|
||||
/* Create a separate mixer for every point to avoid allocating temporary buffers
|
||||
* in the mixer the size of the result point cloud and to improve memory locality. */
|
||||
attribute_math::DefaultMixer<T> mixer{dst.slice(i_dst, 1)};
|
||||
attribute_math::DefaultMixer<T> mixer{dst_attribute.span.slice(i_dst, 1)};
|
||||
|
||||
const IndexRange points(map_offsets[i_dst],
|
||||
map_offsets[i_dst + 1] - map_offsets[i_dst]);
|
||||
@@ -157,7 +156,7 @@ PointCloud *point_merge_by_distance(const PointCloudComponent &src_points,
|
||||
}
|
||||
});
|
||||
|
||||
dst_attribute.save();
|
||||
dst_attribute.finish();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -23,12 +23,13 @@
|
||||
namespace blender::geometry {
|
||||
|
||||
using blender::bke::AttributeIDRef;
|
||||
using blender::bke::AttributeKind;
|
||||
using blender::bke::AttributeMetaData;
|
||||
using blender::bke::custom_data_type_to_cpp_type;
|
||||
using blender::bke::CustomDataAttributes;
|
||||
using blender::bke::GSpanAttributeWriter;
|
||||
using blender::bke::object_get_evaluated_geometry_set;
|
||||
using blender::bke::OutputAttribute;
|
||||
using blender::bke::OutputAttribute_Typed;
|
||||
using blender::bke::ReadAttributeLookup;
|
||||
using blender::bke::SpanAttributeWriter;
|
||||
|
||||
/**
|
||||
* An ordered set of attribute ids. Attributes are ordered to avoid name lookups in many places.
|
||||
@@ -287,26 +288,27 @@ static void copy_generic_attributes_to_result(
|
||||
const AttributeFallbacksArray &attribute_fallbacks,
|
||||
const OrderedAttributes &ordered_attributes,
|
||||
const FunctionRef<IndexRange(eAttrDomain)> &range_fn,
|
||||
MutableSpan<GMutableSpan> dst_attributes)
|
||||
MutableSpan<GSpanAttributeWriter> dst_attribute_writers)
|
||||
{
|
||||
threading::parallel_for(dst_attributes.index_range(), 10, [&](const IndexRange attribute_range) {
|
||||
for (const int attribute_index : attribute_range) {
|
||||
const eAttrDomain domain = ordered_attributes.kinds[attribute_index].domain;
|
||||
const IndexRange element_slice = range_fn(domain);
|
||||
threading::parallel_for(
|
||||
dst_attribute_writers.index_range(), 10, [&](const IndexRange attribute_range) {
|
||||
for (const int attribute_index : attribute_range) {
|
||||
const eAttrDomain domain = ordered_attributes.kinds[attribute_index].domain;
|
||||
const IndexRange element_slice = range_fn(domain);
|
||||
|
||||
GMutableSpan dst_span = dst_attributes[attribute_index].slice(element_slice);
|
||||
if (src_attributes[attribute_index].has_value()) {
|
||||
threaded_copy(*src_attributes[attribute_index], dst_span);
|
||||
}
|
||||
else {
|
||||
const CPPType &cpp_type = dst_span.type();
|
||||
const void *fallback = attribute_fallbacks.array[attribute_index] == nullptr ?
|
||||
cpp_type.default_value() :
|
||||
attribute_fallbacks.array[attribute_index];
|
||||
threaded_fill({cpp_type, fallback}, dst_span);
|
||||
}
|
||||
}
|
||||
});
|
||||
GMutableSpan dst_span = dst_attribute_writers[attribute_index].span.slice(element_slice);
|
||||
if (src_attributes[attribute_index].has_value()) {
|
||||
threaded_copy(*src_attributes[attribute_index], dst_span);
|
||||
}
|
||||
else {
|
||||
const CPPType &cpp_type = dst_span.type();
|
||||
const void *fallback = attribute_fallbacks.array[attribute_index] == nullptr ?
|
||||
cpp_type.default_value() :
|
||||
attribute_fallbacks.array[attribute_index];
|
||||
threaded_fill({cpp_type, fallback}, dst_span);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
static void create_result_ids(const RealizeInstancesOptions &options,
|
||||
@@ -361,7 +363,7 @@ static Vector<std::pair<int, GSpan>> prepare_attribute_fallbacks(
|
||||
const OrderedAttributes &ordered_attributes)
|
||||
{
|
||||
Vector<std::pair<int, GSpan>> attributes_to_override;
|
||||
const CustomDataAttributes &attributes = instances_component.attributes();
|
||||
const CustomDataAttributes &attributes = instances_component.instance_attributes();
|
||||
attributes.foreach_attribute(
|
||||
[&](const AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) {
|
||||
const int attribute_index = ordered_attributes.ids.index_of_try(attribute_id);
|
||||
@@ -447,7 +449,7 @@ static void gather_realize_tasks_for_instances(GatherTasksInfo &gather_info,
|
||||
|
||||
Span<int> stored_instance_ids;
|
||||
if (gather_info.create_id_attribute_on_any_component) {
|
||||
std::optional<GSpan> ids = instances_component.attributes().get_for_read("id");
|
||||
std::optional<GSpan> ids = instances_component.instance_attributes().get_for_read("id");
|
||||
if (ids.has_value()) {
|
||||
stored_instance_ids = ids->typed<int>();
|
||||
}
|
||||
@@ -646,34 +648,34 @@ static AllPointCloudsInfo preprocess_pointclouds(const GeometrySet &geometry_set
|
||||
pointcloud_info.pointcloud = pointcloud;
|
||||
|
||||
/* Access attributes. */
|
||||
PointCloudComponent component;
|
||||
component.replace(const_cast<PointCloud *>(pointcloud), GeometryOwnershipType::ReadOnly);
|
||||
bke::AttributeAccessor attributes = bke::pointcloud_attributes(*pointcloud);
|
||||
pointcloud_info.attributes.reinitialize(info.attributes.size());
|
||||
for (const int attribute_index : info.attributes.index_range()) {
|
||||
const AttributeIDRef &attribute_id = info.attributes.ids[attribute_index];
|
||||
const eCustomDataType data_type = info.attributes.kinds[attribute_index].data_type;
|
||||
const eAttrDomain domain = info.attributes.kinds[attribute_index].domain;
|
||||
if (component.attribute_exists(attribute_id)) {
|
||||
GVArray attribute = component.attribute_get_for_read(attribute_id, domain, data_type);
|
||||
if (attributes.contains(attribute_id)) {
|
||||
GVArray attribute = attributes.lookup_or_default(attribute_id, domain, data_type);
|
||||
pointcloud_info.attributes[attribute_index].emplace(std::move(attribute));
|
||||
}
|
||||
}
|
||||
if (info.create_id_attribute) {
|
||||
ReadAttributeLookup ids_lookup = component.attribute_try_get_for_read("id");
|
||||
if (ids_lookup) {
|
||||
pointcloud_info.stored_ids = ids_lookup.varray.get_internal_span().typed<int>();
|
||||
bke::GAttributeReader ids_attribute = attributes.lookup("id");
|
||||
if (ids_attribute) {
|
||||
pointcloud_info.stored_ids = ids_attribute.varray.get_internal_span().typed<int>();
|
||||
}
|
||||
}
|
||||
}
|
||||
return info;
|
||||
}
|
||||
|
||||
static void execute_realize_pointcloud_task(const RealizeInstancesOptions &options,
|
||||
const RealizePointCloudTask &task,
|
||||
const OrderedAttributes &ordered_attributes,
|
||||
PointCloud &dst_pointcloud,
|
||||
MutableSpan<GMutableSpan> dst_attribute_spans,
|
||||
MutableSpan<int> all_dst_ids)
|
||||
static void execute_realize_pointcloud_task(
|
||||
const RealizeInstancesOptions &options,
|
||||
const RealizePointCloudTask &task,
|
||||
const OrderedAttributes &ordered_attributes,
|
||||
PointCloud &dst_pointcloud,
|
||||
MutableSpan<GSpanAttributeWriter> dst_attribute_writers,
|
||||
MutableSpan<int> all_dst_ids)
|
||||
{
|
||||
const PointCloudRealizeInfo &pointcloud_info = *task.pointcloud_info;
|
||||
const PointCloud &pointcloud = *pointcloud_info.pointcloud;
|
||||
@@ -699,7 +701,7 @@ static void execute_realize_pointcloud_task(const RealizeInstancesOptions &optio
|
||||
UNUSED_VARS_NDEBUG(domain);
|
||||
return point_slice;
|
||||
},
|
||||
dst_attribute_spans);
|
||||
dst_attribute_writers);
|
||||
}
|
||||
|
||||
static void execute_realize_pointcloud_tasks(const RealizeInstancesOptions &options,
|
||||
@@ -721,42 +723,43 @@ static void execute_realize_pointcloud_tasks(const RealizeInstancesOptions &opti
|
||||
PointCloudComponent &dst_component =
|
||||
r_realized_geometry.get_component_for_write<PointCloudComponent>();
|
||||
dst_component.replace(dst_pointcloud);
|
||||
bke::MutableAttributeAccessor dst_attributes = bke::pointcloud_attributes_for_write(
|
||||
*dst_pointcloud);
|
||||
|
||||
/* Prepare id attribute. */
|
||||
OutputAttribute_Typed<int> point_ids;
|
||||
MutableSpan<int> point_ids_span;
|
||||
SpanAttributeWriter<int> point_ids;
|
||||
if (all_pointclouds_info.create_id_attribute) {
|
||||
point_ids = dst_component.attribute_try_get_for_output_only<int>("id", ATTR_DOMAIN_POINT);
|
||||
point_ids_span = point_ids.as_span();
|
||||
point_ids = dst_attributes.lookup_or_add_for_write_only_span<int>("id", ATTR_DOMAIN_POINT);
|
||||
}
|
||||
|
||||
/* Prepare generic output attributes. */
|
||||
Vector<OutputAttribute> dst_attributes;
|
||||
Vector<GMutableSpan> dst_attribute_spans;
|
||||
Vector<GSpanAttributeWriter> dst_attribute_writers;
|
||||
for (const int attribute_index : ordered_attributes.index_range()) {
|
||||
const AttributeIDRef &attribute_id = ordered_attributes.ids[attribute_index];
|
||||
const eCustomDataType data_type = ordered_attributes.kinds[attribute_index].data_type;
|
||||
OutputAttribute dst_attribute = dst_component.attribute_try_get_for_output_only(
|
||||
attribute_id, ATTR_DOMAIN_POINT, data_type);
|
||||
dst_attribute_spans.append(dst_attribute.as_span());
|
||||
dst_attributes.append(std::move(dst_attribute));
|
||||
dst_attribute_writers.append(dst_attributes.lookup_or_add_for_write_only_span(
|
||||
attribute_id, ATTR_DOMAIN_POINT, data_type));
|
||||
}
|
||||
|
||||
/* Actually execute all tasks. */
|
||||
threading::parallel_for(tasks.index_range(), 100, [&](const IndexRange task_range) {
|
||||
for (const int task_index : task_range) {
|
||||
const RealizePointCloudTask &task = tasks[task_index];
|
||||
execute_realize_pointcloud_task(
|
||||
options, task, ordered_attributes, *dst_pointcloud, dst_attribute_spans, point_ids_span);
|
||||
execute_realize_pointcloud_task(options,
|
||||
task,
|
||||
ordered_attributes,
|
||||
*dst_pointcloud,
|
||||
dst_attribute_writers,
|
||||
point_ids.span);
|
||||
}
|
||||
});
|
||||
|
||||
/* Save modified attributes. */
|
||||
for (OutputAttribute &dst_attribute : dst_attributes) {
|
||||
dst_attribute.save();
|
||||
/* Tag modified attributes. */
|
||||
for (GSpanAttributeWriter &dst_attribute : dst_attribute_writers) {
|
||||
dst_attribute.finish();
|
||||
}
|
||||
if (point_ids) {
|
||||
point_ids.save();
|
||||
point_ids.finish();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -837,22 +840,21 @@ static AllMeshesInfo preprocess_meshes(const GeometrySet &geometry_set,
|
||||
}
|
||||
|
||||
/* Access attributes. */
|
||||
MeshComponent component;
|
||||
component.replace(const_cast<Mesh *>(mesh), GeometryOwnershipType::ReadOnly);
|
||||
bke::AttributeAccessor attributes = bke::mesh_attributes(*mesh);
|
||||
mesh_info.attributes.reinitialize(info.attributes.size());
|
||||
for (const int attribute_index : info.attributes.index_range()) {
|
||||
const AttributeIDRef &attribute_id = info.attributes.ids[attribute_index];
|
||||
const eCustomDataType data_type = info.attributes.kinds[attribute_index].data_type;
|
||||
const eAttrDomain domain = info.attributes.kinds[attribute_index].domain;
|
||||
if (component.attribute_exists(attribute_id)) {
|
||||
GVArray attribute = component.attribute_get_for_read(attribute_id, domain, data_type);
|
||||
if (attributes.contains(attribute_id)) {
|
||||
GVArray attribute = attributes.lookup_or_default(attribute_id, domain, data_type);
|
||||
mesh_info.attributes[attribute_index].emplace(std::move(attribute));
|
||||
}
|
||||
}
|
||||
if (info.create_id_attribute) {
|
||||
ReadAttributeLookup ids_lookup = component.attribute_try_get_for_read("id");
|
||||
if (ids_lookup) {
|
||||
mesh_info.stored_vertex_ids = ids_lookup.varray.get_internal_span().typed<int>();
|
||||
bke::GAttributeReader ids_attribute = attributes.lookup("id");
|
||||
if (ids_attribute) {
|
||||
mesh_info.stored_vertex_ids = ids_attribute.varray.get_internal_span().typed<int>();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -863,7 +865,7 @@ static void execute_realize_mesh_task(const RealizeInstancesOptions &options,
|
||||
const RealizeMeshTask &task,
|
||||
const OrderedAttributes &ordered_attributes,
|
||||
Mesh &dst_mesh,
|
||||
MutableSpan<GMutableSpan> dst_attribute_spans,
|
||||
MutableSpan<GSpanAttributeWriter> dst_attribute_writers,
|
||||
MutableSpan<int> all_dst_vertex_ids)
|
||||
{
|
||||
const MeshRealizeInfo &mesh_info = *task.mesh_info;
|
||||
@@ -949,7 +951,7 @@ static void execute_realize_mesh_task(const RealizeInstancesOptions &options,
|
||||
return IndexRange();
|
||||
}
|
||||
},
|
||||
dst_attribute_spans);
|
||||
dst_attribute_writers);
|
||||
}
|
||||
|
||||
static void execute_realize_mesh_tasks(const RealizeInstancesOptions &options,
|
||||
@@ -973,6 +975,7 @@ static void execute_realize_mesh_tasks(const RealizeInstancesOptions &options,
|
||||
Mesh *dst_mesh = BKE_mesh_new_nomain(tot_vertices, tot_edges, 0, tot_loops, tot_poly);
|
||||
MeshComponent &dst_component = r_realized_geometry.get_component_for_write<MeshComponent>();
|
||||
dst_component.replace(dst_mesh);
|
||||
bke::MutableAttributeAccessor dst_attributes = bke::mesh_attributes_for_write(*dst_mesh);
|
||||
|
||||
/* Copy settings from the first input geometry set with a mesh. */
|
||||
const RealizeMeshTask &first_task = tasks.first();
|
||||
@@ -986,24 +989,19 @@ static void execute_realize_mesh_tasks(const RealizeInstancesOptions &options,
|
||||
}
|
||||
|
||||
/* Prepare id attribute. */
|
||||
OutputAttribute_Typed<int> vertex_ids;
|
||||
MutableSpan<int> vertex_ids_span;
|
||||
SpanAttributeWriter<int> vertex_ids;
|
||||
if (all_meshes_info.create_id_attribute) {
|
||||
vertex_ids = dst_component.attribute_try_get_for_output_only<int>("id", ATTR_DOMAIN_POINT);
|
||||
vertex_ids_span = vertex_ids.as_span();
|
||||
vertex_ids = dst_attributes.lookup_or_add_for_write_only_span<int>("id", ATTR_DOMAIN_POINT);
|
||||
}
|
||||
|
||||
/* Prepare generic output attributes. */
|
||||
Vector<OutputAttribute> dst_attributes;
|
||||
Vector<GMutableSpan> dst_attribute_spans;
|
||||
Vector<GSpanAttributeWriter> dst_attribute_writers;
|
||||
for (const int attribute_index : ordered_attributes.index_range()) {
|
||||
const AttributeIDRef &attribute_id = ordered_attributes.ids[attribute_index];
|
||||
const eAttrDomain domain = ordered_attributes.kinds[attribute_index].domain;
|
||||
const eCustomDataType data_type = ordered_attributes.kinds[attribute_index].data_type;
|
||||
OutputAttribute dst_attribute = dst_component.attribute_try_get_for_output_only(
|
||||
attribute_id, domain, data_type);
|
||||
dst_attribute_spans.append(dst_attribute.as_span());
|
||||
dst_attributes.append(std::move(dst_attribute));
|
||||
dst_attribute_writers.append(
|
||||
dst_attributes.lookup_or_add_for_write_only_span(attribute_id, domain, data_type));
|
||||
}
|
||||
|
||||
/* Actually execute all tasks. */
|
||||
@@ -1011,16 +1009,16 @@ static void execute_realize_mesh_tasks(const RealizeInstancesOptions &options,
|
||||
for (const int task_index : task_range) {
|
||||
const RealizeMeshTask &task = tasks[task_index];
|
||||
execute_realize_mesh_task(
|
||||
options, task, ordered_attributes, *dst_mesh, dst_attribute_spans, vertex_ids_span);
|
||||
options, task, ordered_attributes, *dst_mesh, dst_attribute_writers, vertex_ids.span);
|
||||
}
|
||||
});
|
||||
|
||||
/* Save modified attributes. */
|
||||
for (OutputAttribute &dst_attribute : dst_attributes) {
|
||||
dst_attribute.save();
|
||||
/* Tag modified attributes. */
|
||||
for (GSpanAttributeWriter &dst_attribute : dst_attribute_writers) {
|
||||
dst_attribute.finish();
|
||||
}
|
||||
if (vertex_ids) {
|
||||
vertex_ids.save();
|
||||
vertex_ids.finish();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1088,49 +1086,43 @@ static AllCurvesInfo preprocess_curves(const GeometrySet &geometry_set,
|
||||
curve_info.curves = curves_id;
|
||||
|
||||
/* Access attributes. */
|
||||
CurveComponent component;
|
||||
component.replace(const_cast<Curves *>(curves_id), GeometryOwnershipType::ReadOnly);
|
||||
bke::AttributeAccessor attributes = curves.attributes();
|
||||
curve_info.attributes.reinitialize(info.attributes.size());
|
||||
for (const int attribute_index : info.attributes.index_range()) {
|
||||
const eAttrDomain domain = info.attributes.kinds[attribute_index].domain;
|
||||
const AttributeIDRef &attribute_id = info.attributes.ids[attribute_index];
|
||||
const eCustomDataType data_type = info.attributes.kinds[attribute_index].data_type;
|
||||
if (component.attribute_exists(attribute_id)) {
|
||||
GVArray attribute = component.attribute_get_for_read(attribute_id, domain, data_type);
|
||||
if (attributes.contains(attribute_id)) {
|
||||
GVArray attribute = attributes.lookup_or_default(attribute_id, domain, data_type);
|
||||
curve_info.attributes[attribute_index].emplace(std::move(attribute));
|
||||
}
|
||||
}
|
||||
if (info.create_id_attribute) {
|
||||
ReadAttributeLookup ids_lookup = component.attribute_try_get_for_read("id");
|
||||
if (ids_lookup) {
|
||||
curve_info.stored_ids = ids_lookup.varray.get_internal_span().typed<int>();
|
||||
bke::GAttributeReader id_attribute = attributes.lookup("id");
|
||||
if (id_attribute) {
|
||||
curve_info.stored_ids = id_attribute.varray.get_internal_span().typed<int>();
|
||||
}
|
||||
}
|
||||
|
||||
/* Retrieve the radius attribute, if it exists. */
|
||||
if (component.attribute_exists("radius")) {
|
||||
curve_info.radius = component
|
||||
.attribute_get_for_read<float>("radius", ATTR_DOMAIN_POINT, 0.0f)
|
||||
.get_internal_span();
|
||||
if (attributes.contains("radius")) {
|
||||
curve_info.radius =
|
||||
attributes.lookup<float>("radius", ATTR_DOMAIN_POINT).get_internal_span();
|
||||
info.create_radius_attribute = true;
|
||||
}
|
||||
|
||||
/* Retrieve the resolution attribute, if it exists. */
|
||||
curve_info.resolution = curves.resolution();
|
||||
if (component.attribute_exists("resolution")) {
|
||||
if (attributes.contains("resolution")) {
|
||||
info.create_resolution_attribute = true;
|
||||
}
|
||||
|
||||
/* Retrieve handle position attributes, if they exist. */
|
||||
if (component.attribute_exists("handle_right")) {
|
||||
curve_info.handle_left = component
|
||||
.attribute_get_for_read<float3>(
|
||||
"handle_left", ATTR_DOMAIN_POINT, float3(0))
|
||||
.get_internal_span();
|
||||
curve_info.handle_right = component
|
||||
.attribute_get_for_read<float3>(
|
||||
"handle_right", ATTR_DOMAIN_POINT, float3(0))
|
||||
.get_internal_span();
|
||||
if (attributes.contains("handle_right")) {
|
||||
curve_info.handle_left =
|
||||
attributes.lookup<float3>("handle_left", ATTR_DOMAIN_POINT).get_internal_span();
|
||||
curve_info.handle_right =
|
||||
attributes.lookup<float3>("handle_right", ATTR_DOMAIN_POINT).get_internal_span();
|
||||
info.create_handle_postion_attributes = true;
|
||||
}
|
||||
}
|
||||
@@ -1142,7 +1134,7 @@ static void execute_realize_curve_task(const RealizeInstancesOptions &options,
|
||||
const RealizeCurveTask &task,
|
||||
const OrderedAttributes &ordered_attributes,
|
||||
bke::CurvesGeometry &dst_curves,
|
||||
MutableSpan<GMutableSpan> dst_attribute_spans,
|
||||
MutableSpan<GSpanAttributeWriter> dst_attribute_writers,
|
||||
MutableSpan<int> all_dst_ids,
|
||||
MutableSpan<float3> all_handle_left,
|
||||
MutableSpan<float3> all_handle_right,
|
||||
@@ -1220,7 +1212,7 @@ static void execute_realize_curve_task(const RealizeInstancesOptions &options,
|
||||
return IndexRange();
|
||||
}
|
||||
},
|
||||
dst_attribute_spans);
|
||||
dst_attribute_writers);
|
||||
}
|
||||
|
||||
static void execute_realize_curve_tasks(const RealizeInstancesOptions &options,
|
||||
@@ -1244,57 +1236,45 @@ static void execute_realize_curve_tasks(const RealizeInstancesOptions &options,
|
||||
dst_curves.offsets_for_write().last() = points_num;
|
||||
CurveComponent &dst_component = r_realized_geometry.get_component_for_write<CurveComponent>();
|
||||
dst_component.replace(dst_curves_id);
|
||||
bke::MutableAttributeAccessor dst_attributes = dst_curves.attributes_for_write();
|
||||
|
||||
/* Prepare id attribute. */
|
||||
OutputAttribute_Typed<int> point_ids;
|
||||
MutableSpan<int> point_ids_span;
|
||||
SpanAttributeWriter<int> point_ids;
|
||||
if (all_curves_info.create_id_attribute) {
|
||||
point_ids = dst_component.attribute_try_get_for_output_only<int>("id", ATTR_DOMAIN_POINT);
|
||||
point_ids_span = point_ids.as_span();
|
||||
point_ids = dst_attributes.lookup_or_add_for_write_only_span<int>("id", ATTR_DOMAIN_POINT);
|
||||
}
|
||||
|
||||
/* Prepare generic output attributes. */
|
||||
Vector<OutputAttribute> dst_attributes;
|
||||
Vector<GMutableSpan> dst_attribute_spans;
|
||||
Vector<GSpanAttributeWriter> dst_attribute_writers;
|
||||
for (const int attribute_index : ordered_attributes.index_range()) {
|
||||
const AttributeIDRef &attribute_id = ordered_attributes.ids[attribute_index];
|
||||
const eAttrDomain domain = ordered_attributes.kinds[attribute_index].domain;
|
||||
const eCustomDataType data_type = ordered_attributes.kinds[attribute_index].data_type;
|
||||
OutputAttribute dst_attribute = dst_component.attribute_try_get_for_output_only(
|
||||
attribute_id, domain, data_type);
|
||||
dst_attribute_spans.append(dst_attribute.as_span());
|
||||
dst_attributes.append(std::move(dst_attribute));
|
||||
dst_attribute_writers.append(
|
||||
dst_attributes.lookup_or_add_for_write_only_span(attribute_id, domain, data_type));
|
||||
}
|
||||
|
||||
/* Prepare handle position attributes if necessary. */
|
||||
OutputAttribute_Typed<float3> handle_left;
|
||||
OutputAttribute_Typed<float3> handle_right;
|
||||
MutableSpan<float3> handle_left_span;
|
||||
MutableSpan<float3> handle_right_span;
|
||||
SpanAttributeWriter<float3> handle_left;
|
||||
SpanAttributeWriter<float3> handle_right;
|
||||
if (all_curves_info.create_handle_postion_attributes) {
|
||||
handle_left = dst_component.attribute_try_get_for_output_only<float3>("handle_left",
|
||||
ATTR_DOMAIN_POINT);
|
||||
handle_right = dst_component.attribute_try_get_for_output_only<float3>("handle_right",
|
||||
handle_left = dst_attributes.lookup_or_add_for_write_only_span<float3>("handle_left",
|
||||
ATTR_DOMAIN_POINT);
|
||||
handle_left_span = handle_left.as_span();
|
||||
handle_right_span = handle_right.as_span();
|
||||
handle_right = dst_attributes.lookup_or_add_for_write_only_span<float3>("handle_right",
|
||||
ATTR_DOMAIN_POINT);
|
||||
}
|
||||
|
||||
/* Prepare radius attribute if necessary. */
|
||||
OutputAttribute_Typed<float> radius;
|
||||
MutableSpan<float> radius_span;
|
||||
SpanAttributeWriter<float> radius;
|
||||
if (all_curves_info.create_radius_attribute) {
|
||||
radius = dst_component.attribute_try_get_for_output_only<float>("radius", ATTR_DOMAIN_POINT);
|
||||
radius_span = radius.as_span();
|
||||
radius = dst_attributes.lookup_or_add_for_write_only_span<float>("radius", ATTR_DOMAIN_POINT);
|
||||
}
|
||||
|
||||
/* Prepare resolution attribute if necessary. */
|
||||
OutputAttribute_Typed<int> resolution;
|
||||
MutableSpan<int> resolution_span;
|
||||
SpanAttributeWriter<int> resolution;
|
||||
if (all_curves_info.create_resolution_attribute) {
|
||||
resolution = dst_component.attribute_try_get_for_output_only<int>("resolution",
|
||||
ATTR_DOMAIN_CURVE);
|
||||
resolution_span = resolution.as_span();
|
||||
resolution = dst_attributes.lookup_or_add_for_write_only_span<int>("resolution",
|
||||
ATTR_DOMAIN_CURVE);
|
||||
}
|
||||
|
||||
/* Actually execute all tasks. */
|
||||
@@ -1306,31 +1286,31 @@ static void execute_realize_curve_tasks(const RealizeInstancesOptions &options,
|
||||
task,
|
||||
ordered_attributes,
|
||||
dst_curves,
|
||||
dst_attribute_spans,
|
||||
point_ids_span,
|
||||
handle_left_span,
|
||||
handle_right_span,
|
||||
radius_span,
|
||||
resolution_span);
|
||||
dst_attribute_writers,
|
||||
point_ids.span,
|
||||
handle_left.span,
|
||||
handle_right.span,
|
||||
radius.span,
|
||||
resolution.span);
|
||||
}
|
||||
});
|
||||
|
||||
/* Save modified attributes. */
|
||||
for (OutputAttribute &dst_attribute : dst_attributes) {
|
||||
dst_attribute.save();
|
||||
/* Tag modified attributes. */
|
||||
for (GSpanAttributeWriter &dst_attribute : dst_attribute_writers) {
|
||||
dst_attribute.finish();
|
||||
}
|
||||
if (point_ids) {
|
||||
point_ids.save();
|
||||
point_ids.finish();
|
||||
}
|
||||
if (radius) {
|
||||
radius.save();
|
||||
radius.finish();
|
||||
}
|
||||
if (resolution) {
|
||||
resolution.save();
|
||||
resolution.finish();
|
||||
}
|
||||
if (all_curves_info.create_handle_postion_attributes) {
|
||||
handle_left.save();
|
||||
handle_right.save();
|
||||
handle_left.finish();
|
||||
handle_right.finish();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1345,7 +1325,7 @@ static void remove_id_attribute_from_instances(GeometrySet &geometry_set)
|
||||
geometry_set.modify_geometry_sets([&](GeometrySet &sub_geometry) {
|
||||
if (sub_geometry.has<InstancesComponent>()) {
|
||||
InstancesComponent &component = sub_geometry.get_component_for_write<InstancesComponent>();
|
||||
component.attributes().remove("id");
|
||||
component.instance_attributes().remove("id");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -92,17 +92,18 @@ static void retrieve_attribute_spans(const Span<bke::AttributeIDRef> ids,
|
||||
CurveComponent &dst_component,
|
||||
Vector<GSpan> &src,
|
||||
Vector<GMutableSpan> &dst,
|
||||
Vector<bke::OutputAttribute> &dst_attributes)
|
||||
Vector<bke::GSpanAttributeWriter> &dst_attributes)
|
||||
{
|
||||
for (const int i : ids.index_range()) {
|
||||
GVArray src_attribute = src_component.attribute_try_get_for_read(ids[i], ATTR_DOMAIN_POINT);
|
||||
GVArray src_attribute = src_component.attributes()->lookup(ids[i], ATTR_DOMAIN_POINT);
|
||||
BLI_assert(src_attribute);
|
||||
src.append(src_attribute.get_internal_span());
|
||||
|
||||
const eCustomDataType data_type = bke::cpp_type_to_custom_data_type(src_attribute.type());
|
||||
bke::OutputAttribute dst_attribute = dst_component.attribute_try_get_for_output_only(
|
||||
ids[i], ATTR_DOMAIN_POINT, data_type);
|
||||
dst.append(dst_attribute.as_span());
|
||||
bke::GSpanAttributeWriter dst_attribute =
|
||||
dst_component.attributes_for_write()->lookup_or_add_for_write_only_span(
|
||||
ids[i], ATTR_DOMAIN_POINT, data_type);
|
||||
dst.append(dst_attribute.span);
|
||||
dst_attributes.append(std::move(dst_attribute));
|
||||
}
|
||||
}
|
||||
@@ -111,7 +112,7 @@ struct AttributesForInterpolation : NonCopyable, NonMovable {
|
||||
Vector<GSpan> src;
|
||||
Vector<GMutableSpan> dst;
|
||||
|
||||
Vector<bke::OutputAttribute> dst_attributes;
|
||||
Vector<bke::GSpanAttributeWriter> dst_attributes;
|
||||
|
||||
Vector<GSpan> src_no_interpolation;
|
||||
Vector<GMutableSpan> dst_no_interpolation;
|
||||
@@ -129,8 +130,8 @@ static void gather_point_attributes_to_interpolate(const CurveComponent &src_com
|
||||
|
||||
VectorSet<bke::AttributeIDRef> ids;
|
||||
VectorSet<bke::AttributeIDRef> ids_no_interpolation;
|
||||
src_component.attribute_foreach(
|
||||
[&](const bke::AttributeIDRef &id, const AttributeMetaData meta_data) {
|
||||
src_component.attributes()->for_all(
|
||||
[&](const bke::AttributeIDRef &id, const bke::AttributeMetaData meta_data) {
|
||||
if (meta_data.domain != ATTR_DOMAIN_POINT) {
|
||||
return true;
|
||||
}
|
||||
@@ -311,8 +312,8 @@ static Curves *resample_to_uniform(const CurveComponent &src_component,
|
||||
bke::curves::copy_point_data(
|
||||
src_curves, dst_curves, unselected_ranges, src_positions, dst_positions);
|
||||
|
||||
for (bke::OutputAttribute &attribute : attributes.dst_attributes) {
|
||||
attribute.save();
|
||||
for (bke::GSpanAttributeWriter &attribute : attributes.dst_attributes) {
|
||||
attribute.finish();
|
||||
}
|
||||
|
||||
return dst_curves_id;
|
||||
@@ -433,8 +434,8 @@ Curves *resample_to_evaluated(const CurveComponent &src_component,
|
||||
bke::curves::copy_point_data(
|
||||
src_curves, dst_curves, unselected_ranges, src_positions, dst_positions);
|
||||
|
||||
for (bke::OutputAttribute &attribute : attributes.dst_attributes) {
|
||||
attribute.save();
|
||||
for (bke::GSpanAttributeWriter &attribute : attributes.dst_attributes) {
|
||||
attribute.finish();
|
||||
}
|
||||
|
||||
return dst_curves_id;
|
||||
|
||||
@@ -290,32 +290,32 @@ struct GenericAttributes : NonCopyable, NonMovable {
|
||||
Vector<GSpan> src;
|
||||
Vector<GMutableSpan> dst;
|
||||
|
||||
Vector<bke::OutputAttribute> attributes;
|
||||
Vector<bke::GSpanAttributeWriter> attributes;
|
||||
};
|
||||
|
||||
static void retrieve_generic_point_attributes(const CurveComponent &src_component,
|
||||
CurveComponent &dst_component,
|
||||
static void retrieve_generic_point_attributes(const bke::AttributeAccessor &src_attributes,
|
||||
bke::MutableAttributeAccessor &dst_attributes,
|
||||
GenericAttributes &attributes)
|
||||
{
|
||||
src_component.attribute_foreach(
|
||||
[&](const bke::AttributeIDRef &id, const AttributeMetaData meta_data) {
|
||||
src_attributes.for_all(
|
||||
[&](const bke::AttributeIDRef &id, const bke::AttributeMetaData meta_data) {
|
||||
if (meta_data.domain != ATTR_DOMAIN_POINT) {
|
||||
/* Curve domain attributes are all copied directly to the result in one step. */
|
||||
return true;
|
||||
}
|
||||
if (src_component.attribute_is_builtin(id)) {
|
||||
if (src_attributes.is_builtin(id)) {
|
||||
if (!(id.is_named() && ELEM(id, "tilt", "radius"))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
GVArray src_attribute = src_component.attribute_try_get_for_read(id, ATTR_DOMAIN_POINT);
|
||||
GVArray src_attribute = src_attributes.lookup(id, ATTR_DOMAIN_POINT);
|
||||
BLI_assert(src_attribute);
|
||||
attributes.src.append(src_attribute.get_internal_span());
|
||||
|
||||
bke::OutputAttribute dst_attribute = dst_component.attribute_try_get_for_output_only(
|
||||
bke::GSpanAttributeWriter dst_attribute = dst_attributes.lookup_or_add_for_write_span(
|
||||
id, ATTR_DOMAIN_POINT, meta_data.data_type);
|
||||
attributes.dst.append(dst_attribute.as_span());
|
||||
attributes.dst.append(dst_attribute.span);
|
||||
attributes.attributes.append(std::move(dst_attribute));
|
||||
|
||||
return true;
|
||||
@@ -367,8 +367,11 @@ static Curves *convert_curves_to_bezier(const CurveComponent &src_component,
|
||||
bke::curves::accumulate_counts_to_offsets(dst_offsets);
|
||||
dst_curves.resize(dst_offsets.last(), dst_curves.curves_num());
|
||||
|
||||
const bke::AttributeAccessor src_attributes = *src_component.attributes();
|
||||
bke::MutableAttributeAccessor dst_attributes = *dst_component.attributes_for_write();
|
||||
|
||||
GenericAttributes attributes;
|
||||
retrieve_generic_point_attributes(src_component, dst_component, attributes);
|
||||
retrieve_generic_point_attributes(src_attributes, dst_attributes, attributes);
|
||||
|
||||
MutableSpan<float3> dst_positions = dst_curves.positions_for_write();
|
||||
MutableSpan<float3> dst_handles_l = dst_curves.handle_positions_left_for_write();
|
||||
@@ -494,8 +497,8 @@ static Curves *convert_curves_to_bezier(const CurveComponent &src_component,
|
||||
src_curves, dst_curves, unselected_ranges, attributes.src[i], attributes.dst[i]);
|
||||
}
|
||||
|
||||
for (bke::OutputAttribute &attribute : attributes.attributes) {
|
||||
attribute.save();
|
||||
for (bke::GSpanAttributeWriter &attribute : attributes.attributes) {
|
||||
attribute.finish();
|
||||
}
|
||||
|
||||
return dst_curves_id;
|
||||
@@ -524,8 +527,11 @@ static Curves *convert_curves_to_nurbs(const CurveComponent &src_component,
|
||||
bke::curves::accumulate_counts_to_offsets(dst_offsets);
|
||||
dst_curves.resize(dst_offsets.last(), dst_curves.curves_num());
|
||||
|
||||
const bke::AttributeAccessor src_attributes = *src_component.attributes();
|
||||
bke::MutableAttributeAccessor dst_attributes = *dst_component.attributes_for_write();
|
||||
|
||||
GenericAttributes attributes;
|
||||
retrieve_generic_point_attributes(src_component, dst_component, attributes);
|
||||
retrieve_generic_point_attributes(src_attributes, dst_attributes, attributes);
|
||||
|
||||
MutableSpan<float3> dst_positions = dst_curves.positions_for_write();
|
||||
|
||||
@@ -659,8 +665,8 @@ static Curves *convert_curves_to_nurbs(const CurveComponent &src_component,
|
||||
src_curves, dst_curves, unselected_ranges, attributes.src[i], attributes.dst[i]);
|
||||
}
|
||||
|
||||
for (bke::OutputAttribute &attribute : attributes.attributes) {
|
||||
attribute.save();
|
||||
for (bke::GSpanAttributeWriter &attribute : attributes.attributes) {
|
||||
attribute.finish();
|
||||
}
|
||||
|
||||
return dst_curves_id;
|
||||
|
||||
@@ -79,16 +79,17 @@ static void calculate_result_offsets(const bke::CurvesGeometry &src_curves,
|
||||
struct AttributeTransferData {
|
||||
/* Expect that if an attribute exists, it is stored as a contiguous array internally anyway. */
|
||||
GVArraySpan src;
|
||||
bke::OutputAttribute dst;
|
||||
bke::GSpanAttributeWriter dst;
|
||||
};
|
||||
|
||||
static Vector<AttributeTransferData> retrieve_point_attributes(const CurveComponent &src_component,
|
||||
CurveComponent &dst_component,
|
||||
const Set<std::string> &skip = {})
|
||||
static Vector<AttributeTransferData> retrieve_point_attributes(
|
||||
const bke::AttributeAccessor &src_attributes,
|
||||
bke::MutableAttributeAccessor &dst_attributes,
|
||||
const Set<std::string> &skip = {})
|
||||
{
|
||||
Vector<AttributeTransferData> attributes;
|
||||
src_component.attribute_foreach(
|
||||
[&](const bke::AttributeIDRef &id, const AttributeMetaData meta_data) {
|
||||
src_attributes.for_all(
|
||||
[&](const bke::AttributeIDRef &id, const bke::AttributeMetaData meta_data) {
|
||||
if (meta_data.domain != ATTR_DOMAIN_POINT) {
|
||||
/* Curve domain attributes are all copied directly to the result in one step. */
|
||||
return true;
|
||||
@@ -97,9 +98,9 @@ static Vector<AttributeTransferData> retrieve_point_attributes(const CurveCompon
|
||||
return true;
|
||||
}
|
||||
|
||||
GVArray src = src_component.attribute_try_get_for_read(id, ATTR_DOMAIN_POINT);
|
||||
GVArray src = src_attributes.lookup(id, ATTR_DOMAIN_POINT);
|
||||
BLI_assert(src);
|
||||
bke::OutputAttribute dst = dst_component.attribute_try_get_for_output_only(
|
||||
bke::GSpanAttributeWriter dst = dst_attributes.lookup_or_add_for_write_only_span(
|
||||
id, ATTR_DOMAIN_POINT, meta_data.data_type);
|
||||
BLI_assert(dst);
|
||||
attributes.append({std::move(src), std::move(dst)});
|
||||
@@ -384,28 +385,27 @@ Curves *subdivide_curves(const CurveComponent &src_component,
|
||||
|
||||
dst_curves.resize(dst_curves.offsets().last(), dst_curves.curves_num());
|
||||
|
||||
const bke::AttributeAccessor src_attributes = *src_component.attributes();
|
||||
bke::MutableAttributeAccessor dst_attributes = *dst_component.attributes_for_write();
|
||||
|
||||
auto subdivide_catmull_rom = [&](IndexMask selection) {
|
||||
for (auto &attribute : retrieve_point_attributes(src_component, dst_component)) {
|
||||
for (auto &attribute : retrieve_point_attributes(src_attributes, dst_attributes)) {
|
||||
subdivide_attribute_catmull_rom(src_curves,
|
||||
dst_curves,
|
||||
selection,
|
||||
point_offsets,
|
||||
cyclic,
|
||||
attribute.src,
|
||||
attribute.dst.as_span());
|
||||
attribute.dst.save();
|
||||
attribute.dst.span);
|
||||
attribute.dst.finish();
|
||||
}
|
||||
};
|
||||
|
||||
auto subdivide_poly = [&](IndexMask selection) {
|
||||
for (auto &attribute : retrieve_point_attributes(src_component, dst_component)) {
|
||||
subdivide_attribute_linear(src_curves,
|
||||
dst_curves,
|
||||
selection,
|
||||
point_offsets,
|
||||
attribute.src,
|
||||
attribute.dst.as_span());
|
||||
attribute.dst.save();
|
||||
for (auto &attribute : retrieve_point_attributes(src_attributes, dst_attributes)) {
|
||||
subdivide_attribute_linear(
|
||||
src_curves, dst_curves, selection, point_offsets, attribute.src, attribute.dst.span);
|
||||
attribute.dst.finish();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -443,20 +443,16 @@ Curves *subdivide_curves(const CurveComponent &src_component,
|
||||
}
|
||||
});
|
||||
|
||||
for (auto &attribute : retrieve_point_attributes(src_component,
|
||||
dst_component,
|
||||
for (auto &attribute : retrieve_point_attributes(src_attributes,
|
||||
dst_attributes,
|
||||
{"position",
|
||||
"handle_type_left",
|
||||
"handle_type_right",
|
||||
"handle_right",
|
||||
"handle_left"})) {
|
||||
subdivide_attribute_linear(src_curves,
|
||||
dst_curves,
|
||||
selection,
|
||||
point_offsets,
|
||||
attribute.src,
|
||||
attribute.dst.as_span());
|
||||
attribute.dst.save();
|
||||
subdivide_attribute_linear(
|
||||
src_curves, dst_curves, selection, point_offsets, attribute.src, attribute.dst.span);
|
||||
attribute.dst.finish();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -473,10 +469,10 @@ Curves *subdivide_curves(const CurveComponent &src_component,
|
||||
subdivide_nurbs);
|
||||
|
||||
if (!unselected_ranges.is_empty()) {
|
||||
for (auto &attribute : retrieve_point_attributes(src_component, dst_component)) {
|
||||
for (auto &attribute : retrieve_point_attributes(src_attributes, dst_attributes)) {
|
||||
bke::curves::copy_point_data(
|
||||
src_curves, dst_curves, unselected_ranges, attribute.src, attribute.dst.as_span());
|
||||
attribute.dst.save();
|
||||
src_curves, dst_curves, unselected_ranges, attribute.src, attribute.dst.span);
|
||||
attribute.dst.finish();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -254,9 +254,8 @@ void OBJWriter::write_vertex_coords(FormatHandler<eFileType::OBJ> &fh,
|
||||
colors_layer = BKE_id_attributes_active_color_get(&mesh->id);
|
||||
}
|
||||
if (write_colors && (colors_layer != nullptr)) {
|
||||
MeshComponent component;
|
||||
component.replace(mesh, GeometryOwnershipType::ReadOnly);
|
||||
VArray<ColorGeometry4f> attribute = component.attribute_get_for_read<ColorGeometry4f>(
|
||||
const bke::AttributeAccessor attributes = bke::mesh_attributes(*mesh);
|
||||
const VArray<ColorGeometry4f> attribute = attributes.lookup_or_default<ColorGeometry4f>(
|
||||
colors_layer->name, ATTR_DOMAIN_POINT, {0.0f, 0.0f, 0.0f, 0.0f});
|
||||
|
||||
BLI_assert(tot_count == attribute.size());
|
||||
|
||||
@@ -109,7 +109,7 @@ using blender::Span;
|
||||
using blender::StringRef;
|
||||
using blender::StringRefNull;
|
||||
using blender::Vector;
|
||||
using blender::bke::OutputAttribute;
|
||||
using blender::bke::AttributeMetaData;
|
||||
using blender::fn::Field;
|
||||
using blender::fn::GField;
|
||||
using blender::fn::ValueOrField;
|
||||
@@ -999,22 +999,27 @@ static Vector<OutputAttributeToStore> compute_attributes_to_store(
|
||||
continue;
|
||||
}
|
||||
const GeometryComponent &component = *geometry.get_component_for_read(component_type);
|
||||
if (component.is_empty()) {
|
||||
continue;
|
||||
}
|
||||
const blender::bke::AttributeAccessor attributes = *component.attributes();
|
||||
for (const auto item : outputs_by_domain.items()) {
|
||||
const eAttrDomain domain = item.key;
|
||||
const Span<OutputAttributeInfo> outputs_info = item.value;
|
||||
if (!component.attribute_domain_supported(domain)) {
|
||||
if (!attributes.domain_supported(domain)) {
|
||||
continue;
|
||||
}
|
||||
const int domain_num = component.attribute_domain_num(domain);
|
||||
const int domain_size = attributes.domain_size(domain);
|
||||
blender::bke::GeometryComponentFieldContext field_context{component, domain};
|
||||
blender::fn::FieldEvaluator field_evaluator{field_context, domain_num};
|
||||
blender::fn::FieldEvaluator field_evaluator{field_context, domain_size};
|
||||
for (const OutputAttributeInfo &output_info : outputs_info) {
|
||||
const CPPType &type = output_info.field.cpp_type();
|
||||
OutputAttributeToStore store{
|
||||
component_type,
|
||||
domain,
|
||||
output_info.name,
|
||||
GMutableSpan{type, MEM_malloc_arrayN(domain_num, type.size(), __func__), domain_num}};
|
||||
GMutableSpan{
|
||||
type, MEM_malloc_arrayN(domain_size, type.size(), __func__), domain_size}};
|
||||
field_evaluator.add_with_destination(output_info.field, store.data);
|
||||
attributes_to_store.append(store);
|
||||
}
|
||||
@@ -1029,33 +1034,33 @@ static void store_computed_output_attributes(
|
||||
{
|
||||
for (const OutputAttributeToStore &store : attributes_to_store) {
|
||||
GeometryComponent &component = geometry.get_component_for_write(store.component_type);
|
||||
blender::bke::MutableAttributeAccessor attributes = *component.attributes_for_write();
|
||||
|
||||
const eCustomDataType data_type = blender::bke::cpp_type_to_custom_data_type(
|
||||
store.data.type());
|
||||
const std::optional<AttributeMetaData> meta_data = component.attribute_get_meta_data(
|
||||
store.name);
|
||||
const std::optional<AttributeMetaData> meta_data = attributes.lookup_meta_data(store.name);
|
||||
|
||||
/* Attempt to remove the attribute if it already exists but the domain and type don't match.
|
||||
* Removing the attribute won't succeed if it is built in and non-removable. */
|
||||
if (meta_data.has_value() &&
|
||||
(meta_data->domain != store.domain || meta_data->data_type != data_type)) {
|
||||
component.attribute_try_delete(store.name);
|
||||
attributes.remove(store.name);
|
||||
}
|
||||
|
||||
/* Try to create the attribute reusing the stored buffer. This will only succeed if the
|
||||
* attribute didn't exist before, or if it existed but was removed above. */
|
||||
if (component.attribute_try_create(
|
||||
store.name,
|
||||
store.domain,
|
||||
blender::bke::cpp_type_to_custom_data_type(store.data.type()),
|
||||
AttributeInitMove(store.data.data()))) {
|
||||
if (attributes.add(store.name,
|
||||
store.domain,
|
||||
blender::bke::cpp_type_to_custom_data_type(store.data.type()),
|
||||
blender::bke::AttributeInitMove(store.data.data()))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
OutputAttribute attribute = component.attribute_try_get_for_output_only(
|
||||
blender::bke::GAttributeWriter attribute = attributes.lookup_or_add_for_write(
|
||||
store.name, store.domain, data_type);
|
||||
if (attribute) {
|
||||
attribute.varray().set_all(store.data.data());
|
||||
attribute.save();
|
||||
attribute.varray.set_all(store.data.data());
|
||||
attribute.finish();
|
||||
}
|
||||
|
||||
/* We were unable to reuse the data, so it must be destructed and freed. */
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
#include "FN_field.hh"
|
||||
#include "FN_multi_function_builder.hh"
|
||||
|
||||
#include "BKE_attribute_access.hh"
|
||||
#include "BKE_geometry_fields.hh"
|
||||
#include "BKE_geometry_set.hh"
|
||||
|
||||
@@ -20,16 +19,22 @@ struct ModifierData;
|
||||
namespace blender::nodes {
|
||||
|
||||
using bke::AnonymousAttributeFieldInput;
|
||||
using bke::AttributeAccessor;
|
||||
using bke::AttributeFieldInput;
|
||||
using bke::AttributeIDRef;
|
||||
using bke::AttributeKind;
|
||||
using bke::AttributeMetaData;
|
||||
using bke::AttributeReader;
|
||||
using bke::AttributeWriter;
|
||||
using bke::GAttributeReader;
|
||||
using bke::GAttributeWriter;
|
||||
using bke::GeometryComponentFieldContext;
|
||||
using bke::GeometryFieldInput;
|
||||
using bke::OutputAttribute;
|
||||
using bke::OutputAttribute_Typed;
|
||||
using bke::ReadAttributeLookup;
|
||||
using bke::GSpanAttributeWriter;
|
||||
using bke::MutableAttributeAccessor;
|
||||
using bke::SpanAttributeWriter;
|
||||
using bke::StrongAnonymousAttributeID;
|
||||
using bke::WeakAnonymousAttributeID;
|
||||
using bke::WriteAttributeLookup;
|
||||
using fn::Field;
|
||||
using fn::FieldContext;
|
||||
using fn::FieldEvaluator;
|
||||
|
||||
@@ -217,16 +217,20 @@ template<typename T> class AccumulateFieldInput final : public GeometryFieldInpu
|
||||
IndexMask UNUSED(mask)) const final
|
||||
{
|
||||
const GeometryComponentFieldContext field_context{component, source_domain_};
|
||||
const int domain_num = component.attribute_domain_num(field_context.domain());
|
||||
const int domain_size = component.attribute_domain_size(field_context.domain());
|
||||
if (domain_size == 0) {
|
||||
return {};
|
||||
}
|
||||
const AttributeAccessor attributes = *component.attributes();
|
||||
|
||||
fn::FieldEvaluator evaluator{field_context, domain_num};
|
||||
fn::FieldEvaluator evaluator{field_context, domain_size};
|
||||
evaluator.add(input_);
|
||||
evaluator.add(group_index_);
|
||||
evaluator.evaluate();
|
||||
const VArray<T> values = evaluator.get_evaluated<T>(0);
|
||||
const VArray<int> group_indices = evaluator.get_evaluated<int>(1);
|
||||
|
||||
Array<T> accumulations_out(domain_num);
|
||||
Array<T> accumulations_out(domain_size);
|
||||
|
||||
if (group_indices.is_single()) {
|
||||
T accumulation = T();
|
||||
@@ -261,7 +265,7 @@ template<typename T> class AccumulateFieldInput final : public GeometryFieldInpu
|
||||
}
|
||||
}
|
||||
|
||||
return component.attribute_try_adapt_domain<T>(
|
||||
return attributes.adapt_domain<T>(
|
||||
VArray<T>::ForContainer(std::move(accumulations_out)), source_domain_, domain);
|
||||
}
|
||||
|
||||
@@ -303,9 +307,13 @@ template<typename T> class TotalFieldInput final : public GeometryFieldInput {
|
||||
IndexMask UNUSED(mask)) const final
|
||||
{
|
||||
const GeometryComponentFieldContext field_context{component, source_domain_};
|
||||
const int domain_num = component.attribute_domain_num(field_context.domain());
|
||||
const int domain_size = component.attribute_domain_size(field_context.domain());
|
||||
if (domain_size == 0) {
|
||||
return {};
|
||||
}
|
||||
const AttributeAccessor attributes = *component.attributes();
|
||||
|
||||
fn::FieldEvaluator evaluator{field_context, domain_num};
|
||||
fn::FieldEvaluator evaluator{field_context, domain_size};
|
||||
evaluator.add(input_);
|
||||
evaluator.add(group_index_);
|
||||
evaluator.evaluate();
|
||||
@@ -317,10 +325,10 @@ template<typename T> class TotalFieldInput final : public GeometryFieldInput {
|
||||
for (const int i : values.index_range()) {
|
||||
accumulation = values[i] + accumulation;
|
||||
}
|
||||
return VArray<T>::ForSingle(accumulation, domain_num);
|
||||
return VArray<T>::ForSingle(accumulation, domain_size);
|
||||
}
|
||||
|
||||
Array<T> accumulations_out(domain_num);
|
||||
Array<T> accumulations_out(domain_size);
|
||||
Map<int, T> accumulations;
|
||||
for (const int i : values.index_range()) {
|
||||
T &value = accumulations.lookup_or_add_default(group_indices[i]);
|
||||
@@ -330,7 +338,7 @@ template<typename T> class TotalFieldInput final : public GeometryFieldInput {
|
||||
accumulations_out[i] = accumulations.lookup(group_indices[i]);
|
||||
}
|
||||
|
||||
return component.attribute_try_adapt_domain<T>(
|
||||
return attributes.adapt_domain<T>(
|
||||
VArray<T>::ForContainer(std::move(accumulations_out)), source_domain_, domain);
|
||||
}
|
||||
|
||||
|
||||
@@ -111,19 +111,26 @@ static void try_capture_field_on_geometry(GeometryComponent &component,
|
||||
const eAttrDomain domain,
|
||||
const GField &field)
|
||||
{
|
||||
const int domain_size = component.attribute_domain_size(domain);
|
||||
if (domain_size == 0) {
|
||||
return;
|
||||
}
|
||||
GeometryComponentFieldContext field_context{component, domain};
|
||||
const int domain_num = component.attribute_domain_num(domain);
|
||||
const IndexMask mask{IndexMask(domain_num)};
|
||||
MutableAttributeAccessor attributes = *component.attributes_for_write();
|
||||
const IndexMask mask{IndexMask(domain_size)};
|
||||
|
||||
const eCustomDataType data_type = bke::cpp_type_to_custom_data_type(field.cpp_type());
|
||||
OutputAttribute output_attribute = component.attribute_try_get_for_output_only(
|
||||
GAttributeWriter output_attribute = attributes.lookup_or_add_for_write(
|
||||
attribute_id, domain, data_type);
|
||||
if (!output_attribute) {
|
||||
return;
|
||||
}
|
||||
|
||||
fn::FieldEvaluator evaluator{field_context, &mask};
|
||||
evaluator.add_with_destination(field, output_attribute.varray());
|
||||
evaluator.add_with_destination(field, output_attribute.varray);
|
||||
evaluator.evaluate();
|
||||
|
||||
output_attribute.save();
|
||||
output_attribute.finish();
|
||||
}
|
||||
|
||||
static StringRefNull identifier_suffix(eCustomDataType data_type)
|
||||
|
||||
@@ -72,11 +72,11 @@ static void node_geo_exec(GeoNodeExecParams params)
|
||||
case GEO_COMPONENT_TYPE_MESH: {
|
||||
if (geometry_set.has_mesh()) {
|
||||
const MeshComponent *component = geometry_set.get_component_for_read<MeshComponent>();
|
||||
params.set_output("Point Count", component->attribute_domain_num(ATTR_DOMAIN_POINT));
|
||||
params.set_output("Edge Count", component->attribute_domain_num(ATTR_DOMAIN_EDGE));
|
||||
params.set_output("Face Count", component->attribute_domain_num(ATTR_DOMAIN_FACE));
|
||||
params.set_output("Face Corner Count",
|
||||
component->attribute_domain_num(ATTR_DOMAIN_CORNER));
|
||||
const AttributeAccessor attributes = *component->attributes();
|
||||
params.set_output("Point Count", attributes.domain_size(ATTR_DOMAIN_POINT));
|
||||
params.set_output("Edge Count", attributes.domain_size(ATTR_DOMAIN_EDGE));
|
||||
params.set_output("Face Count", attributes.domain_size(ATTR_DOMAIN_FACE));
|
||||
params.set_output("Face Corner Count", attributes.domain_size(ATTR_DOMAIN_CORNER));
|
||||
}
|
||||
else {
|
||||
params.set_default_remaining_outputs();
|
||||
@@ -86,8 +86,9 @@ static void node_geo_exec(GeoNodeExecParams params)
|
||||
case GEO_COMPONENT_TYPE_CURVE: {
|
||||
if (geometry_set.has_curves()) {
|
||||
const CurveComponent *component = geometry_set.get_component_for_read<CurveComponent>();
|
||||
params.set_output("Point Count", component->attribute_domain_num(ATTR_DOMAIN_POINT));
|
||||
params.set_output("Spline Count", component->attribute_domain_num(ATTR_DOMAIN_CURVE));
|
||||
const AttributeAccessor attributes = *component->attributes();
|
||||
params.set_output("Point Count", attributes.domain_size(ATTR_DOMAIN_POINT));
|
||||
params.set_output("Spline Count", attributes.domain_size(ATTR_DOMAIN_CURVE));
|
||||
}
|
||||
else {
|
||||
params.set_default_remaining_outputs();
|
||||
@@ -98,7 +99,7 @@ static void node_geo_exec(GeoNodeExecParams params)
|
||||
if (geometry_set.has_pointcloud()) {
|
||||
const PointCloudComponent *component =
|
||||
geometry_set.get_component_for_read<PointCloudComponent>();
|
||||
params.set_output("Point Count", component->attribute_domain_num(ATTR_DOMAIN_POINT));
|
||||
params.set_output("Point Count", component->attributes()->domain_size(ATTR_DOMAIN_POINT));
|
||||
}
|
||||
else {
|
||||
params.set_default_remaining_outputs();
|
||||
@@ -109,7 +110,8 @@ static void node_geo_exec(GeoNodeExecParams params)
|
||||
if (geometry_set.has_instances()) {
|
||||
const InstancesComponent *component =
|
||||
geometry_set.get_component_for_read<InstancesComponent>();
|
||||
params.set_output("Instance Count", component->attribute_domain_num(ATTR_DOMAIN_INSTANCE));
|
||||
params.set_output("Instance Count",
|
||||
component->attributes()->domain_size(ATTR_DOMAIN_INSTANCE));
|
||||
}
|
||||
else {
|
||||
params.set_default_remaining_outputs();
|
||||
|
||||
@@ -195,9 +195,13 @@ static void node_geo_exec(GeoNodeExecParams params)
|
||||
const Field<float> input_field = params.get_input<Field<float>>("Attribute");
|
||||
Vector<float> data;
|
||||
for (const GeometryComponent *component : components) {
|
||||
if (component->attribute_domain_supported(domain)) {
|
||||
const std::optional<AttributeAccessor> attributes = component->attributes();
|
||||
if (!attributes.has_value()) {
|
||||
continue;
|
||||
}
|
||||
if (attributes->domain_supported(domain)) {
|
||||
GeometryComponentFieldContext field_context{*component, domain};
|
||||
const int domain_num = component->attribute_domain_num(domain);
|
||||
const int domain_num = attributes->domain_size(domain);
|
||||
|
||||
fn::FieldEvaluator data_evaluator{field_context, domain_num};
|
||||
data_evaluator.add(input_field);
|
||||
@@ -273,9 +277,13 @@ static void node_geo_exec(GeoNodeExecParams params)
|
||||
const Field<float3> input_field = params.get_input<Field<float3>>("Attribute_001");
|
||||
Vector<float3> data;
|
||||
for (const GeometryComponent *component : components) {
|
||||
if (component->attribute_domain_supported(domain)) {
|
||||
const std::optional<AttributeAccessor> attributes = component->attributes();
|
||||
if (!attributes.has_value()) {
|
||||
continue;
|
||||
}
|
||||
if (attributes->domain_supported(domain)) {
|
||||
GeometryComponentFieldContext field_context{*component, domain};
|
||||
const int domain_num = component->attribute_domain_num(domain);
|
||||
const int domain_num = attributes->domain_size(domain);
|
||||
|
||||
fn::FieldEvaluator data_evaluator{field_context, domain_num};
|
||||
data_evaluator.add(input_field);
|
||||
|
||||
@@ -154,17 +154,15 @@ static void node_geo_exec(GeoNodeExecParams params)
|
||||
|
||||
/* Store intersecting edges in attribute. */
|
||||
if (attribute_outputs.intersecting_edges_id) {
|
||||
MeshComponent mesh_component;
|
||||
mesh_component.replace(result, GeometryOwnershipType::Editable);
|
||||
OutputAttribute_Typed<bool> attribute = mesh_component.attribute_try_get_for_output_only<bool>(
|
||||
MutableAttributeAccessor attributes = bke::mesh_attributes_for_write(*result);
|
||||
SpanAttributeWriter<bool> selection = attributes.lookup_or_add_for_write_only_span<bool>(
|
||||
attribute_outputs.intersecting_edges_id.get(), ATTR_DOMAIN_EDGE);
|
||||
MutableSpan<bool> selection = attribute.as_span();
|
||||
selection.fill(false);
|
||||
for (const int i : intersecting_edges) {
|
||||
selection[i] = true;
|
||||
}
|
||||
|
||||
attribute.save();
|
||||
selection.span.fill(false);
|
||||
for (const int i : intersecting_edges) {
|
||||
selection.span[i] = true;
|
||||
}
|
||||
selection.finish();
|
||||
|
||||
params.set_output(
|
||||
"Intersecting Edges",
|
||||
|
||||
@@ -143,7 +143,8 @@ static Mesh *compute_hull(const GeometrySet &geometry_set)
|
||||
if (geometry_set.has_mesh()) {
|
||||
count++;
|
||||
const MeshComponent *component = geometry_set.get_component_for_read<MeshComponent>();
|
||||
total_num += component->attribute_domain_num(ATTR_DOMAIN_POINT);
|
||||
const Mesh *mesh = component->get_for_read();
|
||||
total_num += mesh->totvert;
|
||||
}
|
||||
|
||||
if (geometry_set.has_pointcloud()) {
|
||||
@@ -151,10 +152,9 @@ static Mesh *compute_hull(const GeometrySet &geometry_set)
|
||||
span_count++;
|
||||
const PointCloudComponent *component =
|
||||
geometry_set.get_component_for_read<PointCloudComponent>();
|
||||
VArray<float3> varray = component->attribute_get_for_read<float3>(
|
||||
"position", ATTR_DOMAIN_POINT, {0, 0, 0});
|
||||
total_num += varray.size();
|
||||
positions_span = varray.get_internal_span();
|
||||
const PointCloud *pointcloud = component->get_for_read();
|
||||
positions_span = {reinterpret_cast<const float3 *>(pointcloud->co), pointcloud->totpoint};
|
||||
total_num += pointcloud->totpoint;
|
||||
}
|
||||
|
||||
if (geometry_set.has_curves()) {
|
||||
@@ -181,8 +181,8 @@ static Mesh *compute_hull(const GeometrySet &geometry_set)
|
||||
|
||||
if (geometry_set.has_mesh()) {
|
||||
const MeshComponent *component = geometry_set.get_component_for_read<MeshComponent>();
|
||||
VArray<float3> varray = component->attribute_get_for_read<float3>(
|
||||
"position", ATTR_DOMAIN_POINT, {0, 0, 0});
|
||||
const VArray<float3> varray = component->attributes()->lookup<float3>("position",
|
||||
ATTR_DOMAIN_POINT);
|
||||
varray.materialize(positions.as_mutable_span().slice(offset, varray.size()));
|
||||
offset += varray.size();
|
||||
}
|
||||
@@ -190,8 +190,8 @@ static Mesh *compute_hull(const GeometrySet &geometry_set)
|
||||
if (geometry_set.has_pointcloud()) {
|
||||
const PointCloudComponent *component =
|
||||
geometry_set.get_component_for_read<PointCloudComponent>();
|
||||
VArray<float3> varray = component->attribute_get_for_read<float3>(
|
||||
"position", ATTR_DOMAIN_POINT, {0, 0, 0});
|
||||
const VArray<float3> varray = component->attributes()->lookup<float3>("position",
|
||||
ATTR_DOMAIN_POINT);
|
||||
varray.materialize(positions.as_mutable_span().slice(offset, varray.size()));
|
||||
offset += varray.size();
|
||||
}
|
||||
|
||||
@@ -581,8 +581,8 @@ static void calculate_curve_fillet(GeometrySet &geometry_set,
|
||||
|
||||
CurveComponent &component = geometry_set.get_component_for_write<CurveComponent>();
|
||||
GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_POINT};
|
||||
const int domain_num = component.attribute_domain_num(ATTR_DOMAIN_POINT);
|
||||
fn::FieldEvaluator field_evaluator{field_context, domain_num};
|
||||
const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_POINT);
|
||||
fn::FieldEvaluator field_evaluator{field_context, domain_size};
|
||||
|
||||
field_evaluator.add(radius_field);
|
||||
|
||||
|
||||
@@ -61,13 +61,13 @@ static Curves *create_star_curve(const float inner_radius,
|
||||
static void create_selection_output(CurveComponent &component,
|
||||
StrongAnonymousAttributeID &r_attribute)
|
||||
{
|
||||
OutputAttribute_Typed<bool> attribute = component.attribute_try_get_for_output_only<bool>(
|
||||
r_attribute.get(), ATTR_DOMAIN_POINT);
|
||||
MutableSpan<bool> selection = attribute.as_span();
|
||||
for (int i : selection.index_range()) {
|
||||
selection[i] = i % 2 == 0;
|
||||
SpanAttributeWriter<bool> selection =
|
||||
component.attributes_for_write()->lookup_or_add_for_write_only_span<bool>(r_attribute.get(),
|
||||
ATTR_DOMAIN_POINT);
|
||||
for (int i : selection.span.index_range()) {
|
||||
selection.span[i] = i % 2 == 0;
|
||||
}
|
||||
attribute.save();
|
||||
selection.finish();
|
||||
}
|
||||
|
||||
static void node_geo_exec(GeoNodeExecParams params)
|
||||
|
||||
@@ -27,9 +27,9 @@ static void node_geo_exec(GeoNodeExecParams params)
|
||||
Field<bool> selection_field = params.get_input<Field<bool>>("Selection");
|
||||
const CurveComponent &component = *geometry_set.get_component_for_read<CurveComponent>();
|
||||
GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_CURVE};
|
||||
const int domain_num = component.attribute_domain_num(ATTR_DOMAIN_CURVE);
|
||||
const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_CURVE);
|
||||
|
||||
fn::FieldEvaluator selection_evaluator{field_context, domain_num};
|
||||
fn::FieldEvaluator selection_evaluator{field_context, domain_size};
|
||||
selection_evaluator.add(selection_field);
|
||||
selection_evaluator.evaluate();
|
||||
const IndexMask selection = selection_evaluator.get_evaluated_as_mask(0);
|
||||
|
||||
@@ -98,8 +98,8 @@ static void node_geo_exec(GeoNodeExecParams params)
|
||||
}
|
||||
has_curves = true;
|
||||
const CurveComponent &component = *geometry_set.get_component_for_read<CurveComponent>();
|
||||
if (!component.attribute_exists("handle_type_left") ||
|
||||
!component.attribute_exists("handle_type_right")) {
|
||||
const AttributeAccessor attributes = *component.attributes();
|
||||
if (!attributes.contains("handle_type_left") || !attributes.contains("handle_type_right")) {
|
||||
return;
|
||||
}
|
||||
has_bezier = true;
|
||||
|
||||
@@ -126,19 +126,18 @@ static GMutableSpan ensure_point_attribute(PointCloudComponent &points,
|
||||
const AttributeIDRef &attribute_id,
|
||||
const eCustomDataType data_type)
|
||||
{
|
||||
points.attribute_try_create(attribute_id, ATTR_DOMAIN_POINT, data_type, AttributeInitDefault());
|
||||
WriteAttributeLookup attribute = points.attribute_try_get_for_write(attribute_id);
|
||||
BLI_assert(attribute);
|
||||
return attribute.varray.get_internal_span();
|
||||
return points.attributes_for_write()
|
||||
->lookup_or_add_for_write(attribute_id, ATTR_DOMAIN_POINT, data_type)
|
||||
.varray.get_internal_span();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static MutableSpan<T> ensure_point_attribute(PointCloudComponent &points,
|
||||
const AttributeIDRef &attribute_id)
|
||||
{
|
||||
GMutableSpan attribute = ensure_point_attribute(
|
||||
points, attribute_id, bke::cpp_type_to_custom_data_type(CPPType::get<T>()));
|
||||
return attribute.typed<T>();
|
||||
return points.attributes_for_write()
|
||||
->lookup_or_add_for_write<T>(attribute_id, ATTR_DOMAIN_POINT)
|
||||
.varray.get_internal_span();
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
@@ -506,9 +506,9 @@ static void geometry_set_curve_trim(GeometrySet &geometry_set,
|
||||
|
||||
CurveComponent &component = geometry_set.get_component_for_write<CurveComponent>();
|
||||
GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_CURVE};
|
||||
const int domain_num = component.attribute_domain_num(ATTR_DOMAIN_CURVE);
|
||||
const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_CURVE);
|
||||
|
||||
fn::FieldEvaluator evaluator{field_context, domain_num};
|
||||
fn::FieldEvaluator evaluator{field_context, domain_size};
|
||||
evaluator.add(start_field);
|
||||
evaluator.add(end_field);
|
||||
evaluator.evaluate();
|
||||
|
||||
@@ -252,10 +252,8 @@ static void node_geo_exec(GeoNodeExecParams params)
|
||||
|
||||
BKE_mesh_wrapper_ensure_mdata(surface_mesh_eval);
|
||||
|
||||
MeshComponent mesh_eval;
|
||||
mesh_eval.replace(surface_mesh_eval, GeometryOwnershipType::ReadOnly);
|
||||
MeshComponent mesh_orig;
|
||||
mesh_orig.replace(surface_mesh_orig, GeometryOwnershipType::ReadOnly);
|
||||
const AttributeAccessor mesh_attributes_eval = bke::mesh_attributes(*surface_mesh_eval);
|
||||
const AttributeAccessor mesh_attributes_orig = bke::mesh_attributes(*surface_mesh_orig);
|
||||
|
||||
Curves &curves_id = *curves_geometry.get_curves_for_write();
|
||||
CurvesGeometry &curves = CurvesGeometry::wrap(curves_id.geometry);
|
||||
@@ -266,7 +264,7 @@ static void node_geo_exec(GeoNodeExecParams params)
|
||||
params.error_message_add(NodeWarningType::Error, message);
|
||||
return;
|
||||
}
|
||||
if (!mesh_eval.attribute_exists(uv_map_name)) {
|
||||
if (!mesh_attributes_eval.contains(uv_map_name)) {
|
||||
pass_through_input();
|
||||
char *message = BLI_sprintfN(TIP_("Evaluated surface missing UV map: %s."),
|
||||
uv_map_name.c_str());
|
||||
@@ -274,7 +272,7 @@ static void node_geo_exec(GeoNodeExecParams params)
|
||||
MEM_freeN(message);
|
||||
return;
|
||||
}
|
||||
if (!mesh_orig.attribute_exists(uv_map_name)) {
|
||||
if (!mesh_attributes_orig.contains(uv_map_name)) {
|
||||
pass_through_input();
|
||||
char *message = BLI_sprintfN(TIP_("Original surface missing UV map: %s."),
|
||||
uv_map_name.c_str());
|
||||
@@ -282,7 +280,7 @@ static void node_geo_exec(GeoNodeExecParams params)
|
||||
MEM_freeN(message);
|
||||
return;
|
||||
}
|
||||
if (!mesh_eval.attribute_exists(rest_position_name)) {
|
||||
if (!mesh_attributes_eval.contains(rest_position_name)) {
|
||||
pass_through_input();
|
||||
params.error_message_add(NodeWarningType::Error,
|
||||
TIP_("Evaluated surface missing attribute: rest_position."));
|
||||
@@ -294,12 +292,12 @@ static void node_geo_exec(GeoNodeExecParams params)
|
||||
TIP_("Curves are not attached to any UV map."));
|
||||
return;
|
||||
}
|
||||
const VArraySpan<float2> uv_map_orig = mesh_orig.attribute_get_for_read<float2>(
|
||||
uv_map_name, ATTR_DOMAIN_CORNER, {0.0f, 0.0f});
|
||||
const VArraySpan<float2> uv_map_eval = mesh_eval.attribute_get_for_read<float2>(
|
||||
uv_map_name, ATTR_DOMAIN_CORNER, {0.0f, 0.0f});
|
||||
const VArraySpan<float3> rest_positions = mesh_eval.attribute_get_for_read<float3>(
|
||||
rest_position_name, ATTR_DOMAIN_POINT, {0.0f, 0.0f, 0.0f});
|
||||
const VArraySpan<float2> uv_map_orig = mesh_attributes_orig.lookup<float2>(uv_map_name,
|
||||
ATTR_DOMAIN_CORNER);
|
||||
const VArraySpan<float2> uv_map_eval = mesh_attributes_eval.lookup<float2>(uv_map_name,
|
||||
ATTR_DOMAIN_CORNER);
|
||||
const VArraySpan<float3> rest_positions = mesh_attributes_eval.lookup<float3>(rest_position_name,
|
||||
ATTR_DOMAIN_POINT);
|
||||
const Span<float2> surface_uv_coords = curves.surface_uv_coords();
|
||||
|
||||
const Span<MLoopTri> looptris_orig{BKE_mesh_runtime_looptri_ensure(surface_mesh_orig),
|
||||
|
||||
@@ -49,7 +49,7 @@ static void copy_attributes(const Map<AttributeIDRef, AttributeKind> &attributes
|
||||
{
|
||||
for (Map<AttributeIDRef, AttributeKind>::Item entry : attributes.items()) {
|
||||
const AttributeIDRef attribute_id = entry.key;
|
||||
ReadAttributeLookup attribute = in_component.attribute_try_get_for_read(attribute_id);
|
||||
GAttributeReader attribute = in_component.attributes()->lookup(attribute_id);
|
||||
if (!attribute) {
|
||||
continue;
|
||||
}
|
||||
@@ -60,8 +60,9 @@ static void copy_attributes(const Map<AttributeIDRef, AttributeKind> &attributes
|
||||
}
|
||||
const eCustomDataType data_type = bke::cpp_type_to_custom_data_type(attribute.varray.type());
|
||||
|
||||
OutputAttribute result_attribute = result_component.attribute_try_get_for_output_only(
|
||||
attribute_id, attribute.domain, data_type);
|
||||
GSpanAttributeWriter result_attribute =
|
||||
result_component.attributes_for_write()->lookup_or_add_for_write_only_span(
|
||||
attribute_id, attribute.domain, data_type);
|
||||
|
||||
if (!result_attribute) {
|
||||
continue;
|
||||
@@ -70,10 +71,10 @@ static void copy_attributes(const Map<AttributeIDRef, AttributeKind> &attributes
|
||||
attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
|
||||
using T = decltype(dummy);
|
||||
VArraySpan<T> span{attribute.varray.typed<T>()};
|
||||
MutableSpan<T> out_span = result_attribute.as_span<T>();
|
||||
MutableSpan<T> out_span = result_attribute.span.typed<T>();
|
||||
out_span.copy_from(span);
|
||||
});
|
||||
result_attribute.save();
|
||||
result_attribute.finish();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -89,7 +90,7 @@ static void copy_attributes_based_on_mask(const Map<AttributeIDRef, AttributeKin
|
||||
{
|
||||
for (Map<AttributeIDRef, AttributeKind>::Item entry : attributes.items()) {
|
||||
const AttributeIDRef attribute_id = entry.key;
|
||||
ReadAttributeLookup attribute = in_component.attribute_try_get_for_read(attribute_id);
|
||||
GAttributeReader attribute = in_component.attributes()->lookup(attribute_id);
|
||||
if (!attribute) {
|
||||
continue;
|
||||
}
|
||||
@@ -100,8 +101,9 @@ static void copy_attributes_based_on_mask(const Map<AttributeIDRef, AttributeKin
|
||||
}
|
||||
const eCustomDataType data_type = bke::cpp_type_to_custom_data_type(attribute.varray.type());
|
||||
|
||||
OutputAttribute result_attribute = result_component.attribute_try_get_for_output_only(
|
||||
attribute_id, attribute.domain, data_type);
|
||||
GSpanAttributeWriter result_attribute =
|
||||
result_component.attributes_for_write()->lookup_or_add_for_write_only_span(
|
||||
attribute_id, attribute.domain, data_type);
|
||||
|
||||
if (!result_attribute) {
|
||||
continue;
|
||||
@@ -110,10 +112,10 @@ static void copy_attributes_based_on_mask(const Map<AttributeIDRef, AttributeKin
|
||||
attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
|
||||
using T = decltype(dummy);
|
||||
VArraySpan<T> span{attribute.varray.typed<T>()};
|
||||
MutableSpan<T> out_span = result_attribute.as_span<T>();
|
||||
MutableSpan<T> out_span = result_attribute.span.typed<T>();
|
||||
copy_data_based_on_mask(span, out_span, mask);
|
||||
});
|
||||
result_attribute.save();
|
||||
result_attribute.finish();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -125,7 +127,7 @@ static void copy_attributes_based_on_map(const Map<AttributeIDRef, AttributeKind
|
||||
{
|
||||
for (Map<AttributeIDRef, AttributeKind>::Item entry : attributes.items()) {
|
||||
const AttributeIDRef attribute_id = entry.key;
|
||||
ReadAttributeLookup attribute = in_component.attribute_try_get_for_read(attribute_id);
|
||||
GAttributeReader attribute = in_component.attributes()->lookup(attribute_id);
|
||||
if (!attribute) {
|
||||
continue;
|
||||
}
|
||||
@@ -136,8 +138,9 @@ static void copy_attributes_based_on_map(const Map<AttributeIDRef, AttributeKind
|
||||
}
|
||||
const eCustomDataType data_type = bke::cpp_type_to_custom_data_type(attribute.varray.type());
|
||||
|
||||
OutputAttribute result_attribute = result_component.attribute_try_get_for_output_only(
|
||||
attribute_id, attribute.domain, data_type);
|
||||
GSpanAttributeWriter result_attribute =
|
||||
result_component.attributes_for_write()->lookup_or_add_for_write_only_span(
|
||||
attribute_id, attribute.domain, data_type);
|
||||
|
||||
if (!result_attribute) {
|
||||
continue;
|
||||
@@ -146,10 +149,10 @@ static void copy_attributes_based_on_map(const Map<AttributeIDRef, AttributeKind
|
||||
attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
|
||||
using T = decltype(dummy);
|
||||
VArraySpan<T> span{attribute.varray.typed<T>()};
|
||||
MutableSpan<T> out_span = result_attribute.as_span<T>();
|
||||
MutableSpan<T> out_span = result_attribute.span.typed<T>();
|
||||
copy_data_based_on_map(span, out_span, index_map);
|
||||
});
|
||||
result_attribute.save();
|
||||
result_attribute.finish();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -319,7 +322,7 @@ static void delete_curves_selection(GeometrySet &geometry_set,
|
||||
const CurveComponent &src_component = *geometry_set.get_component_for_read<CurveComponent>();
|
||||
GeometryComponentFieldContext field_context{src_component, selection_domain};
|
||||
|
||||
const int domain_num = src_component.attribute_domain_num(selection_domain);
|
||||
const int domain_num = src_component.attribute_domain_size(selection_domain);
|
||||
fn::FieldEvaluator evaluator{field_context, domain_num};
|
||||
evaluator.set_selection(selection_field);
|
||||
evaluator.evaluate();
|
||||
@@ -351,7 +354,7 @@ static void separate_point_cloud_selection(GeometrySet &geometry_set,
|
||||
*geometry_set.get_component_for_read<PointCloudComponent>();
|
||||
GeometryComponentFieldContext field_context{src_points, ATTR_DOMAIN_POINT};
|
||||
|
||||
fn::FieldEvaluator evaluator{field_context, src_points.attribute_domain_num(ATTR_DOMAIN_POINT)};
|
||||
fn::FieldEvaluator evaluator{field_context, src_points.attribute_domain_size(ATTR_DOMAIN_POINT)};
|
||||
evaluator.set_selection(selection_field);
|
||||
evaluator.evaluate();
|
||||
const IndexMask selection = evaluator.get_evaluated_selection_as_mask();
|
||||
@@ -379,8 +382,7 @@ static void delete_selected_instances(GeometrySet &geometry_set,
|
||||
InstancesComponent &instances = geometry_set.get_component_for_write<InstancesComponent>();
|
||||
GeometryComponentFieldContext field_context{instances, ATTR_DOMAIN_INSTANCE};
|
||||
|
||||
fn::FieldEvaluator evaluator{field_context,
|
||||
instances.attribute_domain_num(ATTR_DOMAIN_INSTANCE)};
|
||||
fn::FieldEvaluator evaluator{field_context, instances.instances_num()};
|
||||
evaluator.set_selection(selection_field);
|
||||
evaluator.evaluate();
|
||||
const IndexMask selection = evaluator.get_evaluated_selection_as_mask();
|
||||
@@ -1058,7 +1060,7 @@ static void separate_mesh_selection(GeometrySet &geometry_set,
|
||||
GeometryComponentFieldContext field_context{src_component, selection_domain};
|
||||
|
||||
fn::FieldEvaluator evaluator{field_context,
|
||||
src_component.attribute_domain_num(selection_domain)};
|
||||
src_component.attribute_domain_size(selection_domain)};
|
||||
evaluator.add(selection_field);
|
||||
evaluator.evaluate();
|
||||
const VArray<bool> selection = evaluator.get_evaluated<bool>(0);
|
||||
|
||||
@@ -290,31 +290,32 @@ BLI_NOINLINE static void propagate_existing_attributes(
|
||||
const Span<int> looptri_indices)
|
||||
{
|
||||
const Mesh &mesh = *mesh_component.get_for_read();
|
||||
const AttributeAccessor mesh_attributes = *mesh_component.attributes();
|
||||
MutableAttributeAccessor point_attributes = *point_component.attributes_for_write();
|
||||
|
||||
for (Map<AttributeIDRef, AttributeKind>::Item entry : attributes.items()) {
|
||||
const AttributeIDRef attribute_id = entry.key;
|
||||
const eCustomDataType output_data_type = entry.value.data_type;
|
||||
|
||||
ReadAttributeLookup source_attribute = mesh_component.attribute_try_get_for_read(attribute_id);
|
||||
GAttributeReader source_attribute = mesh_attributes.lookup(attribute_id);
|
||||
if (!source_attribute) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* The output domain is always #ATTR_DOMAIN_POINT, since we are creating a point cloud. */
|
||||
OutputAttribute attribute_out = point_component.attribute_try_get_for_output_only(
|
||||
GSpanAttributeWriter attribute_out = point_attributes.lookup_or_add_for_write_only_span(
|
||||
attribute_id, ATTR_DOMAIN_POINT, output_data_type);
|
||||
if (!attribute_out) {
|
||||
continue;
|
||||
}
|
||||
|
||||
GMutableSpan out_span = attribute_out.as_span();
|
||||
interpolate_attribute(mesh,
|
||||
bary_coords,
|
||||
looptri_indices,
|
||||
source_attribute.domain,
|
||||
source_attribute.varray,
|
||||
out_span);
|
||||
attribute_out.save();
|
||||
attribute_out.span);
|
||||
attribute_out.finish();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -331,25 +332,21 @@ BLI_NOINLINE static void compute_attribute_outputs(const MeshComponent &mesh_com
|
||||
const Span<int> looptri_indices,
|
||||
const AttributeOutputs &attribute_outputs)
|
||||
{
|
||||
OutputAttribute_Typed<int> id_attribute = point_component.attribute_try_get_for_output_only<int>(
|
||||
MutableAttributeAccessor pointcloud_attributes = *point_component.attributes_for_write();
|
||||
|
||||
SpanAttributeWriter<int> ids = pointcloud_attributes.lookup_or_add_for_write_only_span<int>(
|
||||
"id", ATTR_DOMAIN_POINT);
|
||||
MutableSpan<int> ids = id_attribute.as_span();
|
||||
|
||||
OutputAttribute_Typed<float3> normal_attribute;
|
||||
OutputAttribute_Typed<float3> rotation_attribute;
|
||||
|
||||
MutableSpan<float3> normals;
|
||||
MutableSpan<float3> rotations;
|
||||
SpanAttributeWriter<float3> normals;
|
||||
SpanAttributeWriter<float3> rotations;
|
||||
|
||||
if (attribute_outputs.normal_id) {
|
||||
normal_attribute = point_component.attribute_try_get_for_output_only<float3>(
|
||||
normals = pointcloud_attributes.lookup_or_add_for_write_only_span<float3>(
|
||||
attribute_outputs.normal_id.get(), ATTR_DOMAIN_POINT);
|
||||
normals = normal_attribute.as_span();
|
||||
}
|
||||
if (attribute_outputs.rotation_id) {
|
||||
rotation_attribute = point_component.attribute_try_get_for_output_only<float3>(
|
||||
rotations = pointcloud_attributes.lookup_or_add_for_write_only_span<float3>(
|
||||
attribute_outputs.rotation_id.get(), ATTR_DOMAIN_POINT);
|
||||
rotations = rotation_attribute.as_span();
|
||||
}
|
||||
|
||||
const Mesh &mesh = *mesh_component.get_for_read();
|
||||
@@ -368,27 +365,27 @@ BLI_NOINLINE static void compute_attribute_outputs(const MeshComponent &mesh_com
|
||||
const float3 v1_pos = float3(mesh.mvert[v1_index].co);
|
||||
const float3 v2_pos = float3(mesh.mvert[v2_index].co);
|
||||
|
||||
ids[i] = noise::hash(noise::hash_float(bary_coord), looptri_index);
|
||||
ids.span[i] = noise::hash(noise::hash_float(bary_coord), looptri_index);
|
||||
|
||||
float3 normal;
|
||||
if (!normals.is_empty() || !rotations.is_empty()) {
|
||||
if (!normals.span.is_empty() || !rotations.span.is_empty()) {
|
||||
normal_tri_v3(normal, v0_pos, v1_pos, v2_pos);
|
||||
}
|
||||
if (!normals.is_empty()) {
|
||||
normals[i] = normal;
|
||||
if (!normals.span.is_empty()) {
|
||||
normals.span[i] = normal;
|
||||
}
|
||||
if (!rotations.is_empty()) {
|
||||
rotations[i] = normal_to_euler_rotation(normal);
|
||||
if (!rotations.span.is_empty()) {
|
||||
rotations.span[i] = normal_to_euler_rotation(normal);
|
||||
}
|
||||
}
|
||||
|
||||
id_attribute.save();
|
||||
ids.finish();
|
||||
|
||||
if (normal_attribute) {
|
||||
normal_attribute.save();
|
||||
if (normals) {
|
||||
normals.finish();
|
||||
}
|
||||
if (rotation_attribute) {
|
||||
rotation_attribute.save();
|
||||
if (rotations) {
|
||||
rotations.finish();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -398,11 +395,11 @@ static Array<float> calc_full_density_factors_with_selection(const MeshComponent
|
||||
{
|
||||
const eAttrDomain attribute_domain = ATTR_DOMAIN_CORNER;
|
||||
GeometryComponentFieldContext field_context{component, attribute_domain};
|
||||
const int domain_num = component.attribute_domain_num(attribute_domain);
|
||||
const int domain_size = component.attribute_domain_size(attribute_domain);
|
||||
|
||||
Array<float> densities(domain_num, 0.0f);
|
||||
Array<float> densities(domain_size, 0.0f);
|
||||
|
||||
fn::FieldEvaluator evaluator{field_context, domain_num};
|
||||
fn::FieldEvaluator evaluator{field_context, domain_size};
|
||||
evaluator.set_selection(selection_field);
|
||||
evaluator.add_with_destination(density_field, densities.as_mutable_span());
|
||||
evaluator.evaluate();
|
||||
|
||||
@@ -146,9 +146,12 @@ static void transfer_attributes(
|
||||
const GeometryComponent &src_component,
|
||||
GeometryComponent &dst_component)
|
||||
{
|
||||
const AttributeAccessor src_attributes = *src_component.attributes();
|
||||
MutableAttributeAccessor dst_attributes = *dst_component.attributes_for_write();
|
||||
|
||||
for (Map<AttributeIDRef, AttributeKind>::Item entry : attributes.items()) {
|
||||
const AttributeIDRef attribute_id = entry.key;
|
||||
ReadAttributeLookup src_attribute = src_component.attribute_try_get_for_read(attribute_id);
|
||||
GAttributeReader src_attribute = src_attributes.lookup(attribute_id);
|
||||
if (!src_attribute) {
|
||||
continue;
|
||||
}
|
||||
@@ -166,7 +169,7 @@ static void transfer_attributes(
|
||||
}
|
||||
const eCustomDataType data_type = bke::cpp_type_to_custom_data_type(
|
||||
src_attribute.varray.type());
|
||||
OutputAttribute dst_attribute = dst_component.attribute_try_get_for_output_only(
|
||||
GSpanAttributeWriter dst_attribute = dst_attributes.lookup_or_add_for_write_only_span(
|
||||
attribute_id, out_domain, data_type);
|
||||
|
||||
if (!dst_attribute) {
|
||||
@@ -176,7 +179,7 @@ static void transfer_attributes(
|
||||
attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
|
||||
using T = decltype(dummy);
|
||||
VArraySpan<T> span{src_attribute.varray.typed<T>()};
|
||||
MutableSpan<T> dst_span = dst_attribute.as_span<T>();
|
||||
MutableSpan<T> dst_span = dst_attribute.span.typed<T>();
|
||||
if (src_attribute.domain == ATTR_DOMAIN_FACE) {
|
||||
dst_span.take_front(span.size()).copy_from(span);
|
||||
if (keep_boundaries) {
|
||||
@@ -193,7 +196,7 @@ static void transfer_attributes(
|
||||
copy_data_based_on_new_to_old_map(span, dst_span, new_to_old_face_corners_map);
|
||||
}
|
||||
});
|
||||
dst_attribute.save();
|
||||
dst_attribute.finish();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -147,17 +147,17 @@ static void create_duplicate_index_attribute(GeometryComponent &component,
|
||||
const IndexAttributes &attribute_outputs,
|
||||
const Span<int> offsets)
|
||||
{
|
||||
OutputAttribute_Typed<int> copy_attribute = component.attribute_try_get_for_output_only<int>(
|
||||
attribute_outputs.duplicate_index.get(), output_domain);
|
||||
MutableSpan<int> duplicate_indices = copy_attribute.as_span();
|
||||
SpanAttributeWriter<int> duplicate_indices =
|
||||
component.attributes_for_write()->lookup_or_add_for_write_only_span<int>(
|
||||
attribute_outputs.duplicate_index.get(), output_domain);
|
||||
for (const int i : IndexRange(selection.size())) {
|
||||
const IndexRange range = range_for_offsets_index(offsets, i);
|
||||
MutableSpan<int> indices = duplicate_indices.slice(range);
|
||||
MutableSpan<int> indices = duplicate_indices.span.slice(range);
|
||||
for (const int i : indices.index_range()) {
|
||||
indices[i] = i;
|
||||
}
|
||||
}
|
||||
copy_attribute.save();
|
||||
duplicate_indices.finish();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -168,20 +168,21 @@ static void copy_stable_id_point(const Span<int> offsets,
|
||||
const GeometryComponent &src_component,
|
||||
GeometryComponent &dst_component)
|
||||
{
|
||||
ReadAttributeLookup src_attribute = src_component.attribute_try_get_for_read("id");
|
||||
GAttributeReader src_attribute = src_component.attributes()->lookup("id");
|
||||
if (!src_attribute) {
|
||||
return;
|
||||
}
|
||||
OutputAttribute dst_attribute = dst_component.attribute_try_get_for_output_only(
|
||||
"id", ATTR_DOMAIN_POINT, CD_PROP_INT32);
|
||||
GSpanAttributeWriter dst_attribute =
|
||||
dst_component.attributes_for_write()->lookup_or_add_for_write_only_span(
|
||||
"id", ATTR_DOMAIN_POINT, CD_PROP_INT32);
|
||||
if (!dst_attribute) {
|
||||
return;
|
||||
}
|
||||
|
||||
VArraySpan<int> src{src_attribute.varray.typed<int>()};
|
||||
MutableSpan<int> dst = dst_attribute.as_span<int>();
|
||||
MutableSpan<int> dst = dst_attribute.span.typed<int>();
|
||||
threaded_id_offset_copy(offsets, src, dst);
|
||||
dst_attribute.save();
|
||||
dst_attribute.finish();
|
||||
}
|
||||
|
||||
static void copy_attributes_without_id(GeometrySet &geometry_set,
|
||||
@@ -197,25 +198,26 @@ static void copy_attributes_without_id(GeometrySet &geometry_set,
|
||||
|
||||
for (const Map<AttributeIDRef, AttributeKind>::Item entry : attributes.items()) {
|
||||
const AttributeIDRef attribute_id = entry.key;
|
||||
ReadAttributeLookup src_attribute = src_component.attribute_try_get_for_read(attribute_id);
|
||||
GAttributeReader src_attribute = src_component.attributes()->lookup(attribute_id);
|
||||
if (!src_attribute || src_attribute.domain != domain) {
|
||||
continue;
|
||||
}
|
||||
eAttrDomain out_domain = src_attribute.domain;
|
||||
const eCustomDataType data_type = bke::cpp_type_to_custom_data_type(
|
||||
src_attribute.varray.type());
|
||||
OutputAttribute dst_attribute = dst_component.attribute_try_get_for_output_only(
|
||||
attribute_id, out_domain, data_type);
|
||||
GSpanAttributeWriter dst_attribute =
|
||||
dst_component.attributes_for_write()->lookup_or_add_for_write_only_span(
|
||||
attribute_id, out_domain, data_type);
|
||||
if (!dst_attribute) {
|
||||
continue;
|
||||
}
|
||||
attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
|
||||
using T = decltype(dummy);
|
||||
VArraySpan<T> src = src_attribute.varray.typed<T>();
|
||||
MutableSpan<T> dst = dst_attribute.as_span<T>();
|
||||
MutableSpan<T> dst = dst_attribute.span.typed<T>();
|
||||
threaded_slice_fill<T>(offsets, selection, src, dst);
|
||||
});
|
||||
dst_attribute.save();
|
||||
dst_attribute.finish();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -242,7 +244,7 @@ static void copy_curve_attributes_without_id(const GeometrySet &geometry_set,
|
||||
|
||||
for (const Map<AttributeIDRef, AttributeKind>::Item entry : attributes.items()) {
|
||||
const AttributeIDRef attribute_id = entry.key;
|
||||
ReadAttributeLookup src_attribute = src_component.attribute_try_get_for_read(attribute_id);
|
||||
GAttributeReader src_attribute = src_component.attributes()->lookup(attribute_id);
|
||||
if (!src_attribute) {
|
||||
continue;
|
||||
}
|
||||
@@ -250,8 +252,9 @@ static void copy_curve_attributes_without_id(const GeometrySet &geometry_set,
|
||||
eAttrDomain out_domain = src_attribute.domain;
|
||||
const eCustomDataType data_type = bke::cpp_type_to_custom_data_type(
|
||||
src_attribute.varray.type());
|
||||
OutputAttribute dst_attribute = dst_component.attribute_try_get_for_output_only(
|
||||
attribute_id, out_domain, data_type);
|
||||
GSpanAttributeWriter dst_attribute =
|
||||
dst_component.attributes_for_write()->lookup_or_add_for_write_only_span(
|
||||
attribute_id, out_domain, data_type);
|
||||
if (!dst_attribute) {
|
||||
continue;
|
||||
}
|
||||
@@ -259,7 +262,7 @@ static void copy_curve_attributes_without_id(const GeometrySet &geometry_set,
|
||||
attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
|
||||
using T = decltype(dummy);
|
||||
VArraySpan<T> src{src_attribute.varray.typed<T>()};
|
||||
MutableSpan<T> dst = dst_attribute.as_span<T>();
|
||||
MutableSpan<T> dst = dst_attribute.span.typed<T>();
|
||||
|
||||
switch (out_domain) {
|
||||
case ATTR_DOMAIN_CURVE:
|
||||
@@ -280,7 +283,7 @@ static void copy_curve_attributes_without_id(const GeometrySet &geometry_set,
|
||||
break;
|
||||
}
|
||||
});
|
||||
dst_attribute.save();
|
||||
dst_attribute.finish();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -297,18 +300,19 @@ static void copy_stable_id_curves(const bke::CurvesGeometry &src_curves,
|
||||
bke::CurvesGeometry &dst_curves,
|
||||
CurveComponent &dst_component)
|
||||
{
|
||||
ReadAttributeLookup src_attribute = src_component.attribute_try_get_for_read("id");
|
||||
GAttributeReader src_attribute = src_component.attributes()->lookup("id");
|
||||
if (!src_attribute) {
|
||||
return;
|
||||
}
|
||||
OutputAttribute dst_attribute = dst_component.attribute_try_get_for_output_only(
|
||||
"id", ATTR_DOMAIN_POINT, CD_PROP_INT32);
|
||||
GSpanAttributeWriter dst_attribute =
|
||||
dst_component.attributes_for_write()->lookup_or_add_for_write_only_span(
|
||||
"id", ATTR_DOMAIN_POINT, CD_PROP_INT32);
|
||||
if (!dst_attribute) {
|
||||
return;
|
||||
}
|
||||
|
||||
VArraySpan<int> src{src_attribute.varray.typed<int>()};
|
||||
MutableSpan<int> dst = dst_attribute.as_span<int>();
|
||||
MutableSpan<int> dst = dst_attribute.span.typed<int>();
|
||||
|
||||
threading::parallel_for(selection.index_range(), 512, [&](IndexRange range) {
|
||||
for (const int i_selection : range) {
|
||||
@@ -322,7 +326,7 @@ static void copy_stable_id_curves(const bke::CurvesGeometry &src_curves,
|
||||
}
|
||||
}
|
||||
});
|
||||
dst_attribute.save();
|
||||
dst_attribute.finish();
|
||||
}
|
||||
|
||||
static void duplicate_curves(GeometrySet &geometry_set,
|
||||
@@ -423,7 +427,7 @@ static void copy_face_attributes_without_id(GeometrySet &geometry_set,
|
||||
|
||||
for (const Map<AttributeIDRef, AttributeKind>::Item entry : attributes.items()) {
|
||||
const AttributeIDRef attribute_id = entry.key;
|
||||
ReadAttributeLookup src_attribute = src_component.attribute_try_get_for_read(attribute_id);
|
||||
GAttributeReader src_attribute = src_component.attributes()->lookup(attribute_id);
|
||||
if (!src_attribute) {
|
||||
continue;
|
||||
}
|
||||
@@ -431,8 +435,9 @@ static void copy_face_attributes_without_id(GeometrySet &geometry_set,
|
||||
eAttrDomain out_domain = src_attribute.domain;
|
||||
const eCustomDataType data_type = bke::cpp_type_to_custom_data_type(
|
||||
src_attribute.varray.type());
|
||||
OutputAttribute dst_attribute = dst_component.attribute_try_get_for_output_only(
|
||||
attribute_id, out_domain, data_type);
|
||||
GSpanAttributeWriter dst_attribute =
|
||||
dst_component.attributes_for_write()->lookup_or_add_for_write_only_span(
|
||||
attribute_id, out_domain, data_type);
|
||||
if (!dst_attribute) {
|
||||
continue;
|
||||
}
|
||||
@@ -440,7 +445,7 @@ static void copy_face_attributes_without_id(GeometrySet &geometry_set,
|
||||
attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
|
||||
using T = decltype(dummy);
|
||||
VArraySpan<T> src{src_attribute.varray.typed<T>()};
|
||||
MutableSpan<T> dst = dst_attribute.as_span<T>();
|
||||
MutableSpan<T> dst = dst_attribute.span.typed<T>();
|
||||
|
||||
switch (out_domain) {
|
||||
case ATTR_DOMAIN_FACE:
|
||||
@@ -459,7 +464,7 @@ static void copy_face_attributes_without_id(GeometrySet &geometry_set,
|
||||
break;
|
||||
}
|
||||
});
|
||||
dst_attribute.save();
|
||||
dst_attribute.finish();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -477,18 +482,19 @@ static void copy_stable_id_faces(const Mesh &mesh,
|
||||
const MeshComponent &src_component,
|
||||
MeshComponent &dst_component)
|
||||
{
|
||||
ReadAttributeLookup src_attribute = src_component.attribute_try_get_for_read("id");
|
||||
GAttributeReader src_attribute = src_component.attributes()->lookup("id");
|
||||
if (!src_attribute) {
|
||||
return;
|
||||
}
|
||||
OutputAttribute dst_attribute = dst_component.attribute_try_get_for_output_only(
|
||||
"id", ATTR_DOMAIN_POINT, CD_PROP_INT32);
|
||||
GSpanAttributeWriter dst_attribute =
|
||||
dst_component.attributes_for_write()->lookup_or_add_for_write_only_span(
|
||||
"id", ATTR_DOMAIN_POINT, CD_PROP_INT32);
|
||||
if (!dst_attribute) {
|
||||
return;
|
||||
}
|
||||
|
||||
VArraySpan<int> src{src_attribute.varray.typed<int>()};
|
||||
MutableSpan<int> dst = dst_attribute.as_span<int>();
|
||||
MutableSpan<int> dst = dst_attribute.span.typed<int>();
|
||||
|
||||
Span<MPoly> polys(mesh.mpoly, mesh.totpoly);
|
||||
int loop_index = 0;
|
||||
@@ -511,7 +517,7 @@ static void copy_stable_id_faces(const Mesh &mesh,
|
||||
}
|
||||
}
|
||||
|
||||
dst_attribute.save();
|
||||
dst_attribute.finish();
|
||||
}
|
||||
|
||||
static void duplicate_faces(GeometrySet &geometry_set,
|
||||
@@ -636,7 +642,7 @@ static void copy_edge_attributes_without_id(GeometrySet &geometry_set,
|
||||
|
||||
for (const Map<AttributeIDRef, AttributeKind>::Item entry : attributes.items()) {
|
||||
const AttributeIDRef attribute_id = entry.key;
|
||||
ReadAttributeLookup src_attribute = src_component.attribute_try_get_for_read(attribute_id);
|
||||
GAttributeReader src_attribute = src_component.attributes()->lookup(attribute_id);
|
||||
if (!src_attribute) {
|
||||
continue;
|
||||
}
|
||||
@@ -644,15 +650,16 @@ static void copy_edge_attributes_without_id(GeometrySet &geometry_set,
|
||||
const eAttrDomain out_domain = src_attribute.domain;
|
||||
const eCustomDataType data_type = bke::cpp_type_to_custom_data_type(
|
||||
src_attribute.varray.type());
|
||||
OutputAttribute dst_attribute = dst_component.attribute_try_get_for_output_only(
|
||||
attribute_id, out_domain, data_type);
|
||||
GSpanAttributeWriter dst_attribute =
|
||||
dst_component.attributes_for_write()->lookup_or_add_for_write_only_span(
|
||||
attribute_id, out_domain, data_type);
|
||||
if (!dst_attribute) {
|
||||
continue;
|
||||
}
|
||||
attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
|
||||
using T = decltype(dummy);
|
||||
VArraySpan<T> src{src_attribute.varray.typed<T>()};
|
||||
MutableSpan<T> dst = dst_attribute.as_span<T>();
|
||||
MutableSpan<T> dst = dst_attribute.span.typed<T>();
|
||||
|
||||
switch (out_domain) {
|
||||
case ATTR_DOMAIN_EDGE:
|
||||
@@ -665,7 +672,7 @@ static void copy_edge_attributes_without_id(GeometrySet &geometry_set,
|
||||
break;
|
||||
}
|
||||
});
|
||||
dst_attribute.save();
|
||||
dst_attribute.finish();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -679,12 +686,13 @@ static void copy_stable_id_edges(const Mesh &mesh,
|
||||
const MeshComponent &src_component,
|
||||
MeshComponent &dst_component)
|
||||
{
|
||||
ReadAttributeLookup src_attribute = src_component.attribute_try_get_for_read("id");
|
||||
GAttributeReader src_attribute = src_component.attributes()->lookup("id");
|
||||
if (!src_attribute) {
|
||||
return;
|
||||
}
|
||||
OutputAttribute dst_attribute = dst_component.attribute_try_get_for_output_only(
|
||||
"id", ATTR_DOMAIN_POINT, CD_PROP_INT32);
|
||||
GSpanAttributeWriter dst_attribute =
|
||||
dst_component.attributes_for_write()->lookup_or_add_for_write_only_span(
|
||||
"id", ATTR_DOMAIN_POINT, CD_PROP_INT32);
|
||||
if (!dst_attribute) {
|
||||
return;
|
||||
}
|
||||
@@ -692,7 +700,7 @@ static void copy_stable_id_edges(const Mesh &mesh,
|
||||
Span<MEdge> edges(mesh.medge, mesh.totedge);
|
||||
|
||||
VArraySpan<int> src{src_attribute.varray.typed<int>()};
|
||||
MutableSpan<int> dst = dst_attribute.as_span<int>();
|
||||
MutableSpan<int> dst = dst_attribute.span.typed<int>();
|
||||
threading::parallel_for(IndexRange(selection.size()), 1024, [&](IndexRange range) {
|
||||
for (const int i_selection : range) {
|
||||
const IndexRange edge_range = range_for_offsets_index(edge_offsets, i_selection);
|
||||
@@ -710,7 +718,7 @@ static void copy_stable_id_edges(const Mesh &mesh,
|
||||
}
|
||||
}
|
||||
});
|
||||
dst_attribute.save();
|
||||
dst_attribute.finish();
|
||||
}
|
||||
|
||||
static void duplicate_edges(GeometrySet &geometry_set,
|
||||
@@ -837,7 +845,7 @@ static void duplicate_points_curve(GeometrySet &geometry_set,
|
||||
|
||||
for (const Map<AttributeIDRef, AttributeKind>::Item entry : attributes.items()) {
|
||||
const AttributeIDRef attribute_id = entry.key;
|
||||
ReadAttributeLookup src_attribute = src_component.attribute_try_get_for_read(attribute_id);
|
||||
GAttributeReader src_attribute = src_component.attributes()->lookup(attribute_id);
|
||||
if (!src_attribute) {
|
||||
continue;
|
||||
}
|
||||
@@ -845,8 +853,9 @@ static void duplicate_points_curve(GeometrySet &geometry_set,
|
||||
eAttrDomain domain = src_attribute.domain;
|
||||
const eCustomDataType data_type = bke::cpp_type_to_custom_data_type(
|
||||
src_attribute.varray.type());
|
||||
OutputAttribute dst_attribute = dst_component.attribute_try_get_for_output_only(
|
||||
attribute_id, domain, data_type);
|
||||
GSpanAttributeWriter dst_attribute =
|
||||
dst_component.attributes_for_write()->lookup_or_add_for_write_only_span(
|
||||
attribute_id, domain, data_type);
|
||||
if (!dst_attribute) {
|
||||
continue;
|
||||
}
|
||||
@@ -854,7 +863,7 @@ static void duplicate_points_curve(GeometrySet &geometry_set,
|
||||
attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
|
||||
using T = decltype(dummy);
|
||||
VArraySpan<T> src{src_attribute.varray.typed<T>()};
|
||||
MutableSpan<T> dst = dst_attribute.as_span<T>();
|
||||
MutableSpan<T> dst = dst_attribute.span.typed<T>();
|
||||
|
||||
switch (domain) {
|
||||
case ATTR_DOMAIN_CURVE:
|
||||
@@ -873,7 +882,7 @@ static void duplicate_points_curve(GeometrySet &geometry_set,
|
||||
break;
|
||||
}
|
||||
});
|
||||
dst_attribute.save();
|
||||
dst_attribute.finish();
|
||||
}
|
||||
|
||||
copy_stable_id_point(offsets, src_component, dst_component);
|
||||
@@ -949,7 +958,7 @@ static void duplicate_points_pointcloud(GeometrySet &geometry_set,
|
||||
{
|
||||
const PointCloudComponent &src_points =
|
||||
*geometry_set.get_component_for_read<PointCloudComponent>();
|
||||
const int point_num = src_points.attribute_domain_num(ATTR_DOMAIN_POINT);
|
||||
const int point_num = src_points.attribute_domain_size(ATTR_DOMAIN_POINT);
|
||||
|
||||
GeometryComponentFieldContext field_context{src_points, ATTR_DOMAIN_POINT};
|
||||
FieldEvaluator evaluator{field_context, point_num};
|
||||
|
||||
@@ -57,8 +57,8 @@ static void node_geo_exec(GeoNodeExecParams params)
|
||||
|
||||
const MeshComponent &mesh_component = *geometry_set.get_component_for_read<MeshComponent>();
|
||||
GeometryComponentFieldContext field_context{mesh_component, ATTR_DOMAIN_EDGE};
|
||||
const int domain_num = mesh_component.attribute_domain_num(ATTR_DOMAIN_EDGE);
|
||||
fn::FieldEvaluator selection_evaluator{field_context, domain_num};
|
||||
const int domain_size = mesh_component.attribute_domain_size(ATTR_DOMAIN_EDGE);
|
||||
fn::FieldEvaluator selection_evaluator{field_context, domain_size};
|
||||
selection_evaluator.add(selection_field);
|
||||
selection_evaluator.evaluate();
|
||||
const IndexMask selection = selection_evaluator.get_evaluated_as_mask(0);
|
||||
|
||||
@@ -66,21 +66,21 @@ static void save_selection_as_attribute(MeshComponent &component,
|
||||
const eAttrDomain domain,
|
||||
const IndexMask selection)
|
||||
{
|
||||
BLI_assert(!component.attribute_exists(id));
|
||||
BLI_assert(!component.attributes()->contains(id));
|
||||
|
||||
OutputAttribute_Typed<bool> attribute = component.attribute_try_get_for_output_only<bool>(
|
||||
id, domain);
|
||||
SpanAttributeWriter<bool> attribute =
|
||||
component.attributes_for_write()->lookup_or_add_for_write_span<bool>(id, domain);
|
||||
/* Rely on the new attribute being zeroed by default. */
|
||||
BLI_assert(!attribute.as_span().as_span().contains(true));
|
||||
BLI_assert(!attribute.span.as_span().contains(true));
|
||||
|
||||
if (selection.is_range()) {
|
||||
attribute.as_span().slice(selection.as_range()).fill(true);
|
||||
attribute.span.slice(selection.as_range()).fill(true);
|
||||
}
|
||||
else {
|
||||
attribute.as_span().fill_indices(selection, true);
|
||||
attribute.span.fill_indices(selection, true);
|
||||
}
|
||||
|
||||
attribute.save();
|
||||
attribute.finish();
|
||||
}
|
||||
|
||||
static MutableSpan<MVert> mesh_verts(Mesh &mesh)
|
||||
@@ -168,7 +168,7 @@ static MutableSpan<int> get_orig_index_layer(Mesh &mesh, const eAttrDomain domai
|
||||
component.replace(&mesh, GeometryOwnershipType::ReadOnly);
|
||||
CustomData &custom_data = get_customdata(mesh, domain);
|
||||
if (int *orig_indices = static_cast<int *>(CustomData_get_layer(&custom_data, CD_ORIGINDEX))) {
|
||||
return {orig_indices, component.attribute_domain_num(domain)};
|
||||
return {orig_indices, component.attribute_domain_size(domain)};
|
||||
}
|
||||
return {};
|
||||
}
|
||||
@@ -280,16 +280,18 @@ static void extrude_mesh_vertices(MeshComponent &component,
|
||||
new_edges[i_selection] = new_loose_edge(selection[i_selection], new_vert_range[i_selection]);
|
||||
}
|
||||
|
||||
component.attribute_foreach([&](const AttributeIDRef &id, const AttributeMetaData meta_data) {
|
||||
MutableAttributeAccessor attributes = *component.attributes_for_write();
|
||||
|
||||
attributes.for_all([&](const AttributeIDRef &id, const AttributeMetaData meta_data) {
|
||||
if (!ELEM(meta_data.domain, ATTR_DOMAIN_POINT, ATTR_DOMAIN_EDGE)) {
|
||||
return true;
|
||||
}
|
||||
OutputAttribute attribute = component.attribute_try_get_for_output(
|
||||
GSpanAttributeWriter attribute = attributes.lookup_or_add_for_write_span(
|
||||
id, meta_data.domain, meta_data.data_type);
|
||||
attribute_math::convert_to_static_type(meta_data.data_type, [&](auto dummy) {
|
||||
using T = decltype(dummy);
|
||||
MutableSpan<T> data = attribute.as_span().typed<T>();
|
||||
switch (attribute.domain()) {
|
||||
MutableSpan<T> data = attribute.span.typed<T>();
|
||||
switch (attribute.domain) {
|
||||
case ATTR_DOMAIN_POINT: {
|
||||
/* New vertices copy the attribute values from their source vertex. */
|
||||
copy_with_mask(data.slice(new_vert_range), data.as_span(), selection);
|
||||
@@ -307,7 +309,7 @@ static void extrude_mesh_vertices(MeshComponent &component,
|
||||
}
|
||||
});
|
||||
|
||||
attribute.save();
|
||||
attribute.finish();
|
||||
return true;
|
||||
});
|
||||
|
||||
@@ -524,8 +526,10 @@ static void extrude_mesh_edges(MeshComponent &component,
|
||||
const Array<Vector<int>> new_vert_to_duplicate_edge_map = create_vert_to_edge_map(
|
||||
new_vert_range.size(), duplicate_edges, orig_vert_size);
|
||||
|
||||
component.attribute_foreach([&](const AttributeIDRef &id, const AttributeMetaData meta_data) {
|
||||
OutputAttribute attribute = component.attribute_try_get_for_output(
|
||||
MutableAttributeAccessor attributes = *component.attributes_for_write();
|
||||
|
||||
attributes.for_all([&](const AttributeIDRef &id, const AttributeMetaData meta_data) {
|
||||
GSpanAttributeWriter attribute = attributes.lookup_or_add_for_write_span(
|
||||
id, meta_data.domain, meta_data.data_type);
|
||||
if (!attribute) {
|
||||
return true; /* Impossible to write the "normal" attribute. */
|
||||
@@ -533,8 +537,8 @@ static void extrude_mesh_edges(MeshComponent &component,
|
||||
|
||||
attribute_math::convert_to_static_type(meta_data.data_type, [&](auto dummy) {
|
||||
using T = decltype(dummy);
|
||||
MutableSpan<T> data = attribute.as_span().typed<T>();
|
||||
switch (attribute.domain()) {
|
||||
MutableSpan<T> data = attribute.span.typed<T>();
|
||||
switch (attribute.domain) {
|
||||
case ATTR_DOMAIN_POINT: {
|
||||
/* New vertices copy the attribute values from their source vertex. */
|
||||
copy_with_indices(data.slice(new_vert_range), data.as_span(), new_vert_indices);
|
||||
@@ -626,7 +630,7 @@ static void extrude_mesh_edges(MeshComponent &component,
|
||||
}
|
||||
});
|
||||
|
||||
attribute.save();
|
||||
attribute.finish();
|
||||
return true;
|
||||
});
|
||||
|
||||
@@ -902,8 +906,10 @@ static void extrude_mesh_face_regions(MeshComponent &component,
|
||||
const Array<Vector<int>> new_vert_to_duplicate_edge_map = create_vert_to_edge_map(
|
||||
new_vert_range.size(), boundary_edges, orig_vert_size);
|
||||
|
||||
component.attribute_foreach([&](const AttributeIDRef &id, const AttributeMetaData meta_data) {
|
||||
OutputAttribute attribute = component.attribute_try_get_for_output(
|
||||
MutableAttributeAccessor attributes = *component.attributes_for_write();
|
||||
|
||||
attributes.for_all([&](const AttributeIDRef &id, const AttributeMetaData meta_data) {
|
||||
GSpanAttributeWriter attribute = attributes.lookup_or_add_for_write_span(
|
||||
id, meta_data.domain, meta_data.data_type);
|
||||
if (!attribute) {
|
||||
return true; /* Impossible to write the "normal" attribute. */
|
||||
@@ -911,8 +917,8 @@ static void extrude_mesh_face_regions(MeshComponent &component,
|
||||
|
||||
attribute_math::convert_to_static_type(meta_data.data_type, [&](auto dummy) {
|
||||
using T = decltype(dummy);
|
||||
MutableSpan<T> data = attribute.as_span().typed<T>();
|
||||
switch (attribute.domain()) {
|
||||
MutableSpan<T> data = attribute.span.typed<T>();
|
||||
switch (attribute.domain) {
|
||||
case ATTR_DOMAIN_POINT: {
|
||||
/* New vertices copy the attributes from their original vertices. */
|
||||
copy_with_indices(data.slice(new_vert_range), data.as_span(), new_vert_indices);
|
||||
@@ -991,7 +997,7 @@ static void extrude_mesh_face_regions(MeshComponent &component,
|
||||
}
|
||||
});
|
||||
|
||||
attribute.save();
|
||||
attribute.finish();
|
||||
return true;
|
||||
});
|
||||
|
||||
@@ -1154,8 +1160,10 @@ static void extrude_individual_mesh_faces(MeshComponent &component,
|
||||
}
|
||||
});
|
||||
|
||||
component.attribute_foreach([&](const AttributeIDRef &id, const AttributeMetaData meta_data) {
|
||||
OutputAttribute attribute = component.attribute_try_get_for_output(
|
||||
MutableAttributeAccessor attributes = *component.attributes_for_write();
|
||||
|
||||
attributes.for_all([&](const AttributeIDRef &id, const AttributeMetaData meta_data) {
|
||||
GSpanAttributeWriter attribute = attributes.lookup_or_add_for_write_span(
|
||||
id, meta_data.domain, meta_data.data_type);
|
||||
if (!attribute) {
|
||||
return true; /* Impossible to write the "normal" attribute. */
|
||||
@@ -1163,8 +1171,8 @@ static void extrude_individual_mesh_faces(MeshComponent &component,
|
||||
|
||||
attribute_math::convert_to_static_type(meta_data.data_type, [&](auto dummy) {
|
||||
using T = decltype(dummy);
|
||||
MutableSpan<T> data = attribute.as_span().typed<T>();
|
||||
switch (attribute.domain()) {
|
||||
MutableSpan<T> data = attribute.span.typed<T>();
|
||||
switch (attribute.domain) {
|
||||
case ATTR_DOMAIN_POINT: {
|
||||
/* New vertices copy the attributes from their original vertices. */
|
||||
MutableSpan<T> new_data = data.slice(new_vert_range);
|
||||
@@ -1267,7 +1275,7 @@ static void extrude_individual_mesh_faces(MeshComponent &component,
|
||||
}
|
||||
});
|
||||
|
||||
attribute.save();
|
||||
attribute.finish();
|
||||
return true;
|
||||
});
|
||||
|
||||
|
||||
@@ -91,7 +91,7 @@ class FieldAtIndex final : public GeometryFieldInput {
|
||||
{
|
||||
const GeometryComponentFieldContext value_field_context{component, value_field_domain_};
|
||||
FieldEvaluator value_evaluator{value_field_context,
|
||||
component.attribute_domain_num(value_field_domain_)};
|
||||
component.attribute_domain_size(value_field_domain_)};
|
||||
value_evaluator.add(value_field_);
|
||||
value_evaluator.evaluate();
|
||||
const GVArray &values = value_evaluator.get_evaluated(0);
|
||||
|
||||
@@ -85,12 +85,12 @@ class FieldOnDomain final : public GeometryFieldInput {
|
||||
IndexMask /* mask */) const final
|
||||
{
|
||||
const GeometryComponentFieldContext context{component, src_domain_};
|
||||
FieldEvaluator value_evaluator{context, component.attribute_domain_num(src_domain_)};
|
||||
FieldEvaluator value_evaluator{context, component.attribute_domain_size(src_domain_)};
|
||||
value_evaluator.add(src_field_);
|
||||
value_evaluator.evaluate();
|
||||
const GVArray &values = value_evaluator.get_evaluated(0);
|
||||
|
||||
return component.attribute_try_adapt_domain(values, src_domain_, domain);
|
||||
return component.attributes()->adapt_domain(values, src_domain_, domain);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -22,11 +22,11 @@ static void node_declare(NodeDeclarationBuilder &b)
|
||||
static void mesh_flip_faces(MeshComponent &component, const Field<bool> &selection_field)
|
||||
{
|
||||
GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_FACE};
|
||||
const int domain_num = component.attribute_domain_num(ATTR_DOMAIN_FACE);
|
||||
if (domain_num == 0) {
|
||||
const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_FACE);
|
||||
if (domain_size == 0) {
|
||||
return;
|
||||
}
|
||||
fn::FieldEvaluator evaluator{field_context, domain_num};
|
||||
fn::FieldEvaluator evaluator{field_context, domain_size};
|
||||
evaluator.add(selection_field);
|
||||
evaluator.evaluate();
|
||||
const IndexMask selection = evaluator.get_evaluated_as_mask(0);
|
||||
@@ -49,20 +49,21 @@ static void mesh_flip_faces(MeshComponent &component, const Field<bool> &selecti
|
||||
}
|
||||
}
|
||||
|
||||
component.attribute_foreach(
|
||||
MutableAttributeAccessor attributes = *component.attributes_for_write();
|
||||
attributes.for_all(
|
||||
[&](const bke::AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) {
|
||||
if (meta_data.domain == ATTR_DOMAIN_CORNER) {
|
||||
OutputAttribute attribute = component.attribute_try_get_for_output(
|
||||
attribute_id, ATTR_DOMAIN_CORNER, meta_data.data_type, nullptr);
|
||||
GSpanAttributeWriter attribute = attributes.lookup_or_add_for_write_span(
|
||||
attribute_id, ATTR_DOMAIN_CORNER, meta_data.data_type);
|
||||
attribute_math::convert_to_static_type(meta_data.data_type, [&](auto dummy) {
|
||||
using T = decltype(dummy);
|
||||
MutableSpan<T> dst_span = attribute.as_span<T>();
|
||||
MutableSpan<T> dst_span = attribute.span.typed<T>();
|
||||
for (const int j : selection.index_range()) {
|
||||
const MPoly &poly = polys[selection[j]];
|
||||
dst_span.slice(poly.loopstart + 1, poly.totloop - 1).reverse();
|
||||
}
|
||||
});
|
||||
attribute.save();
|
||||
attribute.finish();
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
@@ -39,11 +39,13 @@ class HandlePositionFieldInput final : public GeometryFieldInput {
|
||||
evaluator.evaluate();
|
||||
const VArray<bool> relative = evaluator.get_evaluated<bool>(0);
|
||||
|
||||
VArray<float3> positions = component.attribute_get_for_read<float3>(
|
||||
const AttributeAccessor attributes = *component.attributes();
|
||||
|
||||
VArray<float3> positions = attributes.lookup_or_default<float3>(
|
||||
"position", ATTR_DOMAIN_POINT, {0, 0, 0});
|
||||
|
||||
StringRef side = left_ ? "handle_left" : "handle_right";
|
||||
VArray<float3> handles = component.attribute_get_for_read<float3>(
|
||||
VArray<float3> handles = attributes.lookup_or_default<float3>(
|
||||
side, ATTR_DOMAIN_POINT, {0, 0, 0});
|
||||
|
||||
if (relative.is_single()) {
|
||||
@@ -52,10 +54,10 @@ class HandlePositionFieldInput final : public GeometryFieldInput {
|
||||
for (const int i : positions.index_range()) {
|
||||
output[i] = handles[i] - positions[i];
|
||||
}
|
||||
return component.attribute_try_adapt_domain<float3>(
|
||||
return attributes.adapt_domain<float3>(
|
||||
VArray<float3>::ForContainer(std::move(output)), ATTR_DOMAIN_POINT, domain);
|
||||
}
|
||||
return component.attribute_try_adapt_domain<float3>(handles, ATTR_DOMAIN_POINT, domain);
|
||||
return attributes.adapt_domain<float3>(handles, ATTR_DOMAIN_POINT, domain);
|
||||
}
|
||||
|
||||
Array<float3> output(positions.size());
|
||||
@@ -67,7 +69,7 @@ class HandlePositionFieldInput final : public GeometryFieldInput {
|
||||
output[i] = handles[i];
|
||||
}
|
||||
}
|
||||
return component.attribute_try_adapt_domain<float3>(
|
||||
return component.attributes()->adapt_domain<float3>(
|
||||
VArray<float3>::ForContainer(std::move(output)), ATTR_DOMAIN_POINT, domain);
|
||||
}
|
||||
|
||||
|
||||
@@ -91,7 +91,7 @@ class AngleFieldInput final : public GeometryFieldInput {
|
||||
};
|
||||
|
||||
VArray<float> angles = VArray<float>::ForFunc(mesh->totedge, angle_fn);
|
||||
return component.attribute_try_adapt_domain<float>(
|
||||
return component.attributes()->adapt_domain<float>(
|
||||
std::move(angles), ATTR_DOMAIN_EDGE, domain);
|
||||
}
|
||||
|
||||
@@ -166,7 +166,7 @@ class SignedAngleFieldInput final : public GeometryFieldInput {
|
||||
};
|
||||
|
||||
VArray<float> angles = VArray<float>::ForFunc(mesh->totedge, angle_fn);
|
||||
return component.attribute_try_adapt_domain<float>(
|
||||
return component.attributes()->adapt_domain<float>(
|
||||
std::move(angles), ATTR_DOMAIN_EDGE, domain);
|
||||
}
|
||||
|
||||
|
||||
@@ -40,7 +40,7 @@ class EdgeNeighborCountFieldInput final : public GeometryFieldInput {
|
||||
face_count[mesh->mloop[i].e]++;
|
||||
}
|
||||
|
||||
return mesh_component.attribute_try_adapt_domain<int>(
|
||||
return mesh_component.attributes()->adapt_domain<int>(
|
||||
VArray<int>::ForContainer(std::move(face_count)), ATTR_DOMAIN_EDGE, domain);
|
||||
}
|
||||
return {};
|
||||
|
||||
@@ -93,14 +93,14 @@ static VArray<float3> construct_edge_positions_gvarray(const MeshComponent &comp
|
||||
}
|
||||
|
||||
if (vertex == VERTEX_ONE) {
|
||||
return component.attribute_try_adapt_domain<float3>(
|
||||
return component.attributes()->adapt_domain<float3>(
|
||||
VArray<float3>::ForFunc(
|
||||
mesh->totedge,
|
||||
[mesh](const int i) { return float3(mesh->mvert[mesh->medge[i].v1].co); }),
|
||||
ATTR_DOMAIN_EDGE,
|
||||
domain);
|
||||
}
|
||||
return component.attribute_try_adapt_domain<float3>(
|
||||
return component.attributes()->adapt_domain<float3>(
|
||||
VArray<float3>::ForFunc(
|
||||
mesh->totedge,
|
||||
[mesh](const int i) { return float3(mesh->mvert[mesh->medge[i].v2].co); }),
|
||||
|
||||
@@ -29,7 +29,7 @@ static VArray<float> construct_face_area_gvarray(const MeshComponent &component,
|
||||
return BKE_mesh_calc_poly_area(mp, &mesh->mloop[mp->loopstart], mesh->mvert);
|
||||
};
|
||||
|
||||
return component.attribute_try_adapt_domain<float>(
|
||||
return component.attributes()->adapt_domain<float>(
|
||||
VArray<float>::ForFunc(mesh->totpoly, area_fn), ATTR_DOMAIN_FACE, domain);
|
||||
}
|
||||
|
||||
|
||||
@@ -80,7 +80,7 @@ class PlanarFieldInput final : public GeometryFieldInput {
|
||||
return max - min < thresholds[i_poly] / 2.0f;
|
||||
};
|
||||
|
||||
return component.attribute_try_adapt_domain<bool>(
|
||||
return component.attributes()->adapt_domain<bool>(
|
||||
VArray<bool>::ForFunc(mesh->totpoly, planar_fn), ATTR_DOMAIN_FACE, domain);
|
||||
}
|
||||
|
||||
|
||||
@@ -40,7 +40,7 @@ static VArray<int> construct_neighbor_count_gvarray(const MeshComponent &compone
|
||||
}
|
||||
}
|
||||
|
||||
return component.attribute_try_adapt_domain<int>(
|
||||
return component.attributes()->adapt_domain<int>(
|
||||
VArray<int>::ForContainer(std::move(poly_count)), ATTR_DOMAIN_FACE, domain);
|
||||
}
|
||||
|
||||
@@ -83,7 +83,7 @@ static VArray<int> construct_vertex_count_gvarray(const MeshComponent &component
|
||||
return {};
|
||||
}
|
||||
|
||||
return component.attribute_try_adapt_domain<int>(
|
||||
return component.attributes()->adapt_domain<int>(
|
||||
VArray<int>::ForFunc(mesh->totpoly,
|
||||
[mesh](const int i) -> float { return mesh->mpoly[i].totloop; }),
|
||||
ATTR_DOMAIN_FACE,
|
||||
|
||||
@@ -54,7 +54,7 @@ class IslandFieldInput final : public GeometryFieldInput {
|
||||
output[i] = ordered_roots.index_of_or_add(root);
|
||||
}
|
||||
|
||||
return mesh_component.attribute_try_adapt_domain<int>(
|
||||
return mesh_component.attributes()->adapt_domain<int>(
|
||||
VArray<int>::ForContainer(std::move(output)), ATTR_DOMAIN_POINT, domain);
|
||||
}
|
||||
|
||||
@@ -101,7 +101,8 @@ class IslandCountFieldInput final : public GeometryFieldInput {
|
||||
island_list.add(root);
|
||||
}
|
||||
|
||||
return VArray<int>::ForSingle(island_list.size(), mesh_component.attribute_domain_num(domain));
|
||||
return VArray<int>::ForSingle(island_list.size(),
|
||||
mesh_component.attribute_domain_size(domain));
|
||||
}
|
||||
|
||||
uint64_t hash() const override
|
||||
|
||||
@@ -32,7 +32,7 @@ static VArray<int> construct_curve_point_count_gvarray(const CurveComponent &com
|
||||
}
|
||||
if (domain == ATTR_DOMAIN_POINT) {
|
||||
VArray<int> count = VArray<int>::ForFunc(curves.curves_num(), count_fn);
|
||||
return component.attribute_try_adapt_domain<int>(
|
||||
return component.attributes()->adapt_domain<int>(
|
||||
std::move(count), ATTR_DOMAIN_CURVE, ATTR_DOMAIN_POINT);
|
||||
}
|
||||
|
||||
|
||||
@@ -75,7 +75,7 @@ static VArray<float3> construct_curve_tangent_gvarray(const CurveComponent &comp
|
||||
|
||||
const VArray<int8_t> types = curves.curve_types();
|
||||
if (curves.is_single_type(CURVE_TYPE_POLY)) {
|
||||
return component.attribute_try_adapt_domain<float3>(
|
||||
return component.attributes()->adapt_domain<float3>(
|
||||
VArray<float3>::ForSpan(curves.evaluated_tangents()), ATTR_DOMAIN_POINT, domain);
|
||||
}
|
||||
|
||||
@@ -86,7 +86,7 @@ static VArray<float3> construct_curve_tangent_gvarray(const CurveComponent &comp
|
||||
}
|
||||
|
||||
if (domain == ATTR_DOMAIN_CURVE) {
|
||||
return component.attribute_try_adapt_domain<float3>(
|
||||
return component.attributes()->adapt_domain<float3>(
|
||||
VArray<float3>::ForContainer(std::move(tangents)), ATTR_DOMAIN_POINT, ATTR_DOMAIN_CURVE);
|
||||
}
|
||||
|
||||
|
||||
@@ -50,7 +50,7 @@ static void add_instances_from_component(
|
||||
const Map<AttributeIDRef, AttributeKind> &attributes_to_propagate)
|
||||
{
|
||||
const eAttrDomain domain = ATTR_DOMAIN_POINT;
|
||||
const int domain_num = src_component.attribute_domain_num(domain);
|
||||
const int domain_num = src_component.attribute_domain_size(domain);
|
||||
|
||||
VArray<bool> pick_instance;
|
||||
VArray<int> indices;
|
||||
@@ -82,7 +82,7 @@ static void add_instances_from_component(
|
||||
MutableSpan<float4x4> dst_transforms = dst_component.instance_transforms().slice(start_len,
|
||||
select_len);
|
||||
|
||||
VArray<float3> positions = src_component.attribute_get_for_read<float3>(
|
||||
VArray<float3> positions = src_component.attributes()->lookup_or_default<float3>(
|
||||
"position", domain, {0, 0, 0});
|
||||
|
||||
const InstancesComponent *src_instances = instance.get_component_for_read<InstancesComponent>();
|
||||
@@ -154,12 +154,12 @@ static void add_instances_from_component(
|
||||
}
|
||||
}
|
||||
|
||||
bke::CustomDataAttributes &instance_attributes = dst_component.attributes();
|
||||
bke::CustomDataAttributes &instance_attributes = dst_component.instance_attributes();
|
||||
for (const auto item : attributes_to_propagate.items()) {
|
||||
const AttributeIDRef &attribute_id = item.key;
|
||||
const AttributeKind attribute_kind = item.value;
|
||||
|
||||
const GVArray src_attribute = src_component.attribute_get_for_read(
|
||||
const GVArray src_attribute = src_component.attributes()->lookup_or_default(
|
||||
attribute_id, ATTR_DOMAIN_POINT, attribute_kind.data_type);
|
||||
BLI_assert(src_attribute);
|
||||
std::optional<GMutableSpan> dst_attribute_opt = instance_attributes.get_for_write(
|
||||
|
||||
@@ -40,9 +40,9 @@ static void convert_instances_to_points(GeometrySet &geometry_set,
|
||||
const InstancesComponent &instances = *geometry_set.get_component_for_read<InstancesComponent>();
|
||||
|
||||
GeometryComponentFieldContext field_context{instances, ATTR_DOMAIN_INSTANCE};
|
||||
const int domain_num = instances.attribute_domain_num(ATTR_DOMAIN_INSTANCE);
|
||||
const int domain_size = instances.instances_num();
|
||||
|
||||
fn::FieldEvaluator evaluator{field_context, domain_num};
|
||||
fn::FieldEvaluator evaluator{field_context, domain_size};
|
||||
evaluator.set_selection(std::move(selection_field));
|
||||
evaluator.add(std::move(position_field));
|
||||
evaluator.add(std::move(radius_field));
|
||||
@@ -75,18 +75,18 @@ static void convert_instances_to_points(GeometrySet &geometry_set,
|
||||
const AttributeIDRef &attribute_id = item.key;
|
||||
const AttributeKind attribute_kind = item.value;
|
||||
|
||||
const GVArray src = instances.attribute_get_for_read(
|
||||
const GVArray src = instances.attributes()->lookup_or_default(
|
||||
attribute_id, ATTR_DOMAIN_INSTANCE, attribute_kind.data_type);
|
||||
BLI_assert(src);
|
||||
OutputAttribute dst = points.attribute_try_get_for_output_only(
|
||||
GSpanAttributeWriter dst = points.attributes_for_write()->lookup_or_add_for_write_only_span(
|
||||
attribute_id, ATTR_DOMAIN_POINT, attribute_kind.data_type);
|
||||
BLI_assert(dst);
|
||||
|
||||
attribute_math::convert_to_static_type(attribute_kind.data_type, [&](auto dummy) {
|
||||
using T = decltype(dummy);
|
||||
copy_attribute_to_points(src.typed<T>(), selection, dst.as_span().typed<T>());
|
||||
copy_attribute_to_points(src.typed<T>(), selection, dst.span.typed<T>());
|
||||
});
|
||||
dst.save();
|
||||
dst.finish();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ static Map<AttributeIDRef, AttributeMetaData> get_final_attribute_info(
|
||||
Map<AttributeIDRef, AttributeMetaData> info;
|
||||
|
||||
for (const GeometryComponent *component : components) {
|
||||
component->attribute_foreach(
|
||||
component->attributes()->for_all(
|
||||
[&](const bke::AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) {
|
||||
if (attribute_id.is_named() && ignored_attributes.contains(attribute_id.name())) {
|
||||
return true;
|
||||
@@ -56,11 +56,11 @@ static void fill_new_attribute(Span<const GeometryComponent *> src_components,
|
||||
|
||||
int offset = 0;
|
||||
for (const GeometryComponent *component : src_components) {
|
||||
const int domain_num = component->attribute_domain_num(domain);
|
||||
const int domain_num = component->attribute_domain_size(domain);
|
||||
if (domain_num == 0) {
|
||||
continue;
|
||||
}
|
||||
GVArray read_attribute = component->attribute_get_for_read(
|
||||
GVArray read_attribute = component->attributes()->lookup_or_default(
|
||||
attribute_id, domain, data_type, nullptr);
|
||||
|
||||
GVArraySpan src_span{read_attribute};
|
||||
@@ -83,15 +83,15 @@ static void join_attributes(Span<const GeometryComponent *> src_components,
|
||||
const AttributeIDRef attribute_id = item.key;
|
||||
const AttributeMetaData &meta_data = item.value;
|
||||
|
||||
OutputAttribute write_attribute = result.attribute_try_get_for_output_only(
|
||||
attribute_id, meta_data.domain, meta_data.data_type);
|
||||
GSpanAttributeWriter write_attribute =
|
||||
result.attributes_for_write()->lookup_or_add_for_write_only_span(
|
||||
attribute_id, meta_data.domain, meta_data.data_type);
|
||||
if (!write_attribute) {
|
||||
continue;
|
||||
}
|
||||
GMutableSpan dst_span = write_attribute.as_span();
|
||||
fill_new_attribute(
|
||||
src_components, attribute_id, meta_data.data_type, meta_data.domain, dst_span);
|
||||
write_attribute.save();
|
||||
src_components, attribute_id, meta_data.data_type, meta_data.domain, write_attribute.span);
|
||||
write_attribute.finish();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -71,7 +71,7 @@ class MaterialSelectionFieldInput final : public GeometryFieldInput {
|
||||
|
||||
Array<bool> selection(mesh->totpoly);
|
||||
select_mesh_by_material(*mesh, material_, IndexMask(mesh->totpoly), selection);
|
||||
return mesh_component.attribute_try_adapt_domain<bool>(
|
||||
return mesh_component.attributes()->adapt_domain<bool>(
|
||||
VArray<bool>::ForContainer(std::move(selection)), ATTR_DOMAIN_FACE, domain);
|
||||
|
||||
return nullptr;
|
||||
|
||||
@@ -39,7 +39,7 @@ static PointCloud *pointcloud_merge_by_distance(const PointCloudComponent &src_p
|
||||
const float merge_distance,
|
||||
const Field<bool> &selection_field)
|
||||
{
|
||||
const int src_num = src_points.attribute_domain_num(ATTR_DOMAIN_POINT);
|
||||
const int src_num = src_points.attribute_domain_size(ATTR_DOMAIN_POINT);
|
||||
GeometryComponentFieldContext context{src_points, ATTR_DOMAIN_POINT};
|
||||
FieldEvaluator evaluator{context, src_num};
|
||||
evaluator.add(selection_field);
|
||||
@@ -57,7 +57,7 @@ static std::optional<Mesh *> mesh_merge_by_distance_connected(const MeshComponen
|
||||
const float merge_distance,
|
||||
const Field<bool> &selection_field)
|
||||
{
|
||||
const int src_num = mesh_component.attribute_domain_num(ATTR_DOMAIN_POINT);
|
||||
const int src_num = mesh_component.attribute_domain_size(ATTR_DOMAIN_POINT);
|
||||
Array<bool> selection(src_num);
|
||||
GeometryComponentFieldContext context{mesh_component, ATTR_DOMAIN_POINT};
|
||||
FieldEvaluator evaluator{context, src_num};
|
||||
@@ -72,7 +72,7 @@ static std::optional<Mesh *> mesh_merge_by_distance_all(const MeshComponent &mes
|
||||
const float merge_distance,
|
||||
const Field<bool> &selection_field)
|
||||
{
|
||||
const int src_num = mesh_component.attribute_domain_num(ATTR_DOMAIN_POINT);
|
||||
const int src_num = mesh_component.attribute_domain_size(ATTR_DOMAIN_POINT);
|
||||
GeometryComponentFieldContext context{mesh_component, ATTR_DOMAIN_POINT};
|
||||
FieldEvaluator evaluator{context, src_num};
|
||||
evaluator.add(selection_field);
|
||||
|
||||
@@ -480,53 +480,49 @@ static void calculate_selection_outputs(Mesh *mesh,
|
||||
const ConeConfig &config,
|
||||
ConeAttributeOutputs &attribute_outputs)
|
||||
{
|
||||
MeshComponent mesh_component;
|
||||
mesh_component.replace(mesh, GeometryOwnershipType::Editable);
|
||||
MutableAttributeAccessor attributes = bke::mesh_attributes_for_write(*mesh);
|
||||
|
||||
/* Populate "Top" selection output. */
|
||||
if (attribute_outputs.top_id) {
|
||||
const bool face = !config.top_is_point && config.fill_type != GEO_NODE_MESH_CIRCLE_FILL_NONE;
|
||||
OutputAttribute_Typed<bool> attribute = mesh_component.attribute_try_get_for_output_only<bool>(
|
||||
SpanAttributeWriter<bool> selection = attributes.lookup_or_add_for_write_only_span<bool>(
|
||||
attribute_outputs.top_id.get(), face ? ATTR_DOMAIN_FACE : ATTR_DOMAIN_POINT);
|
||||
MutableSpan<bool> selection = attribute.as_span();
|
||||
|
||||
if (config.top_is_point) {
|
||||
selection[config.first_vert] = true;
|
||||
selection.span[config.first_vert] = true;
|
||||
}
|
||||
else {
|
||||
selection.slice(0, face ? config.top_faces_len : config.circle_segments).fill(true);
|
||||
selection.span.slice(0, face ? config.top_faces_len : config.circle_segments).fill(true);
|
||||
}
|
||||
attribute.save();
|
||||
selection.finish();
|
||||
}
|
||||
|
||||
/* Populate "Bottom" selection output. */
|
||||
if (attribute_outputs.bottom_id) {
|
||||
const bool face = !config.bottom_is_point &&
|
||||
config.fill_type != GEO_NODE_MESH_CIRCLE_FILL_NONE;
|
||||
OutputAttribute_Typed<bool> attribute = mesh_component.attribute_try_get_for_output_only<bool>(
|
||||
SpanAttributeWriter<bool> selection = attributes.lookup_or_add_for_write_only_span<bool>(
|
||||
attribute_outputs.bottom_id.get(), face ? ATTR_DOMAIN_FACE : ATTR_DOMAIN_POINT);
|
||||
MutableSpan<bool> selection = attribute.as_span();
|
||||
|
||||
if (config.bottom_is_point) {
|
||||
selection[config.last_vert] = true;
|
||||
selection.span[config.last_vert] = true;
|
||||
}
|
||||
else if (face) {
|
||||
selection.slice(config.bottom_faces_start, config.bottom_faces_len).fill(true);
|
||||
selection.span.slice(config.bottom_faces_start, config.bottom_faces_len).fill(true);
|
||||
}
|
||||
else {
|
||||
selection.slice(config.last_ring_verts_start + 1, config.circle_segments).fill(true);
|
||||
selection.span.slice(config.last_ring_verts_start + 1, config.circle_segments).fill(true);
|
||||
}
|
||||
attribute.save();
|
||||
selection.finish();
|
||||
}
|
||||
|
||||
/* Populate "Side" selection output. */
|
||||
if (attribute_outputs.side_id) {
|
||||
OutputAttribute_Typed<bool> attribute = mesh_component.attribute_try_get_for_output_only<bool>(
|
||||
SpanAttributeWriter<bool> selection = attributes.lookup_or_add_for_write_only_span<bool>(
|
||||
attribute_outputs.side_id.get(), ATTR_DOMAIN_FACE);
|
||||
MutableSpan<bool> selection = attribute.as_span();
|
||||
|
||||
selection.slice(config.side_faces_start, config.side_faces_len).fill(true);
|
||||
attribute.save();
|
||||
selection.span.slice(config.side_faces_start, config.side_faces_len).fill(true);
|
||||
selection.finish();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -540,11 +536,11 @@ static void calculate_selection_outputs(Mesh *mesh,
|
||||
*/
|
||||
static void calculate_cone_uvs(Mesh *mesh, const ConeConfig &config)
|
||||
{
|
||||
MeshComponent mesh_component;
|
||||
mesh_component.replace(mesh, GeometryOwnershipType::Editable);
|
||||
OutputAttribute_Typed<float2> uv_attribute =
|
||||
mesh_component.attribute_try_get_for_output_only<float2>("uv_map", ATTR_DOMAIN_CORNER);
|
||||
MutableSpan<float2> uvs = uv_attribute.as_span();
|
||||
MutableAttributeAccessor attributes = bke::mesh_attributes_for_write(*mesh);
|
||||
|
||||
SpanAttributeWriter<float2> uv_attribute = attributes.lookup_or_add_for_write_only_span<float2>(
|
||||
"uv_map", ATTR_DOMAIN_CORNER);
|
||||
MutableSpan<float2> uvs = uv_attribute.span;
|
||||
|
||||
Array<float2> circle(config.circle_segments);
|
||||
float angle = 0.0f;
|
||||
@@ -654,7 +650,7 @@ static void calculate_cone_uvs(Mesh *mesh, const ConeConfig &config)
|
||||
}
|
||||
}
|
||||
|
||||
uv_attribute.save();
|
||||
uv_attribute.finish();
|
||||
}
|
||||
|
||||
static Mesh *create_vertex_mesh()
|
||||
|
||||
@@ -18,23 +18,22 @@ namespace blender::nodes {
|
||||
static void calculate_uvs(
|
||||
Mesh *mesh, Span<MVert> verts, Span<MLoop> loops, const float size_x, const float size_y)
|
||||
{
|
||||
MeshComponent mesh_component;
|
||||
mesh_component.replace(mesh, GeometryOwnershipType::Editable);
|
||||
OutputAttribute_Typed<float2> uv_attribute =
|
||||
mesh_component.attribute_try_get_for_output_only<float2>("uv_map", ATTR_DOMAIN_CORNER);
|
||||
MutableSpan<float2> uvs = uv_attribute.as_span();
|
||||
MutableAttributeAccessor attributes = bke::mesh_attributes_for_write(*mesh);
|
||||
|
||||
SpanAttributeWriter<float2> uv_attribute = attributes.lookup_or_add_for_write_only_span<float2>(
|
||||
"uv_map", ATTR_DOMAIN_CORNER);
|
||||
|
||||
const float dx = (size_x == 0.0f) ? 0.0f : 1.0f / size_x;
|
||||
const float dy = (size_y == 0.0f) ? 0.0f : 1.0f / size_y;
|
||||
threading::parallel_for(loops.index_range(), 1024, [&](IndexRange range) {
|
||||
for (const int i : range) {
|
||||
const float3 &co = verts[loops[i].v].co;
|
||||
uvs[i].x = (co.x + size_x * 0.5f) * dx;
|
||||
uvs[i].y = (co.y + size_y * 0.5f) * dy;
|
||||
uv_attribute.span[i].x = (co.x + size_x * 0.5f) * dx;
|
||||
uv_attribute.span[i].y = (co.y + size_y * 0.5f) * dy;
|
||||
}
|
||||
});
|
||||
|
||||
uv_attribute.save();
|
||||
uv_attribute.finish();
|
||||
}
|
||||
|
||||
Mesh *create_grid_mesh(const int verts_x,
|
||||
|
||||
@@ -220,11 +220,11 @@ static void calculate_sphere_faces(MutableSpan<MLoop> loops,
|
||||
|
||||
static void calculate_sphere_uvs(Mesh *mesh, const float segments, const float rings)
|
||||
{
|
||||
MeshComponent mesh_component;
|
||||
mesh_component.replace(mesh, GeometryOwnershipType::Editable);
|
||||
OutputAttribute_Typed<float2> uv_attribute =
|
||||
mesh_component.attribute_try_get_for_output_only<float2>("uv_map", ATTR_DOMAIN_CORNER);
|
||||
MutableSpan<float2> uvs = uv_attribute.as_span();
|
||||
MutableAttributeAccessor attributes = bke::mesh_attributes_for_write(*mesh);
|
||||
|
||||
SpanAttributeWriter<float2> uv_attribute = attributes.lookup_or_add_for_write_only_span<float2>(
|
||||
"uv_map", ATTR_DOMAIN_CORNER);
|
||||
MutableSpan<float2> uvs = uv_attribute.span;
|
||||
|
||||
int loop_index = 0;
|
||||
const float dy = 1.0f / rings;
|
||||
@@ -254,7 +254,7 @@ static void calculate_sphere_uvs(Mesh *mesh, const float segments, const float r
|
||||
uvs[loop_index++] = float2(segment / segments, 1.0f - dy);
|
||||
}
|
||||
|
||||
uv_attribute.save();
|
||||
uv_attribute.finish();
|
||||
}
|
||||
|
||||
static Mesh *create_uv_sphere_mesh(const float radius, const int segments, const int rings)
|
||||
|
||||
@@ -25,7 +25,7 @@ static void node_geo_exec(GeoNodeExecParams params)
|
||||
|
||||
const MeshComponent &component = *geometry_set.get_component_for_read<MeshComponent>();
|
||||
GeometryComponentFieldContext context{component, ATTR_DOMAIN_EDGE};
|
||||
fn::FieldEvaluator evaluator{context, component.attribute_domain_num(ATTR_DOMAIN_EDGE)};
|
||||
fn::FieldEvaluator evaluator{context, component.attribute_domain_size(ATTR_DOMAIN_EDGE)};
|
||||
evaluator.add(params.get_input<Field<bool>>("Selection"));
|
||||
evaluator.evaluate();
|
||||
const IndexMask selection = evaluator.get_evaluated_as_mask(0);
|
||||
|
||||
@@ -66,7 +66,7 @@ static void geometry_set_mesh_to_points(GeometrySet &geometry_set,
|
||||
return;
|
||||
}
|
||||
GeometryComponentFieldContext field_context{*mesh_component, domain};
|
||||
const int domain_num = mesh_component->attribute_domain_num(domain);
|
||||
const int domain_num = mesh_component->attribute_domain_size(domain);
|
||||
if (domain_num == 0) {
|
||||
geometry_set.keep_only({GEO_COMPONENT_TYPE_INSTANCES});
|
||||
return;
|
||||
@@ -83,20 +83,20 @@ static void geometry_set_mesh_to_points(GeometrySet &geometry_set,
|
||||
|
||||
PointCloud *pointcloud = BKE_pointcloud_new_nomain(selection.size());
|
||||
geometry_set.replace_pointcloud(pointcloud);
|
||||
PointCloudComponent &point_component =
|
||||
geometry_set.get_component_for_write<PointCloudComponent>();
|
||||
MutableAttributeAccessor pointcloud_attributes = bke::pointcloud_attributes_for_write(
|
||||
*pointcloud);
|
||||
|
||||
OutputAttribute position = point_component.attribute_try_get_for_output_only(
|
||||
GSpanAttributeWriter position = pointcloud_attributes.lookup_or_add_for_write_only_span(
|
||||
"position", ATTR_DOMAIN_POINT, CD_PROP_FLOAT3);
|
||||
materialize_compressed_to_uninitialized_threaded(
|
||||
evaluator.get_evaluated(0), selection, position.as_span());
|
||||
position.save();
|
||||
evaluator.get_evaluated(0), selection, position.span);
|
||||
position.finish();
|
||||
|
||||
OutputAttribute radius = point_component.attribute_try_get_for_output_only(
|
||||
GSpanAttributeWriter radius = pointcloud_attributes.lookup_or_add_for_write_only_span(
|
||||
"radius", ATTR_DOMAIN_POINT, CD_PROP_FLOAT);
|
||||
materialize_compressed_to_uninitialized_threaded(
|
||||
evaluator.get_evaluated(1), selection, radius.as_span());
|
||||
radius.save();
|
||||
evaluator.get_evaluated(1), selection, radius.span);
|
||||
radius.finish();
|
||||
|
||||
Map<AttributeIDRef, AttributeKind> attributes;
|
||||
geometry_set.gather_attributes_for_propagation(
|
||||
@@ -106,12 +106,12 @@ static void geometry_set_mesh_to_points(GeometrySet &geometry_set,
|
||||
for (Map<AttributeIDRef, AttributeKind>::Item entry : attributes.items()) {
|
||||
const AttributeIDRef attribute_id = entry.key;
|
||||
const eCustomDataType data_type = entry.value.data_type;
|
||||
GVArray src = mesh_component->attribute_get_for_read(attribute_id, domain, data_type);
|
||||
OutputAttribute dst = point_component.attribute_try_get_for_output_only(
|
||||
GVArray src = mesh_component->attributes()->lookup_or_default(attribute_id, domain, data_type);
|
||||
GSpanAttributeWriter dst = pointcloud_attributes.lookup_or_add_for_write_only_span(
|
||||
attribute_id, ATTR_DOMAIN_POINT, data_type);
|
||||
if (dst && src) {
|
||||
materialize_compressed_to_uninitialized_threaded(src, selection, dst.as_span());
|
||||
dst.save();
|
||||
materialize_compressed_to_uninitialized_threaded(src, selection, dst.span);
|
||||
dst.finish();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -72,19 +72,20 @@ static void node_geo_exec(GeoNodeExecParams params)
|
||||
PointCloud *new_point_cloud = BKE_pointcloud_new_nomain(count);
|
||||
GeometrySet geometry_set = GeometrySet::create_with_pointcloud(new_point_cloud);
|
||||
PointCloudComponent &points = geometry_set.get_component_for_write<PointCloudComponent>();
|
||||
OutputAttribute_Typed<float3> output_position = points.attribute_try_get_for_output_only<float3>(
|
||||
MutableAttributeAccessor attributes = *points.attributes_for_write();
|
||||
AttributeWriter<float3> output_position = attributes.lookup_or_add_for_write<float3>(
|
||||
"position", ATTR_DOMAIN_POINT);
|
||||
OutputAttribute_Typed<float> output_radii = points.attribute_try_get_for_output_only<float>(
|
||||
AttributeWriter<float> output_radii = attributes.lookup_or_add_for_write<float>(
|
||||
"radius", ATTR_DOMAIN_POINT);
|
||||
|
||||
PointsFieldContext context{count};
|
||||
fn::FieldEvaluator evaluator{context, count};
|
||||
evaluator.add_with_destination(position_field, output_position.as_span());
|
||||
evaluator.add_with_destination(radius_field, output_radii.as_span());
|
||||
evaluator.add_with_destination(position_field, output_position.varray);
|
||||
evaluator.add_with_destination(radius_field, output_radii.varray);
|
||||
evaluator.evaluate();
|
||||
|
||||
output_position.save();
|
||||
output_radii.save();
|
||||
output_position.finish();
|
||||
output_radii.finish();
|
||||
params.set_output("Geometry", std::move(geometry_set));
|
||||
}
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@ static void geometry_set_points_to_vertices(GeometrySet &geometry_set,
|
||||
}
|
||||
|
||||
GeometryComponentFieldContext field_context{*point_component, ATTR_DOMAIN_POINT};
|
||||
const int domain_num = point_component->attribute_domain_num(ATTR_DOMAIN_POINT);
|
||||
const int domain_num = point_component->attribute_domain_size(ATTR_DOMAIN_POINT);
|
||||
if (domain_num == 0) {
|
||||
geometry_set.keep_only({GEO_COMPONENT_TYPE_INSTANCES});
|
||||
return;
|
||||
@@ -60,18 +60,19 @@ static void geometry_set_points_to_vertices(GeometrySet &geometry_set,
|
||||
for (Map<AttributeIDRef, AttributeKind>::Item entry : attributes.items()) {
|
||||
const AttributeIDRef attribute_id = entry.key;
|
||||
const eCustomDataType data_type = entry.value.data_type;
|
||||
GVArray src = point_component->attribute_get_for_read(
|
||||
attribute_id, ATTR_DOMAIN_POINT, data_type);
|
||||
OutputAttribute dst = mesh_component.attribute_try_get_for_output_only(
|
||||
GVArray src = point_component->attributes()->lookup_or_default(
|
||||
attribute_id, ATTR_DOMAIN_POINT, data_type);
|
||||
GSpanAttributeWriter dst =
|
||||
mesh_component.attributes_for_write()->lookup_or_add_for_write_only_span(
|
||||
attribute_id, ATTR_DOMAIN_POINT, data_type);
|
||||
if (dst && src) {
|
||||
attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
|
||||
using T = decltype(dummy);
|
||||
VArray<T> src_typed = src.typed<T>();
|
||||
VArraySpan<T> src_typed_span{src_typed};
|
||||
copy_attribute_to_vertices(src_typed_span, selection, dst.as_span().typed<T>());
|
||||
copy_attribute_to_vertices(src_typed_span, selection, dst.span.typed<T>());
|
||||
});
|
||||
dst.save();
|
||||
dst.finish();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -163,12 +163,15 @@ static void gather_point_data_from_component(GeoNodeExecParams ¶ms,
|
||||
Vector<float3> &r_positions,
|
||||
Vector<float> &r_radii)
|
||||
{
|
||||
VArray<float3> positions = component.attribute_get_for_read<float3>(
|
||||
if (component.is_empty()) {
|
||||
return;
|
||||
}
|
||||
VArray<float3> positions = component.attributes()->lookup_or_default<float3>(
|
||||
"position", ATTR_DOMAIN_POINT, {0, 0, 0});
|
||||
|
||||
Field<float> radius_field = params.get_input<Field<float>>("Radius");
|
||||
GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_POINT};
|
||||
const int domain_num = component.attribute_domain_num(ATTR_DOMAIN_POINT);
|
||||
const int domain_num = component.attribute_domain_size(ATTR_DOMAIN_POINT);
|
||||
|
||||
r_positions.resize(r_positions.size() + domain_num);
|
||||
positions.materialize(r_positions.as_mutable_span().take_back(domain_num));
|
||||
|
||||
@@ -312,8 +312,8 @@ class RaycastFunction : public fn::MultiFunction {
|
||||
}
|
||||
const MeshComponent &mesh_component = *target_.get_component_for_read<MeshComponent>();
|
||||
target_context_.emplace(GeometryComponentFieldContext{mesh_component, domain_});
|
||||
const int domain_num = mesh_component.attribute_domain_num(domain_);
|
||||
target_evaluator_ = std::make_unique<FieldEvaluator>(*target_context_, domain_num);
|
||||
const int domain_size = mesh_component.attribute_domain_size(domain_);
|
||||
target_evaluator_ = std::make_unique<FieldEvaluator>(*target_context_, domain_size);
|
||||
target_evaluator_->add(std::move(src_field));
|
||||
target_evaluator_->evaluate();
|
||||
target_data_ = &target_evaluator_->get_evaluated(0);
|
||||
|
||||
@@ -39,7 +39,7 @@ static void node_geo_exec(GeoNodeExecParams params)
|
||||
/* First check if the attribute exists before getting write access,
|
||||
* to avoid potentially expensive unnecessary copies. */
|
||||
const GeometryComponent &read_only_component = *geometry_set.get_component_for_read(type);
|
||||
if (read_only_component.attribute_exists(name)) {
|
||||
if (read_only_component.attributes()->contains(name)) {
|
||||
attribute_exists = true;
|
||||
}
|
||||
else {
|
||||
@@ -47,7 +47,7 @@ static void node_geo_exec(GeoNodeExecParams params)
|
||||
}
|
||||
|
||||
GeometryComponent &component = geometry_set.get_component_for_write(type);
|
||||
if (!component.attribute_try_delete(name)) {
|
||||
if (!component.attributes_for_write()->remove(name)) {
|
||||
cannot_delete = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,12 +75,12 @@ static void set_position_in_component(CurveComponent &component,
|
||||
const Field<float3> &offset_field)
|
||||
{
|
||||
GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_POINT};
|
||||
const int domain_num = component.attribute_domain_num(ATTR_DOMAIN_POINT);
|
||||
if (domain_num == 0) {
|
||||
const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_POINT);
|
||||
if (domain_size == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
fn::FieldEvaluator evaluator{field_context, domain_num};
|
||||
fn::FieldEvaluator evaluator{field_context, domain_size};
|
||||
evaluator.set_selection(selection_field);
|
||||
evaluator.add(position_field);
|
||||
evaluator.add(offset_field);
|
||||
@@ -146,8 +146,8 @@ static void node_geo_exec(GeoNodeExecParams params)
|
||||
}
|
||||
has_curves = true;
|
||||
const CurveComponent &component = *geometry_set.get_component_for_read<CurveComponent>();
|
||||
if (!component.attribute_exists("handle_left") ||
|
||||
!component.attribute_exists("handle_right")) {
|
||||
const AttributeAccessor attributes = *component.attributes();
|
||||
if (!attributes.contains("handle_left") || !attributes.contains("handle_right")) {
|
||||
return;
|
||||
}
|
||||
has_bezier = true;
|
||||
|
||||
@@ -20,21 +20,22 @@ static void set_radius_in_component(GeometryComponent &component,
|
||||
const Field<bool> &selection_field,
|
||||
const Field<float> &radius_field)
|
||||
{
|
||||
GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_POINT};
|
||||
const int domain_num = component.attribute_domain_num(ATTR_DOMAIN_POINT);
|
||||
if (domain_num == 0) {
|
||||
const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_POINT);
|
||||
if (domain_size == 0) {
|
||||
return;
|
||||
}
|
||||
MutableAttributeAccessor attributes = *component.attributes_for_write();
|
||||
GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_POINT};
|
||||
|
||||
OutputAttribute_Typed<float> radii = component.attribute_try_get_for_output_only<float>(
|
||||
"radius", ATTR_DOMAIN_POINT);
|
||||
AttributeWriter<float> radii = attributes.lookup_or_add_for_write<float>("radius",
|
||||
ATTR_DOMAIN_POINT);
|
||||
|
||||
fn::FieldEvaluator evaluator{field_context, domain_num};
|
||||
fn::FieldEvaluator evaluator{field_context, domain_size};
|
||||
evaluator.set_selection(selection_field);
|
||||
evaluator.add_with_destination(radius_field, radii.varray());
|
||||
evaluator.add_with_destination(radius_field, radii.varray);
|
||||
evaluator.evaluate();
|
||||
|
||||
radii.save();
|
||||
radii.finish();
|
||||
}
|
||||
|
||||
static void node_geo_exec(GeoNodeExecParams params)
|
||||
|
||||
@@ -16,21 +16,23 @@ static void set_tilt_in_component(GeometryComponent &component,
|
||||
const Field<bool> &selection_field,
|
||||
const Field<float> &tilt_field)
|
||||
{
|
||||
GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_POINT};
|
||||
const int domain_num = component.attribute_domain_num(ATTR_DOMAIN_POINT);
|
||||
if (domain_num == 0) {
|
||||
const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_POINT);
|
||||
if (domain_size == 0) {
|
||||
return;
|
||||
}
|
||||
MutableAttributeAccessor attributes = *component.attributes_for_write();
|
||||
|
||||
OutputAttribute_Typed<float> tilts = component.attribute_try_get_for_output_only<float>(
|
||||
"tilt", ATTR_DOMAIN_POINT);
|
||||
GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_POINT};
|
||||
|
||||
fn::FieldEvaluator evaluator{field_context, domain_num};
|
||||
AttributeWriter<float> tilts = attributes.lookup_or_add_for_write<float>("tilt",
|
||||
ATTR_DOMAIN_POINT);
|
||||
|
||||
fn::FieldEvaluator evaluator{field_context, domain_size};
|
||||
evaluator.set_selection(selection_field);
|
||||
evaluator.add_with_destination(tilt_field, tilts.varray());
|
||||
evaluator.add_with_destination(tilt_field, tilts.varray);
|
||||
evaluator.evaluate();
|
||||
|
||||
tilts.save();
|
||||
tilts.finish();
|
||||
}
|
||||
|
||||
static void node_geo_exec(GeoNodeExecParams params)
|
||||
|
||||
@@ -19,34 +19,34 @@ static void set_id_in_component(GeometryComponent &component,
|
||||
const eAttrDomain domain = (component.type() == GEO_COMPONENT_TYPE_INSTANCES) ?
|
||||
ATTR_DOMAIN_INSTANCE :
|
||||
ATTR_DOMAIN_POINT;
|
||||
GeometryComponentFieldContext field_context{component, domain};
|
||||
const int domain_num = component.attribute_domain_num(domain);
|
||||
if (domain_num == 0) {
|
||||
const int domain_size = component.attribute_domain_size(domain);
|
||||
if (domain_size == 0) {
|
||||
return;
|
||||
}
|
||||
MutableAttributeAccessor attributes = *component.attributes_for_write();
|
||||
GeometryComponentFieldContext field_context{component, domain};
|
||||
|
||||
fn::FieldEvaluator evaluator{field_context, domain_num};
|
||||
fn::FieldEvaluator evaluator{field_context, domain_size};
|
||||
evaluator.set_selection(selection_field);
|
||||
|
||||
/* Since adding the ID attribute can change the result of the field evaluation (the random value
|
||||
* node uses the index if the ID is unavailable), make sure that it isn't added before evaluating
|
||||
* the field. However, as an optimization, use a faster code path when it already exists. */
|
||||
if (component.attribute_exists("id")) {
|
||||
OutputAttribute_Typed<int> id_attribute = component.attribute_try_get_for_output_only<int>(
|
||||
"id", domain);
|
||||
evaluator.add_with_destination(id_field, id_attribute.varray());
|
||||
if (attributes.contains("id")) {
|
||||
AttributeWriter<int> id_attribute = attributes.lookup_or_add_for_write<int>("id", domain);
|
||||
evaluator.add_with_destination(id_field, id_attribute.varray);
|
||||
evaluator.evaluate();
|
||||
id_attribute.save();
|
||||
id_attribute.finish();
|
||||
}
|
||||
else {
|
||||
evaluator.add(id_field);
|
||||
evaluator.evaluate();
|
||||
const IndexMask selection = evaluator.get_evaluated_selection_as_mask();
|
||||
const VArray<int> result_ids = evaluator.get_evaluated<int>(0);
|
||||
OutputAttribute_Typed<int> id_attribute = component.attribute_try_get_for_output_only<int>(
|
||||
"id", domain);
|
||||
result_ids.materialize(selection, id_attribute.as_span());
|
||||
id_attribute.save();
|
||||
SpanAttributeWriter<int> id_attribute = attributes.lookup_or_add_for_write_span<int>("id",
|
||||
domain);
|
||||
result_ids.materialize(selection, id_attribute.span);
|
||||
id_attribute.finish();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -16,20 +16,21 @@ static void set_material_index_in_component(GeometryComponent &component,
|
||||
const Field<bool> &selection_field,
|
||||
const Field<int> &index_field)
|
||||
{
|
||||
GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_FACE};
|
||||
const int domain_num = component.attribute_domain_num(ATTR_DOMAIN_FACE);
|
||||
if (domain_num == 0) {
|
||||
const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_FACE);
|
||||
if (domain_size == 0) {
|
||||
return;
|
||||
}
|
||||
MutableAttributeAccessor attributes = *component.attributes_for_write();
|
||||
GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_FACE};
|
||||
|
||||
OutputAttribute_Typed<int> indices = component.attribute_try_get_for_output_only<int>(
|
||||
"material_index", ATTR_DOMAIN_FACE);
|
||||
AttributeWriter<int> indices = attributes.lookup_or_add_for_write<int>("material_index",
|
||||
ATTR_DOMAIN_FACE);
|
||||
|
||||
fn::FieldEvaluator evaluator{field_context, domain_num};
|
||||
fn::FieldEvaluator evaluator{field_context, domain_size};
|
||||
evaluator.set_selection(selection_field);
|
||||
evaluator.add_with_destination(index_field, indices.varray());
|
||||
evaluator.add_with_destination(index_field, indices.varray);
|
||||
evaluator.evaluate();
|
||||
indices.save();
|
||||
indices.finish();
|
||||
}
|
||||
|
||||
static void node_geo_exec(GeoNodeExecParams params)
|
||||
|
||||
@@ -20,21 +20,22 @@ static void set_radius_in_component(GeometryComponent &component,
|
||||
const Field<bool> &selection_field,
|
||||
const Field<float> &radius_field)
|
||||
{
|
||||
GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_POINT};
|
||||
const int domain_num = component.attribute_domain_num(ATTR_DOMAIN_POINT);
|
||||
if (domain_num == 0) {
|
||||
const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_POINT);
|
||||
if (domain_size == 0) {
|
||||
return;
|
||||
}
|
||||
MutableAttributeAccessor attributes = *component.attributes_for_write();
|
||||
GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_POINT};
|
||||
|
||||
OutputAttribute_Typed<float> radii = component.attribute_try_get_for_output_only<float>(
|
||||
"radius", ATTR_DOMAIN_POINT);
|
||||
AttributeWriter<float> radii = attributes.lookup_or_add_for_write<float>("radius",
|
||||
ATTR_DOMAIN_POINT);
|
||||
|
||||
fn::FieldEvaluator evaluator{field_context, domain_num};
|
||||
fn::FieldEvaluator evaluator{field_context, domain_size};
|
||||
evaluator.set_selection(selection_field);
|
||||
evaluator.add_with_destination(radius_field, radii.varray());
|
||||
evaluator.add_with_destination(radius_field, radii.varray);
|
||||
evaluator.evaluate();
|
||||
|
||||
radii.save();
|
||||
radii.finish();
|
||||
}
|
||||
|
||||
static void node_geo_exec(GeoNodeExecParams params)
|
||||
|
||||
@@ -25,12 +25,10 @@ static void node_declare(NodeDeclarationBuilder &b)
|
||||
static void set_computed_position_and_offset(GeometryComponent &component,
|
||||
const VArray<float3> &in_positions,
|
||||
const VArray<float3> &in_offsets,
|
||||
const eAttrDomain domain,
|
||||
const IndexMask selection)
|
||||
{
|
||||
|
||||
OutputAttribute_Typed<float3> positions = component.attribute_try_get_for_output<float3>(
|
||||
"position", domain, {0, 0, 0});
|
||||
MutableAttributeAccessor attributes = *component.attributes_for_write();
|
||||
AttributeWriter<float3> positions = attributes.lookup_for_write<float3>("position");
|
||||
|
||||
const int grain_size = 10000;
|
||||
|
||||
@@ -38,7 +36,7 @@ static void set_computed_position_and_offset(GeometryComponent &component,
|
||||
case GEO_COMPONENT_TYPE_MESH: {
|
||||
Mesh *mesh = static_cast<MeshComponent &>(component).get_for_write();
|
||||
MutableSpan<MVert> mverts{mesh->mvert, mesh->totvert};
|
||||
if (in_positions.is_same(positions.varray())) {
|
||||
if (in_positions.is_same(positions.varray)) {
|
||||
devirtualize_varray(in_offsets, [&](const auto in_offsets) {
|
||||
threading::parallel_for(
|
||||
selection.index_range(), grain_size, [&](const IndexRange range) {
|
||||
@@ -67,18 +65,13 @@ static void set_computed_position_and_offset(GeometryComponent &component,
|
||||
CurveComponent &curve_component = static_cast<CurveComponent &>(component);
|
||||
Curves &curves_id = *curve_component.get_for_write();
|
||||
bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id.geometry);
|
||||
if (component.attribute_exists("handle_right") &&
|
||||
component.attribute_exists("handle_left")) {
|
||||
OutputAttribute_Typed<float3> handle_right_attribute =
|
||||
component.attribute_try_get_for_output<float3>(
|
||||
"handle_right", ATTR_DOMAIN_POINT, {0, 0, 0});
|
||||
OutputAttribute_Typed<float3> handle_left_attribute =
|
||||
component.attribute_try_get_for_output<float3>(
|
||||
"handle_left", ATTR_DOMAIN_POINT, {0, 0, 0});
|
||||
MutableSpan<float3> handle_right = handle_right_attribute.as_span();
|
||||
MutableSpan<float3> handle_left = handle_left_attribute.as_span();
|
||||
if (attributes.contains("handle_right") && attributes.contains("handle_left")) {
|
||||
SpanAttributeWriter<float3> handle_right_attribute =
|
||||
attributes.lookup_or_add_for_write_span<float3>("handle_right", ATTR_DOMAIN_POINT);
|
||||
SpanAttributeWriter<float3> handle_left_attribute =
|
||||
attributes.lookup_or_add_for_write_span<float3>("handle_left", ATTR_DOMAIN_POINT);
|
||||
|
||||
MutableSpan<float3> out_positions_span = positions.as_span();
|
||||
MutableVArraySpan<float3> out_positions_span = positions.varray;
|
||||
devirtualize_varray2(
|
||||
in_positions, in_offsets, [&](const auto in_positions, const auto in_offsets) {
|
||||
threading::parallel_for(
|
||||
@@ -86,15 +79,16 @@ static void set_computed_position_and_offset(GeometryComponent &component,
|
||||
for (const int i : selection.slice(range)) {
|
||||
const float3 new_position = in_positions[i] + in_offsets[i];
|
||||
const float3 delta = new_position - out_positions_span[i];
|
||||
handle_right[i] += delta;
|
||||
handle_left[i] += delta;
|
||||
handle_right_attribute.span[i] += delta;
|
||||
handle_left_attribute.span[i] += delta;
|
||||
out_positions_span[i] = new_position;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
handle_right_attribute.save();
|
||||
handle_left_attribute.save();
|
||||
out_positions_span.save();
|
||||
handle_right_attribute.finish();
|
||||
handle_left_attribute.finish();
|
||||
|
||||
/* Automatic Bezier handles must be recalculated based on the new positions. */
|
||||
curves.calculate_bezier_auto_handles();
|
||||
@@ -105,8 +99,8 @@ static void set_computed_position_and_offset(GeometryComponent &component,
|
||||
}
|
||||
}
|
||||
default: {
|
||||
MutableSpan<float3> out_positions_span = positions.as_span();
|
||||
if (in_positions.is_same(positions.varray())) {
|
||||
MutableVArraySpan<float3> out_positions_span = positions.varray;
|
||||
if (in_positions.is_same(positions.varray)) {
|
||||
devirtualize_varray(in_offsets, [&](const auto in_offsets) {
|
||||
threading::parallel_for(
|
||||
selection.index_range(), grain_size, [&](const IndexRange range) {
|
||||
@@ -127,11 +121,12 @@ static void set_computed_position_and_offset(GeometryComponent &component,
|
||||
});
|
||||
});
|
||||
}
|
||||
out_positions_span.save();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
positions.save();
|
||||
positions.finish();
|
||||
}
|
||||
|
||||
static void set_position_in_component(GeometryComponent &component,
|
||||
@@ -142,21 +137,22 @@ static void set_position_in_component(GeometryComponent &component,
|
||||
eAttrDomain domain = component.type() == GEO_COMPONENT_TYPE_INSTANCES ? ATTR_DOMAIN_INSTANCE :
|
||||
ATTR_DOMAIN_POINT;
|
||||
GeometryComponentFieldContext field_context{component, domain};
|
||||
const int domain_num = component.attribute_domain_num(domain);
|
||||
if (domain_num == 0) {
|
||||
const int domain_size = component.attribute_domain_size(domain);
|
||||
if (domain_size == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
fn::FieldEvaluator evaluator{field_context, domain_num};
|
||||
fn::FieldEvaluator evaluator{field_context, domain_size};
|
||||
evaluator.set_selection(selection_field);
|
||||
evaluator.add(position_field);
|
||||
evaluator.add(offset_field);
|
||||
evaluator.evaluate();
|
||||
|
||||
const IndexMask selection = evaluator.get_evaluated_selection_as_mask();
|
||||
|
||||
const VArray<float3> positions_input = evaluator.get_evaluated<float3>(0);
|
||||
const VArray<float3> offsets_input = evaluator.get_evaluated<float3>(1);
|
||||
set_computed_position_and_offset(component, positions_input, offsets_input, domain, selection);
|
||||
set_computed_position_and_offset(component, positions_input, offsets_input, selection);
|
||||
}
|
||||
|
||||
static void node_geo_exec(GeoNodeExecParams params)
|
||||
|
||||
@@ -16,21 +16,23 @@ static void set_smooth_in_component(GeometryComponent &component,
|
||||
const Field<bool> &selection_field,
|
||||
const Field<bool> &shade_field)
|
||||
{
|
||||
GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_FACE};
|
||||
const int domain_num = component.attribute_domain_num(ATTR_DOMAIN_FACE);
|
||||
if (domain_num == 0) {
|
||||
const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_FACE);
|
||||
if (domain_size == 0) {
|
||||
return;
|
||||
}
|
||||
MutableAttributeAccessor attributes = *component.attributes_for_write();
|
||||
|
||||
OutputAttribute_Typed<bool> shades = component.attribute_try_get_for_output_only<bool>(
|
||||
"shade_smooth", ATTR_DOMAIN_FACE);
|
||||
GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_FACE};
|
||||
|
||||
fn::FieldEvaluator evaluator{field_context, domain_num};
|
||||
AttributeWriter<bool> shades = attributes.lookup_or_add_for_write<bool>("shade_smooth",
|
||||
ATTR_DOMAIN_FACE);
|
||||
|
||||
fn::FieldEvaluator evaluator{field_context, domain_size};
|
||||
evaluator.set_selection(selection_field);
|
||||
evaluator.add_with_destination(shade_field, shades.varray());
|
||||
evaluator.add_with_destination(shade_field, shades.varray);
|
||||
evaluator.evaluate();
|
||||
|
||||
shades.save();
|
||||
shades.finish();
|
||||
}
|
||||
|
||||
static void node_geo_exec(GeoNodeExecParams params)
|
||||
|
||||
@@ -16,21 +16,23 @@ static void set_cyclic_in_component(GeometryComponent &component,
|
||||
const Field<bool> &selection_field,
|
||||
const Field<bool> &cyclic_field)
|
||||
{
|
||||
GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_CURVE};
|
||||
const int domain_num = component.attribute_domain_num(ATTR_DOMAIN_CURVE);
|
||||
if (domain_num == 0) {
|
||||
const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_CURVE);
|
||||
if (domain_size == 0) {
|
||||
return;
|
||||
}
|
||||
MutableAttributeAccessor attributes = *component.attributes_for_write();
|
||||
|
||||
OutputAttribute_Typed<bool> cyclics = component.attribute_try_get_for_output_only<bool>(
|
||||
"cyclic", ATTR_DOMAIN_CURVE);
|
||||
GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_CURVE};
|
||||
|
||||
fn::FieldEvaluator evaluator{field_context, domain_num};
|
||||
AttributeWriter<bool> cyclics = attributes.lookup_or_add_for_write<bool>("cyclic",
|
||||
ATTR_DOMAIN_CURVE);
|
||||
|
||||
fn::FieldEvaluator evaluator{field_context, domain_size};
|
||||
evaluator.set_selection(selection_field);
|
||||
evaluator.add_with_destination(cyclic_field, cyclics.varray());
|
||||
evaluator.add_with_destination(cyclic_field, cyclics.varray);
|
||||
evaluator.evaluate();
|
||||
|
||||
cyclics.save();
|
||||
cyclics.finish();
|
||||
}
|
||||
|
||||
static void node_geo_exec(GeoNodeExecParams params)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user