GPv3: Initial Geometry Nodes support

This implements the core changes for this design: https://devtalk.blender.org/t/grease-pencil-integration-into-geometry-nodes/31220

The changes include:
* Add `CustomData` for layer attributes
* Add attribute support for the `GreasePencilComponent` to read/write layer attributes. Also introduces a `Layer` domain.
* Implement a `GreasePencilLayerFieldContext` and make `GeometryFieldContext` work with grease pencil layers.
* Implement `Set Position` node for `Grease Pencil`.

Note: These changes are only accessible/visible with the `Grease Pencil 3.0` experimental flag enabled.
Co-authored-by: Jacques Lucke <jacques@blender.org>
Pull Request: https://projects.blender.org/blender/blender/pulls/112535
This commit is contained in:
Falk David 2023-10-10 16:49:30 +02:00 committed by Falk David
parent 7d7ca7b90d
commit 3931a54e08
36 changed files with 1013 additions and 100 deletions

@ -68,7 +68,7 @@ def geometry_modifier_poll(context):
ob = context.object
# Test object support for geometry node modifier
if not ob or ob.type not in {'MESH', 'POINTCLOUD', 'VOLUME', 'CURVE', 'FONT', 'CURVES'}:
if not ob or ob.type not in {'MESH', 'POINTCLOUD', 'VOLUME', 'CURVE', 'FONT', 'CURVES', 'GREASEPENCIL'}:
return False
return True

@ -56,7 +56,7 @@ class OBJECT_MT_modifier_add(ModifierAddMenu, Menu):
def draw(self, context):
layout = self.layout
ob_type = context.object.type
geometry_nodes_supported = ob_type in {'MESH', 'CURVE', 'CURVES', 'FONT', 'SURFACE', 'VOLUME', 'POINTCLOUD'}
geometry_nodes_supported = ob_type in {'MESH', 'CURVE', 'CURVES', 'FONT', 'SURFACE', 'VOLUME', 'POINTCLOUD', 'GREASEPENCIL'}
if geometry_nodes_supported:
self.operator_modifier_add(layout, 'NODES')
layout.separator()

