Fix #121356: GPv3: Conversion operators to/from Curves

Adds object conversion between Grease Pencil and Curves types.

GPv3 object is converted to Curves by combining all visible layers at
the current frame.
Curves object is converted to Grease Pencil by constructing a new layer
and keyframe with the curves geometry.

The type enum for the conversion operator gets a dynamic function to
check for the experimental flags and only show the enabled Grease Pencil
object types. It does not currently exclude unsupported type
combinations.

Pull Request: https://projects.blender.org/blender/blender/pulls/122304
This commit is contained in:
Lukas Tönne
2024-05-27 15:27:36 +02:00
parent 854dc30c63
commit f5b9ff15a0
4 changed files with 205 additions and 1 deletions

View File

@@ -984,6 +984,12 @@ inline bool GreasePencil::has_active_group() const
void *BKE_grease_pencil_add(Main *bmain, const char *name);
GreasePencil *BKE_grease_pencil_new_nomain();
GreasePencil *BKE_grease_pencil_copy_for_eval(const GreasePencil *grease_pencil_src);
/**
* Move data from a grease pencil outside of the main data-base into a grease pencil in the
* data-base. Takes ownership of the source mesh. */
void BKE_grease_pencil_nomain_to_grease_pencil(GreasePencil *grease_pencil_src,
GreasePencil *grease_pencil_dst);
void BKE_grease_pencil_data_update(Depsgraph *depsgraph, Scene *scene, Object *object);
void BKE_grease_pencil_duplicate_drawing_array(const GreasePencil *grease_pencil_src,
GreasePencil *grease_pencil_dst);

View File

