WIP: GPv3: Geometry-based Fill tool #114318
@ -755,8 +755,9 @@ static void update_gap_distance(FillData *fd, const float delta)
|
||||
* in the tool settings.
|
||||
*/
|
||||
static void get_fill_edge_layers(FillData *fd,
|
||||
Vector<GreasePencilDrawing *> &r_drawings,
|
||||
Vector<int> &r_layer_index)
|
||||
Vector<const bke::greasepencil::Drawing *> &r_drawings,
|
||||
Vector<int> &r_layer_index,
|
||||
const int frame_number)
|
||||
{
|
||||
using namespace bke::greasepencil;
|
||||
|
||||
@ -764,37 +765,36 @@ static void get_fill_edge_layers(FillData *fd,
|
||||
int active_layer_index = -1;
|
||||
const Layer *active_layer = fd->grease_pencil->get_active_layer();
|
||||
Span<const Layer *> layers = fd->grease_pencil->layers();
|
||||
for (int index = 0; index < layers.size(); index++) {
|
||||
if (layers[index] == active_layer) {
|
||||
active_layer_index = index;
|
||||
for (int layer_index = 0; layer_index < layers.size(); layer_index++) {
|
||||
if (layers[layer_index] == active_layer) {
|
||||
active_layer_index = layer_index;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Select layers based on position in the layer collection. */
|
||||
Span<GreasePencilDrawingBase *> drawings = fd->grease_pencil->drawings();
|
||||
for (int index = 0; index < layers.size(); index++) {
|
||||
for (int layer_index = 0; layer_index < layers.size(); layer_index++) {
|
||||
/* Skip invisible layers. */
|
||||
if (!layers[index]->is_visible()) {
|
||||
if (!layers[layer_index]->is_visible()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
bool add = false;
|
||||
switch (fd->brush->gpencil_settings->fill_layer_mode) {
|
||||
case GP_FILL_GPLMODE_ACTIVE:
|
||||
add = (index == active_layer_index);
|
||||
add = (layer_index == active_layer_index);
|
||||
break;
|
||||
case GP_FILL_GPLMODE_ABOVE:
|
||||
add = (index == active_layer_index + 1);
|
||||
add = (layer_index == active_layer_index + 1);
|
||||
break;
|
||||
case GP_FILL_GPLMODE_BELOW:
|
||||
add = (index == active_layer_index - 1);
|
||||
add = (layer_index == active_layer_index - 1);
|
||||
break;
|
||||
case GP_FILL_GPLMODE_ALL_ABOVE:
|
||||
add = (index > active_layer_index);
|
||||
add = (layer_index > active_layer_index);
|
||||
break;
|
||||
case GP_FILL_GPLMODE_ALL_BELOW:
|
||||
add = (index < active_layer_index);
|
||||
add = (layer_index < active_layer_index);
|
||||
break;
|
||||
case GP_FILL_GPLMODE_VISIBLE:
|
||||
add = true;
|
||||
@ -804,20 +804,54 @@ static void get_fill_edge_layers(FillData *fd,
|
||||
}
|
||||
|
||||
if (add) {
|
||||
const int drawing_index = layers[index]->drawing_index_at(fd->vc.scene->r.cfra);
|
||||
if (drawing_index == -1) {
|
||||
continue;
|
||||
}
|
||||
GreasePencilDrawingBase *drawing_base = drawings[drawing_index];
|
||||
if (drawing_base->type == GP_DRAWING) {
|
||||
GreasePencilDrawing *drawing = reinterpret_cast<GreasePencilDrawing *>(drawing_base);
|
||||
if (const Drawing *drawing = fd->grease_pencil->get_editable_drawing_at(layers[layer_index],
|
||||
frame_number))
|
||||
{
|
||||
r_drawings.append(drawing);
|
||||
r_layer_index.append(index);
|
||||
r_layer_index.append(layer_index);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get drawings and gap closure data for a given frame number.
|
||||
*/
|
||||
static bool get_drawings_and_gap_closures_at_frame(FillData *fd, const int frame_number)
|
||||
{
|
||||
/* Get layers according to tool settings (visible, above, below, etc.) */
|
||||
Vector<const bke::greasepencil::Drawing *> drawings;
|
||||
Vector<int> layer_indices;
|
||||
|
||||
get_fill_edge_layers(fd, drawings, layer_indices, frame_number);
|
||||
if (drawings.is_empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Convert curves to viewport 2D space. */
|
||||
fd->curves_2d = curves_in_2d_space_get(
|
||||
&fd->vc, fd->vc.obact, drawings, layer_indices, frame_number, true);
|
||||
|
||||
/* Calculate epsilon values of all 2D curve segments, used to avoid floating point precision
|
||||
* errors. */
|
||||
get_curve_segment_epsilons(fd);
|
||||
|
||||
/* When using extensions of the curve ends to close gaps, build an array of those
|
||||
* two-point 'curves'. */
|
||||
if (fd->use_gap_close_extend) {
|
||||
init_curve_end_extensions(fd);
|
||||
get_curve_end_extensions(fd);
|
||||
}
|
||||
|
||||
/* When using radii to close gaps, build KD tree of curve end points. */
|
||||
if (fd->use_gap_close_radius) {
|
||||
init_curve_end_radii(fd);
|
||||
get_connected_curve_end_radii(fd);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the fill operator data.
|
||||
*/
|
||||
@ -857,36 +891,11 @@ static bool operator_init(bContext *C, wmOperator *op)
|
||||
copy_v3_v3(fd->gap_closed_color, default_gap_closed_color);
|
||||
copy_v4_v4(fd->gap_proximity_color, default_gap_proximity_color);
|
||||
|
||||
/* Get layers according to tool settings (visible, above, below, etc.) */
|
||||
Vector<GreasePencilDrawing *> drawings;
|
||||
Vector<int> layer_indices;
|
||||
|
||||
get_fill_edge_layers(fd, drawings, layer_indices);
|
||||
if (drawings.is_empty()) {
|
||||
/* Get drawings for current frame. */
|
||||
if (!get_drawings_and_gap_closures_at_frame(fd, fd->vc.scene->r.cfra)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Convert curves to viewport 2D space. */
|
||||
fd->curves_2d = curves_in_2d_space_get(
|
||||
&fd->vc, fd->vc.obact, drawings, layer_indices, fd->vc.scene->r.cfra, true);
|
||||
|
||||
/* Calculate epsilon values of all 2D curve segments, used to avoid floating point precision
|
||||
* errors. */
|
||||
get_curve_segment_epsilons(fd);
|
||||
|
||||
/* When using extensions of the curve ends to close gaps, build an array of those
|
||||
* two-point 'curves'. */
|
||||
if (fd->use_gap_close_extend) {
|
||||
init_curve_end_extensions(fd);
|
||||
get_curve_end_extensions(fd);
|
||||
}
|
||||
|
||||
/* When using radii to close gaps, build KD tree of curve end points. */
|
||||
if (fd->use_gap_close_radius) {
|
||||
init_curve_end_radii(fd);
|
||||
get_connected_curve_end_radii(fd);
|
||||
}
|
||||
|
||||
/* Activate 3D viewport overlay for showing gap closure visual aids. */
|
||||
if ((fd->brush->gpencil_settings->flag & GP_BRUSH_FILL_SHOW_EXTENDLINES) != 0) {
|
||||
fd->draw_handle = ED_region_draw_cb_activate(
|
||||
@ -921,6 +930,61 @@ static void operator_exit(bContext *C, wmOperator *op)
|
||||
}
|
||||
}
|
||||
|
||||
static bool fill_do(FillData *fd)
|
||||
{
|
||||
/* DEBUG: measure time. */
|
||||
auto t1 = std::chrono::high_resolution_clock::now();
|
||||
|
||||
/* Get latest toolsetting values. */
|
||||
get_latest_toolsettings(fd);
|
||||
|
||||
/* Get the fill method. */
|
||||
bool (*perform_fill)(FillData *) = nullptr;
|
||||
if (fd->brush->gpencil_settings->fill_mode == GP_FILL_MODE_FLOOD) {
|
||||
perform_fill = &flood_fill_do;
|
||||
}
|
||||
else if (fd->brush->gpencil_settings->fill_mode == GP_FILL_MODE_GEOMETRY) {
|
||||
perform_fill = &vector_fill_do;
|
||||
}
|
||||
if (perform_fill == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Perform the fill operation for all selected frames. */
|
||||
const bool use_multi_frame_editing = (fd->vc.scene->toolsettings->gpencil_flags &
|
||||
GP_USE_MULTI_FRAME_EDITING) != 0;
|
||||
const Array<int> frame_numbers = get_frame_numbers_for_layer(
|
||||
fd->grease_pencil->get_active_layer()->wrap(),
|
||||
fd->vc.scene->r.cfra,
|
||||
use_multi_frame_editing);
|
||||
bool get_drawings = false;
|
||||
bool success = false;
|
||||
|
||||
for (const int frame_number : frame_numbers) {
|
||||
/* For the current frame (first entry in the array) we already retrieved the drawings during
|
||||
* the operator invoke, so no need to do it a second time. */
|
||||
if (get_drawings) {
|
||||
if (!get_drawings_and_gap_closures_at_frame(fd, frame_number)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
get_drawings = true;
|
||||
|
||||
fd->frame_number = frame_number;
|
||||
|
||||
if (perform_fill(fd)) {
|
||||
success = true;
|
||||
}
|
||||
}
|
||||
|
||||
/* DEBUG: measure time. */
|
||||
auto t2 = std::chrono::high_resolution_clock::now();
|
||||
auto delta_t = std::chrono::duration_cast<std::chrono::milliseconds>(t2 - t1);
|
||||
printf("Fill operator took %d ms.\n", int(delta_t.count()));
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
/**
|
||||
* Modal handler for the fill operator:
|
||||
* - Change gap closure radius
|
||||
@ -966,27 +1030,8 @@ static int operator_modal(bContext *C, wmOperator *op, const wmEvent *event)
|
||||
fd->mouse_pos[0] = float(event->mval[0]);
|
||||
fd->mouse_pos[1] = float(event->mval[1]);
|
||||
|
||||
/* DEBUG: measure time. */
|
||||
auto t1 = std::chrono::high_resolution_clock::now();
|
||||
|
||||
/* Get latest toolsetting values. */
|
||||
get_latest_toolsettings(fd);
|
||||
|
||||
/* Perform the fill operation. */
|
||||
bool success = false;
|
||||
if (fd->brush->gpencil_settings->fill_mode == GP_FILL_MODE_FLOOD) {
|
||||
success = flood_fill_do(fd);
|
||||
}
|
||||
else if (fd->brush->gpencil_settings->fill_mode == GP_FILL_MODE_GEOMETRY) {
|
||||
success = vector_fill_do(fd);
|
||||
}
|
||||
|
||||
/* DEBUG: measure time. */
|
||||
auto t2 = std::chrono::high_resolution_clock::now();
|
||||
auto delta_t = std::chrono::duration_cast<std::chrono::milliseconds>(t2 - t1);
|
||||
printf("Fill operator took %d ms.\n", int(delta_t.count()));
|
||||
|
||||
if (success) {
|
||||
if (fill_do(fd)) {
|
||||
modal_state = OPERATOR_FINISHED;
|
||||
}
|
||||
else {
|
||||
|
@ -67,6 +67,9 @@ struct FillData {
|
||||
/* Mouse position of the fill operation click. */
|
||||
float2 mouse_pos{};
|
||||
|
||||
/* Frame number to perform the fill operation on. */
|
||||
int frame_number;
|
||||
|
||||
/* True when edge gaps are closed by extending the curve ends. */
|
||||
bool use_gap_close_extend{};
|
||||
/* True when edge gaps are closed by curve end radii. */
|
||||
|
@ -636,18 +636,26 @@ static void create_fill_geometry(FillData *fd)
|
||||
{
|
||||
/* Ensure active frame (autokey is on, we checked on operator invoke). */
|
||||
const bke::greasepencil::Layer *active_layer = fd->grease_pencil->get_active_layer();
|
||||
const int current_frame = fd->vc.scene->r.cfra;
|
||||
if (!fd->grease_pencil->get_active_layer()->frames().contains(current_frame)) {
|
||||
if (!fd->grease_pencil->get_active_layer()->frames().contains(fd->frame_number)) {
|
||||
bke::greasepencil::Layer &active_layer = *fd->grease_pencil->get_active_layer_for_write();
|
||||
|
||||
/* For additive drawing, we duplicate the frame that's currently visible and insert it at the
|
||||
* current frame. */
|
||||
bool frame_created = false;
|
||||
if (fd->additive_drawing) {
|
||||
/* For additive drawing, we duplicate the frame that's currently visible and insert it at the
|
||||
* current frame. */
|
||||
fd->grease_pencil->insert_duplicate_frame(
|
||||
active_layer, active_layer.frame_key_at(current_frame), current_frame, false);
|
||||
if (!fd->grease_pencil->insert_duplicate_frame(
|
||||
active_layer, active_layer.frame_key_at(fd->frame_number), fd->frame_number, false))
|
||||
{
|
||||
frame_created = true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (!frame_created) {
|
||||
/* Otherwise we just insert a blank keyframe. */
|
||||
fd->grease_pencil->insert_blank_frame(active_layer, current_frame, 0, BEZT_KEYTYPE_KEYFRAME);
|
||||
if (!fd->grease_pencil->insert_blank_frame(
|
||||
active_layer, fd->frame_number, 0, BEZT_KEYTYPE_KEYFRAME))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -658,7 +666,7 @@ static void create_fill_geometry(FillData *fd)
|
||||
}
|
||||
|
||||
/* Create geometry. */
|
||||
const int drawing_index = active_layer->drawing_index_at(fd->vc.scene->r.cfra);
|
||||
const int drawing_index = active_layer->drawing_index_at(fd->frame_number);
|
||||
bke::greasepencil::Drawing &drawing = reinterpret_cast<GreasePencilDrawing *>(
|
||||
fd->grease_pencil->drawings()[drawing_index])
|
||||
->wrap();
|
||||
@ -1573,7 +1581,8 @@ static bool walk_along_curve(FillData *fd, EdgeSegment *segment, const int start
|
||||
fd);
|
||||
|
||||
if (!segment->segment_ends.is_empty()) {
|
||||
/* Sort intersections on distance and angle. This is important to find the narrowest edge. */
|
||||
/* Sort intersections on distance and angle. This is important to find the narrowest edge.
|
||||
*/
|
||||
std::sort(segment->segment_ends.begin(),
|
||||
segment->segment_ends.end(),
|
||||
[](const SegmentEnd &a, const SegmentEnd &b) {
|
||||
|
@ -83,14 +83,14 @@ 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 current_frame,
|
||||
const bool use_multi_frame_editing)
|
||||
Array<int> get_frame_numbers_for_layer(const bke::greasepencil::Layer &layer,
|
||||
const int current_frame,
|
||||
const bool use_multi_frame_editing)
|
||||
{
|
||||
Vector<int> frame_numbers({current_frame});
|
||||
if (use_multi_frame_editing) {
|
||||
for (const auto [frame_number, frame] : layer.frames().items()) {
|
||||
if (frame.is_selected()) {
|
||||
if (frame.is_selected() && frame_number != current_frame) {
|
||||
frame_numbers.append_unchecked(frame_number);
|
||||
}
|
||||
}
|
||||
@ -155,7 +155,7 @@ Array<DrawingInfo> retrieve_visible_drawings(const Scene &scene, const GreasePen
|
||||
|
||||
Curves2DSpace curves_in_2d_space_get(ViewContext *vc,
|
||||
Object *ob,
|
||||
Vector<GreasePencilDrawing *> &drawings,
|
||||
Vector<const bke::greasepencil::Drawing *> &drawings,
|
||||
Vector<int> &layer_index,
|
||||
const int frame_number,
|
||||
const bool get_stroke_flag)
|
||||
@ -163,7 +163,7 @@ Curves2DSpace curves_in_2d_space_get(ViewContext *vc,
|
||||
/* Get viewport projection matrix and evaluated GP object. */
|
||||
float4x4 projection;
|
||||
ED_view3d_ob_project_mat_get(vc->rv3d, ob, projection.ptr());
|
||||
const Object *ob_eval = DEG_get_evaluated_object(vc->depsgraph, const_cast<Object *>(ob));
|
||||
const Object *ob_eval = DEG_get_evaluated_object(vc->depsgraph, ob);
|
||||
|
||||
/* Count total number of editable curves and points in given Grease Pencil drawings. */
|
||||
Curves2DSpace cv2d;
|
||||
@ -194,7 +194,7 @@ Curves2DSpace curves_in_2d_space_get(ViewContext *vc,
|
||||
threading::parallel_for(cv2d.drawings.index_range(), 1, [&](const IndexRange range) {
|
||||
for (const int drawing_i : range) {
|
||||
/* Get deformed geomtry. */
|
||||
const bke::CurvesGeometry &curves = cv2d.drawings[drawing_i]->geometry.wrap();
|
||||
const bke::CurvesGeometry &curves = cv2d.drawings[drawing_i]->strokes();
|
||||
const OffsetIndices points_by_curve = curves.points_by_curve();
|
||||
const VArray<bool> cyclic = curves.cyclic();
|
||||
const bke::crazyspace::GeometryDeformation deformation =
|
||||
|
@ -124,6 +124,9 @@ struct MutableDrawingInfo {
|
||||
const int layer_index;
|
||||
const int frame_number;
|
||||
};
|
||||
Array<int> get_frame_numbers_for_layer(const bke::greasepencil::Layer &layer,
|
||||
const int current_frame,
|
||||
const bool use_multi_frame_editing);
|
||||
Array<MutableDrawingInfo> retrieve_editable_drawings(const Scene &scene,
|
||||
GreasePencil &grease_pencil);
|
||||
Array<DrawingInfo> retrieve_visible_drawings(const Scene &scene,
|
||||
@ -167,7 +170,7 @@ IndexMask polyline_detect_corners(Span<float2> points,
|
||||
* first_point_of_curve = points_2d[point_contiguous]
|
||||
*/
|
||||
struct Curves2DSpace {
|
||||
Vector<GreasePencilDrawing *> drawings;
|
||||
Vector<const bke::greasepencil::Drawing *> drawings;
|
||||
/* Curve offset for each drawing (layer). So when there are three drawings with 12, 15 and 8
|
||||
* curves, the curve offsets will be [0, 12, 27 (= 12 + 15)]. */
|
||||
Vector<int> curve_offset;
|
||||
@ -194,7 +197,7 @@ struct Curves2DSpace {
|
||||
*/
|
||||
Curves2DSpace curves_in_2d_space_get(ViewContext *vc,
|
||||
Object *ob,
|
||||
Vector<GreasePencilDrawing *> &drawings,
|
||||
Vector<const bke::greasepencil::Drawing *> &drawings,
|
||||
Vector<int> &layer_index,
|
||||
const int frame_number,
|
||||
const bool get_stroke_flag = false);
|
||||
|
Loading…
Reference in New Issue
Block a user