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:
parent
7d7ca7b90d
commit
3931a54e08
@ -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;
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user