@@ -1735,6 +1735,67 @@ GreasePencil *BKE_grease_pencil_copy_for_eval(const GreasePencil *grease_pencil_
return grease_pencil;
}
void BKE_grease_pencil_nomain_to_grease_pencil(GreasePencil *grease_pencil_src,
GreasePencil *grease_pencil_dst)
{
using namespace blender;
using bke::greasepencil::Drawing;
using bke::greasepencil::DrawingReference;
/* Drawings. */
const int drawing_array_num = grease_pencil_src->drawing_array_num;
grease_pencil_dst->resize_drawings(drawing_array_num);
for (const int i : IndexRange(drawing_array_num)) {
if (grease_pencil_dst->drawing_array[i]) {
switch (grease_pencil_dst->drawing_array[i]->type) {
case GP_DRAWING:
MEM_delete(&reinterpret_cast<GreasePencilDrawing *>(grease_pencil_dst->drawing_array[i])
->wrap());
break;
case GP_DRAWING_REFERENCE:
MEM_delete(&reinterpret_cast<GreasePencilDrawingReference *>(
grease_pencil_dst->drawing_array[i])
->wrap());
break;
}
}
switch (grease_pencil_src->drawing_array[i]->type) {
case GP_DRAWING: {
const Drawing &src_drawing =
reinterpret_cast<GreasePencilDrawing *>(grease_pencil_src->drawing_array[i])->wrap();
grease_pencil_dst->drawing_array[i] = reinterpret_cast<GreasePencilDrawingBase *>(
MEM_new<Drawing>(__func__, src_drawing));
break;
}
case GP_DRAWING_REFERENCE:
const DrawingReference &src_drawing_ref = reinterpret_cast<GreasePencilDrawingReference *>(
grease_pencil_src->drawing_array[i])
->wrap();
grease_pencil_dst->drawing_array[i] = reinterpret_cast<GreasePencilDrawingBase *>(
MEM_new<DrawingReference>(__func__, src_drawing_ref));
break;
}
}
/* Layers. */
if (grease_pencil_dst->root_group_ptr) {
MEM_delete(&grease_pencil_dst->root_group());
}
grease_pencil_dst->root_group_ptr = MEM_new<bke::greasepencil::LayerGroup>(
__func__, grease_pencil_src->root_group_ptr->wrap());
BLI_assert(grease_pencil_src->layers().size() == grease_pencil_dst->layers().size());
CustomData_copy(&grease_pencil_src->layers_data,
&grease_pencil_dst->layers_data,
eCustomDataMask(CD_MASK_ALL),
grease_pencil_src->layers().size());
DEG_id_tag_update(&grease_pencil_dst->id, ID_RECALC_GEOMETRY);
BKE_id_free(nullptr, grease_pencil_src);
}
static void grease_pencil_evaluate_modifiers(Depsgraph *depsgraph,
Scene *scene,
Object *object,

View File

@@ -9,6 +9,7 @@ set(INC
../../blentranslation
../../bmesh
../../functions
../../geometry
../../gpencil_modifiers_legacy
../../gpu
../../ikplugin

View File

@@ -11,6 +11,7 @@
#include <cstring>
#include <optional>
#include "BKE_curves.hh"
#include "MEM_guardedalloc.h"
#include "DNA_anim_types.h"
@@ -97,6 +98,8 @@
#include "DEG_depsgraph_build.hh"
#include "DEG_depsgraph_query.hh"
#include "GEO_join_geometries.hh"
#include "RNA_access.hh"
#include "RNA_define.hh"
#include "RNA_enum_types.hh"
@@ -3012,6 +3015,38 @@ static const EnumPropertyItem convert_target_items[] = {
{0, nullptr, 0, nullptr, nullptr},
};
static const EnumPropertyItem *convert_target_itemf(bContext *C,
PointerRNA * /*ptr*/,
PropertyRNA * /*prop*/,
bool *r_free)
{
if (!C) { /* needed for docs */
return convert_target_items;
}
EnumPropertyItem *item = nullptr;
int totitem = 0;
RNA_enum_items_add_value(&item, &totitem, convert_target_items, OB_MESH);
RNA_enum_items_add_value(&item, &totitem, convert_target_items, OB_CURVES_LEGACY);
RNA_enum_items_add_value(&item, &totitem, convert_target_items, OB_CURVES);
if (U.experimental.use_new_point_cloud_type) {
RNA_enum_items_add_value(&item, &totitem, convert_target_items, OB_POINTCLOUD);
}
if (U.experimental.use_grease_pencil_version3) {
RNA_enum_items_add_value(&item, &totitem, convert_target_items, OB_GREASE_PENCIL);
}
else {
RNA_enum_items_add_value(&item, &totitem, convert_target_items, OB_GPENCIL_LEGACY);
}
RNA_enum_item_end(&item, &totitem);
*r_free = true;
return item;
}
static void object_data_convert_curve_to_mesh(Main *bmain, Depsgraph *depsgraph, Object *ob)
{
Object *object_eval = DEG_get_evaluated_object(depsgraph, ob);
@@ -3346,6 +3381,49 @@ static int object_convert_exec(bContext *C, wmOperator *op)
BKE_object_free_derived_caches(newob);
BKE_object_free_modifiers(newob, 0);
}
else if (geometry.has_grease_pencil()) {
if (keep_original) {
basen = duplibase_for_convert(bmain, depsgraph, scene, view_layer, base, nullptr);
newob = basen->object;
/* Decrement original curve's usage count. */
Curve *legacy_curve = static_cast<Curve *>(newob->data);
id_us_min(&legacy_curve->id);
/* Make a copy of the curve. */
newob->data = BKE_id_copy(bmain, &legacy_curve->id);
}
else {
newob = ob;
}
Curves *new_curves = static_cast<Curves *>(BKE_id_new(bmain, ID_CV, newob->id.name + 2));
newob->data = new_curves;
newob->type = OB_CURVES;
if (const Curves *curves_eval = geometry.get_curves()) {
new_curves->geometry.wrap() = curves_eval->geometry.wrap();
BKE_object_material_from_eval_data(bmain, newob, &curves_eval->id);
}
else if (const GreasePencil *grease_pencil = geometry.get_grease_pencil()) {
const Vector<ed::greasepencil::DrawingInfo> drawings =
ed::greasepencil::retrieve_visible_drawings(*scene, *grease_pencil, false);
Array<bke::GeometrySet> geometries(drawings.size());
for (const int i : drawings.index_range()) {
Curves *curves_id = static_cast<Curves *>(BKE_id_new_nomain(ID_CV, nullptr));
curves_id->geometry.wrap() = drawings[i].drawing.strokes();
geometries[i] = bke::GeometrySet::from_curves(curves_id);
}
bke::GeometrySet joined_curves = geometry::join_geometries(geometries, {});
new_curves->geometry.wrap() = joined_curves.get_curves()->geometry.wrap();
new_curves->geometry.wrap().tag_topology_changed();
BKE_object_material_from_eval_data(bmain, newob, &joined_curves.get_curves()->id);
}
BKE_object_free_derived_caches(newob);
BKE_object_free_modifiers(newob, 0);
}
else {
BKE_reportf(
op->reports, RPT_WARNING, "Object '%s' has no evaluated curves data", ob->id.name + 2);
@@ -3658,6 +3736,62 @@ static int object_convert_exec(bContext *C, wmOperator *op)
BKE_object_free_derived_caches(newob);
BKE_object_free_modifiers(newob, 0);
}
else if (ob->type == OB_CURVES && target == OB_GREASE_PENCIL) {
ob->flag |= OB_DONE;
Object *ob_eval = DEG_get_evaluated_object(depsgraph, ob);
bke::GeometrySet geometry;
if (ob_eval->runtime->geometry_set_eval != nullptr) {
geometry = *ob_eval->runtime->geometry_set_eval;
}
if (keep_original) {
basen = duplibase_for_convert(bmain, depsgraph, scene, view_layer, base, nullptr);
newob = basen->object;
Curves *curves = static_cast<Curves *>(newob->data);
id_us_min(&curves->id);
newob->data = BKE_id_copy(bmain, &curves->id);
}
else {
newob = ob;
}
GreasePencil *new_grease_pencil = static_cast<GreasePencil *>(
BKE_id_new(bmain, ID_GP, newob->id.name + 2));
newob->data = new_grease_pencil;
newob->type = OB_GREASE_PENCIL;
if (const GreasePencil *grease_pencil_eval = geometry.get_grease_pencil()) {
BKE_grease_pencil_nomain_to_grease_pencil(
BKE_grease_pencil_copy_for_eval(grease_pencil_eval), new_grease_pencil);
BKE_object_material_from_eval_data(bmain, newob, &grease_pencil_eval->id);
new_grease_pencil->attributes_for_write().remove_anonymous();
}
else if (const Curves *curves_eval = geometry.get_curves()) {
GreasePencil *grease_pencil = BKE_grease_pencil_new_nomain();
/* Insert a default layer and place the drawing on frame 1. */
const std::string layer_name = "Layer";
const int frame_number = 1;
bke::greasepencil::Layer &layer = grease_pencil->add_layer(layer_name);
bke::greasepencil::Drawing *drawing = grease_pencil->insert_frame(layer, frame_number);
BLI_assert(drawing != nullptr);
drawing->strokes_for_write() = curves_eval->geometry.wrap();
BKE_grease_pencil_nomain_to_grease_pencil(grease_pencil, new_grease_pencil);
BKE_object_material_from_eval_data(bmain, newob, &curves_eval->id);
}
else {
BKE_reportf(op->reports,
RPT_WARNING,
"Object '%s' has no evaluated grease pencil or curves data",
ob->id.name + 2);
}
BKE_object_free_derived_caches(newob);
BKE_object_free_modifiers(newob, 0);
}
else {
continue;
}
@@ -3807,8 +3941,10 @@ void OBJECT_OT_convert(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* properties */
ot->prop = RNA_def_enum(
ot->prop = prop = RNA_def_enum(
ot->srna, "target", convert_target_items, OB_MESH, "Target", "Type of object to convert to");
RNA_def_enum_funcs(prop, convert_target_itemf);
prop = RNA_def_boolean(ot->srna,
"keep_original",
false,