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:
2022-07-08 16:16:56 +02:00
parent f391e8f316
commit b876ce2a4a
109 changed files with 2763 additions and 2808 deletions

View 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

View File

@@ -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

View File

@@ -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.
*/

View File

@@ -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;
};
/**

View File

@@ -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:

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
});

View File

@@ -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());
}

View File

@@ -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());
}

View File

@@ -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());
}
/** \} */

View File

@@ -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());
}
/** \} */

View File

@@ -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());
}
/** \} */

View File

@@ -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;

View File

@@ -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). */

View File

@@ -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);
}
}

View File

@@ -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
{

View File

@@ -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;
}

View File

@@ -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()];

View File

@@ -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();

View File

@@ -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) {

View File

@@ -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! */
}

View File

@@ -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. */

View File

@@ -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. */

View File

@@ -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. */

View File

@@ -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) {

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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,

View File

@@ -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();
});
}

View File

@@ -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();
}
});
}

View File

@@ -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");
}
});
}

View File

@@ -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;

View File

@@ -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;

View File

@@ -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();
}
}

View File

@@ -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());

View File

@@ -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. */

View File

@@ -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;

View File

@@ -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);
}

View File

@@ -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)

View File

@@ -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();

View File

@@ -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);

View File

@@ -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",

View File

@@ -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();
}

View File

@@ -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);

View File

@@ -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)

View File

@@ -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);

View File

@@ -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;

View File

@@ -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 {

View File

@@ -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();

View File

@@ -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),

View File

@@ -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);

View File

@@ -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();

View File

@@ -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();
}
}

View File

@@ -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};

View File

@@ -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);

View File

@@ -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;
});

View File

@@ -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);

View File

@@ -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);
}
};

View File

@@ -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;
});

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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 {};

View File

@@ -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); }),

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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,

View File

@@ -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

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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(

View File

@@ -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();
}
}

View File

@@ -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();
}
}

View File

@@ -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;

View File

@@ -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);

View File

@@ -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()

View File

@@ -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,

View File

@@ -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)

View File

@@ -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);

View File

@@ -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();
}
}

View File

@@ -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));
}

View File

@@ -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();
}
}

View File

@@ -163,12 +163,15 @@ static void gather_point_data_from_component(GeoNodeExecParams &params,
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));

View File

@@ -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);

View File

@@ -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;
}
}

View File

@@ -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;

View File

@@ -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)

View File

@@ -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)

View File

@@ -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();
}
}

View File

@@ -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)

View File

@@ -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)

View File

@@ -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)

View File

@@ -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)

View File

@@ -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