From 8b31e8663b4fa4ab0afddcaa6dd23341078a71fe Mon Sep 17 00:00:00 2001 From: Falk David Date: Fri, 16 Feb 2024 16:51:06 +0100 Subject: [PATCH] =?UTF-8?q?GPv3:=20Convert=20`editcurve`=20to=20b=C3=A9zie?= =?UTF-8?q?r=20curves?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In GPv2, strokes could be edited using bézier curve handles. This was implemented by creating an `editcurve` for a stroke that would store the handles and other attributes. In GPv3, we can directly make use of the `CURVE_TYPE_BEZIER` and store the curve as a bézier curve. Note: This PR only handles conversion. Not rendering or anything else. Pull Request: https://projects.blender.org/blender/blender/pulls/118386 --- .../intern/grease_pencil_convert_legacy.cc | 114 +++++++++++++----- 1 file changed, 84 insertions(+), 30 deletions(-) diff --git a/source/blender/blenkernel/intern/grease_pencil_convert_legacy.cc b/source/blender/blenkernel/intern/grease_pencil_convert_legacy.cc index 548407130cd..713035f5d0b 100644 --- a/source/blender/blenkernel/intern/grease_pencil_convert_legacy.cc +++ b/source/blender/blenkernel/intern/grease_pencil_convert_legacy.cc @@ -77,13 +77,23 @@ void legacy_gpencil_frame_to_grease_pencil_drawing(const bGPDframe &gpf, /* Get the number of points, number of strokes and the offsets for each stroke. */ Vector offsets; + Vector curve_types; offsets.append(0); int num_strokes = 0; int num_points = 0; + bool has_bezier_stroke = false; LISTBASE_FOREACH (bGPDstroke *, gps, &gpf.strokes) { - num_points += gps->totpoints; - offsets.append(num_points); + if (gps->editcurve != nullptr) { + has_bezier_stroke = true; + num_points += gps->editcurve->tot_curve_points; + curve_types.append(CURVE_TYPE_BEZIER); + } + else { + num_points += gps->totpoints; + curve_types.append(CURVE_TYPE_POLY); + } num_strokes++; + offsets.append(num_points); } /* Resize the CurvesGeometry. */ @@ -96,8 +106,14 @@ void legacy_gpencil_frame_to_grease_pencil_drawing(const bGPDframe &gpf, OffsetIndices points_by_curve = curves.points_by_curve(); MutableAttributeAccessor attributes = curves.attributes_for_write(); - /* All strokes are poly curves. */ - curves.fill_curve_types(CURVE_TYPE_POLY); + if (!has_bezier_stroke) { + /* All strokes are poly curves. */ + curves.fill_curve_types(CURVE_TYPE_POLY); + } + else { + curves.curve_types_for_write().copy_from(curve_types); + curves.update_curve_types(); + } /* Find used vertex groups in this drawing. */ ListBase stroke_vertex_group_names; @@ -120,6 +136,12 @@ void legacy_gpencil_frame_to_grease_pencil_drawing(const bGPDframe &gpf, /* Point Attributes. */ MutableSpan positions = curves.positions_for_write(); + MutableSpan handle_positions_left = has_bezier_stroke ? + curves.handle_positions_left_for_write() : + MutableSpan(); + MutableSpan handle_positions_right = has_bezier_stroke ? + curves.handle_positions_right_for_write() : + MutableSpan(); MutableSpan radii = drawing.radii_for_write(); MutableSpan opacities = drawing.opacities_for_write(); SpanAttributeWriter delta_times = attributes.lookup_or_add_for_write_span( @@ -160,8 +182,6 @@ void legacy_gpencil_frame_to_grease_pencil_drawing(const bGPDframe &gpf, int stroke_i = 0; LISTBASE_FOREACH_INDEX (bGPDstroke *, gps, &gpf.strokes, stroke_i) { - /* TODO: check if `gps->editcurve` is not nullptr and parse bezier curve instead. */ - stroke_cyclic.span[stroke_i] = (gps->flag & GP_STROKE_CYCLIC) != 0; /* TODO: This should be a `double` attribute. */ stroke_init_times.span[stroke_i] = float(gps->inittime); @@ -181,6 +201,7 @@ void legacy_gpencil_frame_to_grease_pencil_drawing(const bGPDframe &gpf, continue; } + const Span src_points{gps->points, gps->totpoints}; /* Previously, Grease Pencil used a radius convention where 1 `px` = 0.001 units. This `px` * was the brush size which would be stored in the stroke thickness and then scaled by the * point pressure factor. Finally, the render engine would divide this thickness value by @@ -188,8 +209,13 @@ void legacy_gpencil_frame_to_grease_pencil_drawing(const bGPDframe &gpf, * into blender units. Store the radius now directly in blender units. This makes it * consistent with how hair curves handle the radius. */ const float stroke_thickness = float(gps->thickness) / 2000.0f; - Span src_points{gps->points, gps->totpoints}; MutableSpan dst_positions = positions.slice(points); + MutableSpan dst_handle_positions_left = has_bezier_stroke ? + handle_positions_left.slice(points) : + MutableSpan(); + MutableSpan dst_handle_positions_right = has_bezier_stroke ? + handle_positions_right.slice(points) : + MutableSpan(); MutableSpan dst_radii = radii.slice(points); MutableSpan dst_opacities = opacities.slice(points); MutableSpan dst_deltatimes = delta_times.span.slice(points); @@ -199,30 +225,58 @@ void legacy_gpencil_frame_to_grease_pencil_drawing(const bGPDframe &gpf, MutableSpan dst_dverts = use_dverts ? dverts.slice(points) : MutableSpan(); - threading::parallel_for(src_points.index_range(), 4096, [&](const IndexRange range) { - for (const int point_i : range) { - const bGPDspoint &pt = src_points[point_i]; - dst_positions[point_i] = float3(pt.x, pt.y, pt.z); - dst_radii[point_i] = stroke_thickness * pt.pressure; - dst_opacities[point_i] = pt.strength; - dst_rotations[point_i] = pt.uv_rot; - dst_vertex_colors[point_i] = ColorGeometry4f(pt.vert_color); - dst_selection[point_i] = (pt.flag & GP_SPOINT_SELECT) != 0; - if (use_dverts && gps->dvert) { - copy_dvert(gps->dvert[point_i], dst_dverts[point_i]); - } - } - }); - - dst_deltatimes.first() = 0; - threading::parallel_for( - src_points.index_range().drop_front(1), 4096, [&](const IndexRange range) { - for (const int point_i : range) { - const bGPDspoint &pt = src_points[point_i]; - const bGPDspoint &pt_prev = src_points[point_i - 1]; - dst_deltatimes[point_i] = pt.time - pt_prev.time; + if (curve_types[stroke_i] == CURVE_TYPE_POLY) { + threading::parallel_for(src_points.index_range(), 4096, [&](const IndexRange range) { + for (const int point_i : range) { + const bGPDspoint &pt = src_points[point_i]; + dst_positions[point_i] = float3(pt.x, pt.y, pt.z); + dst_radii[point_i] = stroke_thickness * pt.pressure; + dst_opacities[point_i] = pt.strength; + dst_rotations[point_i] = pt.uv_rot; + dst_vertex_colors[point_i] = ColorGeometry4f(pt.vert_color); + dst_selection[point_i] = (pt.flag & GP_SPOINT_SELECT) != 0; + if (use_dverts && gps->dvert) { + copy_dvert(gps->dvert[point_i], dst_dverts[point_i]); } - }); + } + }); + + dst_deltatimes.first() = 0; + threading::parallel_for( + src_points.index_range().drop_front(1), 4096, [&](const IndexRange range) { + for (const int point_i : range) { + const bGPDspoint &pt = src_points[point_i]; + const bGPDspoint &pt_prev = src_points[point_i - 1]; + dst_deltatimes[point_i] = pt.time - pt_prev.time; + } + }); + } + else if (curve_types[stroke_i] == CURVE_TYPE_BEZIER) { + BLI_assert(gps->editcurve != nullptr); + Span src_curve_points{gps->editcurve->curve_points, + gps->editcurve->tot_curve_points}; + + threading::parallel_for(src_curve_points.index_range(), 4096, [&](const IndexRange range) { + for (const int point_i : range) { + const bGPDcurve_point &cpt = src_curve_points[point_i]; + dst_positions[point_i] = float3(cpt.bezt.vec[1]); + dst_handle_positions_left[point_i] = float3(cpt.bezt.vec[0]); + dst_handle_positions_right[point_i] = float3(cpt.bezt.vec[2]); + dst_radii[point_i] = stroke_thickness * cpt.pressure; + dst_opacities[point_i] = cpt.strength; + dst_rotations[point_i] = cpt.uv_rot; + dst_vertex_colors[point_i] = ColorGeometry4f(cpt.vert_color); + dst_selection[point_i] = (cpt.flag & GP_CURVE_POINT_SELECT) != 0; + if (use_dverts && gps->dvert) { + copy_dvert(gps->dvert[point_i], dst_dverts[point_i]); + } + } + }); + } + else { + /* Unknown curve type. */ + BLI_assert_unreachable(); + } } delta_times.finish();