tornavis/source/blender/blenkernel/intern/grease_pencil.cc

2519 lines
83 KiB
C++

/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup bke
*/
#include <iostream>
#include "BKE_anim_data.h"
#include "BKE_curves.hh"
#include "BKE_customdata.hh"
#include "BKE_geometry_set.hh"
#include "BKE_grease_pencil.h"
#include "BKE_grease_pencil.hh"
#include "BKE_idtype.h"
#include "BKE_lib_id.h"
#include "BKE_lib_query.h"
#include "BKE_material.h"
#include "BKE_modifier.hh"
#include "BKE_object.hh"
#include "BKE_object_types.hh"
#include "BLI_bounds.hh"
#include "BLI_map.hh"
#include "BLI_math_geom.h"
#include "BLI_math_matrix.h"
#include "BLI_math_vector_types.hh"
#include "BLI_memarena.h"
#include "BLI_memory_utils.hh"
#include "BLI_polyfill_2d.h"
#include "BLI_span.hh"
#include "BLI_stack.hh"
#include "BLI_string.h"
#include "BLI_string_ref.hh"
#include "BLI_string_utils.hh"
#include "BLI_vector_set.hh"
#include "BLI_virtual_array.hh"
#include "BLO_read_write.hh"
#include "BLT_translation.h"
#include "DNA_ID.h"
#include "DNA_ID_enums.h"
#include "DNA_brush_types.h"
#include "DNA_grease_pencil_types.h"
#include "DNA_material_types.h"
#include "DNA_modifier_types.h"
#include "DNA_scene_types.h"
#include "DEG_depsgraph.hh"
#include "DEG_depsgraph_query.hh"
#include "MEM_guardedalloc.h"
using blender::float3;
using blender::Span;
using blender::uint3;
using blender::VectorSet;
/* Forward declarations. */
static void read_drawing_array(GreasePencil &grease_pencil, BlendDataReader *reader);
static void write_drawing_array(GreasePencil &grease_pencil, BlendWriter *writer);
static void free_drawing_array(GreasePencil &grease_pencil);
static void read_layer_tree(GreasePencil &grease_pencil, BlendDataReader *reader);
static void write_layer_tree(GreasePencil &grease_pencil, BlendWriter *writer);
static void grease_pencil_init_data(ID *id)
{
using namespace blender::bke;
GreasePencil *grease_pencil = reinterpret_cast<GreasePencil *>(id);
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*/,
ID *id_dst,
const ID *id_src,
const int /*flag*/)
{
using namespace blender;
GreasePencil *grease_pencil_dst = reinterpret_cast<GreasePencil *>(id_dst);
const GreasePencil *grease_pencil_src = reinterpret_cast<const GreasePencil *>(id_src);
/* Duplicate material array. */
grease_pencil_dst->material_array = static_cast<Material **>(
MEM_dupallocN(grease_pencil_src->material_array));
BKE_grease_pencil_duplicate_drawing_array(grease_pencil_src, grease_pencil_dst);
/* Duplicate layer tree. */
grease_pencil_dst->root_group_ptr = MEM_new<bke::greasepencil::LayerGroup>(
__func__, grease_pencil_src->root_group());
/* Set active layer. */
if (grease_pencil_src->has_active_layer()) {
bke::greasepencil::TreeNode *active_node = grease_pencil_dst->find_node_by_name(
grease_pencil_src->active_layer->wrap().name());
BLI_assert(active_node && active_node->is_layer());
grease_pencil_dst->set_active_layer(&active_node->as_layer());
}
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__);
}
static void grease_pencil_free_data(ID *id)
{
GreasePencil *grease_pencil = reinterpret_cast<GreasePencil *>(id);
BKE_animdata_free(&grease_pencil->id, false);
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());
BKE_grease_pencil_batch_cache_free(grease_pencil);
MEM_delete(grease_pencil->runtime);
grease_pencil->runtime = nullptr;
}
static void grease_pencil_foreach_id(ID *id, LibraryForeachIDData *data)
{
GreasePencil *grease_pencil = reinterpret_cast<GreasePencil *>(id);
for (int i = 0; i < grease_pencil->material_array_num; i++) {
BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, grease_pencil->material_array[i], IDWALK_CB_USER);
}
for (GreasePencilDrawingBase *drawing_base : grease_pencil->drawings()) {
if (drawing_base->type == GP_DRAWING_REFERENCE) {
GreasePencilDrawingReference *drawing_reference =
reinterpret_cast<GreasePencilDrawingReference *>(drawing_base);
BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, drawing_reference->id_reference, IDWALK_CB_USER);
}
}
}
static void grease_pencil_blend_write(BlendWriter *writer, ID *id, const void *id_address)
{
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. */
write_layer_tree(*grease_pencil, writer);
/* Write materials. */
BLO_write_pointer_array(
writer, grease_pencil->material_array_num, grease_pencil->material_array);
}
static void grease_pencil_blend_read_data(BlendDataReader *reader, ID *id)
{
using namespace blender::bke::greasepencil;
GreasePencil *grease_pencil = reinterpret_cast<GreasePencil *>(id);
/* Read drawings. */
read_drawing_array(*grease_pencil, reader);
/* 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));
grease_pencil->runtime = MEM_new<blender::bke::GreasePencilRuntime>(__func__);
}
IDTypeInfo IDType_ID_GP = {
/*id_code*/ ID_GP,
/*id_filter*/ FILTER_ID_GP,
/*main_listbase_index*/ INDEX_ID_GP,
/*struct_size*/ sizeof(GreasePencil),
/*name*/ "GreasePencil",
/*name_plural*/ N_("grease_pencils_v3"),
/*translation_context*/ BLT_I18NCONTEXT_ID_GPENCIL,
/*flags*/ IDTYPE_FLAGS_APPEND_IS_REUSABLE,
/*asset_type_info*/ nullptr,
/*init_data*/ grease_pencil_init_data,
/*copy_data*/ grease_pencil_copy_data,
/*free_data*/ grease_pencil_free_data,
/*make_local*/ nullptr,
/*foreach_id*/ grease_pencil_foreach_id,
/*foreach_cache*/ nullptr,
/*foreach_path*/ nullptr,
/*owner_pointer_get*/ nullptr,
/*blend_write*/ grease_pencil_blend_write,
/*blend_read_data*/ grease_pencil_blend_read_data,
/*blend_read_after_liblink*/ nullptr,
/*blend_read_undo_preserve*/ nullptr,
/*lib_override_apply_post*/ nullptr,
};
namespace blender::bke::greasepencil {
DrawingTransforms::DrawingTransforms(const Object &grease_pencil_ob)
{
this->layer_space_to_world_space = float4x4_view(grease_pencil_ob.object_to_world);
this->world_space_to_layer_space = math::invert(this->layer_space_to_world_space);
}
static const std::string ATTR_RADIUS = "radius";
static const std::string ATTR_OPACITY = "opacity";
static const std::string ATTR_VERTEX_COLOR = "vertex_color";
/* Curves attributes getters */
static int domain_num(const CurvesGeometry &curves, const eAttrDomain domain)
{
return domain == ATTR_DOMAIN_POINT ? curves.points_num() : curves.curves_num();
}
static CustomData &domain_custom_data(CurvesGeometry &curves, const eAttrDomain domain)
{
return domain == ATTR_DOMAIN_POINT ? curves.point_data : curves.curve_data;
}
template<typename T>
static MutableSpan<T> get_mutable_attribute(CurvesGeometry &curves,
const eAttrDomain domain,
const StringRefNull name,
const T default_value = T())
{
const int num = domain_num(curves, domain);
const eCustomDataType type = cpp_type_to_custom_data_type(CPPType::get<T>());
CustomData &custom_data = domain_custom_data(curves, domain);
T *data = (T *)CustomData_get_layer_named_for_write(&custom_data, type, name.c_str(), num);
if (data != nullptr) {
return {data, num};
}
data = (T *)CustomData_add_layer_named(&custom_data, type, CD_SET_DEFAULT, num, name.c_str());
MutableSpan<T> span = {data, num};
if (num > 0 && span.first() != default_value) {
span.fill(default_value);
}
return span;
}
Drawing::Drawing()
{
this->base.type = GP_DRAWING;
this->base.flag = 0;
new (&this->geometry) bke::CurvesGeometry();
/* Initialize runtime data. */
this->runtime = MEM_new<bke::greasepencil::DrawingRuntime>(__func__);
}
Drawing::Drawing(const Drawing &other)
{
this->base.type = GP_DRAWING;
this->base.flag = other.base.flag;
new (&this->geometry) bke::CurvesGeometry(other.strokes());
/* Initialize runtime data. */
this->runtime = MEM_new<bke::greasepencil::DrawingRuntime>(__func__);
this->runtime->triangles_cache = other.runtime->triangles_cache;
}
Drawing::~Drawing()
{
this->strokes().~CurvesGeometry();
MEM_delete(this->runtime);
this->runtime = nullptr;
}
Span<uint3> Drawing::triangles() const
{
this->runtime->triangles_cache.ensure([&](Vector<uint3> &r_data) {
MemArena *pf_arena = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, __func__);
const CurvesGeometry &curves = this->strokes();
const Span<float3> positions = curves.positions();
const OffsetIndices<int> points_by_curve = curves.points_by_curve();
int total_triangles = 0;
Array<int> tris_offests(curves.curves_num());
for (int curve_i : curves.curves_range()) {
IndexRange points = points_by_curve[curve_i];
if (points.size() > 2) {
tris_offests[curve_i] = total_triangles;
total_triangles += points.size() - 2;
}
}
r_data.resize(total_triangles);
/* TODO: use threading. */
for (const int curve_i : curves.curves_range()) {
const IndexRange points = points_by_curve[curve_i];
if (points.size() < 3) {
continue;
}
const int num_triangles = points.size() - 2;
MutableSpan<uint3> r_tris = r_data.as_mutable_span().slice(tris_offests[curve_i],
num_triangles);
float(*projverts)[2] = static_cast<float(*)[2]>(
BLI_memarena_alloc(pf_arena, sizeof(*projverts) * size_t(points.size())));
/* TODO: calculate axis_mat properly. */
float3x3 axis_mat;
axis_dominant_v3_to_m3(axis_mat.ptr(), float3(0.0f, -1.0f, 0.0f));
for (const int i : IndexRange(points.size())) {
mul_v2_m3v3(projverts[i], axis_mat.ptr(), positions[points[i]]);
}
BLI_polyfill_calc_arena(
projverts, points.size(), 0, reinterpret_cast<uint32_t(*)[3]>(r_tris.data()), pf_arena);
BLI_memarena_clear(pf_arena);
}
BLI_memarena_free(pf_arena);
});
return this->runtime->triangles_cache.data().as_span();
}
const bke::CurvesGeometry &Drawing::strokes() const
{
return this->geometry.wrap();
}
bke::CurvesGeometry &Drawing::strokes_for_write()
{
return this->geometry.wrap();
}
VArray<float> Drawing::radii() const
{
return *this->strokes().attributes().lookup_or_default<float>(
ATTR_RADIUS, ATTR_DOMAIN_POINT, 0.01f);
}
MutableSpan<float> Drawing::radii_for_write()
{
return get_mutable_attribute<float>(
this->strokes_for_write(), ATTR_DOMAIN_POINT, ATTR_RADIUS, 0.01f);
}
VArray<float> Drawing::opacities() const
{
return *this->strokes().attributes().lookup_or_default<float>(
ATTR_OPACITY, ATTR_DOMAIN_POINT, 1.0f);
}
MutableSpan<float> Drawing::opacities_for_write()
{
return get_mutable_attribute<float>(
this->strokes_for_write(), ATTR_DOMAIN_POINT, ATTR_OPACITY, 1.0f);
}
VArray<ColorGeometry4f> Drawing::vertex_colors() const
{
return *this->strokes().attributes().lookup_or_default<ColorGeometry4f>(
ATTR_VERTEX_COLOR, ATTR_DOMAIN_POINT, ColorGeometry4f(0.0f, 0.0f, 0.0f, 0.0f));
}
MutableSpan<ColorGeometry4f> Drawing::vertex_colors_for_write()
{
return get_mutable_attribute<ColorGeometry4f>(this->strokes_for_write(),
ATTR_DOMAIN_POINT,
ATTR_VERTEX_COLOR,
ColorGeometry4f(0.0f, 0.0f, 0.0f, 0.0f));
}
void Drawing::tag_positions_changed()
{
this->strokes_for_write().tag_positions_changed();
this->runtime->triangles_cache.tag_dirty();
}
void Drawing::tag_topology_changed()
{
this->tag_positions_changed();
}
DrawingReference::DrawingReference()
{
this->base.type = GP_DRAWING_REFERENCE;
this->base.flag = 0;
this->id_reference = nullptr;
}
DrawingReference::DrawingReference(const DrawingReference &other)
{
this->base.type = GP_DRAWING_REFERENCE;
this->base.flag = other.base.flag;
this->id_reference = other.id_reference;
}
DrawingReference::~DrawingReference() {}
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));
}
void copy_drawing_array(Span<const GreasePencilDrawingBase *> src_drawings,
MutableSpan<GreasePencilDrawingBase *> dst_drawings)
{
BLI_assert(src_drawings.size() == dst_drawings.size());
for (const int i : src_drawings.index_range()) {
const GreasePencilDrawingBase *src_drawing_base = src_drawings[i];
switch (src_drawing_base->type) {
case GP_DRAWING: {
const GreasePencilDrawing *src_drawing = reinterpret_cast<const GreasePencilDrawing *>(
src_drawing_base);
dst_drawings[i] = reinterpret_cast<GreasePencilDrawingBase *>(
MEM_new<bke::greasepencil::Drawing>(__func__, src_drawing->wrap()));
break;
}
case GP_DRAWING_REFERENCE: {
const GreasePencilDrawingReference *src_drawing_reference =
reinterpret_cast<const GreasePencilDrawingReference *>(src_drawing_base);
dst_drawings[i] = reinterpret_cast<GreasePencilDrawingBase *>(
MEM_new<bke::greasepencil::DrawingReference>(__func__, src_drawing_reference->wrap()));
break;
}
}
}
}
TreeNode::TreeNode()
{
this->next = this->prev = nullptr;
this->parent = nullptr;
this->GreasePencilLayerTreeNode::name = nullptr;
this->flag = 0;
this->color[0] = this->color[1] = this->color[2] = 0;
}
TreeNode::TreeNode(GreasePencilLayerTreeNodeType type) : TreeNode()
{
this->type = type;
}
TreeNode::TreeNode(GreasePencilLayerTreeNodeType type, StringRefNull name) : TreeNode()
{
this->type = type;
this->GreasePencilLayerTreeNode::name = BLI_strdup(name.c_str());
}
TreeNode::TreeNode(const TreeNode &other) : TreeNode(GreasePencilLayerTreeNodeType(other.type))
{
this->GreasePencilLayerTreeNode::name = BLI_strdup_null(other.GreasePencilLayerTreeNode::name);
this->flag = other.flag;
copy_v3_v3_uchar(this->color, other.color);
}
TreeNode::~TreeNode()
{
MEM_SAFE_FREE(this->GreasePencilLayerTreeNode::name);
}
void TreeNode::set_name(StringRefNull name)
{
MEM_SAFE_FREE(this->GreasePencilLayerTreeNode::name);
this->GreasePencilLayerTreeNode::name = BLI_strdup(name.c_str());
}
const LayerGroup &TreeNode::as_group() const
{
return *reinterpret_cast<const LayerGroup *>(this);
}
const Layer &TreeNode::as_layer() const
{
return *reinterpret_cast<const Layer *>(this);
}
LayerGroup &TreeNode::as_group()
{
return *reinterpret_cast<LayerGroup *>(this);
}
Layer &TreeNode::as_layer()
{
return *reinterpret_cast<Layer *>(this);
}
LayerGroup *TreeNode::parent_group() const
{
return (this->parent) ? &this->parent->wrap() : nullptr;
}
TreeNode *TreeNode::parent_node() const
{
return this->parent_group() ? &this->parent->wrap().as_node() : nullptr;
}
int64_t TreeNode::depth() const
{
const LayerGroup *parent = this->parent_group();
if (parent == nullptr) {
/* The root group has a depth of 0. */
return 0;
}
return 1 + parent->as_node().depth();
}
LayerMask::LayerMask()
{
this->layer_name = nullptr;
this->flag = 0;
}
LayerMask::LayerMask(StringRefNull name) : LayerMask()
{
this->layer_name = BLI_strdup(name.c_str());
}
LayerMask::LayerMask(const LayerMask &other) : LayerMask()
{
if (other.layer_name) {
this->layer_name = BLI_strdup(other.layer_name);
}
this->flag = other.flag;
}
LayerMask::~LayerMask()
{
if (this->layer_name) {
MEM_freeN(this->layer_name);
}
}
Layer::Layer()
{
new (&this->base) TreeNode(GP_LAYER_TREE_LEAF);
this->frames_storage.num = 0;
this->frames_storage.keys = nullptr;
this->frames_storage.values = nullptr;
this->frames_storage.flag = 0;
this->opacity = 1.0f;
BLI_listbase_clear(&this->masks);
this->runtime = MEM_new<LayerRuntime>(__func__);
}
Layer::Layer(StringRefNull name) : Layer()
{
new (&this->base) TreeNode(GP_LAYER_TREE_LEAF, name);
}
Layer::Layer(const Layer &other) : Layer()
{
new (&this->base) TreeNode(other.base.wrap());
/* TODO: duplicate masks. */
/* Note: We do not duplicate the frame storage since it is only needed for writing. */
this->blend_mode = other.blend_mode;
this->opacity = other.opacity;
this->runtime->frames_ = other.runtime->frames_;
this->runtime->sorted_keys_cache_ = other.runtime->sorted_keys_cache_;
/* TODO: what about masks cache? */
}
Layer::~Layer()
{
this->base.wrap().~TreeNode();
MEM_SAFE_FREE(this->frames_storage.keys);
MEM_SAFE_FREE(this->frames_storage.values);
LISTBASE_FOREACH_MUTABLE (GreasePencilLayerMask *, mask, &this->masks) {
MEM_SAFE_FREE(mask->layer_name);
MEM_freeN(mask);
}
MEM_delete(this->runtime);
this->runtime = nullptr;
}
const Map<int, GreasePencilFrame> &Layer::frames() const
{
return this->runtime->frames_;
}
Map<int, GreasePencilFrame> &Layer::frames_for_write()
{
return this->runtime->frames_;
}
Layer::SortedKeysIterator Layer::remove_leading_null_frames_in_range(
Layer::SortedKeysIterator begin, Layer::SortedKeysIterator end)
{
Layer::SortedKeysIterator next_it = begin;
while (next_it != end && this->frames().lookup(*next_it).is_null()) {
this->frames_for_write().remove_contained(*next_it);
this->tag_frames_map_keys_changed();
next_it = std::next(next_it);
}
return next_it;
}
GreasePencilFrame *Layer::add_frame_internal(const FramesMapKey key, const int drawing_index)
{
BLI_assert(drawing_index != -1);
if (!this->frames().contains(key)) {
GreasePencilFrame frame{};
frame.drawing_index = drawing_index;
this->frames_for_write().add_new(key, frame);
this->tag_frames_map_keys_changed();
return this->frames_for_write().lookup_ptr(key);
}
/* Overwrite null-frames. */
if (this->frames().lookup(key).is_null()) {
GreasePencilFrame frame{};
frame.drawing_index = drawing_index;
this->frames_for_write().add_overwrite(key, frame);
this->tag_frames_map_changed();
return this->frames_for_write().lookup_ptr(key);
}
return nullptr;
}
GreasePencilFrame *Layer::add_frame(const FramesMapKey key,
const int drawing_index,
const int duration)
{
BLI_assert(duration >= 0);
GreasePencilFrame *frame = this->add_frame_internal(key, drawing_index);
if (frame == nullptr) {
return nullptr;
}
Span<FramesMapKey> sorted_keys = this->sorted_keys();
const FramesMapKey end_key = key + duration;
/* Finds the next greater key that is stored in the map. */
SortedKeysIterator next_key_it = std::upper_bound(sorted_keys.begin(), sorted_keys.end(), key);
/* If the next frame we found is at the end of the frame we're inserting, then we are done. */
if (next_key_it != sorted_keys.end() && *next_key_it == end_key) {
return frame;
}
next_key_it = this->remove_leading_null_frames_in_range(next_key_it, sorted_keys.end());
/* If the duration is set to 0, the frame is marked as an implicit hold. */
if (duration == 0) {
frame->flag |= GP_FRAME_IMPLICIT_HOLD;
return frame;
}
/* If the next frame comes after the end of the frame we're inserting (or if there are no more
* frames), add a null-frame. */
if (next_key_it == sorted_keys.end() || *next_key_it > end_key) {
this->frames_for_write().add_new(end_key, GreasePencilFrame::null());
this->tag_frames_map_keys_changed();
}
return frame;
}
bool Layer::remove_frame(const FramesMapKey key)
{
/* If the frame number is not in the frames map, do nothing. */
if (!this->frames().contains(key)) {
return false;
}
if (this->frames().size() == 1) {
this->frames_for_write().remove_contained(key);
this->tag_frames_map_keys_changed();
return true;
}
Span<FramesMapKey> sorted_keys = this->sorted_keys();
/* Find the index of the frame to remove in the `sorted_keys` array. */
SortedKeysIterator remove_key_it = std::lower_bound(sorted_keys.begin(), sorted_keys.end(), key);
/* If there is a next frame: */
if (std::next(remove_key_it) != sorted_keys.end()) {
SortedKeysIterator next_key_it = std::next(remove_key_it);
this->remove_leading_null_frames_in_range(next_key_it, sorted_keys.end());
}
/* If there is a previous frame: */
if (remove_key_it != sorted_keys.begin()) {
SortedKeysIterator prev_key_it = std::prev(remove_key_it);
const GreasePencilFrame &prev_frame = this->frames().lookup(*prev_key_it);
/* If the previous frame is not an implicit hold (e.g. it has a fixed duration) and it's not a
* null frame, we cannot just delete the frame. We need to replace it with a null frame. */
if (!prev_frame.is_implicit_hold() && !prev_frame.is_null()) {
this->frames_for_write().lookup(key) = GreasePencilFrame::null();
this->tag_frames_map_changed();
/* Since the original frame was replaced with a null frame, we consider the frame to be
* successfully removed here. */
return true;
}
}
/* Finally, remove the actual frame. */
this->frames_for_write().remove_contained(key);
this->tag_frames_map_keys_changed();
return true;
}
Span<FramesMapKey> Layer::sorted_keys() const
{
this->runtime->sorted_keys_cache_.ensure([&](Vector<FramesMapKey> &r_data) {
r_data.reinitialize(this->frames().size());
int i = 0;
for (FramesMapKey key : this->frames().keys()) {
r_data[i++] = key;
}
std::sort(r_data.begin(), r_data.end());
});
return this->runtime->sorted_keys_cache_.data();
}
FramesMapKey Layer::frame_key_at(const int frame_number) const
{
Span<int> sorted_keys = this->sorted_keys();
/* No keyframes, return no drawing. */
if (sorted_keys.size() == 0) {
return -1;
}
/* Before the first drawing, return no drawing. */
if (frame_number < sorted_keys.first()) {
return -1;
}
/* After or at the last drawing, return the last drawing. */
if (frame_number >= sorted_keys.last()) {
return sorted_keys.last();
}
/* Search for the drawing. upper_bound will get the drawing just after. */
SortedKeysIterator it = std::upper_bound(sorted_keys.begin(), sorted_keys.end(), frame_number);
if (it == sorted_keys.end() || it == sorted_keys.begin()) {
return -1;
}
return *std::prev(it);
}
const GreasePencilFrame *Layer::frame_at(const int frame_number) const
{
const FramesMapKey frame_key = this->frame_key_at(frame_number);
return (frame_key == -1) ? nullptr : this->frames().lookup_ptr(frame_key);
}
GreasePencilFrame *Layer::frame_at(const int frame_number)
{
const FramesMapKey frame_key = this->frame_key_at(frame_number);
return (frame_key == -1) ? nullptr : this->frames_for_write().lookup_ptr(frame_key);
}
int Layer::drawing_index_at(const int frame_number) const
{
const GreasePencilFrame *frame = frame_at(frame_number);
return (frame != nullptr) ? frame->drawing_index : -1;
}
int Layer::get_frame_duration_at(const int frame_number) const
{
const FramesMapKey frame_key = this->frame_key_at(frame_number);
if (frame_key == -1) {
return -1;
}
SortedKeysIterator frame_number_it = std::next(this->sorted_keys().begin(), frame_key);
if (*frame_number_it == this->sorted_keys().last()) {
return -1;
}
const int next_frame_number = *(std::next(frame_number_it));
return next_frame_number - frame_number;
}
void Layer::tag_frames_map_changed()
{
this->frames_storage.flag |= GP_LAYER_FRAMES_STORAGE_DIRTY;
}
void Layer::tag_frames_map_keys_changed()
{
this->tag_frames_map_changed();
this->runtime->sorted_keys_cache_.tag_dirty();
}
LayerGroup::LayerGroup()
{
new (&this->base) TreeNode(GP_LAYER_TREE_GROUP);
BLI_listbase_clear(&this->children);
this->runtime = MEM_new<LayerGroupRuntime>(__func__);
}
LayerGroup::LayerGroup(StringRefNull name) : LayerGroup()
{
new (&this->base) TreeNode(GP_LAYER_TREE_GROUP, name);
}
LayerGroup::LayerGroup(const LayerGroup &other) : LayerGroup()
{
new (&this->base) TreeNode(other.base.wrap());
LISTBASE_FOREACH (GreasePencilLayerTreeNode *, child, &other.children) {
switch (child->type) {
case GP_LAYER_TREE_LEAF: {
GreasePencilLayer *layer = reinterpret_cast<GreasePencilLayer *>(child);
Layer *dup_layer = MEM_new<Layer>(__func__, layer->wrap());
this->add_node(dup_layer->as_node());
break;
}
case GP_LAYER_TREE_GROUP: {
GreasePencilLayerTreeGroup *group = reinterpret_cast<GreasePencilLayerTreeGroup *>(child);
LayerGroup *dup_group = MEM_new<LayerGroup>(__func__, group->wrap());
this->add_node(dup_group->as_node());
break;
}
}
}
}
LayerGroup::~LayerGroup()
{
this->base.wrap().~TreeNode();
LISTBASE_FOREACH_MUTABLE (GreasePencilLayerTreeNode *, child, &this->children) {
switch (child->type) {
case GP_LAYER_TREE_LEAF: {
GreasePencilLayer *layer = reinterpret_cast<GreasePencilLayer *>(child);
MEM_delete(&layer->wrap());
break;
}
case GP_LAYER_TREE_GROUP: {
GreasePencilLayerTreeGroup *group = reinterpret_cast<GreasePencilLayerTreeGroup *>(child);
MEM_delete(&group->wrap());
break;
}
}
}
MEM_delete(this->runtime);
this->runtime = nullptr;
}
Layer &LayerGroup::add_layer(StringRefNull name)
{
Layer *new_layer = MEM_new<Layer>(__func__, name);
return this->add_node(new_layer->as_node()).as_layer();
}
Layer &LayerGroup::add_layer(const Layer &duplicate_layer)
{
Layer *new_layer = MEM_new<Layer>(__func__, duplicate_layer);
return this->add_node(new_layer->as_node()).as_layer();
}
LayerGroup &LayerGroup::add_group(StringRefNull name)
{
LayerGroup *new_group = MEM_new<LayerGroup>(__func__, name);
return this->add_node(new_group->as_node()).as_group();
}
TreeNode &LayerGroup::add_node(TreeNode &node)
{
BLI_addtail(&this->children, &node);
node.parent = reinterpret_cast<GreasePencilLayerTreeGroup *>(this);
this->tag_nodes_cache_dirty();
return node;
}
void LayerGroup::add_node_before(TreeNode &node, TreeNode &link)
{
BLI_assert(BLI_findindex(&this->children, &link) != -1);
BLI_insertlinkbefore(&this->children, &link, &node);
node.parent = reinterpret_cast<GreasePencilLayerTreeGroup *>(this);
this->tag_nodes_cache_dirty();
}
void LayerGroup::add_node_after(TreeNode &node, TreeNode &link)
{
BLI_assert(BLI_findindex(&this->children, &link) != -1);
BLI_insertlinkafter(&this->children, &link, &node);
node.parent = reinterpret_cast<GreasePencilLayerTreeGroup *>(this);
this->tag_nodes_cache_dirty();
}
void LayerGroup::move_node_up(TreeNode &node, const int step)
{
BLI_listbase_link_move(&this->children, &node, step);
this->tag_nodes_cache_dirty();
}
void LayerGroup::move_node_down(TreeNode &node, const int step)
{
BLI_listbase_link_move(&this->children, &node, -step);
this->tag_nodes_cache_dirty();
}
void LayerGroup::move_node_top(TreeNode &node)
{
BLI_remlink(&this->children, &node);
BLI_insertlinkafter(&this->children, this->children.last, &node);
this->tag_nodes_cache_dirty();
}
void LayerGroup::move_node_bottom(TreeNode &node)
{
BLI_remlink(&this->children, &node);
BLI_insertlinkbefore(&this->children, this->children.first, &node);
this->tag_nodes_cache_dirty();
}
int64_t LayerGroup::num_direct_nodes() const
{
return BLI_listbase_count(&this->children);
}
int64_t LayerGroup::num_nodes_total() const
{
this->ensure_nodes_cache();
return this->runtime->nodes_cache_.size();
}
bool LayerGroup::unlink_node(TreeNode &link)
{
if (BLI_remlink_safe(&this->children, &link)) {
this->tag_nodes_cache_dirty();
link.parent = nullptr;
return true;
}
return false;
}
Span<const TreeNode *> LayerGroup::nodes() const
{
this->ensure_nodes_cache();
return this->runtime->nodes_cache_.as_span();
}
Span<TreeNode *> LayerGroup::nodes_for_write()
{
this->ensure_nodes_cache();
return this->runtime->nodes_cache_.as_span();
}
Span<const Layer *> LayerGroup::layers() const
{
this->ensure_nodes_cache();
return this->runtime->layer_cache_.as_span();
}
Span<Layer *> LayerGroup::layers_for_write()
{
this->ensure_nodes_cache();
return this->runtime->layer_cache_.as_span();
}
Span<const LayerGroup *> LayerGroup::groups() const
{
this->ensure_nodes_cache();
return this->runtime->layer_group_cache_.as_span();
}
Span<LayerGroup *> LayerGroup::groups_for_write()
{
this->ensure_nodes_cache();
return this->runtime->layer_group_cache_.as_span();
}
const TreeNode *LayerGroup::find_node_by_name(const StringRefNull name) const
{
for (const TreeNode *node : this->nodes()) {
if (StringRef(node->name()) == StringRef(name)) {
return node;
}
}
return nullptr;
}
TreeNode *LayerGroup::find_node_by_name(const StringRefNull name)
{
for (TreeNode *node : this->nodes_for_write()) {
if (StringRef(node->name()) == StringRef(name)) {
return node;
}
}
return nullptr;
}
void LayerGroup::print_nodes(StringRefNull header) const
{
std::cout << header << std::endl;
Stack<std::pair<int, TreeNode *>> next_node;
LISTBASE_FOREACH_BACKWARD (GreasePencilLayerTreeNode *, child_, &this->children) {
TreeNode *child = reinterpret_cast<TreeNode *>(child_);
next_node.push(std::make_pair(1, child));
}
while (!next_node.is_empty()) {
auto [indent, node] = next_node.pop();
for (int i = 0; i < indent; i++) {
std::cout << " ";
}
if (node->is_layer()) {
std::cout << node->name();
}
else if (node->is_group()) {
std::cout << node->name() << ": ";
LISTBASE_FOREACH_BACKWARD (GreasePencilLayerTreeNode *, child_, &node->as_group().children) {
TreeNode *child = reinterpret_cast<TreeNode *>(child_);
next_node.push(std::make_pair(indent + 1, child));
}
}
std::cout << std::endl;
}
std::cout << std::endl;
}
void LayerGroup::ensure_nodes_cache() const
{
this->runtime->nodes_cache_mutex_.ensure([&]() {
this->runtime->nodes_cache_.clear_and_shrink();
this->runtime->layer_cache_.clear_and_shrink();
this->runtime->layer_group_cache_.clear_and_shrink();
LISTBASE_FOREACH (GreasePencilLayerTreeNode *, child_, &this->children) {
TreeNode *node = reinterpret_cast<TreeNode *>(child_);
this->runtime->nodes_cache_.append(node);
switch (node->type) {
case GP_LAYER_TREE_LEAF: {
this->runtime->layer_cache_.append(&node->as_layer());
break;
}
case GP_LAYER_TREE_GROUP: {
this->runtime->layer_group_cache_.append(&node->as_group());
for (TreeNode *child : node->as_group().nodes_for_write()) {
this->runtime->nodes_cache_.append(child);
if (child->is_layer()) {
this->runtime->layer_cache_.append(&child->as_layer());
}
else if (child->is_group()) {
this->runtime->layer_group_cache_.append(&child->as_group());
}
}
break;
}
}
}
});
}
void LayerGroup::tag_nodes_cache_dirty() const
{
this->runtime->nodes_cache_mutex_.tag_dirty();
if (this->base.parent) {
this->base.parent->wrap().tag_nodes_cache_dirty();
}
}
} // namespace blender::bke::greasepencil
/* ------------------------------------------------------------------- */
/** \name Grease Pencil kernel functions
* \{ */
void *BKE_grease_pencil_add(Main *bmain, const char *name)
{
GreasePencil *grease_pencil = reinterpret_cast<GreasePencil *>(BKE_id_new(bmain, ID_GP, name));
return grease_pencil;
}
GreasePencil *BKE_grease_pencil_new_nomain()
{
GreasePencil *grease_pencil = reinterpret_cast<GreasePencil *>(
BKE_id_new_nomain(ID_GP, nullptr));
return grease_pencil;
}
GreasePencil *BKE_grease_pencil_copy_for_eval(const GreasePencil *grease_pencil_src)
{
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)
{
using namespace blender;
BLI_assert(ob->type == OB_GREASE_PENCIL);
const GreasePencil *grease_pencil = static_cast<const GreasePencil *>(ob->data);
BoundBox bb;
if (const std::optional<Bounds<float3>> bounds = grease_pencil->bounds_min_max()) {
BKE_boundbox_init_from_minmax(&bb, bounds->min, bounds->max);
}
else {
BKE_boundbox_init_from_minmax(&bb, float3(-1), float3(1));
}
return bb;
}
static void grease_pencil_evaluate_modifiers(Depsgraph *depsgraph,
Scene *scene,
Object *object,
blender::bke::GeometrySet &geometry_set)
{
/* Modifier evaluation modes. */
const bool use_render = DEG_get_mode(depsgraph) == DAG_EVAL_RENDER;
ModifierMode required_mode = use_render ? eModifierMode_Render : eModifierMode_Realtime;
if (BKE_object_is_in_editmode(object)) {
required_mode |= eModifierMode_Editmode;
}
ModifierApplyFlag apply_flag = use_render ? MOD_APPLY_RENDER : MOD_APPLY_USECACHE;
const ModifierEvalContext mectx = {depsgraph, object, apply_flag};
BKE_modifiers_clear_errors(object);
/* Get effective list of modifiers to execute. Some effects like shape keys
* are added as virtual modifiers before the user created modifiers. */
VirtualModifierData virtualModifierData;
ModifierData *md = BKE_modifiers_get_virtual_modifierlist(object, &virtualModifierData);
/* Evaluate modifiers. */
for (; md; md = md->next) {
const ModifierTypeInfo *mti = BKE_modifier_get_info(ModifierType(md->type));
if (!BKE_modifier_is_enabled(scene, md, required_mode)) {
continue;
}
if (mti->modify_geometry_set != nullptr) {
mti->modify_geometry_set(md, &mectx, &geometry_set);
}
}
}
void BKE_grease_pencil_data_update(Depsgraph *depsgraph, Scene *scene, Object *object)
{
using namespace blender::bke;
/* Free any evaluated data and restore original data. */
BKE_object_free_derived_caches(object);
/* 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);
/* Only add the edit hint component in edit mode for now so users can properly select deformed
* drawings. */
if (object->mode == OB_MODE_EDIT) {
GeometryComponentEditData &edit_component =
geometry_set.get_component_for_write<GeometryComponentEditData>();
edit_component.grease_pencil_edit_hints_ = std::make_unique<GreasePencilEditHints>(
*static_cast<const GreasePencil *>(DEG_get_original_object(object)->data));
}
grease_pencil_evaluate_modifiers(depsgraph, scene, object, geometry_set);
if (!geometry_set.has_grease_pencil()) {
GreasePencil *empty_grease_pencil = BKE_grease_pencil_new_nomain();
empty_grease_pencil->runtime->eval_frame = int(DEG_get_ctime(depsgraph));
geometry_set.replace_grease_pencil(empty_grease_pencil);
}
/* For now the evaluated data is not const. We could use #get_grease_pencil_for_write, but that
* 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());
/* 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));
}
void BKE_grease_pencil_duplicate_drawing_array(const GreasePencil *grease_pencil_src,
GreasePencil *grease_pencil_dst)
{
using namespace blender;
grease_pencil_dst->drawing_array_num = grease_pencil_src->drawing_array_num;
grease_pencil_dst->drawing_array = MEM_cnew_array<GreasePencilDrawingBase *>(
grease_pencil_src->drawing_array_num, __func__);
bke::greasepencil::copy_drawing_array(grease_pencil_src->drawings(),
grease_pencil_dst->drawings());
}
/** \} */
/* ------------------------------------------------------------------- */
/** \name Grease Pencil material functions
* \{ */
int BKE_grease_pencil_object_material_index_get(Object *ob, Material *ma)
{
short *totcol = BKE_object_material_len_p(ob);
Material *read_ma = nullptr;
for (short i = 0; i < *totcol; i++) {
read_ma = BKE_object_material_get(ob, i + 1);
if (ma == read_ma) {
return i;
}
}
return -1;
}
int BKE_grease_pencil_object_material_index_get_by_name(Object *ob, const char *name)
{
short *totcol = BKE_object_material_len_p(ob);
Material *read_ma = nullptr;
for (short i = 0; i < *totcol; i++) {
read_ma = BKE_object_material_get(ob, i + 1);
if (STREQ(name, read_ma->id.name + 2)) {
return i;
}
}
return -1;
}
Material *BKE_grease_pencil_object_material_new(Main *bmain,
Object *ob,
const char *name,
int *r_index)
{
Material *ma = BKE_gpencil_material_add(bmain, name);
id_us_min(&ma->id); /* no users yet */
BKE_object_material_slot_add(bmain, ob);
BKE_object_material_assign(bmain, ob, ma, ob->totcol, BKE_MAT_ASSIGN_USERPREF);
if (r_index) {
*r_index = ob->actcol - 1;
}
return ma;
}
Material *BKE_grease_pencil_object_material_from_brush_get(Object *ob, Brush *brush)
{
if ((brush) && (brush->gpencil_settings) &&
(brush->gpencil_settings->flag & GP_BRUSH_MATERIAL_PINNED))
{
Material *ma = BKE_grease_pencil_brush_material_get(brush);
return ma;
}
return BKE_object_material_get(ob, ob->actcol);
}
Material *BKE_grease_pencil_object_material_ensure_by_name(Main *bmain,
Object *ob,
const char *name,
int *r_index)
{
int index = BKE_grease_pencil_object_material_index_get_by_name(ob, name);
if (index != -1) {
*r_index = index;
return BKE_object_material_get(ob, index + 1);
}
return BKE_grease_pencil_object_material_new(bmain, ob, name, r_index);
}
Material *BKE_grease_pencil_brush_material_get(Brush *brush)
{
if (brush == nullptr) {
return nullptr;
}
if (brush->gpencil_settings == nullptr) {
return nullptr;
}
return brush->gpencil_settings->material;
}
Material *BKE_grease_pencil_object_material_ensure_from_brush(Main *bmain,
Object *ob,
Brush *brush)
{
if (brush->gpencil_settings->flag & GP_BRUSH_MATERIAL_PINNED) {
Material *ma = BKE_grease_pencil_brush_material_get(brush);
/* check if the material is already on object material slots and add it if missing */
if (ma && BKE_grease_pencil_object_material_index_get(ob, ma) < 0) {
BKE_object_material_slot_add(bmain, ob);
BKE_object_material_assign(bmain, ob, ma, ob->totcol, BKE_MAT_ASSIGN_USERPREF);
}
return ma;
}
/* Use the active material instead. */
return BKE_object_material_get(ob, ob->actcol);
}
Material *BKE_grease_pencil_object_material_ensure_from_active_input_brush(Main *bmain,
Object *ob,
Brush *brush)
{
if (brush == nullptr) {
return BKE_grease_pencil_object_material_ensure_from_active_input_material(ob);
}
if (Material *ma = BKE_grease_pencil_object_material_ensure_from_brush(bmain, ob, brush)) {
return ma;
}
if (brush->gpencil_settings->flag & GP_BRUSH_MATERIAL_PINNED) {
/* It is easier to just unpin a null material, instead of setting a new one. */
brush->gpencil_settings->flag &= ~GP_BRUSH_MATERIAL_PINNED;
}
return BKE_grease_pencil_object_material_ensure_from_active_input_material(ob);
}
Material *BKE_grease_pencil_object_material_ensure_from_active_input_material(Object *ob)
{
if (Material *ma = BKE_object_material_get(ob, ob->actcol)) {
return ma;
}
return BKE_material_default_gpencil();
}
Material *BKE_grease_pencil_object_material_ensure_active(Object *ob)
{
Material *ma = BKE_grease_pencil_object_material_ensure_from_active_input_material(ob);
if (ma->gp_style == nullptr) {
BKE_gpencil_material_attr_init(ma);
}
return ma;
}
void BKE_grease_pencil_material_remap(GreasePencil *grease_pencil, const uint *remap, int totcol)
{
using namespace blender::bke;
for (GreasePencilDrawingBase *base : grease_pencil->drawings()) {
if (base->type != GP_DRAWING) {
continue;
}
greasepencil::Drawing &drawing = reinterpret_cast<GreasePencilDrawing *>(base)->wrap();
MutableAttributeAccessor attributes = drawing.strokes_for_write().attributes_for_write();
SpanAttributeWriter<int> material_indices = attributes.lookup_or_add_for_write_span<int>(
"material_index", ATTR_DOMAIN_CURVE);
if (!material_indices) {
return;
}
for (const int i : material_indices.span.index_range()) {
BLI_assert(blender::IndexRange(totcol).contains(remap[material_indices.span[i]]));
UNUSED_VARS_NDEBUG(totcol);
material_indices.span[i] = remap[material_indices.span[i]];
}
material_indices.finish();
}
}
void BKE_grease_pencil_material_index_remove(GreasePencil *grease_pencil, int index)
{
using namespace blender;
using namespace blender::bke;
for (GreasePencilDrawingBase *base : grease_pencil->drawings()) {
if (base->type != GP_DRAWING) {
continue;
}
greasepencil::Drawing &drawing = reinterpret_cast<GreasePencilDrawing *>(base)->wrap();
MutableAttributeAccessor attributes = drawing.strokes_for_write().attributes_for_write();
AttributeWriter<int> material_indices = attributes.lookup_for_write<int>("material_index");
if (!material_indices) {
return;
}
MutableVArraySpan<int> indices_span(material_indices.varray);
for (const int i : indices_span.index_range()) {
if (indices_span[i] > 0 && indices_span[i] >= index) {
indices_span[i]--;
}
}
indices_span.save();
material_indices.finish();
}
}
bool BKE_grease_pencil_material_index_used(GreasePencil *grease_pencil, int index)
{
using namespace blender;
using namespace blender::bke;
for (GreasePencilDrawingBase *base : grease_pencil->drawings()) {
if (base->type != GP_DRAWING) {
continue;
}
greasepencil::Drawing &drawing = reinterpret_cast<GreasePencilDrawing *>(base)->wrap();
AttributeAccessor attributes = drawing.strokes().attributes();
const VArraySpan<int> material_indices = *attributes.lookup_or_default<int>(
"material_index", ATTR_DOMAIN_CURVE, 0);
if (material_indices.contains(index)) {
return true;
}
}
return false;
}
/** \} */
/* ------------------------------------------------------------------- */
/** \name Grease Pencil reference functions
* \{ */
static bool grease_pencil_references_cyclic_check_internal(const GreasePencil *id_reference,
const GreasePencil *grease_pencil)
{
for (const GreasePencilDrawingBase *base : grease_pencil->drawings()) {
if (base->type == GP_DRAWING_REFERENCE) {
const auto *reference = reinterpret_cast<const GreasePencilDrawingReference *>(base);
if (id_reference == reference->id_reference) {
return true;
}
if (grease_pencil_references_cyclic_check_internal(id_reference, reference->id_reference)) {
return true;
}
}
}
return false;
}
bool BKE_grease_pencil_references_cyclic_check(const GreasePencil *id_reference,
const GreasePencil *grease_pencil)
{
return grease_pencil_references_cyclic_check_internal(id_reference, grease_pencil);
}
/** \} */
/* ------------------------------------------------------------------- */
/** \name Draw Cache
* \{ */
void (*BKE_grease_pencil_batch_cache_dirty_tag_cb)(GreasePencil *grease_pencil,
int mode) = nullptr;
void (*BKE_grease_pencil_batch_cache_free_cb)(GreasePencil *grease_pencil) = nullptr;
void BKE_grease_pencil_batch_cache_dirty_tag(GreasePencil *grease_pencil, int mode)
{
if (grease_pencil->runtime && grease_pencil->runtime->batch_cache) {
BKE_grease_pencil_batch_cache_dirty_tag_cb(grease_pencil, mode);
}
}
void BKE_grease_pencil_batch_cache_free(GreasePencil *grease_pencil)
{
if (grease_pencil->runtime && grease_pencil->runtime->batch_cache) {
BKE_grease_pencil_batch_cache_free_cb(grease_pencil);
}
}
/** \} */
/* ------------------------------------------------------------------- */
/** \name Grease Pencil data-block API
* \{ */
template<typename T> static void grow_array(T **array, int *num, const int add_num)
{
BLI_assert(add_num > 0);
const int new_array_num = *num + add_num;
T *new_array = reinterpret_cast<T *>(MEM_cnew_array<T *>(new_array_num, __func__));
blender::uninitialized_relocate_n(*array, *num, new_array);
if (*array != nullptr) {
MEM_freeN(*array);
}
*array = new_array;
*num = new_array_num;
}
template<typename T> static void shrink_array(T **array, int *num, const int shrink_num)
{
BLI_assert(shrink_num > 0);
const int new_array_num = *num - shrink_num;
T *new_array = reinterpret_cast<T *>(MEM_cnew_array<T *>(new_array_num, __func__));
blender::uninitialized_move_n(*array, new_array_num, new_array);
MEM_freeN(*array);
*array = new_array;
*num = new_array_num;
}
blender::Span<const GreasePencilDrawingBase *> GreasePencil::drawings() const
{
return blender::Span<GreasePencilDrawingBase *>{this->drawing_array, this->drawing_array_num};
}
blender::MutableSpan<GreasePencilDrawingBase *> GreasePencil::drawings()
{
return blender::MutableSpan<GreasePencilDrawingBase *>{this->drawing_array,
this->drawing_array_num};
}
void GreasePencil::add_empty_drawings(const int add_num)
{
using namespace blender;
BLI_assert(add_num > 0);
const int prev_num = this->drawings().size();
grow_array<GreasePencilDrawingBase *>(&this->drawing_array, &this->drawing_array_num, add_num);
MutableSpan<GreasePencilDrawingBase *> new_drawings = this->drawings().drop_front(prev_num);
for (const int i : new_drawings.index_range()) {
new_drawings[i] = reinterpret_cast<GreasePencilDrawingBase *>(
MEM_new<blender::bke::greasepencil::Drawing>(__func__));
}
}
void GreasePencil::add_duplicate_drawings(const int duplicate_num,
const blender::bke::greasepencil::Drawing &drawing)
{
using namespace blender;
BLI_assert(duplicate_num > 0);
const int prev_num = this->drawings().size();
grow_array<GreasePencilDrawingBase *>(
&this->drawing_array, &this->drawing_array_num, duplicate_num);
MutableSpan<GreasePencilDrawingBase *> new_drawings = this->drawings().drop_front(prev_num);
for (const int i : new_drawings.index_range()) {
new_drawings[i] = reinterpret_cast<GreasePencilDrawingBase *>(
MEM_new<bke::greasepencil::Drawing>(__func__, drawing));
}
}
bool GreasePencil::insert_blank_frame(blender::bke::greasepencil::Layer &layer,
int frame_number,
int duration,
eBezTriple_KeyframeType keytype)
{
using namespace blender;
GreasePencilFrame *frame = layer.add_frame(frame_number, int(this->drawings().size()), duration);
if (frame == nullptr) {
return false;
}
frame->type = int8_t(keytype);
this->add_empty_drawings(1);
return true;
}
bool GreasePencil::insert_duplicate_frame(blender::bke::greasepencil::Layer &layer,
const int src_frame_number,
const int dst_frame_number,
const bool do_instance)
{
using namespace blender::bke::greasepencil;
if (!layer.frames().contains(src_frame_number)) {
return false;
}
const GreasePencilFrame &src_frame = layer.frames().lookup(src_frame_number);
/* Create the new frame structure, with the same duration.
* If we want to make an instance of the source frame, the drawing index gets copied from the
* source frame. Otherwise, we set the drawing index to the size of the drawings array, since we
* are going to add a new drawing copied from the source drawing. */
const int duration = src_frame.is_implicit_hold() ?
0 :
layer.get_frame_duration_at(src_frame_number);
const int drawing_index = do_instance ? src_frame.drawing_index : int(this->drawings().size());
GreasePencilFrame *dst_frame = layer.add_frame(dst_frame_number, drawing_index, duration);
if (dst_frame == nullptr) {
return false;
}
dst_frame->type = src_frame.type;
const GreasePencilDrawingBase *src_drawing_base = this->drawing(src_frame.drawing_index);
switch (src_drawing_base->type) {
case GP_DRAWING: {
const Drawing &src_drawing =
reinterpret_cast<const GreasePencilDrawing *>(src_drawing_base)->wrap();
if (do_instance) {
/* Adds the duplicate frame as a new instance of the same drawing. We thus increase the
* user count of the corresponding drawing. */
src_drawing.add_user();
}
else {
/* Create a copy of the drawing, and add it at the end of the drawings array.
* Note that the frame already points to this new drawing, as the drawing index was set to
* `int(this->drawings().size())`. */
this->add_duplicate_drawings(1, src_drawing);
}
break;
}
case GP_DRAWING_REFERENCE:
/* TODO: Duplicate drawing references is not yet implemented.
* For now, just remove the frame that we inserted. */
layer.remove_frame(dst_frame_number);
return false;
}
return true;
}
bool GreasePencil::remove_frames(blender::bke::greasepencil::Layer &layer,
blender::Span<int> frame_numbers)
{
using namespace blender::bke::greasepencil;
bool removed_any_drawing_user = false;
for (const int frame_number : frame_numbers) {
if (!layer.frames().contains(frame_number)) {
continue;
}
const GreasePencilFrame frame_to_remove = layer.frames().lookup(frame_number);
const int64_t drawing_index_to_remove = frame_to_remove.drawing_index;
if (!layer.remove_frame(frame_number)) {
/* If removing the frame was not successful, continue. */
continue;
}
if (frame_to_remove.is_null()) {
/* Null frames don't reference a drawing, continue. */
continue;
}
GreasePencilDrawingBase *drawing_base = this->drawing(drawing_index_to_remove);
if (drawing_base->type != GP_DRAWING) {
/* If the drawing is referenced from another object, we don't track it's users because we
* cannot delete drawings from another object. */
continue;
}
Drawing &drawing = reinterpret_cast<GreasePencilDrawing *>(drawing_base)->wrap();
drawing.remove_user();
removed_any_drawing_user = true;
}
if (removed_any_drawing_user) {
this->remove_drawings_with_no_users();
return true;
}
return false;
}
static void remove_drawings_unchecked(GreasePencil &grease_pencil,
Span<int64_t> sorted_indices_to_remove)
{
using namespace blender::bke::greasepencil;
if (grease_pencil.drawing_array_num == 0 || sorted_indices_to_remove.size() == 0) {
return;
}
const int64_t drawings_to_remove = sorted_indices_to_remove.size();
const blender::IndexRange last_drawings_range(
grease_pencil.drawings().size() - drawings_to_remove, drawings_to_remove);
/* We keep track of the next available index (for swapping) by iterating from the end and
* skipping over drawings that are already in the range to be removed. */
auto next_available_index = last_drawings_range.last();
auto greatest_index_to_remove_it = std::rbegin(sorted_indices_to_remove);
auto get_next_available_index = [&]() {
while (next_available_index == *greatest_index_to_remove_it) {
greatest_index_to_remove_it = std::prev(greatest_index_to_remove_it);
next_available_index--;
}
return next_available_index;
};
/* Move the drawings to be removed to the end of the array by swapping the pointers. Make sure to
* remap any frames pointing to the drawings being swapped. */
for (const int64_t index_to_remove : sorted_indices_to_remove) {
if (index_to_remove >= last_drawings_range.first()) {
/* This drawing and all the next drawings are already in the range to be removed. */
break;
}
const int64_t swap_index = get_next_available_index();
/* Remap the drawing_index for frames that point to the drawing to be swapped with. */
for (Layer *layer : grease_pencil.layers_for_write()) {
for (auto [key, value] : layer->frames_for_write().items()) {
if (value.drawing_index == swap_index) {
value.drawing_index = index_to_remove;
}
}
}
/* Swap the pointers to the drawings in the drawing array. */
std::swap(grease_pencil.drawing_array[index_to_remove],
grease_pencil.drawing_array[swap_index]);
next_available_index--;
}
/* Free the last drawings. */
for (const int64_t drawing_index : last_drawings_range) {
GreasePencilDrawingBase *drawing_base_to_remove = grease_pencil.drawing(drawing_index);
switch (drawing_base_to_remove->type) {
case GP_DRAWING: {
GreasePencilDrawing *drawing_to_remove = reinterpret_cast<GreasePencilDrawing *>(
drawing_base_to_remove);
MEM_delete(&drawing_to_remove->wrap());
break;
}
case GP_DRAWING_REFERENCE: {
GreasePencilDrawingReference *drawing_reference_to_remove =
reinterpret_cast<GreasePencilDrawingReference *>(drawing_base_to_remove);
MEM_delete(&drawing_reference_to_remove->wrap());
break;
}
}
}
/* Shrink drawing array. */
shrink_array<GreasePencilDrawingBase *>(
&grease_pencil.drawing_array, &grease_pencil.drawing_array_num, drawings_to_remove);
}
void GreasePencil::remove_drawings_with_no_users()
{
using namespace blender;
Vector<int64_t> drawings_to_be_removed;
for (const int64_t drawing_i : this->drawings().index_range()) {
GreasePencilDrawingBase *drawing_base = this->drawing(drawing_i);
if (drawing_base->type != GP_DRAWING) {
continue;
}
GreasePencilDrawing *drawing = reinterpret_cast<GreasePencilDrawing *>(drawing_base);
if (!drawing->wrap().has_users()) {
drawings_to_be_removed.append(drawing_i);
}
}
remove_drawings_unchecked(*this, drawings_to_be_removed.as_span());
}
void GreasePencil::update_drawing_users_for_layer(const blender::bke::greasepencil::Layer &layer)
{
using namespace blender;
for (auto [key, value] : layer.frames().items()) {
if (value.drawing_index > 0 && value.drawing_index < this->drawings().size()) {
GreasePencilDrawingBase *drawing_base = this->drawing(value.drawing_index);
if (drawing_base->type != GP_DRAWING) {
continue;
}
bke::greasepencil::Drawing &drawing =
reinterpret_cast<GreasePencilDrawing *>(drawing_base)->wrap();
if (!drawing.has_users()) {
drawing.add_user();
}
}
}
}
void GreasePencil::move_frames(blender::bke::greasepencil::Layer &layer,
const blender::Map<int, int> &frame_number_destinations)
{
return this->move_duplicate_frames(
layer, frame_number_destinations, blender::Map<int, GreasePencilFrame>());
}
void GreasePencil::move_duplicate_frames(
blender::bke::greasepencil::Layer &layer,
const blender::Map<int, int> &frame_number_destinations,
const blender::Map<int, GreasePencilFrame> &duplicate_frames)
{
using namespace blender;
Map<int, GreasePencilFrame> layer_frames_copy = layer.frames();
/* Copy frames durations. */
Map<int, int> layer_frames_durations;
for (const auto [frame_number, frame] : layer.frames().items()) {
if (!frame.is_implicit_hold()) {
layer_frames_durations.add(frame_number, layer.get_frame_duration_at(frame_number));
}
}
for (const auto [src_frame_number, dst_frame_number] : frame_number_destinations.items()) {
const bool use_duplicate = duplicate_frames.contains(src_frame_number);
const Map<int, GreasePencilFrame> &frame_map = use_duplicate ? duplicate_frames :
layer_frames_copy;
if (!frame_map.contains(src_frame_number)) {
continue;
}
const GreasePencilFrame src_frame = frame_map.lookup(src_frame_number);
const int drawing_index = src_frame.drawing_index;
const int duration = layer_frames_durations.lookup_default(src_frame_number, 0);
if (!use_duplicate) {
layer.remove_frame(src_frame_number);
}
/* Add and overwrite the frame at the destination number. */
if (layer.frames().contains(dst_frame_number)) {
GreasePencilFrame frame_to_overwrite = layer.frames().lookup(dst_frame_number);
GreasePencilDrawingBase *drawing_base = this->drawing(frame_to_overwrite.drawing_index);
if (drawing_base->type == GP_DRAWING) {
reinterpret_cast<GreasePencilDrawing *>(drawing_base)->wrap().remove_user();
}
layer.remove_frame(dst_frame_number);
}
GreasePencilFrame *frame = layer.add_frame(dst_frame_number, drawing_index, duration);
*frame = src_frame;
}
/* Remove drawings if they no longer have users. */
this->remove_drawings_with_no_users();
}
const blender::bke::greasepencil::Drawing *GreasePencil::get_drawing_at(
const blender::bke::greasepencil::Layer *layer, const int frame_number) const
{
if (layer == nullptr) {
return nullptr;
}
const int drawing_index = layer->drawing_index_at(frame_number);
if (drawing_index == -1) {
/* No drawing found. */
return nullptr;
}
const GreasePencilDrawingBase *drawing_base = this->drawing(drawing_index);
if (drawing_base->type != GP_DRAWING) {
/* TODO: Get reference drawing. */
return nullptr;
}
const GreasePencilDrawing *drawing = reinterpret_cast<const GreasePencilDrawing *>(drawing_base);
return &drawing->wrap();
}
blender::bke::greasepencil::Drawing *GreasePencil::get_editable_drawing_at(
const blender::bke::greasepencil::Layer *layer, const int frame_number)
{
if (layer == nullptr || !layer->is_editable()) {
return nullptr;
}
const int drawing_index = layer->drawing_index_at(frame_number);
if (drawing_index == -1) {
/* No drawing found. */
return nullptr;
}
GreasePencilDrawingBase *drawing_base = this->drawing(drawing_index);
if (drawing_base->type != GP_DRAWING) {
/* Drawing references are not editable. */
return nullptr;
}
GreasePencilDrawing *drawing = reinterpret_cast<GreasePencilDrawing *>(drawing_base);
return &drawing->wrap();
}
std::optional<blender::Bounds<blender::float3>> GreasePencil::bounds_min_max() const
{
using namespace blender;
std::optional<Bounds<float3>> bounds;
const Span<const bke::greasepencil::Layer *> layers = this->layers();
for (const int layer_i : layers.index_range()) {
const bke::greasepencil::Layer *layer = layers[layer_i];
if (!layer->is_visible()) {
continue;
}
if (const bke::greasepencil::Drawing *drawing = this->get_drawing_at(
layer, this->runtime->eval_frame))
{
const bke::CurvesGeometry &curves = drawing->strokes();
bounds = bounds::merge(bounds, curves.bounds_min_max());
}
}
return bounds;
}
blender::Span<const blender::bke::greasepencil::Layer *> GreasePencil::layers() const
{
BLI_assert(this->runtime != nullptr);
return this->root_group().layers();
}
blender::Span<blender::bke::greasepencil::Layer *> GreasePencil::layers_for_write()
{
BLI_assert(this->runtime != nullptr);
return this->root_group().layers_for_write();
}
blender::Span<const blender::bke::greasepencil::LayerGroup *> GreasePencil::layer_groups() const
{
BLI_assert(this->runtime != nullptr);
return this->root_group().groups();
}
blender::Span<blender::bke::greasepencil::LayerGroup *> GreasePencil::layer_groups_for_write()
{
BLI_assert(this->runtime != nullptr);
return this->root_group().groups_for_write();
}
blender::Span<const blender::bke::greasepencil::TreeNode *> GreasePencil::nodes() const
{
BLI_assert(this->runtime != nullptr);
return this->root_group().nodes();
}
blender::Span<blender::bke::greasepencil::TreeNode *> GreasePencil::nodes_for_write()
{
BLI_assert(this->runtime != nullptr);
return this->root_group().nodes_for_write();
}
const blender::bke::greasepencil::Layer *GreasePencil::get_active_layer() const
{
if (this->active_layer == nullptr) {
return nullptr;
}
return &this->active_layer->wrap();
}
blender::bke::greasepencil::Layer *GreasePencil::get_active_layer_for_write()
{
if (this->active_layer == nullptr) {
return nullptr;
}
return &this->active_layer->wrap();
}
void GreasePencil::set_active_layer(const blender::bke::greasepencil::Layer *layer)
{
this->active_layer = const_cast<GreasePencilLayer *>(
reinterpret_cast<const GreasePencilLayer *>(layer));
}
bool GreasePencil::is_layer_active(const blender::bke::greasepencil::Layer *layer) const
{
if (layer == nullptr) {
return false;
}
return this->get_active_layer() == layer;
}
static blender::VectorSet<blender::StringRefNull> get_node_names(const GreasePencil &grease_pencil)
{
using namespace blender;
VectorSet<StringRefNull> names;
for (const blender::bke::greasepencil::TreeNode *node : grease_pencil.nodes()) {
names.add(node->name());
}
return names;
}
static bool check_unique_node_cb(void *arg, const char *name)
{
using namespace blender;
VectorSet<StringRefNull> &names = *reinterpret_cast<VectorSet<StringRefNull> *>(arg);
return names.contains(name);
}
static void unique_node_name_ex(VectorSet<blender::StringRefNull> &names,
const char *default_name,
char *name)
{
BLI_uniquename_cb(check_unique_node_cb, &names, default_name, '.', name, MAX_NAME);
}
static std::string unique_node_name(const GreasePencil &grease_pencil,
const char *default_name,
blender::StringRefNull name)
{
using namespace blender;
char unique_name[MAX_NAME];
STRNCPY(unique_name, name.c_str());
VectorSet<StringRefNull> names = get_node_names(grease_pencil);
unique_node_name_ex(names, default_name, unique_name);
return unique_name;
}
static std::string unique_layer_name(const GreasePencil &grease_pencil,
blender::StringRefNull name)
{
return unique_node_name(grease_pencil, DATA_("Layer"), name);
}
static std::string unique_layer_group_name(const GreasePencil &grease_pencil,
blender::StringRefNull name)
{
return unique_node_name(grease_pencil, DATA_("Group"), name);
}
blender::bke::greasepencil::Layer &GreasePencil::add_layer(const blender::StringRefNull name)
{
using namespace blender;
std::string unique_name = unique_layer_name(*this, name);
const int numLayers = layers().size();
CustomData_realloc(&layers_data, numLayers, numLayers + 1);
return root_group().add_layer(unique_name);
}
blender::bke::greasepencil::Layer &GreasePencil::add_layer(
blender::bke::greasepencil::LayerGroup &parent_group, const blender::StringRefNull name)
{
using namespace blender;
blender::bke::greasepencil::Layer &new_layer = add_layer(name);
move_node_into(new_layer.as_node(), parent_group);
return new_layer;
}
blender::bke::greasepencil::Layer &GreasePencil::add_layer(
const blender::bke::greasepencil::Layer &duplicate_layer)
{
using namespace blender;
std::string unique_name = unique_layer_name(*this, duplicate_layer.name());
const int numLayers = layers().size();
CustomData_realloc(&layers_data, numLayers, numLayers + 1);
bke::greasepencil::Layer &new_layer = root_group().add_layer(duplicate_layer);
this->update_drawing_users_for_layer(new_layer);
new_layer.set_name(unique_name);
return new_layer;
}
blender::bke::greasepencil::Layer &GreasePencil::add_layer(
blender::bke::greasepencil::LayerGroup &parent_group,
const blender::bke::greasepencil::Layer &duplicate_layer)
{
using namespace blender;
bke::greasepencil::Layer &new_layer = add_layer(duplicate_layer);
move_node_into(new_layer.as_node(), parent_group);
return new_layer;
}
blender::bke::greasepencil::LayerGroup &GreasePencil::add_layer_group(
blender::bke::greasepencil::LayerGroup &parent_group, const blender::StringRefNull name)
{
using namespace blender;
std::string unique_name = unique_layer_group_name(*this, name);
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 reorder_layer_data(GreasePencil &grease_pencil,
const blender::FunctionRef<void()> do_layer_order_changes)
{
using namespace blender;
Span<const bke::greasepencil::Layer *> layers = grease_pencil.layers();
/* Stash the initial layer order that we can refer back to later */
Map<const bke::greasepencil::Layer *, int> old_layer_index_by_layer;
old_layer_index_by_layer.reserve(layers.size());
for (const int i : layers.index_range()) {
old_layer_index_by_layer.add_new(layers[i], i);
}
/* Execute the callback that changes the order of the layers. */
do_layer_order_changes();
layers = grease_pencil.layers();
BLI_assert(layers.size() == old_layer_index_by_layer.size());
/* Compose the mapping from old layer indices to new layer indices */
Array<int> new_by_old_map(layers.size());
for (const int layer_i_new : layers.index_range()) {
const bke::greasepencil::Layer *layer = layers[layer_i_new];
BLI_assert(old_layer_index_by_layer.contains(layer));
const int layer_i_old = old_layer_index_by_layer.pop(layer);
new_by_old_map[layer_i_old] = layer_i_new;
}
BLI_assert(old_layer_index_by_layer.is_empty());
/* Use the mapping to re-order the custom data */
reorder_customdata(grease_pencil.layers_data, new_by_old_map);
}
void GreasePencil::move_node_up(blender::bke::greasepencil::TreeNode &node, const int step)
{
using namespace blender;
if (!node.parent_group()) {
return;
}
reorder_layer_data(*this, [&]() { node.parent_group()->move_node_up(node, step); });
}
void GreasePencil::move_node_down(blender::bke::greasepencil::TreeNode &node, const int step)
{
using namespace blender;
if (!node.parent_group()) {
return;
}
reorder_layer_data(*this, [&]() { node.parent_group()->move_node_down(node, step); });
}
void GreasePencil::move_node_top(blender::bke::greasepencil::TreeNode &node)
{
using namespace blender;
if (!node.parent_group()) {
return;
}
reorder_layer_data(*this, [&]() { node.parent_group()->move_node_top(node); });
}
void GreasePencil::move_node_bottom(blender::bke::greasepencil::TreeNode &node)
{
using namespace blender;
if (!node.parent_group()) {
return;
}
reorder_layer_data(*this, [&]() { 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;
}
reorder_layer_data(*this, [&]() {
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;
}
reorder_layer_data(*this, [&]() {
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)
{
using namespace blender;
if (!node.parent_group()) {
return;
}
reorder_layer_data(*this, [&]() {
node.parent_group()->unlink_node(node);
parent_group.add_node(node);
});
}
const blender::bke::greasepencil::TreeNode *GreasePencil::find_node_by_name(
const blender::StringRefNull name) const
{
return this->root_group().find_node_by_name(name);
}
blender::bke::greasepencil::TreeNode *GreasePencil::find_node_by_name(
const blender::StringRefNull name)
{
return this->root_group().find_node_by_name(name);
}
blender::IndexMask GreasePencil::layer_selection_by_name(const blender::StringRefNull name,
blender::IndexMaskMemory &memory) const
{
using namespace blender::bke::greasepencil;
const TreeNode *node = this->find_node_by_name(name);
if (!node) {
return {};
}
if (node->is_layer()) {
const int64_t index = this->layers().first_index(&node->as_layer());
return blender::IndexMask::from_indices(Span{index}, memory);
}
else if (node->is_group()) {
blender::Vector<int64_t> layer_indices;
for (const int64_t layer_index : this->layers().index_range()) {
const Layer &layer = *this->layers()[layer_index];
if (layer.is_child_of(node->as_group())) {
layer_indices.append(layer_index);
}
}
return blender::IndexMask::from_indices(layer_indices.as_span(), memory);
}
return {};
}
void GreasePencil::rename_node(blender::bke::greasepencil::TreeNode &node,
blender::StringRefNull new_name)
{
using namespace blender;
if (node.name() == new_name) {
return;
}
node.set_name(node.is_layer() ? unique_layer_name(*this, new_name) :
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;
/* If the layer is active, update the active layer. */
const Layer *active_layer = this->get_active_layer();
if (active_layer == &layer) {
Span<const Layer *> layers = this->layers();
/* If there is no other layer available , unset the active layer. */
if (layers.size() == 1) {
this->set_active_layer(nullptr);
}
else {
/* Make the layer below active (if possible). */
if (active_layer == layers.first()) {
this->set_active_layer(layers[1]);
}
else {
int64_t active_index = layers.first_index(active_layer);
this->set_active_layer(layers[active_index - 1]);
}
}
}
/* 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());
/* Remove drawings. */
for (GreasePencilFrame frame : layer.frames_for_write().values()) {
GreasePencilDrawingBase *drawing_base = this->drawing(frame.drawing_index);
if (drawing_base->type != GP_DRAWING) {
continue;
}
GreasePencilDrawing *drawing = reinterpret_cast<GreasePencilDrawing *>(drawing_base);
drawing->wrap().remove_user();
}
this->remove_drawings_with_no_users();
/* Delete the layer. */
MEM_delete(&layer);
}
void GreasePencil::print_layer_tree()
{
using namespace blender::bke::greasepencil;
this->root_group().print_nodes("Layer Tree:");
}
/** \} */
/* ------------------------------------------------------------------- */
/** \name Drawing array read/write functions
* \{ */
static void read_drawing_array(GreasePencil &grease_pencil, BlendDataReader *reader)
{
BLO_read_pointer_array(reader, reinterpret_cast<void **>(&grease_pencil.drawing_array));
for (int i = 0; i < grease_pencil.drawing_array_num; i++) {
BLO_read_data_address(reader, &grease_pencil.drawing_array[i]);
GreasePencilDrawingBase *drawing_base = grease_pencil.drawing_array[i];
switch (drawing_base->type) {
case GP_DRAWING: {
GreasePencilDrawing *drawing = reinterpret_cast<GreasePencilDrawing *>(drawing_base);
drawing->wrap().strokes_for_write().blend_read(*reader);
/* Initialize runtime data. */
drawing->runtime = MEM_new<blender::bke::greasepencil::DrawingRuntime>(__func__);
break;
}
case GP_DRAWING_REFERENCE: {
GreasePencilDrawingReference *drawing_reference =
reinterpret_cast<GreasePencilDrawingReference *>(drawing_base);
BLO_read_data_address(reader, &drawing_reference->id_reference);
break;
}
}
}
}
static void write_drawing_array(GreasePencil &grease_pencil, BlendWriter *writer)
{
using namespace blender;
BLO_write_pointer_array(writer, grease_pencil.drawing_array_num, grease_pencil.drawing_array);
for (int i = 0; i < grease_pencil.drawing_array_num; i++) {
GreasePencilDrawingBase *drawing_base = grease_pencil.drawing_array[i];
switch (drawing_base->type) {
case GP_DRAWING: {
GreasePencilDrawing *drawing = reinterpret_cast<GreasePencilDrawing *>(drawing_base);
bke::CurvesGeometry::BlendWriteData write_data =
drawing->wrap().strokes_for_write().blend_write_prepare();
BLO_write_struct(writer, GreasePencilDrawing, drawing);
drawing->wrap().strokes_for_write().blend_write(*writer, grease_pencil.id, write_data);
break;
}
case GP_DRAWING_REFERENCE: {
GreasePencilDrawingReference *drawing_reference =
reinterpret_cast<GreasePencilDrawingReference *>(drawing_base);
BLO_write_struct(writer, GreasePencilDrawingReference, drawing_reference);
break;
}
}
}
}
static void free_drawing_array(GreasePencil &grease_pencil)
{
if (grease_pencil.drawing_array == nullptr || grease_pencil.drawing_array_num == 0) {
return;
}
for (int i = 0; i < grease_pencil.drawing_array_num; i++) {
GreasePencilDrawingBase *drawing_base = grease_pencil.drawing_array[i];
switch (drawing_base->type) {
case GP_DRAWING: {
GreasePencilDrawing *drawing = reinterpret_cast<GreasePencilDrawing *>(drawing_base);
MEM_delete(&drawing->wrap());
break;
}
case GP_DRAWING_REFERENCE: {
GreasePencilDrawingReference *drawing_reference =
reinterpret_cast<GreasePencilDrawingReference *>(drawing_base);
MEM_delete(&drawing_reference->wrap());
break;
}
}
}
MEM_freeN(grease_pencil.drawing_array);
grease_pencil.drawing_array = nullptr;
grease_pencil.drawing_array_num = 0;
}
/** \} */
/* ------------------------------------------------------------------- */
/** \name Layer tree read/write functions
* \{ */
static void read_layer(BlendDataReader *reader,
GreasePencilLayer *node,
GreasePencilLayerTreeGroup *parent)
{
BLO_read_data_address(reader, &node->base.name);
node->base.parent = parent;
/* Read frames storage. */
BLO_read_int32_array(reader, node->frames_storage.num, &node->frames_storage.keys);
BLO_read_data_address(reader, &node->frames_storage.values);
/* Re-create frames data in runtime map. */
node->wrap().runtime = MEM_new<blender::bke::greasepencil::LayerRuntime>(__func__);
for (int i = 0; i < node->frames_storage.num; i++) {
node->wrap().frames_for_write().add_new(node->frames_storage.keys[i],
node->frames_storage.values[i]);
}
/* Read layer masks. */
BLO_read_list(reader, &node->masks);
LISTBASE_FOREACH (GreasePencilLayerMask *, mask, &node->masks) {
BLO_read_data_address(reader, &mask->layer_name);
}
}
static void read_layer_tree_group(BlendDataReader *reader,
GreasePencilLayerTreeGroup *node,
GreasePencilLayerTreeGroup *parent)
{
BLO_read_data_address(reader, &node->base.name);
node->base.parent = parent;
/* Read list of children. */
BLO_read_list(reader, &node->children);
LISTBASE_FOREACH (GreasePencilLayerTreeNode *, child, &node->children) {
switch (child->type) {
case GP_LAYER_TREE_LEAF: {
GreasePencilLayer *layer = reinterpret_cast<GreasePencilLayer *>(child);
read_layer(reader, layer, node);
break;
}
case GP_LAYER_TREE_GROUP: {
GreasePencilLayerTreeGroup *group = reinterpret_cast<GreasePencilLayerTreeGroup *>(child);
read_layer_tree_group(reader, group, node);
break;
}
}
}
node->wrap().runtime = MEM_new<blender::bke::greasepencil::LayerGroupRuntime>(__func__);
}
static void read_layer_tree(GreasePencil &grease_pencil, BlendDataReader *reader)
{
/* Read root group. */
BLO_read_data_address(reader, &grease_pencil.root_group_ptr);
/* This shouldn't normally happen, but for files that were created before the root group became a
* pointer, this address will not exist. In this case, we clear the pointer to the active layer
* and create an empty root group to avoid crashes. */
if (grease_pencil.root_group_ptr == nullptr) {
grease_pencil.root_group_ptr = MEM_new<blender::bke::greasepencil::LayerGroup>(__func__);
grease_pencil.active_layer = nullptr;
return;
}
/* Read active layer. */
BLO_read_data_address(reader, &grease_pencil.active_layer);
read_layer_tree_group(reader, grease_pencil.root_group_ptr, nullptr);
}
static void write_layer(BlendWriter *writer, GreasePencilLayer *node)
{
using namespace blender::bke::greasepencil;
/* Re-create the frames storage only if it was tagged dirty. */
if ((node->frames_storage.flag & GP_LAYER_FRAMES_STORAGE_DIRTY) != 0) {
MEM_SAFE_FREE(node->frames_storage.keys);
MEM_SAFE_FREE(node->frames_storage.values);
const Layer &layer = node->wrap();
node->frames_storage.num = layer.frames().size();
node->frames_storage.keys = MEM_cnew_array<int>(node->frames_storage.num, __func__);
node->frames_storage.values = MEM_cnew_array<GreasePencilFrame>(node->frames_storage.num,
__func__);
const Span<int> sorted_keys = layer.sorted_keys();
for (const int i : sorted_keys.index_range()) {
node->frames_storage.keys[i] = sorted_keys[i];
node->frames_storage.values[i] = layer.frames().lookup(sorted_keys[i]);
}
/* Reset the flag. */
node->frames_storage.flag &= ~GP_LAYER_FRAMES_STORAGE_DIRTY;
}
BLO_write_struct(writer, GreasePencilLayer, node);
BLO_write_string(writer, node->base.name);
BLO_write_int32_array(writer, node->frames_storage.num, node->frames_storage.keys);
BLO_write_struct_array(
writer, GreasePencilFrame, node->frames_storage.num, node->frames_storage.values);
BLO_write_struct_list(writer, GreasePencilLayerMask, &node->masks);
LISTBASE_FOREACH (GreasePencilLayerMask *, mask, &node->masks) {
BLO_write_string(writer, mask->layer_name);
}
}
static void write_layer_tree_group(BlendWriter *writer, GreasePencilLayerTreeGroup *node)
{
BLO_write_struct(writer, GreasePencilLayerTreeGroup, node);
BLO_write_string(writer, node->base.name);
LISTBASE_FOREACH (GreasePencilLayerTreeNode *, child, &node->children) {
switch (child->type) {
case GP_LAYER_TREE_LEAF: {
GreasePencilLayer *layer = reinterpret_cast<GreasePencilLayer *>(child);
write_layer(writer, layer);
break;
}
case GP_LAYER_TREE_GROUP: {
GreasePencilLayerTreeGroup *group = reinterpret_cast<GreasePencilLayerTreeGroup *>(child);
write_layer_tree_group(writer, group);
break;
}
}
}
}
static void write_layer_tree(GreasePencil &grease_pencil, BlendWriter *writer)
{
write_layer_tree_group(writer, grease_pencil.root_group_ptr);
}
/** \} */