Files
test/source/blender/blenkernel/intern/grease_pencil.cc
Bastien Montagne 80948f43ca Refactor: BLO: Add array size parameter to BLO_read_pointer_array.
For some reason this was the only one of these 'read array' functions
with no such parameter.
2024-07-31 16:55:29 +02:00

3610 lines
119 KiB
C++

/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup bke
*/
#include <iostream>
#include <optional>
#include "BKE_action.h"
#include "BKE_anim_data.hh"
#include "BKE_animsys.h"
#include "BKE_bake_data_block_id.hh"
#include "BKE_curves.hh"
#include "BKE_customdata.hh"
#include "BKE_deform.hh"
#include "BKE_geometry_set.hh"
#include "BKE_grease_pencil.h"
#include "BKE_grease_pencil.hh"
#include "BKE_idtype.hh"
#include "BKE_lib_id.hh"
#include "BKE_lib_query.hh"
#include "BKE_main.hh"
#include "BKE_material.h"
#include "BKE_modifier.hh"
#include "BKE_object.hh"
#include "BKE_object_types.hh"
#include "BLI_bounds.hh"
#include "BLI_enumerable_thread_specific.hh"
#include "BLI_map.hh"
#include "BLI_math_euler_types.hh"
#include "BLI_math_geom.h"
#include "BLI_math_matrix.h"
#include "BLI_math_matrix_types.hh"
#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_utildefines.h"
#include "BLI_vector_set.hh"
#include "BLI_virtual_array.hh"
#include "BLO_read_write.hh"
#include "BLT_translation.hh"
#include "DNA_ID.h"
#include "DNA_ID_enums.h"
#include "DNA_brush_types.h"
#include "DNA_defaults.h"
#include "DNA_gpencil_modifier_types.h"
#include "DNA_grease_pencil_types.h"
#include "DNA_material_types.h"
#include "DNA_modifier_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;
static const char *ATTR_POSITION = "position";
/* 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);
BLI_assert(MEMCMP_STRUCT_AFTER_IS_ZERO(grease_pencil, id));
MEMCPY_STRUCT_AFTER(grease_pencil, DNA_struct_default_get(GreasePencil), id);
grease_pencil->root_group_ptr = MEM_new<greasepencil::LayerGroup>(__func__);
grease_pencil->set_active_node(nullptr);
CustomData_reset(&grease_pencil->layers_data);
grease_pencil->runtime = MEM_new<GreasePencilRuntime>(__func__);
}
static void grease_pencil_copy_data(Main * /*bmain*/,
std::optional<Library *> /*owner_library*/,
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 node. */
if (grease_pencil_src->get_active_node()) {
bke::greasepencil::TreeNode *active_node = grease_pencil_dst->find_node_by_name(
grease_pencil_src->get_active_node()->name());
BLI_assert(active_node);
grease_pencil_dst->set_active_node(active_node);
}
CustomData_copy(&grease_pencil_src->layers_data,
&grease_pencil_dst->layers_data,
CD_MASK_ALL,
grease_pencil_dst->layers().size());
BKE_defgroup_copy_list(&grease_pencil_dst->vertex_group_names,
&grease_pencil_src->vertex_group_names);
/* Make sure the runtime pointer exists. */
grease_pencil_dst->runtime = MEM_new<bke::GreasePencilRuntime>(__func__);
if (grease_pencil_src->runtime->bake_materials) {
grease_pencil_dst->runtime->bake_materials = std::make_unique<bke::bake::BakeMaterialsList>(
*grease_pencil_src->runtime->bake_materials);
}
}
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());
BLI_freelistN(&grease_pencil->vertex_group_names);
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);
/* Write vertex group names. */
BKE_defbase_blend_write(writer, &grease_pencil->vertex_group_names);
}
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,
grease_pencil->material_array_num,
reinterpret_cast<void **>(&grease_pencil->material_array));
/* Read vertex group names. */
BLO_read_struct_list(reader, bDeformGroup, &grease_pencil->vertex_group_names);
grease_pencil->runtime = MEM_new<blender::bke::GreasePencilRuntime>(__func__);
}
IDTypeInfo IDType_ID_GP = {
/*id_code*/ ID_GP,
/*id_filter*/ FILTER_ID_GP,
/*dependencies_id_types*/ FILTER_ID_GP | FILTER_ID_MA,
/*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 {
static const std::string ATTR_RADIUS = "radius";
static const std::string ATTR_OPACITY = "opacity";
static const std::string ATTR_VERTEX_COLOR = "vertex_color";
static const std::string ATTR_FILL_COLOR = "fill_color";
/* Curves attributes getters */
static int domain_num(const CurvesGeometry &curves, const AttrDomain domain)
{
return domain == AttrDomain::Point ? curves.points_num() : curves.curves_num();
}
static CustomData &domain_custom_data(CurvesGeometry &curves, const AttrDomain domain)
{
return domain == AttrDomain::Point ? curves.point_data : curves.curve_data;
}
template<typename T>
static MutableSpan<T> get_mutable_attribute(CurvesGeometry &curves,
const AttrDomain domain,
const StringRef name,
const T default_value = T())
{
const int num = domain_num(curves, domain);
if (num <= 0) {
return {};
}
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, num);
if (data != nullptr) {
return {data, num};
}
data = (T *)CustomData_add_layer_named(&custom_data, type, CD_SET_DEFAULT, num, name);
MutableSpan<T> span = {data, num};
if (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;
this->runtime->curve_plane_normals_cache = other.runtime->curve_plane_normals_cache;
this->runtime->curve_texture_matrices = other.runtime->curve_texture_matrices;
}
Drawing::Drawing(Drawing &&other)
{
this->base.type = GP_DRAWING;
other.base.type = GP_DRAWING;
this->base.flag = other.base.flag;
other.base.flag = 0;
new (&this->geometry) bke::CurvesGeometry(std::move(other.geometry.wrap()));
this->runtime = other.runtime;
other.runtime = nullptr;
}
Drawing &Drawing::operator=(const Drawing &other)
{
if (this == &other) {
return *this;
}
std::destroy_at(this);
new (this) Drawing(other);
return *this;
}
Drawing &Drawing::operator=(Drawing &&other)
{
if (this == &other) {
return *this;
}
std::destroy_at(this);
new (this) Drawing(std::move(other));
return *this;
}
Drawing::~Drawing()
{
this->strokes().~CurvesGeometry();
MEM_delete(this->runtime);
this->runtime = nullptr;
}
Span<uint3> Drawing::triangles() const
{
struct LocalMemArena {
MemArena *pf_arena = nullptr;
LocalMemArena() : pf_arena(BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, "Drawing::triangles")) {}
~LocalMemArena()
{
if (pf_arena != nullptr) {
BLI_memarena_free(pf_arena);
}
}
};
this->runtime->triangles_cache.ensure([&](Vector<uint3> &r_data) {
const CurvesGeometry &curves = this->strokes();
const Span<float3> positions = curves.evaluated_positions();
const Span<float3> normals = this->curve_plane_normals();
const OffsetIndices<int> points_by_curve = curves.evaluated_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);
MutableSpan<uint3> triangles = r_data.as_mutable_span();
threading::EnumerableThreadSpecific<LocalMemArena> all_local_mem_arenas;
threading::parallel_for(curves.curves_range(), 32, [&](const IndexRange range) {
MemArena *pf_arena = all_local_mem_arenas.local().pf_arena;
for (const int curve_i : 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 = triangles.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())));
float3x3 axis_mat;
axis_dominant_v3_to_m3(axis_mat.ptr(), normals[curve_i]);
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);
}
});
});
return this->runtime->triangles_cache.data().as_span();
}
Span<float3> Drawing::curve_plane_normals() const
{
this->runtime->curve_plane_normals_cache.ensure([&](Vector<float3> &r_data) {
const CurvesGeometry &curves = this->strokes();
const Span<float3> positions = curves.positions();
const OffsetIndices<int> points_by_curve = curves.points_by_curve();
r_data.reinitialize(curves.curves_num());
threading::parallel_for(curves.curves_range(), 512, [&](const IndexRange range) {
for (const int curve_i : range) {
const IndexRange points = points_by_curve[curve_i];
if (points.size() < 2) {
r_data[curve_i] = float3(1.0f, 0.0f, 0.0f);
continue;
}
/* Calculate normal using Newell's method. */
float3 normal(0.0f);
float3 prev_point = positions[points.last()];
for (const int point_i : points) {
const float3 curr_point = positions[point_i];
add_newell_cross_v3_v3v3(normal, prev_point, curr_point);
prev_point = curr_point;
}
float length;
normal = math::normalize_and_get_length(normal, length);
/* Check for degenerate case where the points are on a line. */
if (math::is_zero(length)) {
for (const int point_i : points.drop_back(1)) {
float3 segment_vec = positions[point_i] - positions[point_i + 1];
if (math::length_squared(segment_vec) != 0.0f) {
normal = math::normalize(float3(segment_vec.y, -segment_vec.x, 0.0f));
break;
}
}
}
r_data[curve_i] = normal;
}
});
});
return this->runtime->curve_plane_normals_cache.data().as_span();
}
/*
* Returns the matrix that transforms from a 3D point in layer-space to a 2D point in
* stroke-space for the stroke `curve_i`
*/
static float4x2 get_local_to_stroke_matrix(const Span<float3> positions, const float3 normal)
{
using namespace blender::math;
if (positions.size() <= 2) {
return float4x2::identity();
}
const float3 point_0 = positions[0];
const float3 point_1 = positions[1];
/* Local X axis (p0 -> p1) */
const float3 local_x = normalize(point_1 - point_0);
/* Local Y axis (cross to normal/x axis). */
const float3 local_y = normalize(cross(normal, local_x));
if (length_squared(local_x) == 0.0f || length_squared(local_y) == 0.0f) {
return float4x2::identity();
}
/* Get local space using first point as origin. */
const float4x2 mat = transpose(
float2x4(float4(local_x, -dot(point_0, local_x)), float4(local_y, -dot(point_0, local_y))));
return mat;
}
/*
* Returns the matrix that transforms from a 2D point in stroke-space to a 2D point in
* texture-space for a stroke `curve_i`
*/
static float3x2 get_stroke_to_texture_matrix(const float uv_rotation,
const float2 uv_translation,
const float2 uv_scale)
{
using namespace blender::math;
const float2 uv_scale_inv = safe_rcp(uv_scale);
const float s = sin(uv_rotation);
const float c = cos(uv_rotation);
const float2x2 rot = float2x2(float2(c, s), float2(-s, c));
float3x2 texture_matrix = float3x2::identity();
/*
* The order in which the three transforms are applied has been carefully chosen to be easy to
* invert.
*
* The translation is applied last so that the origin goes to `uv_translation`
* The rotation is applied after the scale so that the `u` direction's angle is `uv_rotation`
* Scale is the only transform that changes the length of the basis vectors and if it is applied
* first it's independent of the other transforms.
*
* These properties are not true with a different order.
*/
/* Apply scale. */
texture_matrix = from_scale<float2x2>(uv_scale_inv) * texture_matrix;
/* Apply rotation. */
texture_matrix = rot * texture_matrix;
/* Apply translation. */
texture_matrix[2] += uv_translation;
return texture_matrix;
}
static float4x3 expand_4x2_mat(float4x2 strokemat)
{
float4x3 strokemat4x3 = float4x3(strokemat);
/*
* We need the diagonal of ones to start from the bottom right instead top left to properly
* apply the two matrices.
*
* i.e.
* # # # # # # # #
* We need # # # # Instead of # # # #
* 0 0 0 1 0 0 1 0
*
*/
strokemat4x3[2][2] = 0.0f;
strokemat4x3[3][2] = 1.0f;
return strokemat4x3;
}
Span<float4x2> Drawing::texture_matrices() const
{
this->runtime->curve_texture_matrices.ensure([&](Vector<float4x2> &r_data) {
const CurvesGeometry &curves = this->strokes();
const AttributeAccessor attributes = curves.attributes();
const VArray<float> uv_rotations = *attributes.lookup_or_default<float>(
"uv_rotation", AttrDomain::Curve, 0.0f);
const VArray<float2> uv_translations = *attributes.lookup_or_default<float2>(
"uv_translation", AttrDomain::Curve, float2(0.0f, 0.0f));
const VArray<float2> uv_scales = *attributes.lookup_or_default<float2>(
"uv_scale", AttrDomain::Curve, float2(1.0f, 1.0f));
const OffsetIndices<int> points_by_curve = curves.points_by_curve();
const Span<float3> positions = curves.positions();
const Span<float3> normals = this->curve_plane_normals();
r_data.reinitialize(curves.curves_num());
threading::parallel_for(curves.curves_range(), 512, [&](const IndexRange range) {
for (const int curve_i : range) {
const IndexRange points = points_by_curve[curve_i];
const float3 normal = normals[curve_i];
const float4x2 strokemat = get_local_to_stroke_matrix(positions.slice(points), normal);
const float3x2 texture_matrix = get_stroke_to_texture_matrix(
uv_rotations[curve_i], uv_translations[curve_i], uv_scales[curve_i]);
const float4x2 texspace = texture_matrix * expand_4x2_mat(strokemat);
r_data[curve_i] = texspace;
}
});
});
return this->runtime->curve_texture_matrices.data().as_span();
}
void Drawing::set_texture_matrices(Span<float4x2> matrices, const IndexMask &selection)
{
using namespace blender::math;
CurvesGeometry &curves = this->strokes_for_write();
MutableAttributeAccessor attributes = curves.attributes_for_write();
SpanAttributeWriter<float> uv_rotations = attributes.lookup_or_add_for_write_span<float>(
"uv_rotation", AttrDomain::Curve);
SpanAttributeWriter<float2> uv_translations = attributes.lookup_or_add_for_write_span<float2>(
"uv_translation", AttrDomain::Curve);
SpanAttributeWriter<float2> uv_scales = attributes.lookup_or_add_for_write_span<float2>(
"uv_scale",
AttrDomain::Curve,
AttributeInitVArray(VArray<float2>::ForSingle(float2(1.0f, 1.0f), curves.curves_num())));
const OffsetIndices<int> points_by_curve = curves.points_by_curve();
const Span<float3> positions = curves.positions();
const Span<float3> normals = this->curve_plane_normals();
selection.foreach_index(GrainSize(256), [&](const int64_t curve_i, const int64_t pos) {
const IndexRange points = points_by_curve[curve_i];
const float3 normal = normals[curve_i];
const float4x2 strokemat = get_local_to_stroke_matrix(positions.slice(points), normal);
const float4x2 texspace = matrices[pos];
/* We do the computation using doubles to avoid numerical precision errors. */
const double4x3 strokemat4x3 = double4x3(expand_4x2_mat(strokemat));
/*
* We want to solve for `texture_matrix` in the equation: `texspace = texture_matrix *
* strokemat4x3` Because these matrices are not square we can not use a standard inverse.
*
* Our problem has the form of: `X = A * Y`
* We can solve for `A` using: `A = X * B`
*
* Where `B` is the Right-sided inverse or Moore-Penrose pseudo inverse.
* Calculated as:
*
* |--------------------------|
* | B = T(Y) * (Y * T(Y))^-1 |
* |--------------------------|
*
* And `T()` is transpose and `()^-1` is the inverse.
*/
const double3x4 transpose_strokemat = transpose(strokemat4x3);
const double3x4 right_inverse = transpose_strokemat *
invert(strokemat4x3 * transpose_strokemat);
const float3x2 texture_matrix = float3x2(double4x2(texspace) * right_inverse);
/* Solve for translation, the translation is simply the origin. */
const float2 uv_translation = texture_matrix[2];
/* Solve rotation, the angle of the `u` basis is the rotation. */
const float uv_rotation = atan2(texture_matrix[0][1], texture_matrix[0][0]);
/* Calculate the determinant to check if the `v` scale is negative. */
const float det = determinant(float2x2(texture_matrix));
/* Solve scale, scaling is the only transformation that changes the length, so scale factor
* is simply the length. And flip the sign of `v` if the determinant is negative. */
const float2 uv_scale = safe_rcp(
float2(length(texture_matrix[0]), sign(det) * length(texture_matrix[1])));
uv_rotations.span[curve_i] = uv_rotation;
uv_translations.span[curve_i] = uv_translation;
uv_scales.span[curve_i] = uv_scale;
});
uv_rotations.finish();
uv_translations.finish();
uv_scales.finish();
this->tag_texture_matrices_changed();
}
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, AttrDomain::Point, 0.01f);
}
MutableSpan<float> Drawing::radii_for_write()
{
return get_mutable_attribute<float>(
this->strokes_for_write(), AttrDomain::Point, ATTR_RADIUS, 0.01f);
}
VArray<float> Drawing::opacities() const
{
return *this->strokes().attributes().lookup_or_default<float>(
ATTR_OPACITY, AttrDomain::Point, 1.0f);
}
MutableSpan<float> Drawing::opacities_for_write()
{
return get_mutable_attribute<float>(
this->strokes_for_write(), AttrDomain::Point, ATTR_OPACITY, 1.0f);
}
VArray<ColorGeometry4f> Drawing::vertex_colors() const
{
return *this->strokes().attributes().lookup_or_default<ColorGeometry4f>(
ATTR_VERTEX_COLOR, AttrDomain::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(),
AttrDomain::Point,
ATTR_VERTEX_COLOR,
ColorGeometry4f(0.0f, 0.0f, 0.0f, 0.0f));
}
VArray<ColorGeometry4f> Drawing::fill_colors() const
{
return *this->strokes().attributes().lookup_or_default<ColorGeometry4f>(
ATTR_FILL_COLOR, AttrDomain::Curve, ColorGeometry4f(0.0f, 0.0f, 0.0f, 0.0f));
}
MutableSpan<ColorGeometry4f> Drawing::fill_colors_for_write()
{
return get_mutable_attribute<ColorGeometry4f>(this->strokes_for_write(),
AttrDomain::Curve,
ATTR_FILL_COLOR,
ColorGeometry4f(0.0f, 0.0f, 0.0f, 0.0f));
}
void Drawing::tag_texture_matrices_changed()
{
this->runtime->curve_texture_matrices.tag_dirty();
}
void Drawing::tag_positions_changed()
{
this->strokes_for_write().tag_positions_changed();
this->runtime->triangles_cache.tag_dirty();
this->runtime->curve_plane_normals_cache.tag_dirty();
this->tag_texture_matrices_changed();
}
void Drawing::tag_topology_changed()
{
this->tag_positions_changed();
this->strokes_for_write().tag_topology_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() = default;
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);
}
const LayerGroup *TreeNode::parent_group() const
{
return (this->parent) ? &this->parent->wrap() : nullptr;
}
LayerGroup *TreeNode::parent_group()
{
return (this->parent) ? &this->parent->wrap() : nullptr;
}
const TreeNode *TreeNode::parent_node() const
{
return this->parent_group() ? &this->parent->wrap().as_node() : nullptr;
}
TreeNode *TreeNode::parent_node()
{
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_null(name.c_str());
}
LayerMask::LayerMask(const LayerMask &other) : LayerMask()
{
this->layer_name = BLI_strdup_null(other.layer_name);
this->flag = other.flag;
}
LayerMask::~LayerMask()
{
if (this->layer_name) {
MEM_freeN(this->layer_name);
}
}
void LayerRuntime::clear()
{
frames_.clear_and_shrink();
sorted_keys_cache_.tag_dirty();
masks_.clear_and_shrink();
trans_data_ = {};
}
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->blend_mode = GP_LAYER_BLEND_NONE;
this->opacity = 1.0f;
this->parent = nullptr;
this->parsubstr = nullptr;
unit_m4(this->parentinv);
zero_v3(this->translation);
zero_v3(this->rotation);
copy_v3_fl(this->scale, 1.0f);
this->viewlayername = nullptr;
BLI_listbase_clear(&this->masks);
this->active_mask_index = 0;
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());
LISTBASE_FOREACH (GreasePencilLayerMask *, other_mask, &other.masks) {
LayerMask *new_mask = MEM_new<LayerMask>(__func__, *reinterpret_cast<LayerMask *>(other_mask));
BLI_addtail(&this->masks, reinterpret_cast<GreasePencilLayerMask *>(new_mask));
}
this->active_mask_index = other.active_mask_index;
this->blend_mode = other.blend_mode;
this->opacity = other.opacity;
this->parent = other.parent;
this->set_parent_bone_name(other.parsubstr);
copy_m4_m4(this->parentinv, other.parentinv);
copy_v3_v3(this->translation, other.translation);
copy_v3_v3(this->rotation, other.rotation);
copy_v3_v3(this->scale, other.scale);
this->set_view_layer_name(other.viewlayername);
/* NOTE: We do not duplicate the frame storage since it is only needed for writing to file. */
this->runtime->frames_ = other.runtime->frames_;
this->runtime->sorted_keys_cache_ = other.runtime->sorted_keys_cache_;
/* Tag the frames map, so the frame storage is recreated once the DNA is saved. */
this->tag_frames_map_changed();
/* 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_delete(reinterpret_cast<LayerMask *>(mask));
}
BLI_listbase_clear(&this->masks);
MEM_SAFE_FREE(this->parsubstr);
MEM_SAFE_FREE(this->viewlayername);
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_end_frames_in_range(
Layer::SortedKeysIterator begin, Layer::SortedKeysIterator end)
{
Layer::SortedKeysIterator next_it = begin;
while (next_it != end && this->frames().lookup(*next_it).is_end()) {
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 FramesMapKeyT frame_number)
{
if (!this->frames().contains(frame_number)) {
GreasePencilFrame frame{};
this->frames_for_write().add_new(frame_number, frame);
this->tag_frames_map_keys_changed();
return this->frames_for_write().lookup_ptr(frame_number);
}
/* Overwrite end-frames. */
if (this->frames().lookup(frame_number).is_end()) {
GreasePencilFrame frame{};
this->frames_for_write().add_overwrite(frame_number, frame);
this->tag_frames_map_changed();
return this->frames_for_write().lookup_ptr(frame_number);
}
return nullptr;
}
GreasePencilFrame *Layer::add_frame(const FramesMapKeyT key, const int duration)
{
BLI_assert(duration >= 0);
GreasePencilFrame *frame = this->add_frame_internal(key);
if (frame == nullptr) {
return nullptr;
}
Span<FramesMapKeyT> sorted_keys = this->sorted_keys();
const FramesMapKeyT 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_end_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 an end-frame. */
if (next_key_it == sorted_keys.end() || *next_key_it > end_key) {
this->frames_for_write().add_new(end_key, GreasePencilFrame::end());
this->tag_frames_map_keys_changed();
}
return frame;
}
bool Layer::remove_frame(const FramesMapKeyT 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<FramesMapKeyT> 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_end_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 an
* end frame, we cannot just delete the frame. We need to replace it with an end frame. */
if (!prev_frame.is_implicit_hold() && !prev_frame.is_end()) {
this->frames_for_write().lookup(key) = GreasePencilFrame::end();
this->tag_frames_map_changed();
/* Since the original frame was replaced with an end 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<FramesMapKeyT> Layer::sorted_keys() const
{
this->runtime->sorted_keys_cache_.ensure([&](Vector<FramesMapKeyT> &r_data) {
r_data.reinitialize(this->frames().size());
int i = 0;
for (const FramesMapKeyT key : this->frames().keys()) {
r_data[i++] = key;
}
std::sort(r_data.begin(), r_data.end());
});
return this->runtime->sorted_keys_cache_.data();
}
Layer::SortedKeysIterator Layer::sorted_keys_iterator_at(const int frame_number) const
{
Span<int> sorted_keys = this->sorted_keys();
/* No keyframes, return nullptr. */
if (sorted_keys.is_empty()) {
return nullptr;
}
/* Before the first frame, return nullptr. */
if (frame_number < sorted_keys.first()) {
return nullptr;
}
/* After or at the last frame, return iterator to last. */
if (frame_number >= sorted_keys.last()) {
return std::prev(sorted_keys.end());
}
/* Search for the frame. std::upper_bound will get the frame just after. */
SortedKeysIterator it = std::upper_bound(sorted_keys.begin(), sorted_keys.end(), frame_number);
if (it == sorted_keys.end()) {
return nullptr;
}
return std::prev(it);
}
std::optional<FramesMapKeyT> Layer::frame_key_at(const int frame_number) const
{
SortedKeysIterator it = this->sorted_keys_iterator_at(frame_number);
if (it == nullptr) {
return {};
}
return *it;
}
std::optional<int> Layer::start_frame_at(int frame_number) const
{
const std::optional<FramesMapKeyT> frame_key = this->frame_key_at(frame_number);
/* Return the frame number only if the frame key exists and if it's not an end frame. */
if (frame_key && !this->frames().lookup_ptr(*frame_key)->is_end()) {
return *frame_key;
}
return {};
}
int Layer::sorted_keys_index_at(const int frame_number) const
{
SortedKeysIterator it = this->sorted_keys_iterator_at(frame_number);
if (it == nullptr) {
return -1;
}
return std::distance(this->sorted_keys().begin(), it);
}
const GreasePencilFrame *Layer::frame_at(const int frame_number) const
{
const GreasePencilFrame *frame_ptr = [&]() -> const GreasePencilFrame * {
if (const GreasePencilFrame *frame = this->frames().lookup_ptr(frame_number)) {
return frame;
}
/* Look for a keyframe that starts before `frame_number` and ends after `frame_number`. */
const std::optional<FramesMapKeyT> frame_key = this->frame_key_at(frame_number);
if (!frame_key) {
return nullptr;
}
return this->frames().lookup_ptr(*frame_key);
}();
if (frame_ptr == nullptr || frame_ptr->is_end()) {
/* Not a valid frame. */
return nullptr;
}
return frame_ptr;
}
GreasePencilFrame *Layer::frame_at(const int frame_number)
{
GreasePencilFrame *frame_ptr = [&]() -> GreasePencilFrame * {
if (GreasePencilFrame *frame = this->frames_for_write().lookup_ptr(frame_number)) {
return frame;
}
/* Look for a keyframe that starts before `frame_number`. */
const std::optional<FramesMapKeyT> frame_key = this->frame_key_at(frame_number);
if (!frame_key) {
return nullptr;
}
return this->frames_for_write().lookup_ptr(*frame_key);
}();
if (frame_ptr == nullptr || frame_ptr->is_end()) {
/* Not a valid frame. */
return nullptr;
}
return frame_ptr;
}
int Layer::drawing_index_at(const int frame_number) const
{
const GreasePencilFrame *frame = frame_at(frame_number);
return (frame != nullptr) ? frame->drawing_index : -1;
}
bool Layer::has_drawing_at(const int frame_number) const
{
return frame_at(frame_number) != nullptr;
}
int Layer::get_frame_duration_at(const int frame_number) const
{
SortedKeysIterator it = this->sorted_keys_iterator_at(frame_number);
if (it == nullptr) {
return -1;
}
const FramesMapKeyT key = *it;
const GreasePencilFrame *frame = this->frames().lookup_ptr(key);
/* For frames that are implicitly held, we return a duration of 0. */
if (frame->is_implicit_hold()) {
return 0;
}
/* Frame is an end frame, so there is no keyframe at `frame_number`. */
if (frame->is_end()) {
return -1;
}
/* Compute the distance in frames between this key and the next key. */
const int next_frame_number = *(std::next(it));
return math::distance(key, next_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();
}
void Layer::prepare_for_dna_write()
{
/* Re-create the frames storage only if it was tagged dirty. */
if ((frames_storage.flag & GP_LAYER_FRAMES_STORAGE_DIRTY) == 0) {
return;
}
MEM_SAFE_FREE(frames_storage.keys);
MEM_SAFE_FREE(frames_storage.values);
const size_t frames_num = size_t(frames().size());
frames_storage.num = int(frames_num);
frames_storage.keys = MEM_cnew_array<int>(frames_num, __func__);
frames_storage.values = MEM_cnew_array<GreasePencilFrame>(frames_num, __func__);
const Span<int> sorted_keys_data = sorted_keys();
for (const int64_t i : sorted_keys_data.index_range()) {
frames_storage.keys[i] = sorted_keys_data[i];
frames_storage.values[i] = frames().lookup(sorted_keys_data[i]);
}
/* Reset the flag. */
frames_storage.flag &= ~GP_LAYER_FRAMES_STORAGE_DIRTY;
}
void Layer::update_from_dna_read()
{
/* Re-create frames data in runtime map. */
/* NOTE: Avoid re-allocating runtime data to reduce 'false positive' change detections from
* memfile undo. */
if (runtime) {
runtime->clear();
}
else {
runtime = MEM_new<blender::bke::greasepencil::LayerRuntime>(__func__);
}
Map<int, GreasePencilFrame> &frames = frames_for_write();
for (int i = 0; i < frames_storage.num; i++) {
frames.add_new(frames_storage.keys[i], frames_storage.values[i]);
}
}
float4x4 Layer::to_world_space(const Object &object) const
{
if (this->parent == nullptr) {
return object.object_to_world() * this->local_transform();
}
const Object &parent = *this->parent;
return this->parent_to_world(parent) * this->parent_inverse() * this->local_transform();
}
float4x4 Layer::to_object_space(const Object &object) const
{
if (this->parent == nullptr) {
return this->local_transform();
}
const Object &parent = *this->parent;
return object.world_to_object() * this->parent_to_world(parent) * this->parent_inverse() *
this->local_transform();
}
StringRefNull Layer::parent_bone_name() const
{
return (this->parsubstr != nullptr) ? StringRefNull(this->parsubstr) : StringRefNull();
}
void Layer::set_parent_bone_name(const char *new_name)
{
if (this->parsubstr != nullptr) {
MEM_freeN(this->parsubstr);
}
this->parsubstr = BLI_strdup_null(new_name);
}
float4x4 Layer::parent_to_world(const Object &parent) const
{
const float4x4 &parent_object_to_world = parent.object_to_world();
if (parent.type == OB_ARMATURE && !this->parent_bone_name().is_empty()) {
if (bPoseChannel *channel = BKE_pose_channel_find_name(parent.pose,
this->parent_bone_name().c_str()))
{
return parent_object_to_world * float4x4_view(channel->pose_mat);
}
}
return parent_object_to_world;
}
float4x4 Layer::parent_inverse() const
{
return float4x4_view(this->parentinv);
}
float4x4 Layer::local_transform() const
{
return math::from_loc_rot_scale<float4x4, math::EulerXYZ>(
float3(this->translation), float3(this->rotation), float3(this->scale));
}
void Layer::set_local_transform(const float4x4 &transform)
{
math::to_loc_rot_scale_safe<true>(transform,
*reinterpret_cast<float3 *>(this->translation),
*reinterpret_cast<math::EulerXYZ *>(this->rotation),
*reinterpret_cast<float3 *>(this->scale));
}
StringRefNull Layer::view_layer_name() const
{
return (this->viewlayername != nullptr) ? StringRefNull(this->viewlayername) : StringRefNull();
}
void Layer::set_view_layer_name(const char *new_name)
{
if (this->viewlayername != nullptr) {
MEM_freeN(this->viewlayername);
}
this->viewlayername = BLI_strdup_null(new_name);
}
LayerGroup::LayerGroup()
{
new (&this->base) TreeNode(GP_LAYER_TREE_GROUP);
BLI_listbase_clear(&this->children);
this->color_tag = LAYERGROUP_COLOR_NONE;
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;
}
LayerGroup &LayerGroup::operator=(const LayerGroup &other)
{
if (this == &other) {
return *this;
}
this->~LayerGroup();
new (this) LayerGroup(other);
return *this;
}
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, const bool keep_children)
{
if (link.is_group() && !link.as_group().is_empty() && keep_children) {
if (BLI_findindex(&this->children, &link) == -1) {
return false;
}
/* Take ownership of the children of `link` by replacing the node with the listbase of its
* children. */
ListBase link_children = link.as_group().children;
GreasePencilLayerTreeNode *first = static_cast<GreasePencilLayerTreeNode *>(
link_children.first);
GreasePencilLayerTreeNode *last = static_cast<GreasePencilLayerTreeNode *>(link_children.last);
/* Rewrite the parent pointers. */
LISTBASE_FOREACH (GreasePencilLayerTreeNode *, child, &link_children) {
child->parent = this;
}
/* Update previous and/or next link(s). */
if (link.next != nullptr) {
link.next->prev = last;
last->next = link.next;
}
if (link.prev != nullptr) {
link.prev->next = first;
first->prev = link.prev;
}
/* Update first and/or last link(s). */
if (this->children.last == &link) {
this->children.last = last;
}
if (this->children.first == &link) {
this->children.first = first;
}
/* Listbase has been inserted in `this->children` we can clear the pointers in `link`. */
BLI_listbase_clear(&link.as_group().children);
link.parent = nullptr;
this->tag_nodes_cache_dirty();
return true;
}
else if (BLI_remlink_safe(&this->children, &link)) {
link.parent = nullptr;
this->tag_nodes_cache_dirty();
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();
}
}
void LayerGroup::prepare_for_dna_write()
{
LISTBASE_FOREACH (TreeNode *, child, &children) {
switch (child->type) {
case GP_LAYER_TREE_LEAF: {
child->as_layer().prepare_for_dna_write();
break;
}
case GP_LAYER_TREE_GROUP: {
child->as_group().prepare_for_dna_write();
break;
}
}
}
}
void LayerGroup::update_from_dna_read()
{
LISTBASE_FOREACH (TreeNode *, child, &children) {
switch (child->type) {
case GP_LAYER_TREE_LEAF: {
child->as_layer().update_from_dna_read();
break;
}
case GP_LAYER_TREE_GROUP: {
child->as_group().update_from_dna_read();
break;
}
}
}
}
} // namespace blender::bke::greasepencil
namespace blender::bke {
GreasePencilRuntime::GreasePencilRuntime() = default;
GreasePencilRuntime::~GreasePencilRuntime() = default;
std::optional<Span<float3>> GreasePencilDrawingEditHints::positions() const
{
if (!this->positions_data.has_value()) {
return std::nullopt;
}
const int points_num = this->drawing_orig->geometry.wrap().points_num();
return Span(static_cast<const float3 *>(this->positions_data.data), points_num);
}
std::optional<MutableSpan<float3>> GreasePencilDrawingEditHints::positions_for_write()
{
if (!this->positions_data.has_value()) {
return std::nullopt;
}
const int points_num = this->drawing_orig->geometry.wrap().points_num();
ImplicitSharingPtrAndData &data = this->positions_data;
if (data.sharing_info->is_mutable()) {
/* If the referenced component is already mutable, return it directly. */
data.sharing_info->tag_ensured_mutable();
}
else {
auto *new_sharing_info = new ImplicitSharedValue<Array<float3>>(*this->positions());
data.sharing_info = ImplicitSharingPtr<ImplicitSharingInfo>(new_sharing_info);
data.data = new_sharing_info->data.data();
}
return MutableSpan(const_cast<float3 *>(static_cast<const float3 *>(data.data)), points_num);
}
} // namespace blender::bke
/* ------------------------------------------------------------------- */
/** \name Grease Pencil kernel functions
* \{ */
bool BKE_grease_pencil_drawing_attribute_required(const GreasePencilDrawing * /*drawing*/,
const char *name)
{
return STREQ(name, ATTR_POSITION);
}
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;
}
void BKE_grease_pencil_nomain_to_grease_pencil(GreasePencil *grease_pencil_src,
GreasePencil *grease_pencil_dst)
{
using namespace blender;
using bke::greasepencil::Drawing;
using bke::greasepencil::DrawingReference;
/* Drawings. */
const int drawing_array_num = grease_pencil_src->drawing_array_num;
grease_pencil_dst->resize_drawings(drawing_array_num);
for (const int i : IndexRange(drawing_array_num)) {
if (grease_pencil_dst->drawing_array[i]) {
switch (grease_pencil_dst->drawing_array[i]->type) {
case GP_DRAWING:
MEM_delete(&reinterpret_cast<GreasePencilDrawing *>(grease_pencil_dst->drawing_array[i])
->wrap());
break;
case GP_DRAWING_REFERENCE:
MEM_delete(&reinterpret_cast<GreasePencilDrawingReference *>(
grease_pencil_dst->drawing_array[i])
->wrap());
break;
}
}
switch (grease_pencil_src->drawing_array[i]->type) {
case GP_DRAWING: {
const Drawing &src_drawing =
reinterpret_cast<GreasePencilDrawing *>(grease_pencil_src->drawing_array[i])->wrap();
grease_pencil_dst->drawing_array[i] = reinterpret_cast<GreasePencilDrawingBase *>(
MEM_new<Drawing>(__func__, src_drawing));
break;
}
case GP_DRAWING_REFERENCE:
const DrawingReference &src_drawing_ref = reinterpret_cast<GreasePencilDrawingReference *>(
grease_pencil_src->drawing_array[i])
->wrap();
grease_pencil_dst->drawing_array[i] = reinterpret_cast<GreasePencilDrawingBase *>(
MEM_new<DrawingReference>(__func__, src_drawing_ref));
break;
}
}
/* Layers. */
if (grease_pencil_dst->root_group_ptr) {
MEM_delete(&grease_pencil_dst->root_group());
}
grease_pencil_dst->root_group_ptr = MEM_new<bke::greasepencil::LayerGroup>(
__func__, grease_pencil_src->root_group_ptr->wrap());
BLI_assert(grease_pencil_src->layers().size() == grease_pencil_dst->layers().size());
CustomData_copy(&grease_pencil_src->layers_data,
&grease_pencil_dst->layers_data,
eCustomDataMask(CD_MASK_ALL),
grease_pencil_src->layers().size());
DEG_id_tag_update(&grease_pencil_dst->id, ID_RECALC_GEOMETRY);
BKE_id_free(nullptr, grease_pencil_src);
}
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 time modifiers.
* The time offset modifier can change what drawings are shown on the current frame. But it
* doesn't affect the drawings data. Modifiers that modify the drawings data are only evaluated
* for the current frame, so we run the time offset modifiers before all the other ones. */
ModifierData *tmd = md;
for (; tmd; tmd = tmd->next) {
const ModifierTypeInfo *mti = BKE_modifier_get_info(ModifierType(tmd->type));
if (!BKE_modifier_is_enabled(scene, tmd, required_mode) ||
ModifierType(tmd->type) != eModifierType_GreasePencilTime)
{
continue;
}
if (mti->modify_geometry_set != nullptr) {
mti->modify_geometry_set(tmd, &mectx, &geometry_set);
}
}
/* Evaluate drawing 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) ||
ModifierType(md->type) == eModifierType_GreasePencilTime)
{
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()) {
/* Output geometry set may be different from the input,
* set the frame again to ensure a correct value. */
geometry_set.get_grease_pencil()->runtime->eval_frame = int(DEG_get_ctime(depsgraph));
}
else {
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_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_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", AttrDomain::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", AttrDomain::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;
if (new_array_num == 0) {
MEM_freeN(*array);
*array = nullptr;
*num = 0;
return;
}
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::resize_drawings(const int new_num)
{
using namespace blender;
BLI_assert(new_num > 0);
const int prev_num = int(this->drawings().size());
if (new_num == prev_num) {
return;
}
if (new_num > prev_num) {
const int add_num = new_num - prev_num;
grow_array<GreasePencilDrawingBase *>(&this->drawing_array, &this->drawing_array_num, add_num);
}
else { /* if (new_num < prev_num) */
const int shrink_num = prev_num - new_num;
MutableSpan<GreasePencilDrawingBase *> old_drawings = this->drawings().drop_front(new_num);
for (const int64_t i : old_drawings.index_range()) {
if (old_drawings[i]) {
MEM_delete(old_drawings[i]);
}
}
shrink_array<GreasePencilDrawingBase *>(
&this->drawing_array, &this->drawing_array_num, shrink_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));
}
}
blender::bke::greasepencil::Drawing *GreasePencil::insert_frame(
blender::bke::greasepencil::Layer &layer,
const int frame_number,
const int duration,
const eBezTriple_KeyframeType keytype)
{
using namespace blender;
GreasePencilFrame *frame = layer.add_frame(frame_number, duration);
if (frame == nullptr) {
return nullptr;
}
this->add_empty_drawings(1);
frame->drawing_index = this->drawings().index_range().last();
frame->type = int8_t(keytype);
GreasePencilDrawingBase *drawing_base = this->drawings().last();
BLI_assert(drawing_base->type == GP_DRAWING);
GreasePencilDrawing *drawing = reinterpret_cast<GreasePencilDrawing *>(drawing_base);
return &drawing->wrap();
}
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 = layer.get_frame_duration_at(src_frame_number);
GreasePencilFrame *dst_frame = layer.add_frame(dst_frame_number, duration);
if (dst_frame == nullptr) {
return false;
}
dst_frame->drawing_index = do_instance ? src_frame.drawing_index : int(this->drawings().size());
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_end()) {
/* End 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;
}
void GreasePencil::add_layers_with_empty_drawings_for_eval(const int num)
{
using namespace blender;
using namespace blender::bke::greasepencil;
const int old_drawings_num = this->drawing_array_num;
this->add_empty_drawings(num);
for (const int i : IndexRange(num)) {
const int drawing_i = old_drawings_num + i;
Drawing &drawing = reinterpret_cast<GreasePencilDrawing *>(this->drawing(drawing_i))->wrap();
Layer &layer = this->add_layer(std::to_string(i));
GreasePencilFrame *frame = layer.add_frame(this->runtime->eval_frame);
BLI_assert(frame);
frame->drawing_index = drawing_i;
drawing.add_user();
}
}
void GreasePencil::remove_drawings_with_no_users()
{
using namespace blender;
using namespace blender::bke::greasepencil;
/* Compress the drawings array by finding unused drawings.
* In every step two indices are found:
* - The next unused drawing from the start
* - The last used drawing from the end
* These two drawings are then swapped. Rinse and repeat until both iterators meet somewhere in
* the middle. At this point the drawings array is fully compressed.
* Then the drawing indices in frame data are remapped. */
const MutableSpan<GreasePencilDrawingBase *> drawings = this->drawings();
if (drawings.is_empty()) {
return;
}
auto is_drawing_used = [&](const int drawing_index) {
GreasePencilDrawingBase *drawing_base = drawings[drawing_index];
/* NOTE: GreasePencilDrawingReference does not have a user count currently, but should
* eventually be counted like GreasePencilDrawing. */
if (drawing_base->type != GP_DRAWING) {
return false;
}
GreasePencilDrawing *drawing = reinterpret_cast<GreasePencilDrawing *>(drawing_base);
return drawing->wrap().has_users();
};
/* Index map to remap drawing indices in frame data.
* Index -1 indicates that the drawing has not been moved. */
constexpr const int unchanged_index = -1;
Array<int> drawing_index_map(drawings.size(), unchanged_index);
int first_unused_drawing = -1;
int last_used_drawing = drawings.size();
/* Advance head and tail iterators to the next unused/used drawing respectively.
* Returns true if an index pair was found that needs to be swapped. */
auto find_next_swap_index = [&]() -> bool {
do {
++first_unused_drawing;
} while (first_unused_drawing < last_used_drawing && is_drawing_used(first_unused_drawing));
do {
--last_used_drawing;
} while (first_unused_drawing < last_used_drawing && !is_drawing_used(last_used_drawing));
return first_unused_drawing < last_used_drawing;
};
while (find_next_swap_index()) {
/* Found two valid iterators, now swap drawings. */
std::swap(drawings[first_unused_drawing], drawings[last_used_drawing]);
drawing_index_map[last_used_drawing] = first_unused_drawing;
}
/* Tail range of unused drawings that can be removed. */
const IndexRange drawings_to_remove = (first_unused_drawing > 0) ?
drawings.index_range().drop_front(last_used_drawing +
1) :
drawings.index_range();
if (drawings_to_remove.is_empty()) {
return;
}
/* Free the unused drawings. */
for (const int i : drawings_to_remove) {
GreasePencilDrawingBase *unused_drawing_base = drawings[i];
switch (unused_drawing_base->type) {
case GP_DRAWING: {
auto *unused_drawing = reinterpret_cast<GreasePencilDrawing *>(unused_drawing_base);
MEM_delete(&unused_drawing->wrap());
break;
}
case GP_DRAWING_REFERENCE: {
auto *unused_drawing_ref = reinterpret_cast<GreasePencilDrawingReference *>(
unused_drawing_base);
MEM_delete(&unused_drawing_ref->wrap());
break;
}
}
}
shrink_array<GreasePencilDrawingBase *>(
&this->drawing_array, &this->drawing_array_num, drawings_to_remove.size());
/* Remap drawing indices in frame data. */
for (Layer *layer : this->layers_for_write()) {
for (auto [key, value] : layer->frames_for_write().items()) {
const int new_drawing_index = drawing_index_map[value.drawing_index];
if (new_drawing_index != unchanged_index) {
value.drawing_index = new_drawing_index;
layer->tag_frames_map_changed();
}
}
}
}
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> src_layer_frames_durations;
for (const auto [frame_number, frame] : layer.frames().items()) {
src_layer_frames_durations.add(frame_number, layer.get_frame_duration_at(frame_number));
}
/* Remove original frames for duplicates before inserting any frames.
* This has to be done early to avoid removing frames that may be inserted
* in place of the source frames. */
for (const auto src_frame_number : frame_number_destinations.keys()) {
if (!duplicate_frames.contains(src_frame_number)) {
/* User count not decremented here, the same frame is inserted again later. */
layer.remove_frame(src_frame_number);
}
}
auto get_source_frame = [&](const int frame_number) -> const GreasePencilFrame * {
if (const GreasePencilFrame *ptr = duplicate_frames.lookup_ptr(frame_number)) {
return ptr;
}
return layer_frames_copy.lookup_ptr(frame_number);
};
for (const auto [src_frame_number, dst_frame_number] : frame_number_destinations.items()) {
const GreasePencilFrame *src_frame = get_source_frame(src_frame_number);
if (!src_frame) {
continue;
}
const int duration = src_layer_frames_durations.lookup_default(src_frame_number, 0);
/* 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, 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
{
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_drawing_at(
const blender::bke::greasepencil::Layer &layer, const int frame_number)
{
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) {
/* TODO: Get reference drawing. */
return nullptr;
}
GreasePencilDrawing *drawing = reinterpret_cast<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.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();
}
const blender::bke::greasepencil::Drawing *GreasePencil::get_eval_drawing(
const blender::bke::greasepencil::Layer &layer) const
{
return this->get_drawing_at(layer, this->runtime->eval_frame);
}
blender::bke::greasepencil::Drawing *GreasePencil::get_eval_drawing(
const blender::bke::greasepencil::Layer &layer)
{
return this->get_drawing_at(layer, this->runtime->eval_frame);
}
std::optional<blender::Bounds<blender::float3>> GreasePencil::bounds_min_max(const int frame) 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, frame)) {
const bke::CurvesGeometry &curves = drawing->strokes();
bounds = bounds::merge(bounds, curves.bounds_min_max());
}
}
return bounds;
}
std::optional<blender::Bounds<blender::float3>> GreasePencil::bounds_min_max_eval() const
{
return this->bounds_min_max(this->runtime->eval_frame);
}
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();
}
std::optional<int> GreasePencil::get_layer_index(
const blender::bke::greasepencil::Layer &layer) const
{
const int index = int(this->layers().first_index_try(&layer));
if (index == -1) {
return {};
}
return index;
}
const blender::bke::greasepencil::Layer *GreasePencil::get_active_layer() const
{
if (this->active_node == nullptr) {
return nullptr;
}
const blender::bke::greasepencil::TreeNode &active_node = *this->get_active_node();
if (!active_node.is_layer()) {
return nullptr;
}
return &active_node.as_layer();
}
blender::bke::greasepencil::Layer *GreasePencil::get_active_layer()
{
if (this->active_node == nullptr) {
return nullptr;
}
blender::bke::greasepencil::TreeNode &active_node = *this->get_active_node();
if (!active_node.is_layer()) {
return nullptr;
}
return &active_node.as_layer();
}
void GreasePencil::set_active_layer(blender::bke::greasepencil::Layer *layer)
{
this->active_node = reinterpret_cast<GreasePencilLayerTreeNode *>(&layer->as_node());
if (this->flag & GREASE_PENCIL_AUTOLOCK_LAYERS) {
this->autolock_inactive_layers();
}
}
bool GreasePencil::is_layer_active(const blender::bke::greasepencil::Layer *layer) const
{
if (layer == nullptr) {
return false;
}
return this->get_active_layer() == layer;
}
void GreasePencil::autolock_inactive_layers()
{
using namespace blender::bke::greasepencil;
for (Layer *layer : this->layers_for_write()) {
if (this->is_layer_active(layer)) {
layer->set_locked(false);
continue;
}
layer->set_locked(true);
}
}
const blender::bke::greasepencil::LayerGroup *GreasePencil::get_active_group() const
{
if (this->active_node == nullptr) {
return nullptr;
}
const blender::bke::greasepencil::TreeNode &active_node = *this->get_active_node();
if (!active_node.is_group()) {
return nullptr;
}
return &active_node.as_group();
}
blender::bke::greasepencil::LayerGroup *GreasePencil::get_active_group()
{
if (this->active_node == nullptr) {
return nullptr;
}
blender::bke::greasepencil::TreeNode &active_node = *this->get_active_node();
if (!active_node.is_group()) {
return nullptr;
}
return &active_node.as_group();
}
const blender::bke::greasepencil::TreeNode *GreasePencil::get_active_node() const
{
if (this->active_node == nullptr) {
return nullptr;
}
return &this->active_node->wrap();
}
blender::bke::greasepencil::TreeNode *GreasePencil::get_active_node()
{
if (this->active_node == nullptr) {
return nullptr;
}
return &this->active_node->wrap();
}
void GreasePencil::set_active_node(blender::bke::greasepencil::TreeNode *node)
{
this->active_node = reinterpret_cast<GreasePencilLayerTreeNode *>(node);
if (this->flag & GREASE_PENCIL_AUTOLOCK_LAYERS) {
this->autolock_inactive_layers();
}
}
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);
bke::greasepencil::Layer *new_layer = MEM_new<bke::greasepencil::Layer>(__func__, unique_name);
/* Hide masks by default. */
new_layer->base.flag |= GP_LAYER_TREE_NODE_HIDE_MASKS;
return root_group().add_node(new_layer->as_node()).as_layer();
}
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 = this->add_layer(name);
/* Hide masks by default. */
new_layer.base.flag |= GP_LAYER_TREE_NODE_HIDE_MASKS;
move_node_into(new_layer.as_node(), parent_group);
return new_layer;
}
blender::bke::greasepencil::Layer &GreasePencil::duplicate_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 = MEM_new<bke::greasepencil::Layer>(__func__,
duplicate_layer);
root_group().add_node(new_layer->as_node());
this->update_drawing_users_for_layer(*new_layer);
new_layer->set_name(unique_name);
return *new_layer;
}
blender::bke::greasepencil::Layer &GreasePencil::duplicate_layer(
blender::bke::greasepencil::LayerGroup &parent_group,
const blender::bke::greasepencil::Layer &duplicate_layer)
{
using namespace blender;
bke::greasepencil::Layer &new_layer = this->duplicate_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);
bke::greasepencil::LayerGroup *new_group = MEM_new<bke::greasepencil::LayerGroup>(__func__,
unique_name);
/* Hide masks by default. */
new_group->base.flag |= GP_LAYER_TREE_NODE_HIDE_MASKS;
return parent_group.add_node(new_group->as_node()).as_group();
}
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 int index = *this->get_layer_index(node->as_layer());
return blender::IndexMask::from_indices(blender::Span<int>{index}, memory);
}
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 {};
}
static GreasePencilModifierInfluenceData *influence_data_from_modifier(ModifierData *md)
{
switch (md->type) {
case eModifierType_GreasePencilArmature: {
auto *amd = reinterpret_cast<GreasePencilArmatureModifierData *>(md);
return &amd->influence;
}
case eModifierType_GreasePencilArray: {
auto *mmd = reinterpret_cast<GreasePencilArrayModifierData *>(md);
return &mmd->influence;
}
case eModifierType_GreasePencilBuild: {
auto *bmd = reinterpret_cast<GreasePencilBuildModifierData *>(md);
return &bmd->influence;
}
case eModifierType_GreasePencilColor: {
auto *cmd = reinterpret_cast<GreasePencilColorModifierData *>(md);
return &cmd->influence;
}
case eModifierType_GreasePencilDash: {
auto *dmd = reinterpret_cast<GreasePencilDashModifierData *>(md);
return &dmd->influence;
}
case eModifierType_GreasePencilEnvelope: {
auto *emd = reinterpret_cast<GreasePencilEnvelopeModifierData *>(md);
return &emd->influence;
}
case eModifierType_GreasePencilHook: {
auto *hmd = reinterpret_cast<GreasePencilHookModifierData *>(md);
return &hmd->influence;
}
case eModifierType_GreasePencilLattice: {
auto *lmd = reinterpret_cast<GreasePencilLatticeModifierData *>(md);
return &lmd->influence;
}
case eModifierType_GreasePencilLength: {
auto *lmd = reinterpret_cast<GreasePencilLengthModifierData *>(md);
return &lmd->influence;
}
case eModifierType_GreasePencilMirror: {
auto *mmd = reinterpret_cast<GreasePencilMirrorModifierData *>(md);
return &mmd->influence;
}
case eModifierType_GreasePencilMultiply: {
auto *mmd = reinterpret_cast<GreasePencilMultiModifierData *>(md);
return &mmd->influence;
}
case eModifierType_GreasePencilNoise: {
auto *nmd = reinterpret_cast<GreasePencilNoiseModifierData *>(md);
return &nmd->influence;
}
case eModifierType_GreasePencilOffset: {
auto *omd = reinterpret_cast<GreasePencilOffsetModifierData *>(md);
return &omd->influence;
}
case eModifierType_GreasePencilOpacity: {
auto *omd = reinterpret_cast<GreasePencilOpacityModifierData *>(md);
return &omd->influence;
}
case eModifierType_GreasePencilOutline: {
auto *omd = reinterpret_cast<GreasePencilOutlineModifierData *>(md);
return &omd->influence;
}
case eModifierType_GreasePencilShrinkwrap: {
auto *smd = reinterpret_cast<GreasePencilShrinkwrapModifierData *>(md);
return &smd->influence;
}
case eModifierType_GreasePencilSimplify: {
auto *smd = reinterpret_cast<GreasePencilSimplifyModifierData *>(md);
return &smd->influence;
}
case eModifierType_GreasePencilSmooth: {
auto *smd = reinterpret_cast<GreasePencilSmoothModifierData *>(md);
return &smd->influence;
}
case eModifierType_GreasePencilSubdiv: {
auto *smd = reinterpret_cast<GreasePencilSubdivModifierData *>(md);
return &smd->influence;
}
case eModifierType_GreasePencilTexture: {
auto *tmd = reinterpret_cast<GreasePencilTextureModifierData *>(md);
return &tmd->influence;
}
case eModifierType_GreasePencilThickness: {
auto *tmd = reinterpret_cast<GreasePencilThickModifierData *>(md);
return &tmd->influence;
}
case eModifierType_GreasePencilTime: {
auto *tmd = reinterpret_cast<GreasePencilTimeModifierData *>(md);
return &tmd->influence;
}
case eModifierType_GreasePencilTint: {
auto *tmd = reinterpret_cast<GreasePencilTintModifierData *>(md);
return &tmd->influence;
}
case eModifierType_GreasePencilWeightAngle: {
auto *wmd = reinterpret_cast<GreasePencilWeightAngleModifierData *>(md);
return &wmd->influence;
}
case eModifierType_GreasePencilWeightProximity: {
auto *wmd = reinterpret_cast<GreasePencilWeightProximityModifierData *>(md);
return &wmd->influence;
}
case eModifierType_GreasePencilLineart:
ATTR_FALLTHROUGH;
default:
return nullptr;
}
return nullptr;
}
void GreasePencil::rename_node(Main &bmain,
blender::bke::greasepencil::TreeNode &node,
blender::StringRefNull new_name)
{
using namespace blender;
if (node.name() == new_name) {
return;
}
/* Rename the node. */
std::string old_name = node.name();
if (node.is_layer()) {
node.set_name(unique_layer_name(*this, new_name));
}
else if (node.is_group()) {
node.set_name(unique_layer_group_name(*this, new_name));
}
/* Update layer name dependencies. */
if (node.is_layer()) {
BKE_animdata_fix_paths_rename_all(&this->id, "layers", old_name.c_str(), node.name().c_str());
/* Update names in layer masks. */
for (bke::greasepencil::Layer *layer : this->layers_for_write()) {
LISTBASE_FOREACH (GreasePencilLayerMask *, mask, &layer->masks) {
if (STREQ(mask->layer_name, old_name.c_str())) {
mask->layer_name = BLI_strdup(node.name().c_str());
}
}
}
}
/* Update name dependencies outside of the ID. */
LISTBASE_FOREACH (Object *, object, &bmain.objects) {
if (object->data != this) {
continue;
}
/* Update the layer name of the influence data of the modifiers. */
LISTBASE_FOREACH (ModifierData *, md, &object->modifiers) {
char *dst_layer_name = nullptr;
size_t dst_layer_name_len = 0;
/* LineArt doesn't use the `GreasePencilModifierInfluenceData` struct. */
if (md->type == eModifierType_GreasePencilLineart) {
auto *lmd = reinterpret_cast<GreasePencilLineartModifierData *>(md);
dst_layer_name = lmd->target_layer;
dst_layer_name_len = sizeof(lmd->target_layer);
}
else if (GreasePencilModifierInfluenceData *influence_data = influence_data_from_modifier(
md))
{
dst_layer_name = influence_data->layer_name;
dst_layer_name_len = sizeof(influence_data->layer_name);
}
if (dst_layer_name && STREQ(dst_layer_name, old_name.c_str())) {
BLI_strncpy(dst_layer_name, node.name().c_str(), dst_layer_name_len);
}
}
}
}
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;
}
static void update_active_node(GreasePencil &grease_pencil,
const blender::bke::greasepencil::TreeNode &node)
{
using namespace blender::bke::greasepencil;
if (grease_pencil.get_active_node() == nullptr) {
return;
}
TreeNode &active_node = *grease_pencil.get_active_node();
if (&active_node != &node) {
return;
}
if (node.is_group()) {
grease_pencil.set_active_layer(nullptr);
return;
}
Span<Layer *> layers = grease_pencil.layers_for_write();
/* If there is no other layer available, unset the active layer. */
if (layers.size() == 1) {
grease_pencil.set_active_layer(nullptr);
}
else {
/* Make the layer below active (if possible). */
if (&active_node.as_layer() == layers.first()) {
grease_pencil.set_active_layer(layers[1]);
}
else {
const int active_index = *grease_pencil.get_layer_index(active_node.as_layer());
grease_pencil.set_active_layer(layers[active_index - 1]);
}
}
}
void GreasePencil::remove_layer(blender::bke::greasepencil::Layer &layer)
{
using namespace blender::bke::greasepencil;
/* If the layer is active, update the active layer. */
update_active_node(*this, layer.as_node());
/* Remove all the layer attributes and shrink the `CustomData`. */
const int layer_index = *this->get_layer_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 (const GreasePencilFrame frame : layer.frames().values()) {
GreasePencilDrawingBase *drawing_base = this->drawing(frame.drawing_index);
if (drawing_base->type != GP_DRAWING) {
/* TODO: Remove drawing reference. */
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::remove_group(blender::bke::greasepencil::LayerGroup &group,
const bool keep_children)
{
using namespace blender::bke::greasepencil;
/* If the group is active, update the active layer. */
update_active_node(*this, group.as_node());
if (!keep_children) {
/* Recursively remove sub groups. */
for (LayerGroup *sub_group : group.groups_for_write()) {
this->remove_group(*sub_group, false);
}
/* Remove all the layers. */
for (Layer *layer : group.layers_for_write()) {
this->remove_layer(*layer);
}
}
/* Unlink the group. Keep the children if we want to keep them. */
group.as_node().parent_group()->unlink_node(group.as_node(), keep_children);
/* Delete the group. */
MEM_delete(&group);
}
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,
grease_pencil.drawing_array_num,
reinterpret_cast<void **>(&grease_pencil.drawing_array));
for (int i = 0; i < grease_pencil.drawing_array_num; i++) {
BLO_read_struct(reader, GreasePencilDrawingBase, &grease_pencil.drawing_array[i]);
GreasePencilDrawingBase *drawing_base = grease_pencil.drawing_array[i];
switch (GreasePencilDrawingType(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: {
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 (GreasePencilDrawingType(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) {
BLI_assert(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 (GreasePencilDrawingType(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_string(reader, &node->base.name);
node->base.parent = parent;
BLO_read_string(reader, &node->parsubstr);
BLO_read_string(reader, &node->viewlayername);
/* Read frames storage. */
BLO_read_int32_array(reader, node->frames_storage.num, &node->frames_storage.keys);
BLO_read_struct_array(
reader, GreasePencilFrame, node->frames_storage.num, &node->frames_storage.values);
/* Read layer masks. */
BLO_read_struct_list(reader, GreasePencilLayerMask, &node->masks);
LISTBASE_FOREACH (GreasePencilLayerMask *, mask, &node->masks) {
BLO_read_string(reader, &mask->layer_name);
}
/* NOTE: Ideally this should be cleared on write, to reduce false 'changes' detection in memfile
* undo system. This is not easily doable currently though, since modifying to actual data during
* write is not an option (a shallow copy of the #Layer data would be needed then). */
node->runtime = nullptr;
node->wrap().update_from_dna_read();
}
static void read_layer_tree_group(BlendDataReader *reader,
GreasePencilLayerTreeGroup *node,
GreasePencilLayerTreeGroup *parent)
{
BLO_read_string(reader, &node->base.name);
node->base.parent = parent;
/* Read list of children. */
BLO_read_struct_list(reader, GreasePencilLayerTreeNode, &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_struct(reader, GreasePencilLayerTreeGroup, &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.set_active_node(nullptr);
return;
}
/* Read active layer. */
BLO_read_struct(reader, GreasePencilLayerTreeNode, &grease_pencil.active_node);
read_layer_tree_group(reader, grease_pencil.root_group_ptr, nullptr);
grease_pencil.root_group_ptr->wrap().update_from_dna_read();
}
static void write_layer(BlendWriter *writer, GreasePencilLayer *node)
{
BLO_write_struct(writer, GreasePencilLayer, node);
BLO_write_string(writer, node->base.name);
BLO_write_string(writer, node->parsubstr);
BLO_write_string(writer, node->viewlayername);
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)
{
grease_pencil.root_group_ptr->wrap().prepare_for_dna_write();
write_layer_tree_group(writer, grease_pencil.root_group_ptr);
}