Initial Grease Pencil 3.0 stage #106848

Merged
Falk David merged 224 commits from filedescriptor/blender:grease-pencil-v3 into main 2023-05-30 11:14:22 +02:00
3 changed files with 127 additions and 3 deletions
Showing only changes of commit db356e6c57 - Show all commits

View File

@ -441,10 +441,10 @@ blender::Span<blender::bke::StrokePoint> GreasePencilDrawing::stroke_buffer()
/* GreasePencil API */
static void grease_pencil_grow_drawing_array_by(GreasePencil &self, const int added_capacity)
static void grease_pencil_grow_drawing_array_by(GreasePencil &self, const int add_capacity)
{
BLI_assert(added_capacity > 0);
const int new_drawing_array_size = self.drawing_array_size + added_capacity;
BLI_assert(add_capacity > 0);
const int new_drawing_array_size = self.drawing_array_size + add_capacity;
GreasePencilDrawingOrReference **new_drawing_array =
reinterpret_cast<GreasePencilDrawingOrReference **>(
MEM_cnew_array<GreasePencilDrawingOrReference *>(new_drawing_array_size, __func__));
@ -456,6 +456,21 @@ static void grease_pencil_grow_drawing_array_by(GreasePencil &self, const int ad
self.drawing_array_size = new_drawing_array_size;
}
static void grease_pencil_shrink_drawing_array_by(GreasePencil &self, const int remove_capacity)
{
BLI_assert(remove_capacity > 0);
const int new_drawing_array_size = self.drawing_array_size - remove_capacity;
GreasePencilDrawingOrReference **new_drawing_array =
reinterpret_cast<GreasePencilDrawingOrReference **>(
MEM_cnew_array<GreasePencilDrawingOrReference *>(new_drawing_array_size, __func__));
blender::uninitialized_move_n(self.drawing_array, new_drawing_array_size, new_drawing_array);
MEM_freeN(self.drawing_array);
self.drawing_array = new_drawing_array;
self.drawing_array_size = new_drawing_array_size;
}
blender::Span<GreasePencilDrawingOrReference *> GreasePencil::drawings() const
{
return blender::Span<GreasePencilDrawingOrReference *>{this->drawing_array,
@ -479,9 +494,76 @@ void GreasePencil::add_empty_drawings(int n)
for (const int i : IndexRange(new_drawings.size())) {
new_drawings[i] = reinterpret_cast<GreasePencilDrawingOrReference *>(
MEM_new<GreasePencilDrawing>(__func__));
GreasePencilDrawing *drawing = reinterpret_cast<GreasePencilDrawing *>(new_drawings[i]);
new (&drawing->geometry) bke::CurvesGeometry();
}
}
void GreasePencil::remove_drawing(int index_to_remove)
{
using namespace blender::bke::gpencil;
/* In order to not change the indices of the drawings, we do the following to the drawing to be
* removed:
* - If the drawing (A) is not the last one:
* 1.1) Find any frames in the layers that reference the last drawing (B) and point them to
* A's index.
* 1.2) Swap drawing A with drawing B.
* 2) Destroy A and shrink the array by one.
* 3) Remove any frames in the layers that reference the A's index.
*/
BLI_assert(this->drawing_array_size > 0);
BLI_assert(index_to_remove >= 0 && index_to_remove < this->drawing_array_size);
/* Move the drawing that should be removed to the last index. */
const int last_drawing_index = this->drawing_array_size - 1;
if (index_to_remove != last_drawing_index) {
this->root_group().foreach_layer_pre_order(
[last_drawing_index, index_to_remove](Layer &layer) {
blender::Map<int, int> &frames = layer.frames_for_write();
for (auto item : frames.items()) {
if (item.value == last_drawing_index) {
item.value = index_to_remove;
}
else if (item.value == index_to_remove) {
item.value = last_drawing_index;
}
}
});
std::swap(this->drawings_for_write()[index_to_remove],
this->drawings_for_write()[last_drawing_index]);
}
/* Delete the last drawing. */
GreasePencilDrawingOrReference *drawing_or_ref_to_remove =
this->drawings_for_write()[last_drawing_index];
switch (drawing_or_ref_to_remove->type) {
case GREASE_PENCIL_DRAWING: {
GreasePencilDrawing *drawing_to_remove = reinterpret_cast<GreasePencilDrawing *>(
drawing_or_ref_to_remove);
drawing_to_remove->geometry.wrap().~CurvesGeometry();
MEM_delete(drawing_to_remove->runtime);
drawing_to_remove->runtime = nullptr;
MEM_freeN(drawing_to_remove);
break;
}
case GREASE_PENCIL_DRAWING_REFERENCE: {
GreasePencilDrawingReference *drawing_reference_to_remove =
reinterpret_cast<GreasePencilDrawingReference *>(drawing_or_ref_to_remove);
MEM_freeN(drawing_reference_to_remove);
break;
}
}
/* Remove any frame that points to the last drawing. */
this->root_group().foreach_layer_pre_order([last_drawing_index](Layer &layer) {
blender::Map<int, int> &frames = layer.frames_for_write();
frames.remove_if([last_drawing_index](auto item) { return item.value == last_drawing_index; });
});
/* Shrink drawing array. */
grease_pencil_shrink_drawing_array_by(*this, 1);
}
void GreasePencil::foreach_visible_drawing(
int frame, blender::FunctionRef<void(GreasePencilDrawing &)> function)
{

View File

@ -3,6 +3,7 @@
#include "testing/testing.h"
#include "BKE_curves.hh"
#include "BKE_grease_pencil.hh"
#include "BKE_idtype.h"
#include "BKE_lib_id.h"
@ -49,6 +50,46 @@ TEST(gpencil, add_empty_drawings)
EXPECT_EQ(grease_pencil.drawings().size(), 3);
}
TEST(gpencil, remove_drawing)
{
GreasePencilIDTestContext ctx;
GreasePencil &grease_pencil = *static_cast<GreasePencil *>(BKE_id_new(ctx.bmain, ID_GP, "GP"));
grease_pencil.add_empty_drawings(3);
GreasePencilDrawing *drawing = reinterpret_cast<GreasePencilDrawing *>(
grease_pencil.drawings_for_write()[1]);
drawing->geometry.wrap().resize(0, 10);
Layer layer1("Layer1");
Layer layer2("Layer2");
layer1.frames_for_write().add(0, 0);
layer1.frames_for_write().add(10, 1);
layer1.frames_for_write().add(20, 2);
layer2.frames_for_write().add(0, 1);
grease_pencil.root_group().add_layer(std::move(layer1));
grease_pencil.root_group().add_layer(std::move(layer2));
grease_pencil.remove_drawing(1);
EXPECT_EQ(grease_pencil.drawings().size(), 2);
static int expected_frames_size[] = {2, 0};
static int expected_frames_pairs_layer0[][2] = {{0, 0}, {20, 1}};
int layer_i = 0;
grease_pencil.root_group().foreach_layer_pre_order([&](Layer &layer) {
EXPECT_EQ(layer.frames().size(), expected_frames_size[layer_i]);
if (layer_i == 0) {
for (const int i : IndexRange(2)) {
EXPECT_EQ(layer.frames().lookup(expected_frames_pairs_layer0[i][0]),
expected_frames_pairs_layer0[i][1]);
}
}
layer_i++;
});
}
/* --------------------------------------------------------------------------------------------- */
/* Layer Tree Tests. */

View File

@ -181,6 +181,7 @@ typedef struct GreasePencil {
blender::Span<GreasePencilDrawingOrReference *> drawings() const;
blender::MutableSpan<GreasePencilDrawingOrReference *> drawings_for_write();
void add_empty_drawings(int n);

Pretty sure null-terminated goes without saying for char * pointers in DNA, I don't think it's worth mentioning here

Pretty sure null-terminated goes without saying for `char *` pointers in DNA, I don't think it's worth mentioning here

Not sure about dynamic names for strings. It is not something typically used in the DNA.

Not sure about dynamic names for strings. It is not something typically used in the DNA.

It's been done more recently I think. It's properly integrated with RNA now too, to avoid that boilerplate. It's nice not to have to worry about choosing a future-proof length.

It's been done more recently I think. It's properly integrated with RNA now too, to avoid that boilerplate. It's nice not to have to worry about choosing a future-proof length.
void remove_drawing(int index);
void foreach_visible_drawing(int frame,
blender::FunctionRef<void(GreasePencilDrawing &)> function);
void read_drawing_array(BlendDataReader *reader);