diff --git a/source/blender/editors/object/object_add.cc b/source/blender/editors/object/object_add.cc index d3245b9ca52..0e47cf1b00c 100644 --- a/source/blender/editors/object/object_add.cc +++ b/source/blender/editors/object/object_add.cc @@ -35,6 +35,7 @@ #include "DNA_scene_types.h" #include "DNA_vfont_types.h" +#include "BLI_array_utils.hh" #include "BLI_ghash.h" #include "BLI_listbase.h" #include "BLI_math_matrix.h" @@ -2874,6 +2875,7 @@ struct ObjectConversionInfo { Object *obact; bool keep_original; bool do_merge_customdata; + PointerRNA *op_props; ReportList *reports; }; @@ -3086,6 +3088,145 @@ static Object *convert_mesh_to_mesh(Base &base, ObjectConversionInfo &info, Base return newob; } +static int mesh_to_grease_pencil_add_material(Main &bmain, + Object &ob_grease_pencil, + const StringRefNull name, + const std::optional &stroke_color, + const std::optional &fill_color) +{ + int index; + Material *ma = BKE_grease_pencil_object_material_ensure_by_name( + &bmain, &ob_grease_pencil, DATA_(name.c_str()), &index); + + if (stroke_color.has_value()) { + copy_v4_v4(ma->gp_style->stroke_rgba, stroke_color.value()); + srgb_to_linearrgb_v4(ma->gp_style->stroke_rgba, ma->gp_style->stroke_rgba); + } + + if (fill_color.has_value()) { + copy_v4_v4(ma->gp_style->fill_rgba, fill_color.value()); + srgb_to_linearrgb_v4(ma->gp_style->fill_rgba, ma->gp_style->fill_rgba); + } + + SET_FLAG_FROM_TEST(ma->gp_style->flag, stroke_color.has_value(), GP_MATERIAL_STROKE_SHOW); + SET_FLAG_FROM_TEST(ma->gp_style->flag, fill_color.has_value(), GP_MATERIAL_FILL_SHOW); + + return index; +} + +static void mesh_data_to_grease_pencil(const Mesh &mesh_eval, + GreasePencil &grease_pencil, + const int current_frame, + const bool generate_faces, + const float stroke_radius, + const float offset) +{ + grease_pencil.flag |= GREASE_PENCIL_STROKE_ORDER_3D; + + if (mesh_eval.edges_num <= 0) { + return; + } + + bke::greasepencil::Layer &layer_line = grease_pencil.add_layer(DATA_("Lines")); + bke::greasepencil::Drawing *drawing_line = grease_pencil.insert_frame(layer_line, current_frame); + + constexpr int face_mat_index = 1; + const Span mesh_positions = mesh_eval.vert_positions(); + const Span vert_normals = mesh_eval.vert_normals(); + const Span edges = mesh_eval.edges(); + const OffsetIndices faces = mesh_eval.faces(); + Span faces_span = faces.data(); + const Span corner_verts = mesh_eval.corner_verts(); + + if (generate_faces && !faces.is_empty()) { + bke::greasepencil::Layer &layer_fill = grease_pencil.add_layer(DATA_("Fills")); + bke::greasepencil::Drawing *drawing_fill = grease_pencil.insert_frame(layer_fill, + current_frame); + const int fills_num = faces.size(); + const int fills_points_num = corner_verts.size(); + + drawing_fill->strokes_for_write().resize(fills_points_num, fills_num); + bke::CurvesGeometry &curves_fill = drawing_fill->strokes_for_write(); + MutableSpan positions_fill = curves_fill.positions_for_write(); + MutableSpan offsets_fill = curves_fill.offsets_for_write(); + MutableSpan cyclic_fill = curves_fill.cyclic_for_write(); + bke::SpanAttributeWriter stroke_materials_fill = + curves_fill.attributes_for_write().lookup_or_add_for_write_span( + "material_index", bke::AttrDomain::Curve); + curves_fill.fill_curve_types(CURVE_TYPE_POLY); + + array_utils::gather(mesh_positions, corner_verts, positions_fill); + array_utils::copy(faces_span, offsets_fill); + cyclic_fill.fill(true); + stroke_materials_fill.span.fill(face_mat_index); + stroke_materials_fill.finish(); + } + + const int edges_num = edges.size(); + const int points_num = edges_num * 2; + + bke::CurvesGeometry &curves = drawing_line->strokes_for_write(); + curves.resize(points_num, edges_num); + MutableSpan positions = curves.positions_for_write(); + MutableSpan offsets = curves.offsets_for_write(); + MutableSpan radii = curves.radius_for_write(); + curves.fill_curve_types(CURVE_TYPE_POLY); + + for (const int edge_i : edges.index_range()) { + const int2 edge = edges[edge_i]; + const int point_i = edge_i * 2; + positions[point_i] = mesh_positions[edge[0]] + offset * vert_normals[edge[0]]; + positions[point_i + 1] = mesh_positions[edge[1]] + offset * vert_normals[edge[1]]; + radii[point_i] = radii[point_i + 1] = stroke_radius; + } + radii.fill(stroke_radius); + + offset_indices::fill_constant_group_size(2, 0, offsets); +} + +static Object *convert_mesh_to_grease_pencil(Base &base, + ObjectConversionInfo &info, + Base **r_new_base) +{ + Object *ob = base.object; + ob->flag |= OB_DONE; + Object *newob = get_object_for_conversion(base, info, r_new_base); + + const bool generate_faces = RNA_boolean_get(info.op_props, "faces"); + const int thickness = RNA_int_get(info.op_props, "thickness"); + const float offset = RNA_float_get(info.op_props, "offset"); + + /* To be compatible with the thickness value prior to Grease Pencil v3. */ + const float stroke_radius = float(thickness) / 1000.0f; + + const Object *ob_eval = DEG_get_evaluated_object(info.depsgraph, ob); + const Mesh *mesh_eval = BKE_object_get_evaluated_mesh(ob_eval); + + BKE_object_free_derived_caches(newob); + BKE_object_free_modifiers(newob, 0); + + GreasePencil *grease_pencil = BKE_grease_pencil_add(info.bmain, BKE_id_name(mesh_eval->id)); + newob->data = grease_pencil; + newob->type = OB_GREASE_PENCIL; + + /* Reset `ob->totcol` since currently the generic / grease pencil material functions still + * depend on this value being coherent (The same value as `GreasePencil::material_array_num`). + */ + short *totcol = BKE_object_material_len_p(newob); + newob->totcol = *totcol; + + mesh_to_grease_pencil_add_material( + *info.bmain, *newob, DATA_("Stroke"), float4(0.0f, 0.0f, 0.0f, 1.0f), {}); + if (generate_faces) { + mesh_to_grease_pencil_add_material(*info.bmain, *newob, DATA_("Fill"), {}, float4(1.0f)); + } + + mesh_data_to_grease_pencil( + *mesh_eval, *grease_pencil, info.scene->r.cfra, generate_faces, stroke_radius, offset); + + return newob; +} + static Object *convert_mesh(Base &base, const ObjectType target, ObjectConversionInfo &info, @@ -3100,6 +3241,8 @@ static Object *convert_mesh(Base &base, return convert_mesh_to_point_cloud(base, info, r_new_base); case OB_MESH: return convert_mesh_to_mesh(base, info, r_new_base); + case OB_GREASE_PENCIL: + return convert_mesh_to_grease_pencil(base, info, r_new_base); default: /* Current logic does convert mesh to mesh for any other target types. This would change * after other types of conversion are designed and implemented. */ @@ -3728,6 +3871,7 @@ static int object_convert_exec(bContext *C, wmOperator *op) info.obact = obact; info.keep_original = keep_original; info.do_merge_customdata = do_merge_customdata; + info.op_props = op->ptr; info.reports = op->reports; Base *act_base = nullptr; @@ -3913,11 +4057,9 @@ static void object_convert_ui(bContext * /*C*/, wmOperator *op) if (target == OB_MESH) { uiItemR(layout, op->ptr, "merge_customdata", UI_ITEM_NONE, std::nullopt, ICON_NONE); } - else if (target == OB_GPENCIL_LEGACY) { + else if (target == OB_GREASE_PENCIL) { uiItemR(layout, op->ptr, "thickness", UI_ITEM_NONE, std::nullopt, ICON_NONE); - uiItemR(layout, op->ptr, "angle", UI_ITEM_NONE, std::nullopt, ICON_NONE); uiItemR(layout, op->ptr, "offset", UI_ITEM_NONE, std::nullopt, ICON_NONE); - uiItemR(layout, op->ptr, "seams", UI_ITEM_NONE, std::nullopt, ICON_NONE); uiItemR(layout, op->ptr, "faces", UI_ITEM_NONE, std::nullopt, ICON_NONE); } } @@ -3959,20 +4101,7 @@ void OBJECT_OT_convert(wmOperatorType *ot) "Merge UVs", "Merge UV coordinates that share a vertex to account for imprecision in some modifiers"); - prop = RNA_def_float_rotation(ot->srna, - "angle", - 0, - nullptr, - DEG2RADF(0.0f), - DEG2RADF(180.0f), - "Threshold Angle", - "Threshold to determine ends of the strokes", - DEG2RADF(0.0f), - DEG2RADF(180.0f)); - RNA_def_property_float_default(prop, DEG2RADF(70.0f)); - RNA_def_int(ot->srna, "thickness", 5, 1, 100, "Thickness", "", 1, 100); - RNA_def_boolean(ot->srna, "seams", false, "Only Seam Edges", "Convert only seam edges"); RNA_def_boolean(ot->srna, "faces", true, "Export Faces", "Export faces as filled strokes"); RNA_def_float_distance(ot->srna, "offset", diff --git a/tests/python/object_conversion.py b/tests/python/object_conversion.py index 089a9dddd01..0046d217743 100644 --- a/tests/python/object_conversion.py +++ b/tests/python/object_conversion.py @@ -136,10 +136,10 @@ def main(): ConversionPair('Curves 6', 'BezierCircle', 'CURVES', 'CURVES'), ConversionPair('Curves 7', 'BezierCurve', 'CURVES', 'CURVES'), ConversionPair('Curves 8', 'Text', 'CURVES', 'CURVES'), - ConversionPair('GreasePencil 1', 'Cube', 'GREASEPENCIL', 'MESH'), - ConversionPair('GreasePencil 2', 'CubeWithEdges', 'GREASEPENCIL', 'MESH'), - ConversionPair('GreasePencil 3', 'Plane', 'GREASEPENCIL', 'MESH'), - ConversionPair('GreasePencil 4', 'HollowPlane', 'GREASEPENCIL', 'MESH'), + ConversionPair('GreasePencil 1', 'Cube', 'GREASEPENCIL', 'GREASEPENCIL'), + ConversionPair('GreasePencil 2', 'CubeWithEdges', 'GREASEPENCIL', 'GREASEPENCIL'), + ConversionPair('GreasePencil 3', 'Plane', 'GREASEPENCIL', 'GREASEPENCIL'), + ConversionPair('GreasePencil 4', 'HollowPlane', 'GREASEPENCIL', 'GREASEPENCIL'), ConversionPair('GreasePencil 5', 'Suzanne', 'GREASEPENCIL', 'GREASEPENCIL'), ConversionPair('GreasePencil 6', 'BezierCircle', 'GREASEPENCIL', 'GREASEPENCIL'), ConversionPair('GreasePencil 7', 'BezierCurve', 'GREASEPENCIL', 'GREASEPENCIL'),