@ -24,15 +24,16 @@ struct ReportList;
/** #Attribute.domain */
typedef enum eAttrDomain {
ATTR_DOMAIN_AUTO = -1, /* Use for nodes to choose automatically based on other data. */
ATTR_DOMAIN_POINT = 0, /* Mesh, Curve or Point Cloud Point */
ATTR_DOMAIN_EDGE = 1, /* Mesh Edge */
ATTR_DOMAIN_FACE = 2, /* Mesh Face */
ATTR_DOMAIN_CORNER = 3, /* Mesh Corner */
ATTR_DOMAIN_CURVE = 4, /* A single curve in a larger curve data-block */
ATTR_DOMAIN_INSTANCE = 5, /* Instance */
ATTR_DOMAIN_AUTO = -1, /* Use for nodes to choose automatically based on other data. */
ATTR_DOMAIN_POINT = 0, /* Mesh, Curve or Point Cloud Point */
ATTR_DOMAIN_EDGE = 1, /* Mesh Edge */
ATTR_DOMAIN_FACE = 2, /* Mesh Face */
ATTR_DOMAIN_CORNER = 3, /* Mesh Corner */
ATTR_DOMAIN_CURVE = 4, /* A single curve in a larger curve data-block */
ATTR_DOMAIN_INSTANCE = 5, /* Instance */
ATTR_DOMAIN_GREASE_PENCIL_LAYER = 6,/* A layer in a grease pencil data-block */
} eAttrDomain;
#define ATTR_DOMAIN_NUM 6
#define ATTR_DOMAIN_NUM 7
typedef enum eAttrDomainMask {
ATTR_DOMAIN_MASK_POINT = (1 << 0),
@ -40,7 +41,8 @@ typedef enum eAttrDomainMask {
ATTR_DOMAIN_MASK_FACE = (1 << 2),
ATTR_DOMAIN_MASK_CORNER = (1 << 3),
ATTR_DOMAIN_MASK_CURVE = (1 << 4),
ATTR_DOMAIN_MASK_ALL = (1 << 5) - 1
ATTR_DOMAIN_MASK_GREASE_PENCIL_LAYER = (1 << 6),
ATTR_DOMAIN_MASK_ALL = (1 << 7) - 1
} eAttrDomainMask;
ENUM_OPERATORS(eAttrDomainMask, ATTR_DOMAIN_MASK_ALL);

@ -21,6 +21,9 @@ namespace blender::bke {
class CurvesGeometry;
class GeometryFieldInput;
namespace greasepencil {
class Drawing;
}
class MeshFieldContext : public fn::FieldContext {
private:
@ -72,6 +75,40 @@ class PointCloudFieldContext : public fn::FieldContext {
}
};
class GreasePencilLayerFieldContext : public fn::FieldContext {
private:
const GreasePencil &grease_pencil_;
const eAttrDomain domain_;
const int layer_index_;
public:
GreasePencilLayerFieldContext(const GreasePencil &grease_pencil,
eAttrDomain domain,
int layer_index)
: grease_pencil_(grease_pencil), domain_(domain), layer_index_(layer_index)
{
}
const GreasePencil &grease_pencil() const
{
return grease_pencil_;
}
eAttrDomain domain() const
{
return domain_;
}
int layer_index() const
{
return layer_index_;
}
GVArray get_varray_for_input(const fn::FieldInput &field_input,
const IndexMask &mask,
ResourceScope &scope) const;
};
class InstancesFieldContext : public fn::FieldContext {
private:
const Instances &instances_;
@ -86,8 +123,8 @@ class InstancesFieldContext : public fn::FieldContext {
};
/**
* A field context that can represent meshes, curves, point clouds, or instances,
* used for field inputs that can work for multiple geometry types.
* A field context that can represent meshes, curves, point clouds, instances or grease pencil
* layers, used for field inputs that can work for multiple geometry types.
*/
class GeometryFieldContext : public fn::FieldContext {
private:
@ -99,12 +136,26 @@ class GeometryFieldContext : public fn::FieldContext {
const void *geometry_;
const GeometryComponent::Type type_;
const eAttrDomain domain_;
/**
* Only used when the type is grease pencil and the domain is either points or curves
* (not layers).
*/
int grease_pencil_layer_index_;
friend GeometryFieldInput;
public:
GeometryFieldContext(const GeometryFieldContext &other, eAttrDomain domain);
GeometryFieldContext(const GeometryComponent &component, eAttrDomain domain);
GeometryFieldContext(const void *geometry, GeometryComponent::Type type, eAttrDomain domain);
GeometryFieldContext(const void *geometry,
GeometryComponent::Type type,
eAttrDomain domain,
int grease_pencil_layer_index);
GeometryFieldContext(const Mesh &mesh, eAttrDomain domain);
GeometryFieldContext(const CurvesGeometry &curves, eAttrDomain domain);
GeometryFieldContext(const GreasePencil &grease_pencil, eAttrDomain domain, int layer_index);
GeometryFieldContext(const PointCloud &points);
GeometryFieldContext(const Instances &instances);
const void *geometry() const
{
@ -121,17 +172,22 @@ class GeometryFieldContext : public fn::FieldContext {
return domain_;
}
int grease_pencil_layer_index() const
{
BLI_assert(this->type_ == GeometryComponent::Type::GreasePencil);
BLI_assert(ELEM(
this->domain_, ATTR_DOMAIN_GREASE_PENCIL_LAYER, ATTR_DOMAIN_CURVE, ATTR_DOMAIN_POINT));
return grease_pencil_layer_index_;
}
std::optional<AttributeAccessor> attributes() const;
const Mesh *mesh() const;
const CurvesGeometry *curves() const;
const PointCloud *pointcloud() const;
const GreasePencil *grease_pencil() const;
const greasepencil::Drawing *grease_pencil_layer_drawing() const;
const Instances *instances() const;
private:
GeometryFieldContext(const Mesh &mesh, eAttrDomain domain);
GeometryFieldContext(const CurvesGeometry &curves, eAttrDomain domain);
GeometryFieldContext(const PointCloud &points);
GeometryFieldContext(const Instances &instances);
const CurvesGeometry *curves_or_strokes() const;
};
class GeometryFieldInput : public fn::FieldInput {

@ -690,8 +690,8 @@ class GeometryComponentEditData final : public GeometryComponent {
/**
* A geometry component that stores #GreasePencil data.
* This component does not implement an attribute API, because the #GreasePencil data itself does
* not store any attributes, only the individual drawings within it.
* The attributes on this component are only on the layer domain. Each individual layer represents
* a #CurvesGeometry with its own curve and point domain. See #CurveComponent.
*/
class GreasePencilComponent : public GeometryComponent {
private:
@ -725,6 +725,9 @@ class GreasePencilComponent : public GeometryComponent {
void ensure_owns_direct_data() override;
static constexpr inline GeometryComponent::Type static_type = Type::GreasePencil;
std::optional<AttributeAccessor> attributes() const final;
std::optional<MutableAttributeAccessor> attributes_for_write() final;
};
} // namespace blender::bke

@ -646,6 +646,10 @@ void legacy_gpencil_to_grease_pencil(Main &main, GreasePencil &grease_pencil, bG
} // namespace convert
const Drawing *get_eval_grease_pencil_layer_drawing(const GreasePencil &grease_pencil,
int layer_index);
Drawing *get_eval_grease_pencil_layer_drawing_for_write(GreasePencil &grease_pencil,
int layer_index);
} // namespace greasepencil
class GreasePencilRuntime {

@ -32,6 +32,7 @@
#include "BKE_curves.hh"
#include "BKE_customdata.h"
#include "BKE_editmesh.h"
#include "BKE_grease_pencil.hh"
#include "BKE_mesh.hh"
#include "BKE_pointcloud.h"
#include "BKE_report.h"
@ -90,6 +91,12 @@ static void get_domains(const ID *id, DomainInfo info[ATTR_DOMAIN_NUM])
info[ATTR_DOMAIN_CURVE].length = curves->geometry.curve_num;
break;
}
case ID_GP: {
GreasePencil *grease_pencil = (GreasePencil *)id;
info[ATTR_DOMAIN_GREASE_PENCIL_LAYER].customdata = &grease_pencil->layers_data;
info[ATTR_DOMAIN_GREASE_PENCIL_LAYER].length = grease_pencil->layers().size();
break;
}
default:
break;
}
@ -116,6 +123,10 @@ static std::optional<blender::bke::MutableAttributeAccessor> get_attribute_acces
CurvesGeometry &curves = curves_id.geometry.wrap();
return curves.attributes_for_write();
}
case ID_GP: {
GreasePencil &grease_pencil = reinterpret_cast<GreasePencil &>(id);
return grease_pencil.attributes_for_write();
}
default: {
BLI_assert_unreachable();
return {};
@ -707,6 +718,9 @@ int *BKE_id_attributes_active_index_p(ID *id)
case ID_CV: {
return &((Curves *)id)->attributes_active_index;
}
case ID_GP: {
return &((GreasePencil *)id)->attributes_active_index;
}
default:
return nullptr;
}

@ -147,16 +147,18 @@ static int attribute_domain_priority(const eAttrDomain domain)
switch (domain) {
case ATTR_DOMAIN_INSTANCE:
return 0;
case ATTR_DOMAIN_CURVE:
case ATTR_DOMAIN_GREASE_PENCIL_LAYER:
return 1;
case ATTR_DOMAIN_FACE:
case ATTR_DOMAIN_CURVE:
return 2;
case ATTR_DOMAIN_EDGE:
case ATTR_DOMAIN_FACE:
return 3;
case ATTR_DOMAIN_POINT:
case ATTR_DOMAIN_EDGE:
return 4;
case ATTR_DOMAIN_CORNER:
case ATTR_DOMAIN_POINT:
return 5;
case ATTR_DOMAIN_CORNER:
return 6;
default:
/* Domain not supported in nodes yet. */
BLI_assert_unreachable();

@ -6,6 +6,10 @@
#include "BKE_grease_pencil.hh"
#include "BKE_lib_id.h"
#include "DNA_grease_pencil_types.h"
#include "attribute_access_intern.hh"
namespace blender::bke {
/* -------------------------------------------------------------------- */
@ -95,4 +99,104 @@ void GreasePencilComponent::ensure_owns_direct_data()
}
}
static ComponentAttributeProviders create_attribute_providers_for_grease_pencil()
{
static CustomDataAccessInfo layers_access = {
[](void *owner) -> CustomData * {
GreasePencil &grease_pencil = *static_cast<GreasePencil *>(owner);
return &grease_pencil.layers_data;
},
[](const void *owner) -> const CustomData * {
const GreasePencil &grease_pencil = *static_cast<const GreasePencil *>(owner);
return &grease_pencil.layers_data;
},
[](const void *owner) -> int {
const GreasePencil &grease_pencil = *static_cast<const GreasePencil *>(owner);
return grease_pencil.layers().size();
}};
static CustomDataAttributeProvider layer_custom_data(ATTR_DOMAIN_GREASE_PENCIL_LAYER,
layers_access);
return ComponentAttributeProviders({}, {&layer_custom_data});
}
static GVArray adapt_grease_pencil_attribute_domain(const GreasePencil & /*grease_pencil*/,
const GVArray &varray,
const eAttrDomain from,
const eAttrDomain to)
{
if (from == to) {
return varray;
}
return {};
}
static AttributeAccessorFunctions get_grease_pencil_accessor_functions()
{
static const ComponentAttributeProviders providers =
create_attribute_providers_for_grease_pencil();
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 GreasePencil &grease_pencil = *static_cast<const GreasePencil *>(owner);
switch (domain) {
case ATTR_DOMAIN_GREASE_PENCIL_LAYER:
return int(grease_pencil.layers().size());
default:
return 0;
}
};
fn.domain_supported = [](const void * /*owner*/, const eAttrDomain domain) {
return domain == ATTR_DOMAIN_GREASE_PENCIL_LAYER;
};
fn.adapt_domain = [](const void *owner,
const GVArray &varray,
const eAttrDomain from_domain,
const eAttrDomain to_domain) -> GVArray {
if (owner == nullptr) {
return {};
}
const GreasePencil &grease_pencil = *static_cast<const GreasePencil *>(owner);
return adapt_grease_pencil_attribute_domain(grease_pencil, varray, from_domain, to_domain);
};
return fn;
}
static const AttributeAccessorFunctions &get_grease_pencil_accessor_functions_ref()
{
static const AttributeAccessorFunctions fn = get_grease_pencil_accessor_functions();
return fn;
}
} // namespace blender::bke
blender::bke::AttributeAccessor GreasePencil::attributes() const
{
return blender::bke::AttributeAccessor(this,
blender::bke::get_grease_pencil_accessor_functions_ref());
}
blender::bke::MutableAttributeAccessor GreasePencil::attributes_for_write()
{
return blender::bke::MutableAttributeAccessor(
this, blender::bke::get_grease_pencil_accessor_functions_ref());
}
namespace blender::bke {
std::optional<AttributeAccessor> GreasePencilComponent::attributes() const
{
return AttributeAccessor(grease_pencil_, get_grease_pencil_accessor_functions_ref());
}
std::optional<MutableAttributeAccessor> GreasePencilComponent::attributes_for_write()
{
GreasePencil *grease_pencil = this->get_for_write();
return MutableAttributeAccessor(grease_pencil, get_grease_pencil_accessor_functions_ref());
}
} // namespace blender::bke

@ -8,6 +8,7 @@
#include "BKE_curves.hh"
#include "BKE_geometry_fields.hh"
#include "BKE_geometry_set.hh"
#include "BKE_grease_pencil.hh"
#include "BKE_instances.hh"
#include "BKE_mesh.hh"
#include "BKE_pointcloud.h"
@ -34,15 +35,50 @@ CurvesFieldContext::CurvesFieldContext(const CurvesGeometry &curves, const eAttr
BLI_assert(curves.attributes().domain_supported(domain));
}
GVArray GreasePencilLayerFieldContext::get_varray_for_input(const fn::FieldInput &field_input,
const IndexMask &mask,
ResourceScope &scope) const
{
if (const CurvesFieldInput *curves_field_input = dynamic_cast<const CurvesFieldInput *>(
&field_input))
{
if (const bke::greasepencil::Drawing *drawing =
bke::greasepencil::get_eval_grease_pencil_layer_drawing(this->grease_pencil(),
this->layer_index()))
{
if (drawing->strokes().attributes().domain_supported(this->domain())) {
const CurvesFieldContext context{drawing->strokes(), this->domain()};
return curves_field_input->get_varray_for_context(context, mask, scope);
}
}
return {};
}
return field_input.get_varray_for_context(*this, mask, scope);
}
GeometryFieldContext::GeometryFieldContext(const GeometryFieldContext &other,
const eAttrDomain domain)
: geometry_(other.geometry_),
type_(other.type_),
domain_(domain),
grease_pencil_layer_index_(other.grease_pencil_layer_index_)
{
}
GeometryFieldContext::GeometryFieldContext(const void *geometry,
const GeometryComponent::Type type,
const eAttrDomain domain)
: geometry_(geometry), type_(type), domain_(domain)
const eAttrDomain domain,
const int grease_pencil_layer_index)
: geometry_(geometry),
type_(type),
domain_(domain),
grease_pencil_layer_index_(grease_pencil_layer_index)
{
BLI_assert(ELEM(type,
GeometryComponent::Type::Mesh,
GeometryComponent::Type::Curve,
GeometryComponent::Type::PointCloud,
GeometryComponent::Type::GreasePencil,
GeometryComponent::Type::Instance));
}
@ -68,6 +104,14 @@ GeometryFieldContext::GeometryFieldContext(const GeometryComponent &component,
geometry_ = pointcloud_component.get();
break;
}
case GeometryComponent::Type::GreasePencil: {
const GreasePencilComponent &grease_pencil_component =
static_cast<const GreasePencilComponent &>(component);
geometry_ = grease_pencil_component.get();
/* Need to use another constructor for other domains. */
BLI_assert(domain == ATTR_DOMAIN_GREASE_PENCIL_LAYER);
break;
}
case GeometryComponent::Type::Instance: {
const InstancesComponent &instances_component = static_cast<const InstancesComponent &>(
component);
@ -76,7 +120,6 @@ GeometryFieldContext::GeometryFieldContext(const GeometryComponent &component,
}
case GeometryComponent::Type::Volume:
case GeometryComponent::Type::Edit:
case GeometryComponent::Type::GreasePencil:
BLI_assert_unreachable();
break;
}
@ -94,6 +137,15 @@ GeometryFieldContext::GeometryFieldContext(const PointCloud &points)
: geometry_(&points), type_(GeometryComponent::Type::PointCloud), domain_(ATTR_DOMAIN_POINT)
{
}
GeometryFieldContext::GeometryFieldContext(const GreasePencil &grease_pencil,
const eAttrDomain domain,
const int layer_index)
: geometry_(&grease_pencil),
type_(GeometryComponent::Type::GreasePencil),
domain_(domain),
grease_pencil_layer_index_(layer_index)
{
}
GeometryFieldContext::GeometryFieldContext(const Instances &instances)
: geometry_(&instances),
type_(GeometryComponent::Type::Instance),
@ -112,6 +164,17 @@ std::optional<AttributeAccessor> GeometryFieldContext::attributes() const
if (const PointCloud *pointcloud = this->pointcloud()) {
return pointcloud->attributes();
}
if (const GreasePencil *grease_pencil = this->grease_pencil()) {
if (domain_ == ATTR_DOMAIN_GREASE_PENCIL_LAYER) {
return grease_pencil->attributes();
}
else if (const greasepencil::Drawing *drawing =
greasepencil::get_eval_grease_pencil_layer_drawing(*grease_pencil,
grease_pencil_layer_index_))
{
return drawing->strokes().attributes();
}
}
if (const Instances *instances = this->instances()) {
return instances->attributes();
}
@ -135,6 +198,32 @@ const PointCloud *GeometryFieldContext::pointcloud() const
static_cast<const PointCloud *>(geometry_) :
nullptr;
}
const GreasePencil *GeometryFieldContext::grease_pencil() const
{
return this->type() == GeometryComponent::Type::GreasePencil ?
static_cast<const GreasePencil *>(geometry_) :
nullptr;
}
const greasepencil::Drawing *GeometryFieldContext::grease_pencil_layer_drawing() const
{
if (!(this->type() == GeometryComponent::Type::GreasePencil) ||
!ELEM(domain_, ATTR_DOMAIN_CURVE, ATTR_DOMAIN_POINT))
{
return nullptr;
}
return greasepencil::get_eval_grease_pencil_layer_drawing(*this->grease_pencil(),
this->grease_pencil_layer_index_);
}
const CurvesGeometry *GeometryFieldContext::curves_or_strokes() const
{
if (const CurvesGeometry *curves = this->curves()) {
return curves;
}
if (const greasepencil::Drawing *drawing = this->grease_pencil_layer_drawing()) {
return &drawing->strokes();
}
return nullptr;
}
const Instances *GeometryFieldContext::instances() const
{
return this->type() == GeometryComponent::Type::Instance ?
@ -163,6 +252,14 @@ GVArray GeometryFieldInput::get_varray_for_context(const fn::FieldContext &conte
{
return this->get_varray_for_context({point_context->pointcloud()}, mask);
}
if (const GreasePencilLayerFieldContext *grease_pencil_context =
dynamic_cast<const GreasePencilLayerFieldContext *>(&context))
{
return this->get_varray_for_context({grease_pencil_context->grease_pencil(),
grease_pencil_context->domain(),
grease_pencil_context->layer_index()},
mask);
}
if (const InstancesFieldContext *instances_context = dynamic_cast<const InstancesFieldContext *>(
&context))
{
@ -265,8 +362,31 @@ GVArray AttributeFieldInput::get_varray_for_context(const GeometryFieldContext &
const IndexMask & /*mask*/) const
{
const eCustomDataType data_type = cpp_type_to_custom_data_type(*type_);
if (auto attributes = context.attributes()) {
return *attributes->lookup(name_, context.domain(), data_type);
const eAttrDomain domain = context.domain();
if (const GreasePencil *grease_pencil = context.grease_pencil()) {
const AttributeAccessor layer_attributes = grease_pencil->attributes();
if (domain == ATTR_DOMAIN_GREASE_PENCIL_LAYER) {
return *layer_attributes.lookup(name_, data_type);
}
else if (ELEM(domain, ATTR_DOMAIN_POINT, ATTR_DOMAIN_CURVE)) {
const int layer_index = context.grease_pencil_layer_index();
const AttributeAccessor curves_attributes = *context.attributes();
if (const GAttributeReader reader = curves_attributes.lookup(name_, domain, data_type)) {
return *reader;
}
/* Lookup attribute on the layer domain if it does not exist on points or curves. */
if (const GAttributeReader reader = layer_attributes.lookup(name_)) {
const CPPType &cpp_type = reader.varray.type();
BUFFER_FOR_CPP_TYPE_VALUE(cpp_type, value);
BLI_SCOPED_DEFER([&]() { cpp_type.destruct(value); });
reader.varray.get_to_uninitialized(layer_index, value);
const int domain_size = curves_attributes.domain_size(domain);
return GVArray::ForSingle(cpp_type, domain_size, value);
}
}
}
else if (auto attributes = context.attributes()) {
return *attributes->lookup(name_, domain, data_type);
}
return {};
}
@ -479,13 +599,13 @@ static bool try_add_shared_field_attribute(MutableAttributeAccessor attributes,
return attributes.add(id_to_create, domain, data_type, init);
}
bool try_capture_field_on_geometry(GeometryComponent &component,
const AttributeIDRef &attribute_id,
const eAttrDomain domain,
const fn::Field<bool> &selection,
const fn::GField &field)
static bool try_capture_field_on_geometry(MutableAttributeAccessor attributes,
const GeometryFieldContext &field_context,
const AttributeIDRef &attribute_id,
const eAttrDomain domain,
const fn::Field<bool> &selection,
const fn::GField &field)
{
MutableAttributeAccessor attributes = *component.attributes_for_write();
const int domain_size = attributes.domain_size(domain);
const CPPType &type = field.cpp_type();
const eCustomDataType data_type = bke::cpp_type_to_custom_data_type(type);
@ -494,7 +614,6 @@ bool try_capture_field_on_geometry(GeometryComponent &component,
return attributes.add(attribute_id, domain, data_type, AttributeInitConstruct{});
}
const bke::GeometryFieldContext field_context{component, domain};
const bke::AttributeValidator validator = attributes.lookup_validator(attribute_id);
const std::optional<AttributeMetaData> meta_data = attributes.lookup_meta_data(attribute_id);
@ -504,7 +623,6 @@ bool try_capture_field_on_geometry(GeometryComponent &component,
/* We are writing to an attribute that exists already with the correct domain and type. */
if (attribute_matches) {
if (GSpanAttributeWriter dst_attribute = attributes.lookup_for_write_span(attribute_id)) {
const bke::GeometryFieldContext field_context{component, domain};
fn::FieldEvaluator evaluator{field_context, domain_size};
evaluator.add(validator.validate_field_if_necessary(field));
evaluator.set_selection(selection);
@ -562,6 +680,51 @@ bool try_capture_field_on_geometry(GeometryComponent &component,
return false;
}
bool try_capture_field_on_geometry(GeometryComponent &component,
const AttributeIDRef &attribute_id,
const eAttrDomain domain,
const fn::Field<bool> &selection,
const fn::GField &field)
{
if (component.type() == GeometryComponent::Type::GreasePencil &&
ELEM(domain, ATTR_DOMAIN_POINT, ATTR_DOMAIN_CURVE))
{
/* Capture the field on every layer individually. */
auto &grease_pencil_component = static_cast<GreasePencilComponent &>(component);
GreasePencil *grease_pencil = grease_pencil_component.get_for_write();
if (grease_pencil == nullptr) {
return false;
}
bool any_success = false;
threading::parallel_for(grease_pencil->layers().index_range(), 8, [&](const IndexRange range) {
for (const int layer_index : range) {
if (greasepencil::Drawing *drawing =
greasepencil::get_eval_grease_pencil_layer_drawing_for_write(*grease_pencil,
layer_index))
{
const GeometryFieldContext field_context{*grease_pencil, domain, layer_index};
const bool success = try_capture_field_on_geometry(
drawing->strokes_for_write().attributes_for_write(),
field_context,
attribute_id,
domain,
selection,
field);
if (success & !any_success) {
any_success = true;
}
}
}
});
return any_success;
}
MutableAttributeAccessor attributes = *component.attributes_for_write();
const GeometryFieldContext field_context{component, domain};
return try_capture_field_on_geometry(
attributes, field_context, attribute_id, domain, selection, field);
}
bool try_capture_field_on_geometry(GeometryComponent &component,
const AttributeIDRef &attribute_id,
const eAttrDomain domain,
@ -578,6 +741,9 @@ std::optional<eAttrDomain> try_detect_field_domain(const GeometryComponent &comp
if (component_type == GeometryComponent::Type::PointCloud) {
return ATTR_DOMAIN_POINT;
}
if (component_type == GeometryComponent::Type::GreasePencil) {
return ATTR_DOMAIN_GREASE_PENCIL_LAYER;
}
if (component_type == GeometryComponent::Type::Instance) {
return ATTR_DOMAIN_INSTANCE;
}

@ -240,6 +240,9 @@ std::ostream &operator<<(std::ostream &stream, const GeometrySet &geometry_set)
parts.append(std::to_string(curves->geometry.point_num) + " control points");
parts.append(std::to_string(curves->geometry.curve_num) + " curves");
}
if (const GreasePencil *grease_pencil = geometry_set.get_grease_pencil()) {
parts.append(std::to_string(grease_pencil->layers().size()) + " grease pencil layers");
}
if (const PointCloud *point_cloud = geometry_set.get_pointcloud()) {
parts.append(std::to_string(point_cloud->totpoint) + " points");
}

@ -70,11 +70,14 @@ static void grease_pencil_init_data(ID *id)
using namespace blender::bke;
GreasePencil *grease_pencil = reinterpret_cast<GreasePencil *>(id);
grease_pencil->runtime = MEM_new<GreasePencilRuntime>(__func__);
grease_pencil->root_group_ptr = MEM_new<greasepencil::LayerGroup>(__func__);
grease_pencil->active_layer = nullptr;
grease_pencil->flag |= GREASE_PENCIL_ANIM_CHANNEL_EXPANDED;
CustomData_reset(&grease_pencil->layers_data);
grease_pencil->runtime = MEM_new<GreasePencilRuntime>(__func__);
}
static void grease_pencil_copy_data(Main * /*bmain*/,
@ -125,6 +128,11 @@ static void grease_pencil_copy_data(Main * /*bmain*/,
grease_pencil_dst->find_layer_by_name(grease_pencil_src->active_layer->wrap().name()));
}
CustomData_copy(&grease_pencil_src->layers_data,
&grease_pencil_dst->layers_data,
CD_MASK_ALL,
grease_pencil_dst->layers().size());
/* Make sure the runtime pointer exists. */
grease_pencil_dst->runtime = MEM_new<bke::GreasePencilRuntime>(__func__);
}
@ -136,6 +144,8 @@ static void grease_pencil_free_data(ID *id)
MEM_SAFE_FREE(grease_pencil->material_array);
CustomData_free(&grease_pencil->layers_data, grease_pencil->layers().size());
free_drawing_array(*grease_pencil);
MEM_delete(&grease_pencil->root_group());
@ -164,10 +174,20 @@ static void grease_pencil_blend_write(BlendWriter *writer, ID *id, const void *i
{
GreasePencil *grease_pencil = reinterpret_cast<GreasePencil *>(id);
blender::Vector<CustomDataLayer, 16> layers_data_layers;
CustomData_blend_write_prepare(grease_pencil->layers_data, layers_data_layers);
/* Write LibData */
BLO_write_id_struct(writer, GreasePencil, id_address, &grease_pencil->id);
BKE_id_blend_write(writer, &grease_pencil->id);
CustomData_blend_write(writer,
&grease_pencil->layers_data,
layers_data_layers,
grease_pencil->layers().size(),
CD_MASK_ALL,
id);
/* Write drawings. */
write_drawing_array(*grease_pencil, writer);
/* Write layer tree. */
@ -188,6 +208,8 @@ static void grease_pencil_blend_read_data(BlendDataReader *reader, ID *id)
/* Read layer tree. */
read_layer_tree(*grease_pencil, reader);
CustomData_blend_read(reader, &grease_pencil->layers_data, grease_pencil->layers().size());
/* Read materials. */
BLO_read_pointer_array(reader, reinterpret_cast<void **>(&grease_pencil->material_array));
@ -409,6 +431,29 @@ void Drawing::tag_topology_changed()
this->tag_positions_changed();
}
const Drawing *get_eval_grease_pencil_layer_drawing(const GreasePencil &grease_pencil,
const int layer_index)
{
BLI_assert(layer_index >= 0 && layer_index < grease_pencil.layers().size());
const Layer &layer = *grease_pencil.layers()[layer_index];
const int drawing_index = layer.drawing_index_at(grease_pencil.runtime->eval_frame);
if (drawing_index == -1) {
return nullptr;
}
const GreasePencilDrawingBase *drawing_base = grease_pencil.drawing(drawing_index);
if (drawing_base->type != GP_DRAWING) {
return nullptr;
}
const Drawing &drawing = reinterpret_cast<const GreasePencilDrawing *>(drawing_base)->wrap();
return &drawing;
}
Drawing *get_eval_grease_pencil_layer_drawing_for_write(GreasePencil &grease_pencil,
const int layer)
{
return const_cast<Drawing *>(get_eval_grease_pencil_layer_drawing(grease_pencil, layer));
}
TreeNode::TreeNode()
{
this->next = this->prev = nullptr;
@ -1063,8 +1108,10 @@ GreasePencil *BKE_grease_pencil_new_nomain()
GreasePencil *BKE_grease_pencil_copy_for_eval(const GreasePencil *grease_pencil_src)
{
return reinterpret_cast<GreasePencil *>(
GreasePencil *grease_pencil = reinterpret_cast<GreasePencil *>(
BKE_id_copy_ex(nullptr, &grease_pencil_src->id, nullptr, LIB_ID_COPY_LOCALIZE));
grease_pencil->runtime->eval_frame = grease_pencil_src->runtime->eval_frame;
return grease_pencil;
}
BoundBox *BKE_grease_pencil_boundbox_get(Object *ob)
@ -1132,6 +1179,8 @@ void BKE_grease_pencil_data_update(Depsgraph *depsgraph, Scene *scene, Object *o
/* Evaluate modifiers. */
GreasePencil *grease_pencil = static_cast<GreasePencil *>(object->data);
/* Store the frame that this grease pencil is evaluated on. */
grease_pencil->runtime->eval_frame = int(DEG_get_ctime(depsgraph));
GeometrySet geometry_set = GeometrySet::from_grease_pencil(grease_pencil,
GeometryOwnershipType::ReadOnly);
grease_pencil_evaluate_modifiers(depsgraph, scene, object, geometry_set);
@ -1144,9 +1193,6 @@ void BKE_grease_pencil_data_update(Depsgraph *depsgraph, Scene *scene, Object *o
* would result in a copy when it's shared. So for now, we use a const_cast here. */
GreasePencil *grease_pencil_eval = const_cast<GreasePencil *>(geometry_set.get_grease_pencil());
/* Store the frame that this grease pencil data was evaluated on. */
grease_pencil_eval->runtime->eval_frame = int(DEG_get_ctime(depsgraph));
/* Assign evaluated object. */
BKE_object_eval_assign_data(object, &grease_pencil_eval->id, false);
object->runtime.geometry_set_eval = new GeometrySet(std::move(geometry_set));
@ -1944,11 +1990,75 @@ static std::string unique_layer_group_name(const GreasePencil &grease_pencil,
return unique_node_name(grease_pencil, DATA_("GP_Group"), name);
}
static void grow_customdata(CustomData &data, const int insertion_index, const int size)
{
using namespace blender;
CustomData new_data;
CustomData_copy_layout(&data, &new_data, CD_MASK_ALL, CD_CONSTRUCT, size);
CustomData_realloc(&new_data, size, size + 1);
const IndexRange range_before(insertion_index + 1);
const IndexRange range_after(insertion_index + 1, size - insertion_index - 1);
if (range_before.size() > 0) {
CustomData_copy_data(
&data, &new_data, range_before.start(), range_before.start(), range_before.size());
}
if (range_after.size() > 0) {
CustomData_copy_data(
&data, &new_data, range_after.start(), range_after.start() + 1, range_after.size());
}
CustomData_free(&data, size);
data = new_data;
}
static int find_layer_insertion_index(
const blender::Span<const blender::bke::greasepencil::Layer *> layers,
const blender::bke::greasepencil::LayerGroup &group,
const bool above = true)
{
using namespace blender;
if (!group.layers().is_empty()) {
if (above) {
return layers.first_index(group.layers().last());
}
return layers.first_index(group.layers().first());
}
if (!group.as_node().parent_group()) {
return 0;
}
bke::greasepencil::LayerGroup &parent_group = *group.as_node().parent_group();
const Span<const bke::greasepencil::TreeNode *> nodes = parent_group.nodes();
int index = nodes.first_index(&group.as_node());
while (index > 0 && index < nodes.size()) {
if (nodes[index]->is_layer()) {
break;
}
if (above) {
index++;
}
else {
index--;
}
}
return index;
}
blender::bke::greasepencil::Layer &GreasePencil::add_layer(
blender::bke::greasepencil::LayerGroup &parent_group, const blender::StringRefNull name)
{
using namespace blender;
std::string unique_name = unique_layer_name(*this, name);
const Span<const bke::greasepencil::Layer *> layers = this->layers();
if (layers.is_empty()) {
CustomData_realloc(&this->layers_data, 0, 1);
return parent_group.add_layer(unique_name);
}
int insertion_index = find_layer_insertion_index(layers, parent_group, false);
grow_customdata(this->layers_data, insertion_index, layers.size());
return parent_group.add_layer(unique_name);
}
@ -1960,55 +2070,246 @@ blender::bke::greasepencil::LayerGroup &GreasePencil::add_layer_group(
return parent_group.add_group(unique_name);
}
static void reorder_customdata(CustomData &data, const Span<int> new_by_old_map)
{
CustomData new_data;
CustomData_copy_layout(&data, &new_data, CD_MASK_ALL, CD_CONSTRUCT, new_by_old_map.size());
for (const int old_i : new_by_old_map.index_range()) {
const int new_i = new_by_old_map[old_i];
CustomData_copy_data(&data, &new_data, old_i, new_i, 1);
}
CustomData_free(&data, new_by_old_map.size());
data = new_data;
}
static void fill_reorder_indices_array(const int reorder_from,
const int reorder_to,
const int size,
blender::MutableSpan<int> reorder_indices)
{
using namespace blender;
BLI_assert(reorder_from >= 0 && reorder_from < size);
BLI_assert(reorder_to >= 0 && reorder_to < size);
const int start = math::min(reorder_from, reorder_to);
const int end = math::max(reorder_from, reorder_to);
const int dist = math::abs(reorder_to - reorder_from);
array_utils::fill_index_range(reorder_indices.slice(IndexRange(start)));
reorder_indices[reorder_from] = reorder_to;
if (reorder_from < reorder_to) {
array_utils::fill_index_range(reorder_indices.slice(IndexRange(reorder_from + 1, dist)),
reorder_from);
}
else {
array_utils::fill_index_range(reorder_indices.slice(IndexRange(reorder_to, dist)),
reorder_to + 1);
}
array_utils::fill_index_range(reorder_indices.slice(IndexRange(end + 1, size - end - 1)),
end + 1);
}
static void reorder_layer_data(GreasePencil &grease_pencil,
const int reorder_from,
const int reorder_to)
{
using namespace blender;
if (reorder_from != reorder_to) {
Array<int> indices(grease_pencil.layers().size());
fill_reorder_indices_array(reorder_from, reorder_to, grease_pencil.layers().size(), indices);
reorder_customdata(grease_pencil.layers_data, indices);
}
}
void GreasePencil::move_node_up(blender::bke::greasepencil::TreeNode &node, const int step)
{
if (node.parent_group()) {
node.parent_group()->move_node_up(node, step);
using namespace blender;
if (!node.parent_group()) {
return;
}
if (node.is_layer()) {
const Span<const bke::greasepencil::Layer *> layers = this->layers();
if (&node.as_layer() != node.parent_group()->layers().last()) {
const bke::greasepencil::TreeNode &target_node =
reinterpret_cast<GreasePencilLayerTreeNode *>(
BLI_findlinkfrom(reinterpret_cast<Link *>(&node), step))
->wrap();
const int from_index = layers.first_index(&node.as_layer());
int to_index = -1;
if (target_node.is_layer()) {
to_index = layers.first_index(&target_node.as_layer());
}
else if (target_node.is_group()) {
const bke::greasepencil::LayerGroup &group = target_node.as_group();
to_index = layers.first_index(group.layers().last());
}
reorder_layer_data(*this, from_index, to_index);
}
}
if (node.is_group()) {
BLI_assert_msg(0, "Reordering custom data when moving a group is not implemented");
}
node.parent_group()->move_node_up(node, step);
}
void GreasePencil::move_node_down(blender::bke::greasepencil::TreeNode &node, const int step)
{
if (node.parent_group()) {
node.parent_group()->move_node_down(node, step);
using namespace blender;
if (!node.parent_group()) {
return;
}
if (node.is_layer()) {
const Span<const bke::greasepencil::Layer *> layers = this->layers();
if (&node.as_layer() != node.parent_group()->layers().first()) {
const bke::greasepencil::TreeNode &target_node =
reinterpret_cast<GreasePencilLayerTreeNode *>(
BLI_findlinkfrom(reinterpret_cast<Link *>(&node), -step))
->wrap();
const int from_index = layers.first_index(&node.as_layer());
int to_index = -1;
if (target_node.is_layer()) {
to_index = layers.first_index(&target_node.as_layer());
}
else if (target_node.is_group()) {
const bke::greasepencil::LayerGroup &group = target_node.as_group();
to_index = find_layer_insertion_index(layers, group, false);
}
reorder_layer_data(*this, from_index, to_index);
}
}
if (node.is_group()) {
BLI_assert_msg(0, "Reordering custom data when moving a group is not implemented");
}
node.parent_group()->move_node_down(node, step);
}
void GreasePencil::move_node_top(blender::bke::greasepencil::TreeNode &node)
{
if (node.parent_group()) {
node.parent_group()->move_node_top(node);
using namespace blender;
if (!node.parent_group()) {
return;
}
if (node.is_layer()) {
const Span<const bke::greasepencil::Layer *> layers = this->layers();
const blender::bke::greasepencil::LayerGroup &group = *node.parent_group();
const int from_index = layers.first_index(&node.as_layer());
/* Since `group` is the parent of `node`, we know `group` can never be empty. */
const int to_index = layers.first_index(group.layers().last());
reorder_layer_data(*this, from_index, to_index);
}
if (node.is_group()) {
BLI_assert_msg(0, "Reordering custom data when moving a group is not implemented");
}
node.parent_group()->move_node_top(node);
}
void GreasePencil::move_node_bottom(blender::bke::greasepencil::TreeNode &node)
{
if (node.parent_group()) {
node.parent_group()->move_node_bottom(node);
using namespace blender;
if (!node.parent_group()) {
return;
}
if (node.is_layer()) {
const Span<const bke::greasepencil::Layer *> layers = this->layers();
const blender::bke::greasepencil::LayerGroup &group = *node.parent_group();
const int from_index = layers.first_index(&node.as_layer());
/* Since `group` is the parent of `node`, we know `group` can never be empty. */
const int to_index = layers.first_index(group.layers().first());
reorder_layer_data(*this, from_index, to_index);
}
if (node.is_group()) {
BLI_assert_msg(0, "Reordering custom data when moving a group is not implemented");
}
node.parent_group()->move_node_bottom(node);
}
void GreasePencil::move_node_after(blender::bke::greasepencil::TreeNode &node,
blender::bke::greasepencil::TreeNode &target_node)
{
using namespace blender;
if (!target_node.parent_group() || !node.parent_group()) {
return;
}
if (node.is_layer()) {
const Span<const bke::greasepencil::Layer *> layers = this->layers();
const int from_index = layers.first_index(&node.as_layer());
int to_index = -1;
if (target_node.is_layer()) {
to_index = layers.first_index(&target_node.as_layer());
}
else if (target_node.is_group()) {
const bke::greasepencil::LayerGroup &group = target_node.as_group();
to_index = find_layer_insertion_index(layers, group, true);
}
if (from_index > to_index) {
to_index++;
}
reorder_layer_data(*this, from_index, to_index);
}
if (node.is_group() && node.as_group().num_nodes_total() > 0) {
BLI_assert_msg(0, "Reordering custom data when moving a group is not implemented");
}
node.parent_group()->unlink_node(node);
target_node.parent_group()->add_node_after(node, target_node);
}
void GreasePencil::move_node_before(blender::bke::greasepencil::TreeNode &node,
blender::bke::greasepencil::TreeNode &target_node)
{
using namespace blender;
if (!target_node.parent_group() || !node.parent_group()) {
return;
}
if (node.is_layer()) {
const Span<const bke::greasepencil::Layer *> layers = this->layers();
const int from_index = layers.first_index(&node.as_layer());
int to_index = -1;
if (target_node.is_layer()) {
to_index = layers.first_index(&target_node.as_layer());
}
else if (target_node.is_group()) {
const bke::greasepencil::LayerGroup &group = target_node.as_group();
to_index = find_layer_insertion_index(layers, group, false);
}
if (to_index > from_index) {
to_index--;
}
reorder_layer_data(*this, from_index, to_index);
}
if (node.is_group()) {
BLI_assert_msg(0, "Reordering custom data when moving a group is not implemented");
}
node.parent_group()->unlink_node(node);
target_node.parent_group()->add_node_before(node, target_node);
}
void GreasePencil::move_node_into(blender::bke::greasepencil::TreeNode &node,
blender::bke::greasepencil::LayerGroup &parent_group)
{
if (node.parent_group()) {
node.parent_group()->unlink_node(node);
using namespace blender;
if (!node.parent_group()) {
return;
}
if (node.is_layer()) {
const Span<const bke::greasepencil::Layer *> layers = this->layers();
const int from_index = layers.first_index(&node.as_layer());
int to_index = find_layer_insertion_index(layers, parent_group, true);
if (from_index > to_index) {
to_index++;
}
reorder_layer_data(*this, from_index, to_index);
}
if (node.is_group()) {
BLI_assert_msg(0, "Reordering custom data when moving a group is not implemented");
}
node.parent_group()->unlink_node(node);
parent_group.add_node(node);
}
@ -2047,6 +2348,29 @@ void GreasePencil::rename_node(blender::bke::greasepencil::TreeNode &node,
unique_layer_group_name(*this, new_name));
}
static void shrink_customdata(CustomData &data, const int index_to_remove, const int size)
{
using namespace blender;
CustomData new_data;
CustomData_copy_layout(&data, &new_data, CD_MASK_ALL, CD_CONSTRUCT, size);
CustomData_realloc(&new_data, size, size - 1);
const IndexRange range_before(index_to_remove);
const IndexRange range_after(index_to_remove + 1, size - index_to_remove - 1);
if (!range_before.is_empty()) {
CustomData_copy_data(
&data, &new_data, range_before.start(), range_before.start(), range_before.size());
}
if (!range_after.is_empty()) {
CustomData_copy_data(
&data, &new_data, range_after.start(), range_after.start() - 1, range_after.size());
}
CustomData_free(&data, size);
data = new_data;
}
void GreasePencil::remove_layer(blender::bke::greasepencil::Layer &layer)
{
using namespace blender::bke::greasepencil;
@ -2070,6 +2394,10 @@ void GreasePencil::remove_layer(blender::bke::greasepencil::Layer &layer)
}
}
/* Remove all the layer attributes and shrink the `CustomData`. */
const int64_t layer_index = this->layers().first_index(&layer);
shrink_customdata(this->layers_data, layer_index, this->layers().size());
/* Unlink the layer from the parent group. */
layer.parent_group().unlink_node(layer.as_node());

@ -93,35 +93,49 @@ TEST(greasepencil, remove_drawings)
/* --------------------------------------------------------------------------------------------- */
/* Layer Tree Tests. */
struct GreasePencilHelper : public ::GreasePencil {
GreasePencilHelper()
{
this->root_group_ptr = MEM_new<greasepencil::LayerGroup>(__func__);
this->active_layer = nullptr;
CustomData_reset(&this->layers_data);
this->runtime = MEM_new<GreasePencilRuntime>(__func__);
}
~GreasePencilHelper()
{
CustomData_free(&this->layers_data, this->layers().size());
MEM_delete(&this->root_group());
MEM_delete(this->runtime);
this->runtime = nullptr;
}
};
TEST(greasepencil, layer_tree_empty)
{
GreasePencil grease_pencil;
grease_pencil.root_group_ptr = MEM_new<greasepencil::LayerGroup>(__func__);
GreasePencilHelper grease_pencil;
EXPECT_EQ(grease_pencil.root_group().num_nodes_total(), 0);
MEM_delete(&grease_pencil.root_group());
}
TEST(greasepencil, layer_tree_build_simple)
{
GreasePencil grease_pencil;
grease_pencil.root_group_ptr = MEM_new<greasepencil::LayerGroup>(__func__);
GreasePencilHelper grease_pencil;
LayerGroup &group = grease_pencil.add_layer_group(grease_pencil.root_group(), "Group1");
grease_pencil.add_layer(group, "Layer1");
grease_pencil.add_layer(group, "Layer2");
EXPECT_EQ(grease_pencil.root_group().num_nodes_total(), 3);
MEM_delete(&grease_pencil.root_group());
}
struct GreasePencilLayerTreeExample {
StringRefNull names[7] = {"Group1", "Layer1", "Layer2", "Group2", "Layer3", "Layer4", "Layer5"};
const bool is_layer[7] = {false, true, true, false, true, true, true};
GreasePencil grease_pencil;
GreasePencilHelper grease_pencil;
GreasePencilLayerTreeExample()
{
grease_pencil.root_group_ptr = MEM_new<greasepencil::LayerGroup>(__func__);
LayerGroup &group = grease_pencil.add_layer_group(grease_pencil.root_group(), names[0]);
grease_pencil.add_layer(group, names[1]);
grease_pencil.add_layer(group, names[2]);
@ -132,11 +146,6 @@ struct GreasePencilLayerTreeExample {
grease_pencil.add_layer(grease_pencil.root_group(), names[6]);
}
~GreasePencilLayerTreeExample()
{
MEM_delete(&grease_pencil.root_group());
}
};
TEST(greasepencil, layer_tree_pre_order_iteration)

@ -30,6 +30,7 @@
#include "DNA_anim_types.h"
#include "DNA_collection_types.h"
#include "DNA_curves_types.h"
#include "DNA_grease_pencil_types.h"
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
#include "DNA_modifier_types.h"
@ -927,6 +928,11 @@ static void make_duplis_geometry_set_impl(const DupliContext *ctx,
make_dupli(ctx, ctx->object, &pointcloud->id, parent_transform, component_index++);
}
}
if (ctx->object->type != OB_GREASE_PENCIL || geometry_set_is_instance) {
if (const GreasePencil *grease_pencil = geometry_set.get_grease_pencil()) {
make_dupli(ctx, ctx->object, &grease_pencil->id, parent_transform, component_index++);
}
}
const bool creates_duplis_for_components = component_index >= 1;
const Instances *instances = geometry_set.get_instances();

@ -1394,7 +1394,14 @@ static void create_inspection_string_for_geometry_info(const geo_log::GeometryIn
break;
}
case bke::GeometryComponent::Type::GreasePencil: {
/* TODO. Do nothing for now. */
const geo_log::GeometryInfoLog::GreasePencilInfo &grease_pencil_info =
*value_log.grease_pencil_info;
char line[256];
SNPRINTF(line,
TIP_("\u2022 Grease Pencil: %s layers"),
to_string(grease_pencil_info.layers_num).c_str());
ss << line;
break;
break;
}
}

@ -311,7 +311,8 @@ Object *spreadsheet_get_object_eval(const SpaceSpreadsheet *sspreadsheet,
OB_VOLUME,
OB_CURVES_LEGACY,
OB_FONT,
OB_CURVES))
OB_CURVES,
OB_GREASE_PENCIL))
{
return nullptr;
}

@ -94,6 +94,19 @@ class GeometryDataSetTreeView : public ui::AbstractTreeView {
curve.add_tree_item<GeometryDataSetTreeViewItem>(
bke::GeometryComponent::Type::Curve, ATTR_DOMAIN_CURVE, IFACE_("Spline"), ICON_CURVE_PATH);
if (U.experimental.use_grease_pencil_version3) {
GeometryDataSetTreeViewItem &grease_pencil =
this->add_tree_item<GeometryDataSetTreeViewItem>(
bke::GeometryComponent::Type::GreasePencil,
IFACE_("Grease Pencil"),
ICON_OUTLINER_DATA_GREASEPENCIL);
grease_pencil.add_tree_item<GeometryDataSetTreeViewItem>(
bke::GeometryComponent::Type::GreasePencil,
ATTR_DOMAIN_GREASE_PENCIL_LAYER,
IFACE_("Layer"),
ICON_OUTLINER_DATA_GP_LAYER);
}
GeometryDataSetTreeViewItem &pointcloud = this->add_tree_item<GeometryDataSetTreeViewItem>(
bke::GeometryComponent::Type::PointCloud, IFACE_("Point Cloud"), ICON_POINTCLOUD_DATA);
pointcloud.add_tree_item<GeometryDataSetTreeViewItem>(bke::GeometryComponent::Type::PointCloud,

@ -16,10 +16,13 @@
#ifdef __cplusplus
# include "BLI_bounds_types.hh"
# include "BLI_function_ref.hh"
# include "BLI_generic_virtual_array.hh"
# include "BLI_map.hh"
# include "BLI_math_vector_types.hh"
# include "BLI_span.hh"
namespace blender::bke {
class AttributeAccessor;
class MutableAttributeAccessor;
class GreasePencilRuntime;
class GreasePencilDrawingRuntime;
namespace greasepencil {
@ -399,6 +402,16 @@ typedef struct GreasePencil {
/* Root group of the layer tree. */
GreasePencilLayerTreeGroup *root_group_ptr;
/**
* All attributes stored on the grease pencil layers (#ATTR_DOMAIN_GREASE_PENCIL_LAYER).
*/
CustomData layers_data;
/**
* The index of the active attribute in the UI.
*/
int attributes_active_index;
char _pad2[4];
/**
* Pointer to the active layer. Can be NULL.
* This pointer does not own the data.
@ -410,7 +423,7 @@ typedef struct GreasePencil {
*/
struct Material **material_array;
short material_array_num;
char _pad2[2];
char _pad3[2];
/**
* Global flag on the data-block.
*/
@ -558,6 +571,9 @@ typedef struct GreasePencil {
std::optional<blender::Bounds<blender::float3>> bounds_min_max() const;
blender::bke::AttributeAccessor attributes() const;
blender::bke::MutableAttributeAccessor attributes_for_write();
/* For debugging purposes. */
void print_layer_tree();
#endif

@ -88,6 +88,11 @@ const EnumPropertyItem rna_enum_attribute_domain_items[] = {
// {ATTR_DOMAIN_GRIDS, "GRIDS", 0, "Grids", "Attribute on mesh multires grids"},
{ATTR_DOMAIN_CURVE, "CURVE", 0, "Spline", "Attribute on spline"},
{ATTR_DOMAIN_INSTANCE, "INSTANCE", 0, "Instance", "Attribute on instance"},
{ATTR_DOMAIN_GREASE_PENCIL_LAYER,
"GREASE_PENCIL_LAYER",
0,
"Grease Pencil Layer",
"Attribute on grease pencil layer"},
{0, nullptr, 0, nullptr, nullptr},
};
@ -246,6 +251,11 @@ const EnumPropertyItem *rna_enum_attribute_domain_itemf(ID *id,
if (!include_instances && domain_item->value == ATTR_DOMAIN_INSTANCE) {
continue;
}
if (!U.experimental.use_grease_pencil_version3 &&
domain_item->value == ATTR_DOMAIN_GREASE_PENCIL_LAYER)
{
continue;
}
if (domain_item->value == ATTR_DOMAIN_POINT && id_type == ID_ME) {
RNA_enum_item_add(&item, &totitem, &mesh_vertex_domain_item);

@ -297,6 +297,9 @@ static void rna_def_grease_pencil_data(BlenderRNA *brna)
RNA_def_struct_ui_text(srna, "Grease Pencil", "Grease Pencil data-block");
RNA_def_struct_ui_icon(srna, ICON_OUTLINER_DATA_GREASEPENCIL);
/* attributes */
rna_def_attributes_common(srna);
/* Animation Data */
rna_def_animdata_common(srna);

@ -28,6 +28,7 @@ static const EnumPropertyItem node_tree_interface_socket_in_out_items[] = {
#ifdef RNA_RUNTIME
# include "BKE_attribute.h"
# include "BKE_node.h"
# include "BKE_node_runtime.hh"
# include "BKE_node_tree_interface.hh"
@ -385,6 +386,27 @@ static const EnumPropertyItem *rna_NodeTreeInterfaceSocket_socket_type_itemf(
ntree->typeinfo, rna_NodeTreeInterfaceSocket_socket_type_poll, r_free);
}
static const EnumPropertyItem *rna_NodeTreeInterfaceSocket_attribute_domain_itemf(
bContext * /*C*/, PointerRNA * /*ptr*/, PropertyRNA * /*prop*/, bool *r_free)
{
EnumPropertyItem *item_array = nullptr;
int items_len = 0;
for (const EnumPropertyItem *item = rna_enum_attribute_domain_items; item->identifier != nullptr;
item++)
{
if (!U.experimental.use_grease_pencil_version3 &&
item->value == ATTR_DOMAIN_GREASE_PENCIL_LAYER) {
continue;
}
RNA_enum_item_add(&item_array, &items_len, item);
}
RNA_enum_item_end(&item_array, &items_len);
*r_free = true;
return item_array;
}
static PointerRNA rna_NodeTreeInterfaceItems_active_get(PointerRNA *ptr)
{
bNodeTreeInterface *interface = static_cast<bNodeTreeInterface *>(ptr->data);
@ -919,6 +941,8 @@ static void rna_def_node_interface_socket(BlenderRNA *brna)
prop = RNA_def_property(srna, "attribute_domain", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_items(prop, rna_enum_attribute_domain_items);
RNA_def_property_enum_funcs(
prop, nullptr, nullptr, "rna_NodeTreeInterfaceSocket_attribute_domain_itemf");
RNA_def_property_ui_text(
prop,
"Attribute Domain",

@ -1877,6 +1877,27 @@ static const EnumPropertyItem *rna_GeometryNodeAttributeType_type_with_socket_it
generic_attribute_type_supported_with_socket);
}
static const EnumPropertyItem *rna_GeometryNodeAttributeDomain_attribute_domain_itemf(
bContext * /*C*/, PointerRNA * /*ptr*/, PropertyRNA * /*prop*/, bool *r_free)
{
EnumPropertyItem *item_array = nullptr;
int items_len = 0;
for (const EnumPropertyItem *item = rna_enum_attribute_domain_items; item->identifier != nullptr;
item++)
{
if (!U.experimental.use_grease_pencil_version3 &&
item->value == ATTR_DOMAIN_GREASE_PENCIL_LAYER) {
continue;
}
RNA_enum_item_add(&item_array, &items_len, item);
}
RNA_enum_item_end(&item_array, &items_len);
*r_free = true;
return item_array;
}
static StructRNA *rna_ShaderNode_register(Main *bmain,
ReportList *reports,
void *data,
@ -8835,6 +8856,8 @@ static void rna_def_simulation_state_item(BlenderRNA *brna)
prop = RNA_def_property(srna, "attribute_domain", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_items(prop, rna_enum_attribute_domain_items);
RNA_def_property_enum_funcs(
prop, nullptr, nullptr, "rna_GeometryNodeAttributeDomain_attribute_domain_itemf");
RNA_def_property_ui_text(
prop,
"Attribute Domain",
@ -9027,6 +9050,8 @@ static void def_geo_sample_index(StructRNA *srna)
prop = RNA_def_property(srna, "domain", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_items(prop, rna_enum_attribute_domain_items);
RNA_def_property_enum_funcs(
prop, nullptr, nullptr, "rna_GeometryNodeAttributeDomain_attribute_domain_itemf");
RNA_def_property_enum_default(prop, ATTR_DOMAIN_POINT);
RNA_def_property_ui_text(prop, "Domain", "");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");

@ -85,6 +85,11 @@ const EnumPropertyItem rna_enum_geometry_component_type_items[] = {
ICON_EMPTY_AXIS,
"Instances",
"Instances of objects or collections"},
{int(blender::bke::GeometryComponent::Type::GreasePencil),
"GREASEPENCIL",
ICON_GREASEPENCIL,
"Grease Pencil",
"Grease Pencil component containing layers and curves data"},
{0, nullptr, 0, nullptr, nullptr},
};
@ -3317,6 +3322,10 @@ const EnumPropertyItem *rna_SpaceSpreadsheet_attribute_domain_itemf(bContext * /
continue;
}
}
if (!U.experimental.use_grease_pencil_version3 &&
item->value == ATTR_DOMAIN_GREASE_PENCIL_LAYER) {
continue;
}
if (item->value == ATTR_DOMAIN_POINT &&
component_type == blender::bke::GeometryComponent::Type::Mesh)
{

@ -1969,10 +1969,10 @@ ModifierTypeInfo modifierType_Nodes = {
/*srna*/ &RNA_NodesModifier,
/*type*/ eModifierTypeType_Constructive,
/*flags*/
static_cast<ModifierTypeFlag>(eModifierTypeFlag_AcceptsMesh | eModifierTypeFlag_AcceptsCVs |
eModifierTypeFlag_SupportsEditmode |
eModifierTypeFlag_EnableInEditmode |
eModifierTypeFlag_SupportsMapping),
static_cast<ModifierTypeFlag>(
eModifierTypeFlag_AcceptsMesh | eModifierTypeFlag_AcceptsCVs |
eModifierTypeFlag_SupportsEditmode | eModifierTypeFlag_EnableInEditmode |
eModifierTypeFlag_SupportsMapping | eModifierTypeFlag_AcceptsGreasePencil),
/*icon*/ ICON_GEOMETRY_NODES,
/*copy_data*/ blender::copy_data,

@ -40,6 +40,7 @@ using bke::GeometryComponent;
using bke::GeometryComponentEditData;
using bke::GeometrySet;
using bke::GSpanAttributeWriter;
using bke::GreasePencilComponent;
using bke::InstancesComponent;
using bke::MeshComponent;
using bke::MutableAttributeAccessor;

@ -135,6 +135,9 @@ class GeometryInfoLog : public ValueLog {
struct PointCloudInfo {
int points_num;
};
struct GreasePencilInfo {
int layers_num;
};
struct InstancesInfo {
int instances_num;
};
@ -146,6 +149,7 @@ class GeometryInfoLog : public ValueLog {
std::optional<MeshInfo> mesh_info;
std::optional<CurveInfo> curve_info;
std::optional<PointCloudInfo> pointcloud_info;
std::optional<GreasePencilInfo> grease_pencil_info;
std::optional<InstancesInfo> instances_info;
std::optional<EditDataInfo> edit_data_info;

@ -93,6 +93,20 @@ bool generic_attribute_type_supported(const EnumPropertyItem &item)
CD_PROP_QUATERNION);
}
const EnumPropertyItem *domain_experimental_grease_pencil_version3_fn(bContext * /*C*/,
PointerRNA * /*ptr*/,
PropertyRNA * /*prop*/,
bool *r_free)
{
*r_free = true;
return enum_items_filter(rna_enum_attribute_domain_items,
[](const EnumPropertyItem &item) -> bool {
return (item.value == ATTR_DOMAIN_GREASE_PENCIL_LAYER) ?
U.experimental.use_grease_pencil_version3 :
true;
});
}
} // namespace enums
} // namespace blender::nodes

@ -131,6 +131,11 @@ const EnumPropertyItem *attribute_type_type_with_socket_fn(bContext * /*C*/,
bool generic_attribute_type_supported(const EnumPropertyItem &item);
const EnumPropertyItem *domain_experimental_grease_pencil_version3_fn(bContext * /*C*/,
PointerRNA * /*ptr*/,
PropertyRNA * /*prop*/,
bool *r_free);
} // namespace enums
} // namespace blender::nodes

@ -230,8 +230,7 @@ class AccumulateFieldInput final : public bke::GeometryFieldInput {
return {};
}
const bke::GeometryFieldContext source_context{
context.geometry(), context.type(), source_domain_};
const bke::GeometryFieldContext source_context{context, source_domain_};
fn::FieldEvaluator evaluator{source_context, domain_size};
evaluator.add(input_);
evaluator.add(group_index_);
@ -336,8 +335,7 @@ class TotalFieldInput final : public bke::GeometryFieldInput {
return {};
}
const bke::GeometryFieldContext source_context{
context.geometry(), context.type(), source_domain_};
const bke::GeometryFieldContext source_context{context, source_domain_};
fn::FieldEvaluator evaluator{source_context, domain_size};
evaluator.add(input_);
evaluator.add(group_index_);
@ -466,7 +464,8 @@ static void node_rna(StructRNA *srna)
"",
rna_enum_attribute_domain_items,
NOD_storage_enum_accessors(domain),
ATTR_DOMAIN_POINT);
ATTR_DOMAIN_POINT,
enums::domain_experimental_grease_pencil_version3_fn);
}
static void node_register()

@ -237,7 +237,8 @@ static void node_geo_exec(GeoNodeExecParams params)
else {
static const Array<GeometryComponent::Type> types = {GeometryComponent::Type::Mesh,
GeometryComponent::Type::PointCloud,
GeometryComponent::Type::Curve};
GeometryComponent::Type::Curve,
GeometryComponent::Type::GreasePencil};
geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
for (const GeometryComponent::Type type : types) {
@ -268,7 +269,8 @@ static void node_rna(StructRNA *srna)
"Which domain to store the data in",
rna_enum_attribute_domain_items,
NOD_storage_enum_accessors(domain),
ATTR_DOMAIN_POINT);
ATTR_DOMAIN_POINT,
enums::domain_experimental_grease_pencil_version3_fn);
}
static void node_register()

@ -407,7 +407,8 @@ static void node_rna(StructRNA *srna)
"Which domain to read the data from",
rna_enum_attribute_domain_items,
NOD_inline_enum_accessors(custom2),
ATTR_DOMAIN_POINT);
ATTR_DOMAIN_POINT,
enums::domain_experimental_grease_pencil_version3_fn);
}
static void node_register()

@ -37,8 +37,7 @@ GVArray EvaluateAtIndexInput::get_varray_for_context(const bke::GeometryFieldCon
return {};
}
const bke::GeometryFieldContext value_context{
context.geometry(), context.type(), value_field_domain_};
const bke::GeometryFieldContext value_context{context, value_field_domain_};
FieldEvaluator value_evaluator{value_context, attributes->domain_size(value_field_domain_)};
value_evaluator.add(value_field_);
value_evaluator.evaluate();
@ -191,7 +190,8 @@ static void node_rna(StructRNA *srna)
"Domain the field is evaluated in",
rna_enum_attribute_domain_items,
NOD_inline_enum_accessors(custom1),
ATTR_DOMAIN_POINT);
ATTR_DOMAIN_POINT,
enums::domain_experimental_grease_pencil_version3_fn);
RNA_def_node_enum(srna,
"data_type",

@ -10,6 +10,7 @@
#include "UI_resources.hh"
#include "BKE_attribute_math.hh"
#include "BKE_grease_pencil.hh"
#include "BLI_task.hh"
@ -111,17 +112,45 @@ class EvaluateOnDomainInput final : public bke::GeometryFieldInput {
GVArray get_varray_for_context(const bke::GeometryFieldContext &context,
const IndexMask & /*mask*/) const final
{
const eAttrDomain dst_domain = context.domain();
const int dst_domain_size = context.attributes()->domain_size(dst_domain);
const CPPType &cpp_type = src_field_.cpp_type();
if (context.type() == GeometryComponent::Type::GreasePencil &&
(src_domain_ == ATTR_DOMAIN_GREASE_PENCIL_LAYER) !=
(dst_domain == ATTR_DOMAIN_GREASE_PENCIL_LAYER))
{
/* Evaluate field just for the current layer. */
if (src_domain_ == ATTR_DOMAIN_GREASE_PENCIL_LAYER) {
const bke::GeometryFieldContext src_domain_context{context,
ATTR_DOMAIN_GREASE_PENCIL_LAYER};
const int layer_index = context.grease_pencil_layer_index();
const IndexMask single_layer_mask = IndexRange(layer_index, 1);
FieldEvaluator value_evaluator{src_domain_context, &single_layer_mask};
value_evaluator.add(src_field_);
value_evaluator.evaluate();
const GVArray &values = value_evaluator.get_evaluated(0);
BUFFER_FOR_CPP_TYPE_VALUE(cpp_type, value);
BLI_SCOPED_DEFER([&]() { cpp_type.destruct(value); });
values.get_to_uninitialized(layer_index, value);
return GVArray::ForSingle(cpp_type, dst_domain_size, value);
}
/* We don't adapt from curve to layer domain currently. */
return GVArray::ForSingleDefault(cpp_type, dst_domain_size);
}
const bke::AttributeAccessor attributes = *context.attributes();
const bke::GeometryFieldContext other_domain_context{
context.geometry(), context.type(), src_domain_};
const bke::GeometryFieldContext other_domain_context{context, src_domain_};
const int64_t src_domain_size = attributes.domain_size(src_domain_);
GArray<> values(src_field_.cpp_type(), src_domain_size);
GArray<> values(cpp_type, src_domain_size);
FieldEvaluator value_evaluator{other_domain_context, src_domain_size};
value_evaluator.add_with_destination(src_field_, values.as_mutable_span());
value_evaluator.evaluate();
return attributes.adapt_domain(
GVArray::ForGArray(std::move(values)), src_domain_, context.domain());
return attributes.adapt_domain(GVArray::ForGArray(std::move(values)), src_domain_, dst_domain);
}
void for_each_field_input_recursive(FunctionRef<void(const FieldInput &)> fn) const override
@ -180,7 +209,8 @@ static void node_rna(StructRNA *srna)
"Domain the field is evaluated in",
rna_enum_attribute_domain_items,
NOD_inline_enum_accessors(custom1),
ATTR_DOMAIN_POINT);
ATTR_DOMAIN_POINT,
enums::domain_experimental_grease_pencil_version3_fn);
RNA_def_node_enum(srna,
"data_type",

@ -10,6 +10,7 @@
#include "DNA_meshdata_types.h"
#include "BKE_curves.hh"
#include "BKE_grease_pencil.hh"
#include "BKE_mesh.hh"
#include "node_geometry_util.hh"
@ -28,10 +29,9 @@ 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 IndexMask &selection)
const IndexMask &selection,
MutableAttributeAccessor attributes)
{
MutableAttributeAccessor attributes = *component.attributes_for_write();
/* Optimize the case when `in_positions` references the original positions array. */
const bke::AttributeReader positions_read_only = attributes.lookup<float3>("position");
bool positions_are_original = false;
@ -108,6 +108,42 @@ static void set_computed_position_and_offset(GeometryComponent &component,
}
}
static void set_position_in_grease_pencil(GreasePencilComponent &grease_pencil_component,
const Field<bool> &selection_field,
const Field<float3> &position_field,
const Field<float3> &offset_field)
{
using namespace blender::bke::greasepencil;
GreasePencil &grease_pencil = *grease_pencil_component.get_for_write();
/* Set position for each layer. */
for (const int layer_index : grease_pencil.layers().index_range()) {
Drawing *drawing = bke::greasepencil::get_eval_grease_pencil_layer_drawing_for_write(
grease_pencil, layer_index);
if (drawing == nullptr || drawing->strokes().points_num() == 0) {
continue;
}
bke::GreasePencilLayerFieldContext field_context(
grease_pencil, ATTR_DOMAIN_POINT, layer_index);
fn::FieldEvaluator evaluator{field_context, drawing->strokes().points_num()};
evaluator.set_selection(selection_field);
evaluator.add(position_field);
evaluator.add(offset_field);
evaluator.evaluate();
const IndexMask selection = evaluator.get_evaluated_selection_as_mask();
if (selection.is_empty()) {
continue;
}
MutableAttributeAccessor attributes = drawing->strokes_for_write().attributes_for_write();
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(
grease_pencil_component, positions_input, offsets_input, selection, attributes);
drawing->tag_positions_changed();
}
}
static void set_position_in_component(GeometrySet &geometry,
GeometryComponent::Type component_type,
const Field<bool> &selection_field,
@ -136,9 +172,11 @@ static void set_position_in_component(GeometrySet &geometry,
}
GeometryComponent &mutable_component = geometry.get_component_for_write(component_type);
MutableAttributeAccessor attributes = *mutable_component.attributes_for_write();
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(mutable_component, positions_input, offsets_input, selection);
set_computed_position_and_offset(
mutable_component, positions_input, offsets_input, selection, attributes);
}
static void node_geo_exec(GeoNodeExecParams params)
@ -148,6 +186,13 @@ static void node_geo_exec(GeoNodeExecParams params)
Field<float3> offset_field = params.extract_input<Field<float3>>("Offset");
Field<float3> position_field = params.extract_input<Field<float3>>("Position");
if (geometry.has_grease_pencil()) {
set_position_in_grease_pencil(geometry.get_component_for_write<GreasePencilComponent>(),
selection_field,
position_field,
offset_field);
}
for (const GeometryComponent::Type type : {GeometryComponent::Type::Mesh,
GeometryComponent::Type::PointCloud,
GeometryComponent::Type::Curve,

@ -176,7 +176,8 @@ static void node_geo_exec(GeoNodeExecParams params)
geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
for (const GeometryComponent::Type type : {GeometryComponent::Type::Mesh,
GeometryComponent::Type::PointCloud,
GeometryComponent::Type::Curve})
GeometryComponent::Type::Curve,
GeometryComponent::Type::GreasePencil})
{
if (geometry_set.has(type)) {
GeometryComponent &component = geometry_set.get_component_for_write(type);
@ -228,7 +229,8 @@ static void node_rna(StructRNA *srna)
"Which domain to store the data in",
rna_enum_attribute_domain_items,
NOD_storage_enum_accessors(domain),
ATTR_DOMAIN_POINT);
ATTR_DOMAIN_POINT,
enums::domain_experimental_grease_pencil_version3_fn);
}
static void node_register()

@ -64,6 +64,7 @@ GeometryInfoLog::GeometryInfoLog(const bke::GeometrySet &geometry_set)
bke::GeometryComponent::Type::Instance,
bke::GeometryComponent::Type::Mesh,
bke::GeometryComponent::Type::PointCloud,
bke::GeometryComponent::Type::GreasePencil,
bke::GeometryComponent::Type::Volume};
/* Keep track handled attribute names to make sure that we do not return the same name twice.
@ -128,7 +129,11 @@ GeometryInfoLog::GeometryInfoLog(const bke::GeometrySet &geometry_set)
break;
}
case bke::GeometryComponent::Type::GreasePencil: {
/* TODO. Do nothing for now. */
const auto &grease_pencil_component = *static_cast<const bke::GreasePencilComponent *>(
component);
GreasePencilInfo &info = this->grease_pencil_info.emplace();
info.layers_num = grease_pencil_component.attribute_domain_size(
ATTR_DOMAIN_GREASE_PENCIL_LAYER);
break;
}
}