GPv3: Multi-frame editing

Adds a new scene tool setting `use_grease_pencil_multi_frame_editing`.

The `foreach_*_drawing` functions are moved to the `ed::greasepencil` namespace in the editor, since they are now context sensitive and depend on the toolsetting. They are now named `retrieve_editable_drawings` and `retrieve_visible_drawings` and return
an array of drawings instead of calling a callback function.

Pull Request: https://projects.blender.org/blender/blender/pulls/114283
This commit is contained in:
Falk David
2023-11-02 17:10:59 +01:00
committed by Falk David
parent b679ea939a
commit 91db8fc5a0
19 changed files with 599 additions and 552 deletions
+3
View File
@@ -826,6 +826,9 @@ class VIEW3D_HT_header(Header):
depress=(tool_settings.gpencil_selectmode_edit == 'STROKE'),
).mode = 'STROKE'
row = layout.row(align=True)
row.prop(tool_settings, "use_grease_pencil_multi_frame_editing", text="")
if object_mode == 'PAINT_GREASE_PENCIL':
row = layout.row()
sub = row.row(align=True)
+4 -2
View File
@@ -53,9 +53,11 @@ GeometryDeformation get_evaluated_curves_deformation(const Depsgraph &depsgraph,
const Object &ob_orig);
GeometryDeformation get_evaluated_grease_pencil_drawing_deformation(const Object *ob_eval,
const Object &ob_orig,
int layer_index);
int layer_index,
int frame);
GeometryDeformation get_evaluated_grease_pencil_drawing_deformation(const Depsgraph &depsgraph,
const Object &ob_orig,
int layer_index);
int layer_index,
int frame);
} // namespace blender::bke::crazyspace
@@ -670,16 +670,15 @@ GeometryDeformation get_evaluated_curves_deformation(const Depsgraph &depsgraph,
GeometryDeformation get_evaluated_grease_pencil_drawing_deformation(const Object *ob_eval,
const Object &ob_orig,
const int layer_index)
const int layer_index,
const int frame)
{
BLI_assert(ob_orig.type == OB_GREASE_PENCIL);
const GreasePencil &grease_pencil = *static_cast<const GreasePencil *>(ob_eval->data);
const GreasePencil &grease_pencil_orig = *static_cast<const GreasePencil *>(ob_orig.data);
const int eval_frame = grease_pencil.runtime->eval_frame;
const Span<const bke::greasepencil::Layer *> layers_orig = grease_pencil_orig.layers();
BLI_assert(layer_index >= 0 && layer_index < layers_orig.size());
const int drawing_index = layers_orig[layer_index]->drawing_index_at(eval_frame);
const int drawing_index = layers_orig[layer_index]->drawing_index_at(frame);
if (drawing_index == -1) {
return {};
}
@@ -726,7 +725,7 @@ GeometryDeformation get_evaluated_grease_pencil_drawing_deformation(const Object
Span<const bke::greasepencil::Layer *> layers_eval = grease_pencil_eval->layers();
if (layers_eval.size() != layers_orig.size()) {
const bke::greasepencil::Layer *layer_eval = layers_eval[layer_index];
const int drawing_index_eval = layer_eval->drawing_index_at(eval_frame);
const int drawing_index_eval = layer_eval->drawing_index_at(frame);
if (drawing_index_eval != -1) {
const GreasePencilDrawingBase *drawing_base_eval = grease_pencil_eval->drawing(
drawing_index_eval);
@@ -748,10 +747,11 @@ GeometryDeformation get_evaluated_grease_pencil_drawing_deformation(const Object
GeometryDeformation get_evaluated_grease_pencil_drawing_deformation(const Depsgraph &depsgraph,
const Object &ob_orig,
const int layer_index)
const int layer_index,
const int frame)
{
const Object *ob_eval = DEG_get_evaluated_object(&depsgraph, const_cast<Object *>(&ob_orig));
return get_evaluated_grease_pencil_drawing_deformation(ob_eval, ob_orig, layer_index);
return get_evaluated_grease_pencil_drawing_deformation(ob_eval, ob_orig, layer_index, frame);
}
} // namespace blender::bke::crazyspace
@@ -1823,101 +1823,23 @@ blender::bke::greasepencil::Drawing *GreasePencil::get_editable_drawing_at(
return &drawing->wrap();
}
enum ForeachDrawingMode {
VISIBLE,
EDITABLE,
};
static void foreach_drawing_ex(
const GreasePencil &grease_pencil,
const int frame,
const ForeachDrawingMode mode,
blender::FunctionRef<void(const int, const blender::bke::greasepencil::Drawing &)> function)
{
using namespace blender::bke::greasepencil;
blender::Span<const GreasePencilDrawingBase *> drawings = grease_pencil.drawings();
blender::Span<const Layer *> layers = grease_pencil.layers();
for (const int layer_i : layers.index_range()) {
const Layer *layer = layers[layer_i];
switch (mode) {
case VISIBLE: {
if (!layer->is_visible()) {
continue;
}
break;
}
case EDITABLE: {
if (!layer->is_editable()) {
continue;
}
break;
}
}
int index = layer->drawing_index_at(frame);
if (index == -1) {
continue;
}
const GreasePencilDrawingBase *drawing_base = drawings[index];
if (drawing_base->type == GP_DRAWING) {
const GreasePencilDrawing *drawing = reinterpret_cast<const GreasePencilDrawing *>(
drawing_base);
function(layer_i, drawing->wrap());
}
else if (drawing_base->type == GP_DRAWING_REFERENCE) {
/* TODO: Drawing references are not implemented yet. */
BLI_assert_unreachable();
}
}
}
void GreasePencil::foreach_visible_drawing(
const int frame,
blender::FunctionRef<void(const int, blender::bke::greasepencil::Drawing &)> function)
{
foreach_drawing_ex(
*this,
frame,
VISIBLE,
[&](const int layer_index, const blender::bke::greasepencil::Drawing &drawing) {
/* We const_cast here to be able to implement `foreach_drawing_ex` only once. */
function(layer_index, const_cast<blender::bke::greasepencil::Drawing &>(drawing));
});
}
void GreasePencil::foreach_visible_drawing(
const int frame,
blender::FunctionRef<void(const int, const blender::bke::greasepencil::Drawing &)> function)
const
{
foreach_drawing_ex(*this, frame, VISIBLE, function);
}
void GreasePencil::foreach_editable_drawing(
const int frame,
blender::FunctionRef<void(const int, blender::bke::greasepencil::Drawing &)> function)
{
foreach_drawing_ex(
*this,
frame,
EDITABLE,
[&](const int layer_index, const blender::bke::greasepencil::Drawing &drawing) {
/* We const_cast here to be able to implement `foreach_drawing_ex` only once. */
function(layer_index, const_cast<blender::bke::greasepencil::Drawing &>(drawing));
});
}
std::optional<blender::Bounds<blender::float3>> GreasePencil::bounds_min_max() const
{
using namespace blender;
std::optional<Bounds<float3>> bounds;
this->foreach_visible_drawing(
this->runtime->eval_frame,
[&](const int /*layer_index*/, const bke::greasepencil::Drawing &drawing) {
const bke::CurvesGeometry &curves = drawing.strokes();
bounds = bounds::merge(bounds, curves.bounds_min_max());
});
const Span<const bke::greasepencil::Layer *> layers = this->layers();
for (const int layer_i : layers.index_range()) {
const bke::greasepencil::Layer *layer = layers[layer_i];
if (!layer->is_visible()) {
continue;
}
if (const bke::greasepencil::Drawing *drawing = this->get_drawing_at(
layer, this->runtime->eval_frame))
{
const bke::CurvesGeometry &curves = drawing->strokes();
bounds = bounds::merge(bounds, curves.bounds_min_max());
}
}
return bounds;
}
@@ -59,8 +59,8 @@ class ObjectModule {
/** Forward vector used to sort gpencil objects. */
float3 camera_forward_;
float3 camera_pos_;
/** Scene current frame. */
float current_frame_ = 0;
const Scene *scene_ = nullptr;
/** \note Needs not to be temporary variable since it is dereferenced later. */
std::array<float4, 2> clear_colors_ = {float4(0.0f, 0.0f, 0.0f, 0.0f),
@@ -73,6 +73,7 @@ class ObjectModule {
void init(const View3D *v3d, const Scene *scene)
{
const bool is_viewport = (v3d != nullptr);
scene_ = scene;
if (is_viewport) {
/* TODO(fclem): Avoid access to global DRW. */
@@ -93,11 +94,10 @@ class ObjectModule {
}
}
void begin_sync(Depsgraph *depsgraph, const View &main_view)
void begin_sync(Depsgraph * /*depsgraph*/, const View &main_view)
{
camera_forward_ = main_view.forward();
camera_pos_ = main_view.location();
current_frame_ = DEG_get_ctime(depsgraph);
is_object_fb_needed_ = false;
is_layer_fb_needed_ = false;
@@ -157,9 +157,9 @@ class ObjectModule {
object_subpass.state_set(state);
object_subpass.shader_set(shaders_.static_shader_get(GREASE_PENCIL));
GPUVertBuf *position_tx = DRW_cache_grease_pencil_position_buffer_get(object, current_frame_);
GPUVertBuf *color_tx = DRW_cache_grease_pencil_color_buffer_get(object, current_frame_);
GPUBatch *geom = DRW_cache_grease_pencil_get(object, current_frame_);
GPUVertBuf *position_tx = DRW_cache_grease_pencil_position_buffer_get(scene_, object);
GPUVertBuf *color_tx = DRW_cache_grease_pencil_color_buffer_get(scene_, object);
GPUBatch *geom = DRW_cache_grease_pencil_get(scene_, object);
/* TODO(fclem): Pass per frame object matrix here. */
ResourceHandle handle = manager.resource_handle(object_ref);
@@ -43,17 +43,18 @@ void OVERLAY_edit_grease_pencil_cache_init(OVERLAY_Data *vedata)
void OVERLAY_edit_grease_pencil_cache_populate(OVERLAY_Data *vedata, Object *ob)
{
OVERLAY_PrivateData *pd = vedata->stl->pd;
const DRWContextState *draw_ctx = DRW_context_state_get();
DRWShadingGroup *lines_grp = pd->edit_grease_pencil_wires_grp;
if (lines_grp) {
GPUBatch *geom_lines = DRW_cache_grease_pencil_edit_lines_get(ob, pd->cfra);
GPUBatch *geom_lines = DRW_cache_grease_pencil_edit_lines_get(draw_ctx->scene, ob);
DRW_shgroup_call_no_cull(lines_grp, geom_lines, ob);
}
DRWShadingGroup *points_grp = pd->edit_grease_pencil_points_grp;
if (points_grp) {
GPUBatch *geom_points = DRW_cache_grease_pencil_edit_points_get(ob, pd->cfra);
GPUBatch *geom_points = DRW_cache_grease_pencil_edit_points_get(draw_ctx->scene, ob);
DRW_shgroup_call_no_cull(points_grp, geom_points, ob);
}
}
+10 -5
View File
@@ -23,6 +23,7 @@ struct Volume;
struct VolumeGrid;
struct bGPDstroke;
struct bGPdata;
struct Scene;
/**
* Shape resolution level of detail.
@@ -279,11 +280,15 @@ void DRW_cache_gpencil_sbuffer_clear(struct Object *ob);
/* Grease Pencil */
struct GPUBatch *DRW_cache_grease_pencil_get(struct Object *ob, int cfra);
struct GPUBatch *DRW_cache_grease_pencil_edit_points_get(struct Object *ob, int cfra);
struct GPUBatch *DRW_cache_grease_pencil_edit_lines_get(struct Object *ob, int cfra);
struct GPUVertBuf *DRW_cache_grease_pencil_position_buffer_get(struct Object *ob, int cfra);
struct GPUVertBuf *DRW_cache_grease_pencil_color_buffer_get(struct Object *ob, int cfra);
struct GPUBatch *DRW_cache_grease_pencil_get(const struct Scene *scene, struct Object *ob);
struct GPUBatch *DRW_cache_grease_pencil_edit_points_get(const struct Scene *scene,
struct Object *ob);
struct GPUBatch *DRW_cache_grease_pencil_edit_lines_get(const struct Scene *scene,
struct Object *ob);
struct GPUVertBuf *DRW_cache_grease_pencil_position_buffer_get(const struct Scene *scene,
struct Object *ob);
struct GPUVertBuf *DRW_cache_grease_pencil_color_buffer_get(const struct Scene *scene,
struct Object *ob);
#ifdef __cplusplus
}
@@ -19,6 +19,8 @@
#include "DRW_engine.h"
#include "DRW_render.h"
#include "ED_grease_pencil.hh"
#include "GPU_batch.h"
#include "draw_cache_impl.hh"
@@ -193,14 +195,13 @@ BLI_INLINE int32_t pack_rotation_aspect_hardness(float rot, float asp, float har
}
static void grease_pencil_edit_lines_batch_ensure(
const Span<const blender::bke::greasepencil::Drawing *> drawings,
GreasePencilBatchCache *cache)
const Span<blender::ed::greasepencil::DrawingInfo> drawings, GreasePencilBatchCache *cache)
{
using namespace blender::bke::greasepencil;
int total_line_ids_num = 0;
for (const Drawing *drawing : drawings) {
const bke::CurvesGeometry &curves = drawing->strokes();
for (const ed::greasepencil::DrawingInfo &info : drawings) {
const bke::CurvesGeometry &curves = info.drawing.strokes();
/* Add one id for the restart after every curve. */
total_line_ids_num += curves.curves_num();
/* Add one id for every non-cyclic segment. */
@@ -218,8 +219,8 @@ static void grease_pencil_edit_lines_batch_ensure(
/* Fill buffers with data. */
int drawing_start_offset = 0;
for (const int drawing_i : drawings.index_range()) {
const Drawing &drawing = *drawings[drawing_i];
const bke::CurvesGeometry &curves = drawing.strokes();
const ed::greasepencil::DrawingInfo &info = drawings[drawing_i];
const bke::CurvesGeometry &curves = info.drawing.strokes();
const OffsetIndices<int> points_by_curve = curves.points_by_curve();
const VArray<bool> cyclic = curves.cyclic();
@@ -250,7 +251,7 @@ static void grease_pencil_edit_lines_batch_ensure(
}
static void grease_pencil_geom_batch_ensure(const GreasePencil &grease_pencil,
int cfra,
const Scene &scene,
const bool lines)
{
using namespace blender::bke::greasepencil;
@@ -267,9 +268,8 @@ static void grease_pencil_geom_batch_ensure(const GreasePencil &grease_pencil,
BLI_assert(cache->geom_batch == nullptr);
/* Get the visible drawings. */
Vector<const Drawing *> drawings;
grease_pencil.foreach_visible_drawing(
cfra, [&](const int /*layer_index*/, const Drawing &drawing) { drawings.append(&drawing); });
const Array<ed::greasepencil::DrawingInfo> drawings =
ed::greasepencil::retrieve_visible_drawings(scene, grease_pencil);
/* First, count how many vertices and triangles are needed for the whole object. Also record the
* offsets into the curves for the vertices and triangles. */
@@ -279,8 +279,8 @@ static void grease_pencil_geom_batch_ensure(const GreasePencil &grease_pencil,
int v_offset = 0;
Vector<Array<int>> verts_start_offsets_per_visible_drawing;
Vector<Array<int>> tris_start_offsets_per_visible_drawing;
for (const Drawing *drawing : drawings) {
const bke::CurvesGeometry &curves = drawing->strokes();
for (const ed::greasepencil::DrawingInfo &info : drawings) {
const bke::CurvesGeometry &curves = info.drawing.strokes();
const OffsetIndices<int> points_by_curve = curves.points_by_curve();
const VArray<bool> cyclic = curves.cyclic();
@@ -314,7 +314,7 @@ static void grease_pencil_geom_batch_ensure(const GreasePencil &grease_pencil,
/* One vertex is stored before and after as padding. Cyclic strokes have one extra vertex. */
total_verts_num += curves.points_num() + num_cyclic + curves.curves_num() * 2;
total_triangles_num += (curves.points_num() + num_cyclic) * 2;
total_triangles_num += drawing->triangles().size();
total_triangles_num += info.drawing.triangles().size();
verts_start_offsets_per_visible_drawing.append(std::move(verts_start_offsets));
tris_start_offsets_per_visible_drawing.append(std::move(tris_start_offsets));
@@ -365,14 +365,14 @@ static void grease_pencil_geom_batch_ensure(const GreasePencil &grease_pencil,
/* Fill buffers with data. */
int drawing_start_offset = 0;
for (const int drawing_i : drawings.index_range()) {
const Drawing &drawing = *drawings[drawing_i];
const bke::CurvesGeometry &curves = drawing.strokes();
const ed::greasepencil::DrawingInfo &info = drawings[drawing_i];
const bke::CurvesGeometry &curves = info.drawing.strokes();
const bke::AttributeAccessor attributes = curves.attributes();
const OffsetIndices<int> points_by_curve = curves.points_by_curve();
const Span<float3> positions = curves.positions();
const VArray<bool> cyclic = curves.cyclic();
const VArray<float> radii = drawing.radii();
const VArray<float> opacities = drawing.opacities();
const VArray<float> radii = info.drawing.radii();
const VArray<float> opacities = info.drawing.opacities();
const VArray<ColorGeometry4f> vertex_colors = *attributes.lookup_or_default<ColorGeometry4f>(
"vertex_color", ATTR_DOMAIN_POINT, ColorGeometry4f(0.0f, 0.0f, 0.0f, 0.0f));
/* Assumes that if the ".selection" attribute does not exist, all points are selected. */
@@ -384,7 +384,7 @@ static void grease_pencil_geom_batch_ensure(const GreasePencil &grease_pencil,
"end_cap", ATTR_DOMAIN_CURVE, 0);
const VArray<int> materials = *attributes.lookup_or_default<int>(
"material_index", ATTR_DOMAIN_CURVE, -1);
const Span<uint3> triangles = drawing.triangles();
const Span<uint3> triangles = info.drawing.triangles();
const Span<int> verts_start_offsets = verts_start_offsets_per_visible_drawing[drawing_i];
const Span<int> tris_start_offsets = tris_start_offsets_per_visible_drawing[drawing_i];
@@ -547,52 +547,52 @@ void DRW_grease_pencil_batch_cache_free(GreasePencil *grease_pencil)
grease_pencil->runtime->batch_cache = nullptr;
}
GPUBatch *DRW_cache_grease_pencil_get(Object *ob, int cfra)
GPUBatch *DRW_cache_grease_pencil_get(const Scene *scene, Object *ob)
{
using namespace blender::draw;
GreasePencil &grease_pencil = *static_cast<GreasePencil *>(ob->data);
GreasePencilBatchCache *cache = grease_pencil_batch_cache_get(grease_pencil);
grease_pencil_geom_batch_ensure(grease_pencil, cfra, false);
grease_pencil_geom_batch_ensure(grease_pencil, *scene, false);
return cache->geom_batch;
}
GPUBatch *DRW_cache_grease_pencil_edit_points_get(Object *ob, int cfra)
GPUBatch *DRW_cache_grease_pencil_edit_points_get(const Scene *scene, Object *ob)
{
using namespace blender::draw;
GreasePencil &grease_pencil = *static_cast<GreasePencil *>(ob->data);
GreasePencilBatchCache *cache = grease_pencil_batch_cache_get(grease_pencil);
grease_pencil_geom_batch_ensure(grease_pencil, cfra, false);
grease_pencil_geom_batch_ensure(grease_pencil, *scene, false);
return cache->edit_points;
}
GPUBatch *DRW_cache_grease_pencil_edit_lines_get(Object *ob, int cfra)
GPUBatch *DRW_cache_grease_pencil_edit_lines_get(const Scene *scene, Object *ob)
{
using namespace blender::draw;
GreasePencil &grease_pencil = *static_cast<GreasePencil *>(ob->data);
GreasePencilBatchCache *cache = grease_pencil_batch_cache_get(grease_pencil);
grease_pencil_geom_batch_ensure(grease_pencil, cfra, true);
grease_pencil_geom_batch_ensure(grease_pencil, *scene, true);
return cache->edit_lines;
}
GPUVertBuf *DRW_cache_grease_pencil_position_buffer_get(Object *ob, int cfra)
GPUVertBuf *DRW_cache_grease_pencil_position_buffer_get(const Scene *scene, Object *ob)
{
using namespace blender::draw;
GreasePencil &grease_pencil = *static_cast<GreasePencil *>(ob->data);
GreasePencilBatchCache *cache = grease_pencil_batch_cache_get(grease_pencil);
grease_pencil_geom_batch_ensure(grease_pencil, cfra, true);
grease_pencil_geom_batch_ensure(grease_pencil, *scene, true);
return cache->vbo;
}
GPUVertBuf *DRW_cache_grease_pencil_color_buffer_get(Object *ob, int cfra)
GPUVertBuf *DRW_cache_grease_pencil_color_buffer_get(const Scene *scene, Object *ob)
{
using namespace blender::draw;
GreasePencil &grease_pencil = *static_cast<GreasePencil *>(ob->data);
GreasePencilBatchCache *cache = grease_pencil_batch_cache_get(grease_pencil);
grease_pencil_geom_batch_ensure(grease_pencil, cfra, false);
grease_pencil_geom_batch_ensure(grease_pencil, *scene, false);
return cache->vbo_col;
}
@@ -305,59 +305,65 @@ static int grease_pencil_stroke_smooth_exec(bContext *C, wmOperator *op)
return OPERATOR_FINISHED;
}
grease_pencil.foreach_editable_drawing(
scene->r.cfra, [&](const int /*layer_index*/, bke::greasepencil::Drawing &drawing) {
bke::CurvesGeometry &curves = drawing.strokes_for_write();
if (curves.points_num() == 0) {
return;
}
bool changed = false;
const Array<MutableDrawingInfo> drawings = retrieve_editable_drawings(*scene, grease_pencil);
threading::parallel_for_each(drawings, [&](const MutableDrawingInfo &info) {
bke::CurvesGeometry &curves = info.drawing.strokes_for_write();
if (curves.points_num() == 0) {
return;
}
bke::MutableAttributeAccessor attributes = curves.attributes_for_write();
const OffsetIndices points_by_curve = curves.points_by_curve();
const VArray<bool> cyclic = curves.cyclic();
const VArray<bool> selection = *curves.attributes().lookup_or_default<bool>(
".selection", ATTR_DOMAIN_POINT, true);
bke::MutableAttributeAccessor attributes = curves.attributes_for_write();
const OffsetIndices points_by_curve = curves.points_by_curve();
const VArray<bool> cyclic = curves.cyclic();
const VArray<bool> selection = *curves.attributes().lookup_or_default<bool>(
".selection", ATTR_DOMAIN_POINT, true);
if (smooth_position) {
bke::GSpanAttributeWriter positions = attributes.lookup_for_write_span("position");
smooth_curve_attribute(points_by_curve,
selection,
cyclic,
iterations,
influence,
smooth_ends,
keep_shape,
positions.span);
positions.finish();
}
if (smooth_opacity && drawing.opacities().is_span()) {
bke::GSpanAttributeWriter opacities = attributes.lookup_for_write_span("opacity");
smooth_curve_attribute(points_by_curve,
selection,
cyclic,
iterations,
influence,
smooth_ends,
false,
opacities.span);
opacities.finish();
}
if (smooth_radius && drawing.radii().is_span()) {
bke::GSpanAttributeWriter radii = attributes.lookup_for_write_span("radius");
smooth_curve_attribute(points_by_curve,
selection,
cyclic,
iterations,
influence,
smooth_ends,
false,
radii.span);
radii.finish();
}
});
if (smooth_position) {
bke::GSpanAttributeWriter positions = attributes.lookup_for_write_span("position");
smooth_curve_attribute(points_by_curve,
selection,
cyclic,
iterations,
influence,
smooth_ends,
keep_shape,
positions.span);
positions.finish();
changed = true;
}
if (smooth_opacity && info.drawing.opacities().is_span()) {
bke::GSpanAttributeWriter opacities = attributes.lookup_for_write_span("opacity");
smooth_curve_attribute(points_by_curve,
selection,
cyclic,
iterations,
influence,
smooth_ends,
false,
opacities.span);
opacities.finish();
changed = true;
}
if (smooth_radius && info.drawing.radii().is_span()) {
bke::GSpanAttributeWriter radii = attributes.lookup_for_write_span("radius");
smooth_curve_attribute(points_by_curve,
selection,
cyclic,
iterations,
influence,
smooth_ends,
false,
radii.span);
radii.finish();
changed = true;
}
});
DEG_id_tag_update(&grease_pencil.id, ID_RECALC_GEOMETRY);
WM_event_add_notifier(C, NC_GEOM | ND_DATA, &grease_pencil);
if (changed) {
DEG_id_tag_update(&grease_pencil.id, ID_RECALC_GEOMETRY);
WM_event_add_notifier(C, NC_GEOM | ND_DATA, &grease_pencil);
}
return OPERATOR_FINISHED;
}
@@ -453,84 +459,84 @@ static int grease_pencil_stroke_simplify_exec(bContext *C, wmOperator *op)
const float epsilon = RNA_float_get(op->ptr, "factor");
bool changed = false;
grease_pencil.foreach_editable_drawing(
scene->r.cfra, [&](const int /*layer_index*/, bke::greasepencil::Drawing &drawing) {
bke::CurvesGeometry &curves = drawing.strokes_for_write();
if (curves.points_num() == 0) {
return;
}
if (!ed::curves::has_anything_selected(curves)) {
return;
}
const Array<MutableDrawingInfo> drawings = retrieve_editable_drawings(*scene, grease_pencil);
threading::parallel_for_each(drawings, [&](const MutableDrawingInfo &info) {
bke::CurvesGeometry &curves = info.drawing.strokes_for_write();
if (curves.points_num() == 0) {
return;
}
if (!ed::curves::has_anything_selected(curves)) {
return;
}
const Span<float3> positions = curves.positions();
const VArray<float> radii = drawing.radii();
const Span<float3> positions = curves.positions();
const VArray<float> radii = info.drawing.radii();
/* Distance functions for `ramer_douglas_peucker_simplify`. */
const auto dist_function_positions =
[positions](int64_t first_index, int64_t last_index, int64_t index) {
const float dist_position = dist_to_line_v3(
positions[index], positions[first_index], positions[last_index]);
return dist_position;
};
const auto dist_function_positions_and_radii =
[positions, radii](int64_t first_index, int64_t last_index, int64_t index) {
const float dist_position = dist_to_line_v3(
positions[index], positions[first_index], positions[last_index]);
/* We divide the distance by 2000.0f to convert from "pixels" to an actual distance.
* For some reason, grease pencil strokes the thickness of strokes in pixels rather
* than object space distance. */
const float dist_radii = dist_to_interpolated(positions[index],
positions[first_index],
positions[last_index],
radii[index],
radii[first_index],
radii[last_index]) /
2000.0f;
return math::max(dist_position, dist_radii);
};
/* Distance functions for `ramer_douglas_peucker_simplify`. */
const auto dist_function_positions =
[positions](int64_t first_index, int64_t last_index, int64_t index) {
const float dist_position = dist_to_line_v3(
positions[index], positions[first_index], positions[last_index]);
return dist_position;
};
const auto dist_function_positions_and_radii =
[positions, radii](int64_t first_index, int64_t last_index, int64_t index) {
const float dist_position = dist_to_line_v3(
positions[index], positions[first_index], positions[last_index]);
/* We divide the distance by 2000.0f to convert from "pixels" to an actual
* distance. For some reason, grease pencil strokes the thickness of strokes in
* pixels rather than object space distance. */
const float dist_radii = dist_to_interpolated(positions[index],
positions[first_index],
positions[last_index],
radii[index],
radii[first_index],
radii[last_index]) /
2000.0f;
return math::max(dist_position, dist_radii);
};
const VArray<bool> cyclic = curves.cyclic();
const OffsetIndices<int> points_by_curve = curves.points_by_curve();
const VArray<bool> selection = *curves.attributes().lookup_or_default<bool>(
".selection", ATTR_DOMAIN_POINT, true);
const VArray<bool> cyclic = curves.cyclic();
const OffsetIndices<int> points_by_curve = curves.points_by_curve();
const VArray<bool> selection = *curves.attributes().lookup_or_default<bool>(
".selection", ATTR_DOMAIN_POINT, true);
Array<bool> points_to_delete(curves.points_num());
selection.materialize(points_to_delete);
Array<bool> points_to_delete(curves.points_num());
selection.materialize(points_to_delete);
std::atomic<int64_t> total_points_to_delete = 0;
if (radii.is_single()) {
threading::parallel_for(curves.curves_range(), 128, [&](const IndexRange range) {
for (const int curve_i : range) {
const IndexRange points = points_by_curve[curve_i];
total_points_to_delete += stroke_simplify(points,
cyclic[curve_i],
epsilon,
dist_function_positions,
points_to_delete.as_mutable_span());
}
});
}
else if (radii.is_span()) {
threading::parallel_for(curves.curves_range(), 128, [&](const IndexRange range) {
for (const int curve_i : range) {
const IndexRange points = points_by_curve[curve_i];
total_points_to_delete += stroke_simplify(points,
cyclic[curve_i],
epsilon,
dist_function_positions_and_radii,
points_to_delete.as_mutable_span());
}
});
}
if (total_points_to_delete > 0) {
IndexMaskMemory memory;
curves.remove_points(IndexMask::from_bools(points_to_delete, memory));
drawing.tag_topology_changed();
changed = true;
std::atomic<int64_t> total_points_to_delete = 0;
if (radii.is_single()) {
threading::parallel_for(curves.curves_range(), 128, [&](const IndexRange range) {
for (const int curve_i : range) {
const IndexRange points = points_by_curve[curve_i];
total_points_to_delete += stroke_simplify(points,
cyclic[curve_i],
epsilon,
dist_function_positions,
points_to_delete.as_mutable_span());
}
});
}
else if (radii.is_span()) {
threading::parallel_for(curves.curves_range(), 128, [&](const IndexRange range) {
for (const int curve_i : range) {
const IndexRange points = points_by_curve[curve_i];
total_points_to_delete += stroke_simplify(points,
cyclic[curve_i],
epsilon,
dist_function_positions_and_radii,
points_to_delete.as_mutable_span());
}
});
}
if (total_points_to_delete > 0) {
IndexMaskMemory memory;
curves.remove_points(IndexMask::from_bools(points_to_delete, memory));
info.drawing.tag_topology_changed();
changed = true;
}
});
if (changed) {
DEG_id_tag_update(&grease_pencil.id, ID_RECALC_GEOMETRY);
@@ -601,8 +607,8 @@ static Array<bool> get_points_to_dissolve(bke::CurvesGeometry &curves, const Dis
return points_to_dissolve;
}
/* Both `between` and `unselect` have the unselected point being the ones dissolved so we need to
* invert. */
/* Both `between` and `unselect` have the unselected point being the ones dissolved so we need
* to invert. */
BLI_assert(ELEM(mode, DissolveMode::BETWEEN, DissolveMode::UNSELECT));
const OffsetIndices<int> points_by_curve = curves.points_by_curve();
@@ -659,25 +665,25 @@ static int grease_pencil_dissolve_exec(bContext *C, wmOperator *op)
const DissolveMode mode = DissolveMode(RNA_enum_get(op->ptr, "type"));
bool changed = false;
grease_pencil.foreach_editable_drawing(
scene->r.cfra, [&](const int /*layer_index*/, bke::greasepencil::Drawing &drawing) {
bke::CurvesGeometry &curves = drawing.strokes_for_write();
if (curves.points_num() == 0) {
return;
}
if (!ed::curves::has_anything_selected(curves)) {
return;
}
const Array<MutableDrawingInfo> drawings = retrieve_editable_drawings(*scene, grease_pencil);
threading::parallel_for_each(drawings, [&](const MutableDrawingInfo &info) {
bke::CurvesGeometry &curves = info.drawing.strokes_for_write();
if (curves.points_num() == 0) {
return;
}
if (!ed::curves::has_anything_selected(curves)) {
return;
}
const Array<bool> points_to_dissolve = get_points_to_dissolve(curves, mode);
const Array<bool> points_to_dissolve = get_points_to_dissolve(curves, mode);
if (points_to_dissolve.as_span().contains(true)) {
IndexMaskMemory memory;
curves.remove_points(IndexMask::from_bools(points_to_dissolve, memory));
drawing.tag_topology_changed();
changed = true;
}
});
if (points_to_dissolve.as_span().contains(true)) {
IndexMaskMemory memory;
curves.remove_points(IndexMask::from_bools(points_to_dissolve, memory));
info.drawing.tag_topology_changed();
changed = true;
}
});
if (changed) {
DEG_id_tag_update(&grease_pencil.id, ID_RECALC_GEOMETRY);
@@ -815,25 +821,25 @@ static int grease_pencil_stroke_material_set_exec(bContext *C, wmOperator * /*op
return OPERATOR_CANCELLED;
}
grease_pencil.foreach_editable_drawing(
scene->r.cfra, [&](int /*layer_index*/, bke::greasepencil::Drawing &drawing) {
bke::CurvesGeometry &curves = drawing.strokes_for_write();
const Array<MutableDrawingInfo> drawings = retrieve_editable_drawings(*scene, grease_pencil);
threading::parallel_for_each(drawings, [&](const MutableDrawingInfo &info) {
bke::CurvesGeometry &curves = info.drawing.strokes_for_write();
IndexMaskMemory memory;
IndexMask selected_curves = ed::curves::retrieve_selected_curves(curves, memory);
IndexMaskMemory memory;
IndexMask selected_curves = ed::curves::retrieve_selected_curves(curves, memory);
if (selected_curves.is_empty()) {
return;
}
if (selected_curves.is_empty()) {
return;
}
bke::SpanAttributeWriter<int> materials =
curves.attributes_for_write().lookup_or_add_for_write_span<int>("material_index",
ATTR_DOMAIN_CURVE);
selected_curves.foreach_index(
[&](const int curve_index) { materials.span[curve_index] = material_index; });
bke::SpanAttributeWriter<int> materials =
curves.attributes_for_write().lookup_or_add_for_write_span<int>("material_index",
ATTR_DOMAIN_CURVE);
selected_curves.foreach_index(
[&](const int curve_index) { materials.span[curve_index] = material_index; });
materials.finish();
});
materials.finish();
});
DEG_id_tag_update(&grease_pencil.id, ID_RECALC_GEOMETRY);
WM_event_add_notifier(C, NC_GEOM | ND_DATA | NA_EDITED, &grease_pencil);
@@ -883,45 +889,44 @@ static int grease_pencil_cyclical_set_exec(bContext *C, wmOperator *op)
const CyclicalMode mode = CyclicalMode(RNA_enum_get(op->ptr, "type"));
bool changed = false;
grease_pencil.foreach_editable_drawing(
scene->r.cfra, [&](int /*layer_index*/, bke::greasepencil::Drawing &drawing) {
bke::CurvesGeometry &curves = drawing.strokes_for_write();
const Array<MutableDrawingInfo> drawings = retrieve_editable_drawings(*scene, grease_pencil);
threading::parallel_for_each(drawings, [&](const MutableDrawingInfo &info) {
bke::CurvesGeometry &curves = info.drawing.strokes_for_write();
if (mode == CyclicalMode::OPEN && !curves.attributes().contains("cyclic")) {
/* Avoid creating unneeded attribute. */
return;
}
if (mode == CyclicalMode::OPEN && !curves.attributes().contains("cyclic")) {
/* Avoid creating unneeded attribute. */
return;
}
IndexMaskMemory memory;
const IndexMask curve_selection = ed::curves::retrieve_selected_curves(curves, memory);
if (curve_selection.is_empty()) {
return;
}
IndexMaskMemory memory;
const IndexMask curve_selection = ed::curves::retrieve_selected_curves(curves, memory);
if (curve_selection.is_empty()) {
return;
}
MutableSpan<bool> cyclic = curves.cyclic_for_write();
MutableSpan<bool> cyclic = curves.cyclic_for_write();
switch (mode) {
case CyclicalMode::CLOSE:
index_mask::masked_fill(cyclic, true, curve_selection);
break;
case CyclicalMode::OPEN:
index_mask::masked_fill(cyclic, false, curve_selection);
break;
case CyclicalMode::TOGGLE:
array_utils::invert_booleans(cyclic, curve_selection);
break;
}
switch (mode) {
case CyclicalMode::CLOSE:
index_mask::masked_fill(cyclic, true, curve_selection);
break;
case CyclicalMode::OPEN:
index_mask::masked_fill(cyclic, false, curve_selection);
break;
case CyclicalMode::TOGGLE:
array_utils::invert_booleans(cyclic, curve_selection);
break;
}
/* Remove the attribute if it is empty. */
if (mode != CyclicalMode::CLOSE) {
if (array_utils::booleans_mix_calc(curves.cyclic()) == array_utils::BooleanMix::AllFalse)
{
curves.attributes_for_write().remove("cyclic");
}
}
/* Remove the attribute if it is empty. */
if (mode != CyclicalMode::CLOSE) {
if (array_utils::booleans_mix_calc(curves.cyclic()) == array_utils::BooleanMix::AllFalse) {
curves.attributes_for_write().remove("cyclic");
}
}
changed = true;
});
changed = true;
});
if (changed) {
DEG_id_tag_update(&grease_pencil.id, ID_RECALC_GEOMETRY);
@@ -36,10 +36,10 @@ static int select_all_exec(bContext *C, wmOperator *op)
GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object->data);
eAttrDomain selection_domain = ED_grease_pencil_selection_domain_get(scene->toolsettings);
grease_pencil.foreach_editable_drawing(
scene->r.cfra, [&](const int /*layer_index*/, blender::bke::greasepencil::Drawing &drawing) {
blender::ed::curves::select_all(drawing.strokes_for_write(), selection_domain, action);
});
const Array<MutableDrawingInfo> drawings = retrieve_editable_drawings(*scene, grease_pencil);
threading::parallel_for_each(drawings, [&](const MutableDrawingInfo &info) {
blender::ed::curves::select_all(info.drawing.strokes_for_write(), selection_domain, action);
});
/* Use #ID_RECALC_GEOMETRY instead of #ID_RECALC_SELECT because it is handled as a generic
* attribute for now. */
@@ -69,10 +69,10 @@ static int select_more_exec(bContext *C, wmOperator * /*op*/)
Object *object = CTX_data_active_object(C);
GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object->data);
grease_pencil.foreach_editable_drawing(
scene->r.cfra, [](const int /*layer_index*/, blender::bke::greasepencil::Drawing &drawing) {
blender::ed::curves::select_adjacent(drawing.strokes_for_write(), false);
});
const Array<MutableDrawingInfo> drawings = retrieve_editable_drawings(*scene, grease_pencil);
threading::parallel_for_each(drawings, [&](const MutableDrawingInfo &info) {
blender::ed::curves::select_adjacent(info.drawing.strokes_for_write(), false);
});
/* Use #ID_RECALC_GEOMETRY instead of #ID_RECALC_SELECT because it is handled as a generic
* attribute for now. */
@@ -100,10 +100,10 @@ static int select_less_exec(bContext *C, wmOperator * /*op*/)
Object *object = CTX_data_active_object(C);
GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object->data);
grease_pencil.foreach_editable_drawing(
scene->r.cfra, [](const int /*layer_index*/, blender::bke::greasepencil::Drawing &drawing) {
blender::ed::curves::select_adjacent(drawing.strokes_for_write(), true);
});
const Array<MutableDrawingInfo> drawings = retrieve_editable_drawings(*scene, grease_pencil);
threading::parallel_for_each(drawings, [&](const MutableDrawingInfo &info) {
blender::ed::curves::select_adjacent(info.drawing.strokes_for_write(), true);
});
/* Use #ID_RECALC_GEOMETRY instead of #ID_RECALC_SELECT because it is handled as a generic
* attribute for now. */
@@ -131,10 +131,10 @@ static int select_linked_exec(bContext *C, wmOperator * /*op*/)
Object *object = CTX_data_active_object(C);
GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object->data);
grease_pencil.foreach_editable_drawing(
scene->r.cfra, [](const int /*layer_index*/, blender::bke::greasepencil::Drawing &drawing) {
blender::ed::curves::select_linked(drawing.strokes_for_write());
});
const Array<MutableDrawingInfo> drawings = retrieve_editable_drawings(*scene, grease_pencil);
threading::parallel_for_each(drawings, [&](const MutableDrawingInfo &info) {
blender::ed::curves::select_linked(info.drawing.strokes_for_write());
});
/* Use #ID_RECALC_GEOMETRY instead of #ID_RECALC_SELECT because it is handled as a generic
* attribute for now. */
@@ -166,28 +166,28 @@ static int select_random_exec(bContext *C, wmOperator *op)
GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object->data);
eAttrDomain selection_domain = ED_grease_pencil_selection_domain_get(scene->toolsettings);
grease_pencil.foreach_editable_drawing(
scene->r.cfra, [&](const int layer_index, bke::greasepencil::Drawing &drawing) {
bke::CurvesGeometry &curves = drawing.strokes_for_write();
const Array<MutableDrawingInfo> drawings = retrieve_editable_drawings(*scene, grease_pencil);
threading::parallel_for_each(drawings, [&](const MutableDrawingInfo &info) {
bke::CurvesGeometry &curves = info.drawing.strokes_for_write();
IndexMaskMemory memory;
const IndexMask random_elements = ed::curves::random_mask(
curves,
selection_domain,
blender::get_default_hash_2<int>(seed, layer_index),
ratio,
memory);
IndexMaskMemory memory;
const IndexMask random_elements = ed::curves::random_mask(
curves,
selection_domain,
blender::get_default_hash_2<int>(seed, info.layer_index),
ratio,
memory);
const bool was_anything_selected = ed::curves::has_anything_selected(curves);
bke::GSpanAttributeWriter selection = ed::curves::ensure_selection_attribute(
curves, selection_domain, CD_PROP_BOOL);
if (!was_anything_selected) {
curves::fill_selection_true(selection.span);
}
const bool was_anything_selected = ed::curves::has_anything_selected(curves);
bke::GSpanAttributeWriter selection = ed::curves::ensure_selection_attribute(
curves, selection_domain, CD_PROP_BOOL);
if (!was_anything_selected) {
curves::fill_selection_true(selection.span);
}
curves::fill_selection_false(selection.span, random_elements);
selection.finish();
});
curves::fill_selection_false(selection.span, random_elements);
selection.finish();
});
/* Use #ID_RECALC_GEOMETRY instead of #ID_RECALC_SELECT because it is handled as a generic
* attribute for now. */
@@ -218,10 +218,10 @@ static int select_alternate_exec(bContext *C, wmOperator *op)
Object *object = CTX_data_active_object(C);
GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object->data);
grease_pencil.foreach_editable_drawing(
scene->r.cfra, [&](const int /*layer_index*/, blender::bke::greasepencil::Drawing &drawing) {
blender::ed::curves::select_alternate(drawing.strokes_for_write(), deselect_ends);
});
const Array<MutableDrawingInfo> drawings = retrieve_editable_drawings(*scene, grease_pencil);
threading::parallel_for_each(drawings, [&](const MutableDrawingInfo &info) {
blender::ed::curves::select_alternate(info.drawing.strokes_for_write(), deselect_ends);
});
/* Use #ID_RECALC_GEOMETRY instead of #ID_RECALC_SELECT because it is handled as a generic
* attribute for now. */
@@ -257,29 +257,29 @@ static int select_ends_exec(bContext *C, wmOperator *op)
Object *object = CTX_data_active_object(C);
GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object->data);
grease_pencil.foreach_editable_drawing(
scene->r.cfra, [&](const int /*layer_index*/, blender::bke::greasepencil::Drawing &drawing) {
bke::CurvesGeometry &curves = drawing.strokes_for_write();
const Array<MutableDrawingInfo> drawings = retrieve_editable_drawings(*scene, grease_pencil);
threading::parallel_for_each(drawings, [&](const MutableDrawingInfo &info) {
bke::CurvesGeometry &curves = info.drawing.strokes_for_write();
IndexMaskMemory memory;
const IndexMask inverted_end_points_mask = ed::curves::end_points(
curves, amount_start, amount_end, true, memory);
IndexMaskMemory memory;
const IndexMask inverted_end_points_mask = ed::curves::end_points(
curves, amount_start, amount_end, true, memory);
const bool was_anything_selected = ed::curves::has_anything_selected(curves);
bke::GSpanAttributeWriter selection = ed::curves::ensure_selection_attribute(
curves, ATTR_DOMAIN_POINT, CD_PROP_BOOL);
if (!was_anything_selected) {
ed::curves::fill_selection_true(selection.span);
}
const bool was_anything_selected = ed::curves::has_anything_selected(curves);
bke::GSpanAttributeWriter selection = ed::curves::ensure_selection_attribute(
curves, ATTR_DOMAIN_POINT, CD_PROP_BOOL);
if (!was_anything_selected) {
ed::curves::fill_selection_true(selection.span);
}
if (selection.span.type().is<bool>()) {
index_mask::masked_fill(selection.span.typed<bool>(), false, inverted_end_points_mask);
}
if (selection.span.type().is<float>()) {
index_mask::masked_fill(selection.span.typed<float>(), 0.0f, inverted_end_points_mask);
}
selection.finish();
});
if (selection.span.type().is<bool>()) {
index_mask::masked_fill(selection.span.typed<bool>(), false, inverted_end_points_mask);
}
if (selection.span.type().is<float>()) {
index_mask::masked_fill(selection.span.typed<float>(), 0.0f, inverted_end_points_mask);
}
selection.finish();
});
/* Use #ID_RECALC_GEOMETRY instead of #ID_RECALC_SELECT because it is handled as a generic
* attribute for now. */
@@ -76,4 +76,75 @@ float brush_radius_world_space(bContext &C, int x, int y)
return radius;
}
static Array<int> get_frame_numbers_for_layer(const bke::greasepencil::Layer &layer,
const int frame,
const bool use_multi_frame_editing)
{
if (!use_multi_frame_editing) {
return Array<int>({frame});
}
Vector<int> frame_numbers;
for (const auto [frame_number, frame] : layer.frames().items()) {
if (frame.is_selected()) {
frame_numbers.append_unchecked(frame_number);
}
}
return frame_numbers.as_span();
}
Array<MutableDrawingInfo> retrieve_editable_drawings(const Scene &scene,
GreasePencil &grease_pencil)
{
using namespace blender::bke::greasepencil;
const int current_frame = scene.r.cfra;
const ToolSettings *toolsettings = scene.toolsettings;
const bool use_multi_frame_editing = (toolsettings->gpencil_flags &
GP_USE_MULTI_FRAME_EDITING) != 0;
Vector<MutableDrawingInfo> editable_drawings;
Span<Layer *> layers = grease_pencil.layers_for_write();
for (const int layer_i : layers.index_range()) {
Layer *layer = layers[layer_i];
if (!layer->is_editable()) {
continue;
}
const Array<int> frame_numbers = get_frame_numbers_for_layer(
*layer, current_frame, use_multi_frame_editing);
for (const int frame_number : frame_numbers) {
if (Drawing *drawing = grease_pencil.get_editable_drawing_at(layer, frame_number)) {
editable_drawings.append({*drawing, layer_i, frame_number});
}
}
}
return editable_drawings.as_span();
}
Array<DrawingInfo> retrieve_visible_drawings(const Scene &scene, const GreasePencil &grease_pencil)
{
using namespace blender::bke::greasepencil;
const int current_frame = scene.r.cfra;
const ToolSettings *toolsettings = scene.toolsettings;
const bool use_multi_frame_editing = (toolsettings->gpencil_flags &
GP_USE_MULTI_FRAME_EDITING) != 0;
Vector<DrawingInfo> visible_drawings;
Span<const Layer *> layers = grease_pencil.layers();
for (const int layer_i : layers.index_range()) {
const Layer *layer = layers[layer_i];
if (!layer->is_visible()) {
continue;
}
const Array<int> frame_numbers = get_frame_numbers_for_layer(
*layer, current_frame, use_multi_frame_editing);
for (const int frame_number : frame_numbers) {
if (const Drawing *drawing = grease_pencil.get_drawing_at(layer, frame_number)) {
visible_drawings.append({*drawing, layer_i, frame_number});
}
}
}
return visible_drawings.as_span();
}
} // namespace blender::ed::greasepencil
@@ -110,6 +110,21 @@ bool editable_grease_pencil_poll(bContext *C);
bool editable_grease_pencil_point_selection_poll(bContext *C);
bool grease_pencil_painting_poll(bContext *C);
struct DrawingInfo {
const bke::greasepencil::Drawing &drawing;
const int layer_index;
const int frame_number;
};
struct MutableDrawingInfo {
bke::greasepencil::Drawing &drawing;
const int layer_index;
const int frame_number;
};
Array<MutableDrawingInfo> retrieve_editable_drawings(const Scene &scene,
GreasePencil &grease_pencil);
Array<DrawingInfo> retrieve_visible_drawings(const Scene &scene,
const GreasePencil &grease_pencil);
void create_blank(Main &bmain, Object &object, int frame_number);
void create_stroke(Main &bmain, Object &object, float4x4 matrix, int frame_number);
void create_suzanne(Main &bmain, Object &object, float4x4 matrix, const int frame_number);
@@ -23,6 +23,7 @@
#include "DEG_depsgraph_query.hh"
#include "DNA_brush_enums.h"
#include "ED_grease_pencil.hh"
#include "ED_view3d.hh"
#include "WM_api.hh"
@@ -753,49 +754,50 @@ struct EraseOperationExecutor {
GreasePencil &grease_pencil = *static_cast<GreasePencil *>(obact->data);
bool changed = false;
const auto execute_eraser_on_drawing = [&](const int layer_index, Drawing &drawing) {
const bke::CurvesGeometry &src = drawing.strokes();
const auto execute_eraser_on_drawing =
[&](const int layer_index, const int frame_number, Drawing &drawing) {
const bke::CurvesGeometry &src = drawing.strokes();
/* Evaluated geometry. */
bke::crazyspace::GeometryDeformation deformation =
bke::crazyspace::get_evaluated_grease_pencil_drawing_deformation(
ob_eval, *obact, layer_index);
/* Evaluated geometry. */
bke::crazyspace::GeometryDeformation deformation =
bke::crazyspace::get_evaluated_grease_pencil_drawing_deformation(
ob_eval, *obact, layer_index, frame_number);
/* Compute screen space positions. */
Array<float2> screen_space_positions(src.points_num());
threading::parallel_for(src.points_range(), 4096, [&](const IndexRange src_points) {
for (const int src_point : src_points) {
ED_view3d_project_float_global(
region,
math::transform_point(transforms_.layer_space_to_world_space,
deformation.positions[src_point]),
screen_space_positions[src_point],
V3D_PROJ_TEST_NOP);
}
});
/* Compute screen space positions. */
Array<float2> screen_space_positions(src.points_num());
threading::parallel_for(src.points_range(), 4096, [&](const IndexRange src_points) {
for (const int src_point : src_points) {
ED_view3d_project_float_global(
region,
math::transform_point(transforms_.layer_space_to_world_space,
deformation.positions[src_point]),
screen_space_positions[src_point],
V3D_PROJ_TEST_NOP);
}
});
/* Erasing operator. */
bke::CurvesGeometry dst;
bool erased = false;
switch (self.eraser_mode) {
case GP_BRUSH_ERASER_STROKE:
erased = stroke_eraser(src, screen_space_positions, dst);
break;
case GP_BRUSH_ERASER_HARD:
erased = hard_eraser(src, screen_space_positions, dst, self.keep_caps);
break;
case GP_BRUSH_ERASER_SOFT:
// To be implemented
return;
}
/* Erasing operator. */
bke::CurvesGeometry dst;
bool erased = false;
switch (self.eraser_mode) {
case GP_BRUSH_ERASER_STROKE:
erased = stroke_eraser(src, screen_space_positions, dst);
break;
case GP_BRUSH_ERASER_HARD:
erased = hard_eraser(src, screen_space_positions, dst, self.keep_caps);
break;
case GP_BRUSH_ERASER_SOFT:
// To be implemented
return;
}
if (erased) {
/* Set the new geometry. */
drawing.geometry.wrap() = std::move(dst);
drawing.tag_topology_changed();
changed = true;
}
};
if (erased) {
/* Set the new geometry. */
drawing.geometry.wrap() = std::move(dst);
drawing.tag_topology_changed();
changed = true;
}
};
if (self.active_layer_only) {
/* Erase only on the drawing at the current frame of the active layer. */
@@ -806,11 +808,17 @@ struct EraseOperationExecutor {
return;
}
execute_eraser_on_drawing(active_layer->drawing_index_at(scene->r.cfra), *drawing);
execute_eraser_on_drawing(
active_layer->drawing_index_at(scene->r.cfra), scene->r.cfra, *drawing);
}
else {
/* Erase on all editable drawings. */
grease_pencil.foreach_editable_drawing(scene->r.cfra, execute_eraser_on_drawing);
const Array<ed::greasepencil::MutableDrawingInfo> drawings =
ed::greasepencil::retrieve_editable_drawings(*scene, grease_pencil);
threading::parallel_for_each(
drawings, [&](const ed::greasepencil::MutableDrawingInfo &info) {
execute_eraser_on_drawing(info.layer_index, info.frame_number, info.drawing);
});
}
if (changed) {
@@ -1192,20 +1192,21 @@ static bool do_lasso_select_grease_pencil(ViewContext *vc,
vc->scene->toolsettings);
bool changed = false;
grease_pencil.foreach_editable_drawing(
vc->scene->r.cfra, [&](const int layer_index, blender::bke::greasepencil::Drawing &drawing) {
bke::crazyspace::GeometryDeformation deformation =
bke::crazyspace::get_evaluated_grease_pencil_drawing_deformation(
ob_eval, *vc->obedit, layer_index);
const Array<ed::greasepencil::MutableDrawingInfo> drawings =
ed::greasepencil::retrieve_editable_drawings(*vc->scene, grease_pencil);
for (const ed::greasepencil::MutableDrawingInfo info : drawings) {
bke::crazyspace::GeometryDeformation deformation =
bke::crazyspace::get_evaluated_grease_pencil_drawing_deformation(
ob_eval, *vc->obedit, info.layer_index, info.frame_number);
changed = ed::curves::select_lasso(
*vc,
drawing.strokes_for_write(),
deformation.positions,
selection_domain,
Span<int2>(reinterpret_cast<const int2 *>(mcoords), mcoords_len),
sel_op);
});
changed = ed::curves::select_lasso(
*vc,
info.drawing.strokes_for_write(),
deformation.positions,
selection_domain,
Span<int2>(reinterpret_cast<const int2 *>(mcoords), mcoords_len),
sel_op);
}
if (changed) {
/* Use #ID_RECALC_GEOMETRY instead of #ID_RECALC_SELECT because it is handled as a
@@ -3167,13 +3168,8 @@ static bool ed_grease_pencil_select_pick(bContext *C,
/* Collect editable drawings. */
const Object *ob_eval = DEG_get_evaluated_object(vc.depsgraph, const_cast<Object *>(vc.obedit));
GreasePencil &grease_pencil = *static_cast<GreasePencil *>(vc.obedit->data);
Vector<blender::bke::greasepencil::Drawing *> drawings;
Vector<int> layer_indices;
grease_pencil.foreach_editable_drawing(
vc.scene->r.cfra, [&](const int layer_index, blender::bke::greasepencil::Drawing &drawing) {
drawings.append(&drawing);
layer_indices.append(layer_index);
});
const Array<ed::greasepencil::MutableDrawingInfo> drawings =
ed::greasepencil::retrieve_editable_drawings(*vc.scene, grease_pencil);
/* Get selection domain from tool settings. */
const eAttrDomain selection_domain = ED_grease_pencil_selection_domain_get(
@@ -3186,21 +3182,22 @@ static bool ed_grease_pencil_select_pick(bContext *C,
[&](const IndexRange range, const ClosestGreasePencilDrawing &init) {
ClosestGreasePencilDrawing new_closest = init;
for (const int i : range) {
ed::greasepencil::MutableDrawingInfo info = drawings[i];
/* Get deformation by modifiers. */
bke::crazyspace::GeometryDeformation deformation =
bke::crazyspace::get_evaluated_grease_pencil_drawing_deformation(
ob_eval, *vc.obedit, layer_indices[i]);
ob_eval, *vc.obedit, info.layer_index, info.frame_number);
std::optional<ed::curves::FindClosestData> new_closest_elem =
ed::curves::closest_elem_find_screen_space(vc,
*vc.obedit,
drawings[i]->strokes_for_write(),
info.drawing.strokes_for_write(),
deformation.positions,
selection_domain,
mval,
new_closest.elem);
if (new_closest_elem) {
new_closest.elem = *new_closest_elem;
new_closest.drawing = drawings[i];
new_closest.drawing = &info.drawing;
}
}
return new_closest;
@@ -3213,7 +3210,8 @@ static bool ed_grease_pencil_select_pick(bContext *C,
if (params.deselect_all || params.sel_op == SEL_OP_SET) {
threading::parallel_for(drawings.index_range(), 1L, [&](const IndexRange range) {
for (const int i : range) {
bke::CurvesGeometry &curves = drawings[i]->geometry.wrap();
ed::greasepencil::MutableDrawingInfo info = drawings[i];
bke::CurvesGeometry &curves = info.drawing.strokes_for_write();
if (!ed::curves::has_anything_selected(curves)) {
continue;
}
@@ -4191,18 +4189,19 @@ static bool do_grease_pencil_box_select(ViewContext *vc, const rcti *rect, const
const eAttrDomain selection_domain = ED_grease_pencil_selection_domain_get(scene->toolsettings);
bool changed = false;
grease_pencil.foreach_editable_drawing(
scene->r.cfra, [&](const int layer_index, blender::bke::greasepencil::Drawing &drawing) {
bke::crazyspace::GeometryDeformation deformation =
bke::crazyspace::get_evaluated_grease_pencil_drawing_deformation(
ob_eval, *vc->obedit, layer_index);
changed |= ed::curves::select_box(*vc,
drawing.strokes_for_write(),
deformation.positions,
selection_domain,
*rect,
sel_op);
});
const Array<ed::greasepencil::MutableDrawingInfo> drawings =
ed::greasepencil::retrieve_editable_drawings(*scene, grease_pencil);
for (const ed::greasepencil::MutableDrawingInfo info : drawings) {
bke::crazyspace::GeometryDeformation deformation =
bke::crazyspace::get_evaluated_grease_pencil_drawing_deformation(
ob_eval, *vc->obedit, info.layer_index, info.frame_number);
changed |= ed::curves::select_box(*vc,
info.drawing.strokes_for_write(),
deformation.positions,
selection_domain,
*rect,
sel_op);
}
if (changed) {
DEG_id_tag_update(&grease_pencil.id, ID_RECALC_GEOMETRY);
@@ -5039,20 +5038,21 @@ static bool grease_pencil_circle_select(ViewContext *vc,
vc->scene->toolsettings);
bool changed = false;
grease_pencil.foreach_editable_drawing(
vc->scene->r.cfra, [&](const int layer_index, blender::bke::greasepencil::Drawing &drawing) {
bke::crazyspace::GeometryDeformation deformation =
bke::crazyspace::get_evaluated_grease_pencil_drawing_deformation(
ob_eval, *vc->obedit, layer_index);
const Array<ed::greasepencil::MutableDrawingInfo> drawings =
ed::greasepencil::retrieve_editable_drawings(*vc->scene, grease_pencil);
for (const ed::greasepencil::MutableDrawingInfo info : drawings) {
bke::crazyspace::GeometryDeformation deformation =
bke::crazyspace::get_evaluated_grease_pencil_drawing_deformation(
ob_eval, *vc->obedit, info.layer_index, info.frame_number);
changed = ed::curves::select_circle(*vc,
drawing.strokes_for_write(),
deformation.positions,
selection_domain,
int2(mval),
rad,
sel_op);
});
changed = ed::curves::select_circle(*vc,
info.drawing.strokes_for_write(),
deformation.positions,
selection_domain,
int2(mval),
rad,
sel_op);
}
if (changed) {
/* Use #ID_RECALC_GEOMETRY instead of #ID_RECALC_SELECT because it is handled as a
@@ -29,42 +29,42 @@ static void createTransGreasePencilVerts(bContext *C, TransInfo *t)
const bool use_proportional_edit = (t->flag & T_PROP_EDIT_ALL) != 0;
const bool use_connected_only = (t->flag & T_PROP_CONNECTED) != 0;
int number_of_layers_total = 0;
int total_number_of_drawings = 0;
Vector<Array<ed::greasepencil::MutableDrawingInfo>> all_drawings;
/* Count the number layers in all objects. */
for (const int i : trans_data_contrainers.index_range()) {
TransDataContainer &tc = trans_data_contrainers[i];
GreasePencil &grease_pencil = *static_cast<GreasePencil *>(tc.obedit->data);
grease_pencil.foreach_editable_drawing(
scene->r.cfra, [&](int /*layer_index*/, bke::greasepencil::Drawing & /*drawing*/) {
number_of_layers_total++;
});
const Array<ed::greasepencil::MutableDrawingInfo> drawings =
ed::greasepencil::retrieve_editable_drawings(*scene, grease_pencil);
all_drawings.append(drawings);
total_number_of_drawings += drawings.size();
}
int layer_offset = 0;
IndexMaskMemory memory;
Array<IndexMask> selection_per_layer_per_object(number_of_layers_total);
Array<IndexMask> selection_per_layer_per_object(total_number_of_drawings);
/* Count selected elements per layer per object and create TransData structs. */
for (const int i : trans_data_contrainers.index_range()) {
TransDataContainer &tc = trans_data_contrainers[i];
GreasePencil &grease_pencil = *static_cast<GreasePencil *>(tc.obedit->data);
grease_pencil.foreach_editable_drawing(
scene->r.cfra, [&](int /*layer_index*/, bke::greasepencil::Drawing &drawing) {
const bke::CurvesGeometry &curves = drawing.strokes();
const Array<ed::greasepencil::MutableDrawingInfo> drawings = all_drawings[i];
for (ed::greasepencil::MutableDrawingInfo info : drawings) {
const bke::CurvesGeometry &curves = info.drawing.strokes();
if (use_proportional_edit) {
tc.data_len += curves.point_num;
}
else {
selection_per_layer_per_object[layer_offset] = ed::curves::retrieve_selected_points(
curves, memory);
tc.data_len += selection_per_layer_per_object[layer_offset].size();
}
if (use_proportional_edit) {
tc.data_len += curves.point_num;
}
else {
selection_per_layer_per_object[layer_offset] = ed::curves::retrieve_selected_points(
curves, memory);
tc.data_len += selection_per_layer_per_object[layer_offset].size();
}
layer_offset++;
});
layer_offset++;
}
if (tc.data_len > 0) {
tc.data = MEM_cnew_array<TransData>(tc.data_len, __func__);
@@ -88,37 +88,37 @@ static void createTransGreasePencilVerts(bContext *C, TransInfo *t)
int layer_points_offset = 0;
grease_pencil.foreach_editable_drawing(
scene->r.cfra, [&](int /*layer_index*/, bke::greasepencil::Drawing &drawing) {
bke::CurvesGeometry &curves = drawing.strokes_for_write();
const IndexMask selected_indices = selection_per_layer_per_object[layer_offset];
const Array<ed::greasepencil::MutableDrawingInfo> drawings = all_drawings[i];
for (ed::greasepencil::MutableDrawingInfo info : drawings) {
bke::CurvesGeometry &curves = info.drawing.strokes_for_write();
const IndexMask selected_indices = selection_per_layer_per_object[layer_offset];
std::optional<MutableSpan<float>> value_attribute;
if (t->mode == TFM_CURVE_SHRINKFATTEN) {
MutableSpan<float> radii = drawing.radii_for_write();
value_attribute = radii;
}
else if (t->mode == TFM_GPENCIL_OPACITY) {
MutableSpan<float> opacities = drawing.opacities_for_write();
value_attribute = opacities;
}
std::optional<MutableSpan<float>> value_attribute;
if (t->mode == TFM_CURVE_SHRINKFATTEN) {
MutableSpan<float> radii = info.drawing.radii_for_write();
value_attribute = radii;
}
else if (t->mode == TFM_GPENCIL_OPACITY) {
MutableSpan<float> opacities = info.drawing.opacities_for_write();
value_attribute = opacities;
}
curve_populate_trans_data_structs(tc,
curves,
value_attribute,
selected_indices,
use_proportional_edit,
use_connected_only,
layer_points_offset);
curve_populate_trans_data_structs(tc,
curves,
value_attribute,
selected_indices,
use_proportional_edit,
use_connected_only,
layer_points_offset);
if (use_proportional_edit) {
layer_points_offset += curves.points_num();
}
else {
layer_points_offset += selected_indices.size();
}
layer_offset++;
});
if (use_proportional_edit) {
layer_points_offset += curves.points_num();
}
else {
layer_points_offset += selected_indices.size();
}
layer_offset++;
}
}
}
@@ -131,14 +131,15 @@ static void recalcData_grease_pencil(TransInfo *t)
for (const TransDataContainer &tc : trans_data_contrainers) {
GreasePencil &grease_pencil = *static_cast<GreasePencil *>(tc.obedit->data);
grease_pencil.foreach_editable_drawing(
scene->r.cfra, [&](int /*layer_index*/, bke::greasepencil::Drawing &drawing) {
bke::CurvesGeometry &curves = drawing.strokes_for_write();
const Array<ed::greasepencil::MutableDrawingInfo> drawings =
ed::greasepencil::retrieve_editable_drawings(*scene, grease_pencil);
for (ed::greasepencil::MutableDrawingInfo info : drawings) {
bke::CurvesGeometry &curves = info.drawing.strokes_for_write();
curves.calculate_bezier_auto_handles();
curves.tag_positions_changed();
drawing.tag_positions_changed();
});
curves.calculate_bezier_auto_handles();
curves.tag_positions_changed();
info.drawing.tag_positions_changed();
}
DEG_id_tag_update(&grease_pencil.id, ID_RECALC_GEOMETRY);
}
@@ -818,13 +818,15 @@ static int gizmo_3d_foreach_selected(const bContext *C,
mat_local = float4x4(obedit->world_to_object) * float4x4(ob_iter->object_to_world);
}
grease_pencil.foreach_editable_drawing(
scene->r.cfra, [&](int layer_index, blender::bke::greasepencil::Drawing &drawing) {
const bke::CurvesGeometry &curves = drawing.strokes();
const Array<ed::greasepencil::MutableDrawingInfo> drawings =
ed::greasepencil::retrieve_editable_drawings(*scene, grease_pencil);
threading::parallel_for_each(
drawings, [&](const ed::greasepencil::MutableDrawingInfo &info) {
const bke::CurvesGeometry &curves = info.drawing.strokes();
const bke::crazyspace::GeometryDeformation deformation =
bke::crazyspace::get_evaluated_grease_pencil_drawing_deformation(
*depsgraph, *ob, layer_index);
*depsgraph, *ob, info.layer_index, info.frame_number);
IndexMaskMemory memory;
const IndexMask selected_points = ed::curves::retrieve_selected_points(curves,
@@ -577,20 +577,6 @@ typedef struct GreasePencil {
blender::bke::greasepencil::Drawing *get_editable_drawing_at(
const blender::bke::greasepencil::Layer *layer, int frame_number);
void foreach_visible_drawing(
const int frame,
blender::FunctionRef<void(const int /*layer_index*/,
blender::bke::greasepencil::Drawing & /*drawing*/)> function);
void foreach_visible_drawing(
const int frame,
blender::FunctionRef<void(const int /*layer_index*/,
const blender::bke::greasepencil::Drawing & /*drawing*/)> function)
const;
void foreach_editable_drawing(
const int frame,
blender::FunctionRef<void(const int /*layer_index*/,
blender::bke::greasepencil::Drawing & /*drawing*/)> function);
std::optional<blender::Bounds<blender::float3>> bounds_min_max() const;
blender::bke::AttributeAccessor attributes() const;
@@ -2694,6 +2694,8 @@ enum {
/** #ToolSettings::gpencil_flags */
typedef enum eGPencil_Flags {
/** Enables multi-frame editing. */
GP_USE_MULTI_FRAME_EDITING = (1 << 0),
/** When creating new frames, the last frame gets used as the basis for the new one. */
GP_TOOL_FLAG_RETAIN_LAST = (1 << 1),
/** Add the strokes below all strokes in the layer. */
@@ -11,6 +11,7 @@
#include "DNA_brush_types.h"
#include "DNA_collection_types.h"
#include "DNA_gpencil_legacy_types.h"
#include "DNA_grease_pencil_types.h"
#include "DNA_layer_types.h"
#include "DNA_linestyle_types.h"
#include "DNA_modifier_types.h"
@@ -878,6 +879,19 @@ static void rna_Gpencil_vertex_mask_segment_update(bContext *C, PointerRNA *ptr)
ED_gpencil_tag_scene_gpencil(CTX_data_scene(C));
}
# ifdef WITH_GREASE_PENCIL_V3
static void rna_active_grease_pencil_update(bContext *C, PointerRNA * /*ptr*/)
{
Object *active_object = CTX_data_active_object(C);
if (!active_object || active_object->type != OB_GREASE_PENCIL) {
return;
}
GreasePencil *grease_pencil = static_cast<GreasePencil *>(active_object->data);
DEG_id_tag_update(&grease_pencil->id, ID_RECALC_GEOMETRY);
WM_main_add_notifier(NC_GPENCIL | NA_EDITED, nullptr);
}
# endif
/* Read-only Iterator of all the scene objects. */
static void rna_Scene_objects_begin(CollectionPropertyIterator *iter, PointerRNA *ptr)
@@ -3734,6 +3748,16 @@ static void rna_def_tool_settings(BlenderRNA *brna)
RNA_def_property_update(
prop, NC_SPACE | ND_SPACE_VIEW3D, "rna_Gpencil_vertex_mask_segment_update");
# ifdef WITH_GREASE_PENCIL_V3
prop = RNA_def_property(srna, "use_grease_pencil_multi_frame_editing", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, nullptr, "gpencil_flags", GP_USE_MULTI_FRAME_EDITING);
RNA_def_property_ui_text(prop, "Multi-frame Editing", "Enable multi-frame editing");
RNA_def_property_ui_icon(prop, ICON_GP_MULTIFRAME_EDITING, 0);
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
RNA_def_property_flag(prop, PROP_CONTEXT_UPDATE);
RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_active_grease_pencil_update");
# endif
/* Annotations - 2D Views Stroke Placement */
prop = RNA_def_property(srna, "annotation_stroke_placement_view2d", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_bitflag_sdna(prop, nullptr, "gpencil_v2d_align");