From 27424b758a874bb285ae439f9707cfd4009a9c16 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Mon, 28 Mar 2022 16:29:36 -0500 Subject: Cleanup: Move two grease pencil files to C++ This is meant to allow using C++ data structures in this file as a performance improvement. Particularly `Vector` instead of `ListBase` for `duplilist`. This changes builds on all platforms on the buildbot. --- source/blender/editors/gpencil/CMakeLists.txt | 4 +- .../editors/gpencil/gpencil_bake_animation.c | 436 ------------------- .../editors/gpencil/gpencil_bake_animation.cc | 439 +++++++++++++++++++ source/blender/editors/gpencil/gpencil_intern.h | 8 + source/blender/editors/gpencil/gpencil_mesh.c | 468 -------------------- source/blender/editors/gpencil/gpencil_mesh.cc | 471 +++++++++++++++++++++ 6 files changed, 920 insertions(+), 906 deletions(-) delete mode 100644 source/blender/editors/gpencil/gpencil_bake_animation.c create mode 100644 source/blender/editors/gpencil/gpencil_bake_animation.cc delete mode 100644 source/blender/editors/gpencil/gpencil_mesh.c create mode 100644 source/blender/editors/gpencil/gpencil_mesh.cc (limited to 'source/blender/editors/gpencil') diff --git a/source/blender/editors/gpencil/CMakeLists.txt b/source/blender/editors/gpencil/CMakeLists.txt index f8fa23a54d1..09a3cac0d48 100644 --- a/source/blender/editors/gpencil/CMakeLists.txt +++ b/source/blender/editors/gpencil/CMakeLists.txt @@ -28,7 +28,7 @@ set(SRC gpencil_add_monkey.c gpencil_add_stroke.c gpencil_armature.c - gpencil_bake_animation.c + gpencil_bake_animation.cc gpencil_convert.c gpencil_data.c gpencil_edit.c @@ -36,7 +36,7 @@ set(SRC gpencil_fill.c gpencil_interpolate.c gpencil_merge.c - gpencil_mesh.c + gpencil_mesh.cc gpencil_ops.c gpencil_ops_versioning.c gpencil_paint.c diff --git a/source/blender/editors/gpencil/gpencil_bake_animation.c b/source/blender/editors/gpencil/gpencil_bake_animation.c deleted file mode 100644 index 5d33377900f..00000000000 --- a/source/blender/editors/gpencil/gpencil_bake_animation.c +++ /dev/null @@ -1,436 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later - * Copyright 2021 Blender Foundation. */ - -/** \file - * \ingroup edgpencil - */ - -#include "MEM_guardedalloc.h" - -#include "BLI_blenlib.h" -#include "BLI_ghash.h" -#include "BLI_math.h" - -#include "DNA_anim_types.h" -#include "DNA_gpencil_types.h" -#include "DNA_material_types.h" -#include "DNA_scene_types.h" -#include "DNA_screen_types.h" - -#include "BKE_anim_data.h" -#include "BKE_context.h" -#include "BKE_duplilist.h" -#include "BKE_gpencil.h" -#include "BKE_gpencil_geom.h" -#include "BKE_gpencil_modifier.h" -#include "BKE_layer.h" -#include "BKE_main.h" -#include "BKE_material.h" -#include "BKE_object.h" -#include "BKE_report.h" -#include "BKE_scene.h" - -#include "DEG_depsgraph.h" -#include "DEG_depsgraph_query.h" - -#include "WM_api.h" -#include "WM_types.h" - -#include "RNA_access.h" -#include "RNA_define.h" - -#include "ED_gpencil.h" -#include "ED_transform_snap_object_context.h" - -#include "gpencil_intern.h" - -const EnumPropertyItem rna_gpencil_reproject_type_items[] = { - {GP_REPROJECT_KEEP, "KEEP", 0, "No Reproject", ""}, - {GP_REPROJECT_FRONT, "FRONT", 0, "Front", "Reproject the strokes using the X-Z plane"}, - {GP_REPROJECT_SIDE, "SIDE", 0, "Side", "Reproject the strokes using the Y-Z plane"}, - {GP_REPROJECT_TOP, "TOP", 0, "Top", "Reproject the strokes using the X-Y plane"}, - {GP_REPROJECT_VIEW, - "VIEW", - 0, - "View", - "Reproject the strokes to end up on the same plane, as if drawn from the current " - "viewpoint " - "using 'Cursor' Stroke Placement"}, - {GP_REPROJECT_CURSOR, - "CURSOR", - 0, - "Cursor", - "Reproject the strokes using the orientation of 3D cursor"}, - {0, NULL, 0, NULL, NULL}, -}; - -/* Check frame_end is always > start frame! */ -static void gpencil_bake_set_frame_end(struct Main *UNUSED(main), - struct Scene *UNUSED(scene), - struct PointerRNA *ptr) -{ - int frame_start = RNA_int_get(ptr, "frame_start"); - int frame_end = RNA_int_get(ptr, "frame_end"); - - if (frame_end <= frame_start) { - RNA_int_set(ptr, "frame_end", frame_start + 1); - } -} - -/* Extract mesh animation to Grease Pencil. */ -static bool gpencil_bake_grease_pencil_animation_poll(bContext *C) -{ - Object *obact = CTX_data_active_object(C); - if (CTX_data_mode_enum(C) != CTX_MODE_OBJECT) { - return false; - } - - /* Check if grease pencil or empty for dupli groups. */ - if ((obact == NULL) || (!ELEM(obact->type, OB_GPENCIL, OB_EMPTY))) { - return false; - } - - /* Only if the current view is 3D View. */ - ScrArea *area = CTX_wm_area(C); - return (area && area->spacetype); -} - -typedef struct GpBakeOb { - struct GpBakeOb *next, *prev; - Object *ob; -} GpBakeOb; - -/* Get list of keyframes used by selected objects. */ -static void animdata_keyframe_list_get(ListBase *ob_list, - const bool only_selected, - GHash *r_keyframes) -{ - /* Loop all objects to get the list of keyframes used. */ - LISTBASE_FOREACH (GpBakeOb *, elem, ob_list) { - Object *ob = elem->ob; - AnimData *adt = BKE_animdata_from_id(&ob->id); - if ((adt == NULL) || (adt->action == NULL)) { - continue; - } - LISTBASE_FOREACH (FCurve *, fcurve, &adt->action->curves) { - int i; - BezTriple *bezt; - for (i = 0, bezt = fcurve->bezt; i < fcurve->totvert; i++, bezt++) { - /* Keyframe number is x value of point. */ - if ((bezt->f2 & SELECT) || (!only_selected)) { - /* Insert only one key for each keyframe number. */ - int key = (int)bezt->vec[1][0]; - if (!BLI_ghash_haskey(r_keyframes, POINTER_FROM_INT(key))) { - BLI_ghash_insert(r_keyframes, POINTER_FROM_INT(key), POINTER_FROM_INT(key)); - } - } - } - } - } -} - -static void gpencil_bake_duplilist(Depsgraph *depsgraph, Scene *scene, Object *ob, ListBase *list) -{ - GpBakeOb *elem = NULL; - ListBase *lb; - DupliObject *dob; - lb = object_duplilist(depsgraph, scene, ob); - for (dob = lb->first; dob; dob = dob->next) { - if (dob->ob->type != OB_GPENCIL) { - continue; - } - - elem = MEM_callocN(sizeof(GpBakeOb), __func__); - elem->ob = dob->ob; - BLI_addtail(list, elem); - } - - free_object_duplilist(lb); -} - -static void gpencil_bake_ob_list(bContext *C, Depsgraph *depsgraph, Scene *scene, ListBase *list) -{ - GpBakeOb *elem = NULL; - - /* Add active object. In some files this could not be in selected array. */ - Object *obact = CTX_data_active_object(C); - - if (obact->type == OB_GPENCIL) { - elem = MEM_callocN(sizeof(GpBakeOb), __func__); - elem->ob = obact; - BLI_addtail(list, elem); - } - /* Add duplilist. */ - else if (obact->type == OB_EMPTY) { - gpencil_bake_duplilist(depsgraph, scene, obact, list); - } - - /* Add other selected objects. */ - CTX_DATA_BEGIN (C, Object *, ob, selected_objects) { - if (ob == obact) { - continue; - } - /* Add selected objects. */ - if (ob->type == OB_GPENCIL) { - elem = MEM_callocN(sizeof(GpBakeOb), __func__); - elem->ob = ob; - BLI_addtail(list, elem); - } - - /* Add duplilist. */ - if (ob->type == OB_EMPTY) { - gpencil_bake_duplilist(depsgraph, scene, ob, list); - } - } - CTX_DATA_END; -} - -static void gpencil_bake_free_ob_list(ListBase *list) -{ - LISTBASE_FOREACH_MUTABLE (GpBakeOb *, elem, list) { - MEM_SAFE_FREE(elem); - } -} - -static int gpencil_bake_grease_pencil_animation_exec(bContext *C, wmOperator *op) -{ - Main *bmain = CTX_data_main(C); - Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); - Scene *scene = CTX_data_scene(C); - View3D *v3d = CTX_wm_view3d(C); - - ListBase ob_selected_list = {NULL, NULL}; - gpencil_bake_ob_list(C, depsgraph, scene, &ob_selected_list); - - /* Grab all relevant settings. */ - const int step = RNA_int_get(op->ptr, "step"); - - const int frame_start = (scene->r.sfra > RNA_int_get(op->ptr, "frame_start")) ? - scene->r.sfra : - RNA_int_get(op->ptr, "frame_start"); - - const int frame_end = (scene->r.efra < RNA_int_get(op->ptr, "frame_end")) ? - scene->r.efra : - RNA_int_get(op->ptr, "frame_end"); - - const bool only_selected = RNA_boolean_get(op->ptr, "only_selected"); - const int frame_offset = RNA_int_get(op->ptr, "frame_target") - frame_start; - const int project_type = RNA_enum_get(op->ptr, "project_type"); - - /* Create a new grease pencil object. */ - Object *ob_gpencil = NULL; - ushort local_view_bits = (v3d && v3d->localvd) ? v3d->local_view_uuid : 0; - ob_gpencil = ED_gpencil_add_object(C, scene->cursor.location, local_view_bits); - float invmat[4][4]; - invert_m4_m4(invmat, ob_gpencil->obmat); - - bGPdata *gpd_dst = (bGPdata *)ob_gpencil->data; - gpd_dst->draw_mode = GP_DRAWMODE_2D; - - /* Set cursor to indicate working. */ - WM_cursor_wait(true); - - GP_SpaceConversion gsc = {NULL}; - SnapObjectContext *sctx = NULL; - if (project_type != GP_REPROJECT_KEEP) { - /* Init space conversion stuff. */ - gpencil_point_conversion_init(C, &gsc); - /* Move the grease pencil object to conversion data. */ - gsc.ob = ob_gpencil; - - /* Init snap context for geometry projection. */ - sctx = ED_transform_snap_object_context_create(scene, 0); - } - - /* Loop all frame range. */ - int oldframe = (int)DEG_get_ctime(depsgraph); - int key = -1; - - /* Get list of keyframes. */ - GHash *keyframe_list = BLI_ghash_int_new(__func__); - if (only_selected) { - animdata_keyframe_list_get(&ob_selected_list, only_selected, keyframe_list); - } - - for (int i = frame_start; i < frame_end + 1; i++) { - key++; - /* Jump if not step limit but include last frame always. */ - if ((key % step != 0) && (i != frame_end)) { - continue; - } - - /* Check if frame is in the list of frames to be exported. */ - if ((only_selected) && (!BLI_ghash_haskey(keyframe_list, POINTER_FROM_INT(i)))) { - continue; - } - - /* Move scene to new frame. */ - CFRA = i; - BKE_scene_graph_update_for_newframe(depsgraph); - - /* Loop all objects in the list. */ - LISTBASE_FOREACH (GpBakeOb *, elem, &ob_selected_list) { - Object *ob_eval = (Object *)DEG_get_evaluated_object(depsgraph, elem->ob); - bGPdata *gpd_src = ob_eval->data; - - LISTBASE_FOREACH (bGPDlayer *, gpl_src, &gpd_src->layers) { - /* Create destination layer. */ - char *layer_name; - layer_name = BLI_sprintfN("%s_%s", elem->ob->id.name + 2, gpl_src->info); - bGPDlayer *gpl_dst = BKE_gpencil_layer_named_get(gpd_dst, layer_name); - if (gpl_dst == NULL) { - gpl_dst = BKE_gpencil_layer_addnew(gpd_dst, layer_name, true, false); - } - MEM_freeN(layer_name); - - /* Layer Transform matrix. */ - float matrix[4][4]; - BKE_gpencil_layer_transform_matrix_get(depsgraph, elem->ob, gpl_src, matrix); - - /* Apply time modifier. */ - int remap_cfra = BKE_gpencil_time_modifier_cfra( - depsgraph, scene, elem->ob, gpl_src, CFRA, false); - /* Duplicate frame. */ - bGPDframe *gpf_src = BKE_gpencil_layer_frame_get( - gpl_src, remap_cfra, GP_GETFRAME_USE_PREV); - if (gpf_src == NULL) { - continue; - } - bGPDframe *gpf_dst = BKE_gpencil_frame_duplicate(gpf_src, true); - gpf_dst->framenum = CFRA + frame_offset; - gpf_dst->flag &= ~GP_FRAME_SELECT; - BLI_addtail(&gpl_dst->frames, gpf_dst); - - LISTBASE_FOREACH (bGPDstroke *, gps, &gpf_dst->strokes) { - /* Create material of the stroke. */ - Material *ma_src = BKE_object_material_get(elem->ob, gps->mat_nr + 1); - bool found = false; - for (int index = 0; index < ob_gpencil->totcol; index++) { - Material *ma_dst = BKE_object_material_get(ob_gpencil, index + 1); - if (ma_src == ma_dst) { - found = true; - break; - } - } - if (!found) { - BKE_object_material_slot_add(bmain, ob_gpencil); - BKE_object_material_assign( - bmain, ob_gpencil, ma_src, ob_gpencil->totcol, BKE_MAT_ASSIGN_USERPREF); - } - - /* Set new material index. */ - gps->mat_nr = BKE_gpencil_object_material_index_get(ob_gpencil, ma_src); - - /* Update point location to new object space. */ - for (int j = 0; j < gps->totpoints; j++) { - bGPDspoint *pt = &gps->points[j]; - mul_m4_v3(matrix, &pt->x); - mul_m4_v3(invmat, &pt->x); - } - - /* Reproject stroke. */ - if (project_type != GP_REPROJECT_KEEP) { - ED_gpencil_stroke_reproject( - depsgraph, &gsc, sctx, gpl_dst, gpf_dst, gps, project_type, false); - } - else { - BKE_gpencil_stroke_geometry_update(gpd_dst, gps); - } - } - } - } - } - /* Return scene frame state and DB to original state. */ - CFRA = oldframe; - BKE_scene_graph_update_for_newframe(depsgraph); - - /* Free memory. */ - gpencil_bake_free_ob_list(&ob_selected_list); - if (sctx != NULL) { - ED_transform_snap_object_context_destroy(sctx); - } - /* Free temp hash table. */ - if (keyframe_list != NULL) { - BLI_ghash_free(keyframe_list, NULL, NULL); - } - - /* Notifiers. */ - DEG_relations_tag_update(bmain); - DEG_id_tag_update(&scene->id, ID_RECALC_SELECT); - DEG_id_tag_update(&gpd_dst->id, ID_RECALC_COPY_ON_WRITE); - WM_event_add_notifier(C, NC_OBJECT | NA_ADDED, NULL); - WM_event_add_notifier(C, NC_SCENE | ND_OB_ACTIVE, scene); - - /* Reset cursor. */ - WM_cursor_wait(false); - - /* done */ - return OPERATOR_FINISHED; -} - -static int gpencil_bake_grease_pencil_animation_invoke(bContext *C, - wmOperator *op, - const wmEvent *UNUSED(event)) -{ - PropertyRNA *prop; - Scene *scene = CTX_data_scene(C); - - prop = RNA_struct_find_property(op->ptr, "frame_start"); - if (!RNA_property_is_set(op->ptr, prop)) { - const int frame_start = RNA_property_int_get(op->ptr, prop); - if (frame_start < scene->r.sfra) { - RNA_property_int_set(op->ptr, prop, scene->r.sfra); - } - } - - prop = RNA_struct_find_property(op->ptr, "frame_end"); - if (!RNA_property_is_set(op->ptr, prop)) { - const int frame_end = RNA_property_int_get(op->ptr, prop); - if (frame_end > scene->r.efra) { - RNA_property_int_set(op->ptr, prop, scene->r.efra); - } - } - - /* Show popup dialog to allow editing. */ - return WM_operator_props_dialog_popup(C, op, 250); -} - -void GPENCIL_OT_bake_grease_pencil_animation(wmOperatorType *ot) -{ - PropertyRNA *prop; - - /* identifiers */ - ot->name = "Bake Object Transform to Grease Pencil"; - ot->idname = "GPENCIL_OT_bake_grease_pencil_animation"; - ot->description = "Bake grease pencil object transform to grease pencil keyframes"; - - /* callbacks */ - ot->invoke = gpencil_bake_grease_pencil_animation_invoke; - ot->exec = gpencil_bake_grease_pencil_animation_exec; - ot->poll = gpencil_bake_grease_pencil_animation_poll; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - /* properties */ - prop = RNA_def_int( - ot->srna, "frame_start", 1, 1, 100000, "Start Frame", "The start frame", 1, 100000); - - prop = RNA_def_int( - ot->srna, "frame_end", 250, 1, 100000, "End Frame", "The end frame of animation", 1, 100000); - RNA_def_property_update_runtime(prop, gpencil_bake_set_frame_end); - - prop = RNA_def_int(ot->srna, "step", 1, 1, 100, "Step", "Step between generated frames", 1, 100); - - RNA_def_boolean( - ot->srna, "only_selected", 0, "Only Selected Keyframes", "Convert only selected keyframes"); - RNA_def_int( - ot->srna, "frame_target", 1, 1, 100000, "Target Frame", "Destination frame", 1, 100000); - - RNA_def_enum(ot->srna, - "project_type", - rna_gpencil_reproject_type_items, - GP_REPROJECT_KEEP, - "Projection Type", - ""); -} diff --git a/source/blender/editors/gpencil/gpencil_bake_animation.cc b/source/blender/editors/gpencil/gpencil_bake_animation.cc new file mode 100644 index 00000000000..0667da46e25 --- /dev/null +++ b/source/blender/editors/gpencil/gpencil_bake_animation.cc @@ -0,0 +1,439 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2021 Blender Foundation. */ + +/** \file + * \ingroup edgpencil + */ + +#include "MEM_guardedalloc.h" + +#include "BLI_blenlib.h" +#include "BLI_ghash.h" +#include "BLI_math.h" + +#include "DNA_anim_types.h" +#include "DNA_gpencil_types.h" +#include "DNA_material_types.h" +#include "DNA_scene_types.h" +#include "DNA_screen_types.h" + +#include "BKE_anim_data.h" +#include "BKE_context.h" +#include "BKE_duplilist.h" +#include "BKE_gpencil.h" +#include "BKE_gpencil_geom.h" +#include "BKE_gpencil_modifier.h" +#include "BKE_layer.h" +#include "BKE_main.h" +#include "BKE_material.h" +#include "BKE_object.h" +#include "BKE_report.h" +#include "BKE_scene.h" + +#include "DEG_depsgraph.h" +#include "DEG_depsgraph_query.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "RNA_access.h" +#include "RNA_define.h" + +#include "ED_gpencil.h" +#include "ED_transform_snap_object_context.h" + +#include "gpencil_intern.h" + +const EnumPropertyItem rna_gpencil_reproject_type_items[] = { + {GP_REPROJECT_KEEP, "KEEP", 0, "No Reproject", ""}, + {GP_REPROJECT_FRONT, "FRONT", 0, "Front", "Reproject the strokes using the X-Z plane"}, + {GP_REPROJECT_SIDE, "SIDE", 0, "Side", "Reproject the strokes using the Y-Z plane"}, + {GP_REPROJECT_TOP, "TOP", 0, "Top", "Reproject the strokes using the X-Y plane"}, + {GP_REPROJECT_VIEW, + "VIEW", + 0, + "View", + "Reproject the strokes to end up on the same plane, as if drawn from the current " + "viewpoint " + "using 'Cursor' Stroke Placement"}, + {GP_REPROJECT_CURSOR, + "CURSOR", + 0, + "Cursor", + "Reproject the strokes using the orientation of 3D cursor"}, + {0, nullptr, 0, nullptr, nullptr}, +}; + +/* Check frame_end is always > start frame! */ +static void gpencil_bake_set_frame_end(struct Main *UNUSED(main), + struct Scene *UNUSED(scene), + struct PointerRNA *ptr) +{ + int frame_start = RNA_int_get(ptr, "frame_start"); + int frame_end = RNA_int_get(ptr, "frame_end"); + + if (frame_end <= frame_start) { + RNA_int_set(ptr, "frame_end", frame_start + 1); + } +} + +/* Extract mesh animation to Grease Pencil. */ +static bool gpencil_bake_grease_pencil_animation_poll(bContext *C) +{ + Object *obact = CTX_data_active_object(C); + if (CTX_data_mode_enum(C) != CTX_MODE_OBJECT) { + return false; + } + + /* Check if grease pencil or empty for dupli groups. */ + if ((obact == nullptr) || (!ELEM(obact->type, OB_GPENCIL, OB_EMPTY))) { + return false; + } + + /* Only if the current view is 3D View. */ + ScrArea *area = CTX_wm_area(C); + return (area && area->spacetype); +} + +struct GpBakeOb { + struct GpBakeOb *next, *prev; + Object *ob; +}; + +/* Get list of keyframes used by selected objects. */ +static void animdata_keyframe_list_get(ListBase *ob_list, + const bool only_selected, + GHash *r_keyframes) +{ + /* Loop all objects to get the list of keyframes used. */ + LISTBASE_FOREACH (GpBakeOb *, elem, ob_list) { + Object *ob = elem->ob; + AnimData *adt = BKE_animdata_from_id(&ob->id); + if ((adt == nullptr) || (adt->action == nullptr)) { + continue; + } + LISTBASE_FOREACH (FCurve *, fcurve, &adt->action->curves) { + int i; + BezTriple *bezt; + for (i = 0, bezt = fcurve->bezt; i < fcurve->totvert; i++, bezt++) { + /* Keyframe number is x value of point. */ + if ((bezt->f2 & SELECT) || (!only_selected)) { + /* Insert only one key for each keyframe number. */ + int key = (int)bezt->vec[1][0]; + if (!BLI_ghash_haskey(r_keyframes, POINTER_FROM_INT(key))) { + BLI_ghash_insert(r_keyframes, POINTER_FROM_INT(key), POINTER_FROM_INT(key)); + } + } + } + } + } +} + +static void gpencil_bake_duplilist(Depsgraph *depsgraph, Scene *scene, Object *ob, ListBase *list) +{ + GpBakeOb *elem = nullptr; + ListBase *lb; + lb = object_duplilist(depsgraph, scene, ob); + LISTBASE_FOREACH (DupliObject *, dob, lb) { + if (dob->ob->type != OB_GPENCIL) { + continue; + } + + elem = MEM_cnew(__func__); + elem->ob = dob->ob; + BLI_addtail(list, elem); + } + + free_object_duplilist(lb); +} + +static void gpencil_bake_ob_list(bContext *C, Depsgraph *depsgraph, Scene *scene, ListBase *list) +{ + GpBakeOb *elem = nullptr; + + /* Add active object. In some files this could not be in selected array. */ + Object *obact = CTX_data_active_object(C); + + if (obact->type == OB_GPENCIL) { + elem = MEM_cnew(__func__); + elem->ob = obact; + BLI_addtail(list, elem); + } + /* Add duplilist. */ + else if (obact->type == OB_EMPTY) { + gpencil_bake_duplilist(depsgraph, scene, obact, list); + } + + /* Add other selected objects. */ + CTX_DATA_BEGIN (C, Object *, ob, selected_objects) { + if (ob == obact) { + continue; + } + /* Add selected objects. */ + if (ob->type == OB_GPENCIL) { + elem = MEM_cnew(__func__); + elem->ob = ob; + BLI_addtail(list, elem); + } + + /* Add duplilist. */ + if (ob->type == OB_EMPTY) { + gpencil_bake_duplilist(depsgraph, scene, ob, list); + } + } + CTX_DATA_END; +} + +static void gpencil_bake_free_ob_list(ListBase *list) +{ + LISTBASE_FOREACH_MUTABLE (GpBakeOb *, elem, list) { + MEM_SAFE_FREE(elem); + } +} + +static int gpencil_bake_grease_pencil_animation_exec(bContext *C, wmOperator *op) +{ + Main *bmain = CTX_data_main(C); + Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); + Scene *scene = CTX_data_scene(C); + View3D *v3d = CTX_wm_view3d(C); + + ListBase ob_selected_list = {nullptr, nullptr}; + gpencil_bake_ob_list(C, depsgraph, scene, &ob_selected_list); + + /* Grab all relevant settings. */ + const int step = RNA_int_get(op->ptr, "step"); + + const int frame_start = (scene->r.sfra > RNA_int_get(op->ptr, "frame_start")) ? + scene->r.sfra : + RNA_int_get(op->ptr, "frame_start"); + + const int frame_end = (scene->r.efra < RNA_int_get(op->ptr, "frame_end")) ? + scene->r.efra : + RNA_int_get(op->ptr, "frame_end"); + + const bool only_selected = RNA_boolean_get(op->ptr, "only_selected"); + const int frame_offset = RNA_int_get(op->ptr, "frame_target") - frame_start; + const eGP_ReprojectModes project_type = (eGP_ReprojectModes)RNA_enum_get(op->ptr, + "project_type"); + + /* Create a new grease pencil object. */ + Object *ob_gpencil = nullptr; + ushort local_view_bits = (v3d && v3d->localvd) ? v3d->local_view_uuid : 0; + ob_gpencil = ED_gpencil_add_object(C, scene->cursor.location, local_view_bits); + float invmat[4][4]; + invert_m4_m4(invmat, ob_gpencil->obmat); + + bGPdata *gpd_dst = (bGPdata *)ob_gpencil->data; + gpd_dst->draw_mode = GP_DRAWMODE_2D; + + /* Set cursor to indicate working. */ + WM_cursor_wait(true); + + GP_SpaceConversion gsc = {nullptr}; + SnapObjectContext *sctx = nullptr; + if (project_type != GP_REPROJECT_KEEP) { + /* Init space conversion stuff. */ + gpencil_point_conversion_init(C, &gsc); + /* Move the grease pencil object to conversion data. */ + gsc.ob = ob_gpencil; + + /* Init snap context for geometry projection. */ + sctx = ED_transform_snap_object_context_create(scene, 0); + } + + /* Loop all frame range. */ + int oldframe = (int)DEG_get_ctime(depsgraph); + int key = -1; + + /* Get list of keyframes. */ + GHash *keyframe_list = BLI_ghash_int_new(__func__); + if (only_selected) { + animdata_keyframe_list_get(&ob_selected_list, only_selected, keyframe_list); + } + + for (int i = frame_start; i < frame_end + 1; i++) { + key++; + /* Jump if not step limit but include last frame always. */ + if ((key % step != 0) && (i != frame_end)) { + continue; + } + + /* Check if frame is in the list of frames to be exported. */ + if ((only_selected) && (!BLI_ghash_haskey(keyframe_list, POINTER_FROM_INT(i)))) { + continue; + } + + /* Move scene to new frame. */ + CFRA = i; + BKE_scene_graph_update_for_newframe(depsgraph); + + /* Loop all objects in the list. */ + LISTBASE_FOREACH (GpBakeOb *, elem, &ob_selected_list) { + Object *ob_eval = (Object *)DEG_get_evaluated_object(depsgraph, elem->ob); + bGPdata *gpd_src = static_cast(ob_eval->data); + + LISTBASE_FOREACH (bGPDlayer *, gpl_src, &gpd_src->layers) { + /* Create destination layer. */ + char *layer_name; + layer_name = BLI_sprintfN("%s_%s", elem->ob->id.name + 2, gpl_src->info); + bGPDlayer *gpl_dst = BKE_gpencil_layer_named_get(gpd_dst, layer_name); + if (gpl_dst == nullptr) { + gpl_dst = BKE_gpencil_layer_addnew(gpd_dst, layer_name, true, false); + } + MEM_freeN(layer_name); + + /* Layer Transform matrix. */ + float matrix[4][4]; + BKE_gpencil_layer_transform_matrix_get(depsgraph, elem->ob, gpl_src, matrix); + + /* Apply time modifier. */ + int remap_cfra = BKE_gpencil_time_modifier_cfra( + depsgraph, scene, elem->ob, gpl_src, CFRA, false); + /* Duplicate frame. */ + bGPDframe *gpf_src = BKE_gpencil_layer_frame_get( + gpl_src, remap_cfra, GP_GETFRAME_USE_PREV); + if (gpf_src == nullptr) { + continue; + } + bGPDframe *gpf_dst = BKE_gpencil_frame_duplicate(gpf_src, true); + gpf_dst->framenum = CFRA + frame_offset; + gpf_dst->flag &= ~GP_FRAME_SELECT; + BLI_addtail(&gpl_dst->frames, gpf_dst); + + LISTBASE_FOREACH (bGPDstroke *, gps, &gpf_dst->strokes) { + /* Create material of the stroke. */ + Material *ma_src = BKE_object_material_get(elem->ob, gps->mat_nr + 1); + bool found = false; + for (int index = 0; index < ob_gpencil->totcol; index++) { + Material *ma_dst = BKE_object_material_get(ob_gpencil, index + 1); + if (ma_src == ma_dst) { + found = true; + break; + } + } + if (!found) { + BKE_object_material_slot_add(bmain, ob_gpencil); + BKE_object_material_assign( + bmain, ob_gpencil, ma_src, ob_gpencil->totcol, BKE_MAT_ASSIGN_USERPREF); + } + + /* Set new material index. */ + gps->mat_nr = BKE_gpencil_object_material_index_get(ob_gpencil, ma_src); + + /* Update point location to new object space. */ + for (int j = 0; j < gps->totpoints; j++) { + bGPDspoint *pt = &gps->points[j]; + mul_m4_v3(matrix, &pt->x); + mul_m4_v3(invmat, &pt->x); + } + + /* Reproject stroke. */ + if (project_type != GP_REPROJECT_KEEP) { + ED_gpencil_stroke_reproject( + depsgraph, &gsc, sctx, gpl_dst, gpf_dst, gps, project_type, false); + } + else { + BKE_gpencil_stroke_geometry_update(gpd_dst, gps); + } + } + } + } + } + /* Return scene frame state and DB to original state. */ + CFRA = oldframe; + BKE_scene_graph_update_for_newframe(depsgraph); + + /* Free memory. */ + gpencil_bake_free_ob_list(&ob_selected_list); + if (sctx != nullptr) { + ED_transform_snap_object_context_destroy(sctx); + } + /* Free temp hash table. */ + if (keyframe_list != nullptr) { + BLI_ghash_free(keyframe_list, nullptr, nullptr); + } + + /* Notifiers. */ + DEG_relations_tag_update(bmain); + DEG_id_tag_update(&scene->id, ID_RECALC_SELECT); + DEG_id_tag_update(&gpd_dst->id, ID_RECALC_COPY_ON_WRITE); + WM_event_add_notifier(C, NC_OBJECT | NA_ADDED, nullptr); + WM_event_add_notifier(C, NC_SCENE | ND_OB_ACTIVE, scene); + + /* Reset cursor. */ + WM_cursor_wait(false); + + /* done */ + return OPERATOR_FINISHED; +} + +static int gpencil_bake_grease_pencil_animation_invoke(bContext *C, + wmOperator *op, + const wmEvent *UNUSED(event)) +{ + PropertyRNA *prop; + Scene *scene = CTX_data_scene(C); + + prop = RNA_struct_find_property(op->ptr, "frame_start"); + if (!RNA_property_is_set(op->ptr, prop)) { + const int frame_start = RNA_property_int_get(op->ptr, prop); + if (frame_start < scene->r.sfra) { + RNA_property_int_set(op->ptr, prop, scene->r.sfra); + } + } + + prop = RNA_struct_find_property(op->ptr, "frame_end"); + if (!RNA_property_is_set(op->ptr, prop)) { + const int frame_end = RNA_property_int_get(op->ptr, prop); + if (frame_end > scene->r.efra) { + RNA_property_int_set(op->ptr, prop, scene->r.efra); + } + } + + /* Show popup dialog to allow editing. */ + return WM_operator_props_dialog_popup(C, op, 250); +} + +void GPENCIL_OT_bake_grease_pencil_animation(wmOperatorType *ot) +{ + PropertyRNA *prop; + + /* identifiers */ + ot->name = "Bake Object Transform to Grease Pencil"; + ot->idname = "GPENCIL_OT_bake_grease_pencil_animation"; + ot->description = "Bake grease pencil object transform to grease pencil keyframes"; + + /* callbacks */ + ot->invoke = gpencil_bake_grease_pencil_animation_invoke; + ot->exec = gpencil_bake_grease_pencil_animation_exec; + ot->poll = gpencil_bake_grease_pencil_animation_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* properties */ + prop = RNA_def_int( + ot->srna, "frame_start", 1, 1, 100000, "Start Frame", "The start frame", 1, 100000); + + prop = RNA_def_int( + ot->srna, "frame_end", 250, 1, 100000, "End Frame", "The end frame of animation", 1, 100000); + RNA_def_property_update_runtime(prop, (void *)gpencil_bake_set_frame_end); + + prop = RNA_def_int(ot->srna, "step", 1, 1, 100, "Step", "Step between generated frames", 1, 100); + + RNA_def_boolean(ot->srna, + "only_selected", + false, + "Only Selected Keyframes", + "Convert only selected keyframes"); + RNA_def_int( + ot->srna, "frame_target", 1, 1, 100000, "Target Frame", "Destination frame", 1, 100000); + + RNA_def_enum(ot->srna, + "project_type", + rna_gpencil_reproject_type_items, + GP_REPROJECT_KEEP, + "Projection Type", + ""); +} diff --git a/source/blender/editors/gpencil/gpencil_intern.h b/source/blender/editors/gpencil/gpencil_intern.h index 1bb98ce51c7..56c94e4494d 100644 --- a/source/blender/editors/gpencil/gpencil_intern.h +++ b/source/blender/editors/gpencil/gpencil_intern.h @@ -13,6 +13,10 @@ #define DEPTH_INVALID 1.0f +#ifdef __cplusplus +extern "C" { +#endif + /* internal exports only */ struct Material; struct bGPDspoint; @@ -826,3 +830,7 @@ struct GP_EditableStrokes_Iter { extern const EnumPropertyItem rna_gpencil_reproject_type_items[]; /* ****************************************************** */ + +#ifdef __cplusplus +} +#endif diff --git a/source/blender/editors/gpencil/gpencil_mesh.c b/source/blender/editors/gpencil/gpencil_mesh.c deleted file mode 100644 index 7db55796053..00000000000 --- a/source/blender/editors/gpencil/gpencil_mesh.c +++ /dev/null @@ -1,468 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later - * Copyright 2008 Blender Foundation. */ - -/** \file - * \ingroup edgpencil - * Operator for converting Grease Pencil data to geometry. - */ - -#include "MEM_guardedalloc.h" - -#include "BLI_blenlib.h" -#include "BLI_ghash.h" -#include "BLI_math.h" - -#include "DNA_anim_types.h" -#include "DNA_gpencil_types.h" -#include "DNA_scene_types.h" -#include "DNA_screen_types.h" - -#include "BKE_anim_data.h" -#include "BKE_context.h" -#include "BKE_duplilist.h" -#include "BKE_gpencil_geom.h" -#include "BKE_layer.h" -#include "BKE_main.h" -#include "BKE_material.h" -#include "BKE_object.h" -#include "BKE_report.h" -#include "BKE_scene.h" - -#include "DEG_depsgraph.h" -#include "DEG_depsgraph_query.h" - -#include "WM_api.h" -#include "WM_types.h" - -#include "RNA_access.h" -#include "RNA_define.h" - -#include "ED_gpencil.h" -#include "ED_transform_snap_object_context.h" - -#include "gpencil_intern.h" - -/* Check frame_end is always > start frame! */ -static void gpencil_bake_set_frame_end(struct Main *UNUSED(main), - struct Scene *UNUSED(scene), - struct PointerRNA *ptr) -{ - int frame_start = RNA_int_get(ptr, "frame_start"); - int frame_end = RNA_int_get(ptr, "frame_end"); - - if (frame_end <= frame_start) { - RNA_int_set(ptr, "frame_end", frame_start + 1); - } -} - -/* Extract mesh animation to Grease Pencil. */ -static bool gpencil_bake_mesh_animation_poll(bContext *C) -{ - if (CTX_data_mode_enum(C) != CTX_MODE_OBJECT) { - return false; - } - - /* Only if the current view is 3D View. */ - ScrArea *area = CTX_wm_area(C); - return (area && area->spacetype); -} - -typedef struct GpBakeOb { - struct GpBakeOb *next, *prev; - Object *ob; -} GpBakeOb; - -/* Get list of keyframes used by selected objects. */ -static void animdata_keyframe_list_get(ListBase *ob_list, - const bool only_selected, - GHash *r_keyframes) -{ - /* Loop all objects to get the list of keyframes used. */ - LISTBASE_FOREACH (GpBakeOb *, elem, ob_list) { - Object *ob = elem->ob; - AnimData *adt = BKE_animdata_from_id(&ob->id); - if ((adt == NULL) || (adt->action == NULL)) { - continue; - } - LISTBASE_FOREACH (FCurve *, fcurve, &adt->action->curves) { - int i; - BezTriple *bezt; - for (i = 0, bezt = fcurve->bezt; i < fcurve->totvert; i++, bezt++) { - /* Keyframe number is x value of point. */ - if ((bezt->f2 & SELECT) || (!only_selected)) { - /* Insert only one key for each keyframe number. */ - int key = (int)bezt->vec[1][0]; - if (!BLI_ghash_haskey(r_keyframes, POINTER_FROM_INT(key))) { - BLI_ghash_insert(r_keyframes, POINTER_FROM_INT(key), POINTER_FROM_INT(key)); - } - } - } - } - } -} - -static void gpencil_bake_duplilist(Depsgraph *depsgraph, Scene *scene, Object *ob, ListBase *list) -{ - GpBakeOb *elem = NULL; - ListBase *lb; - DupliObject *dob; - lb = object_duplilist(depsgraph, scene, ob); - for (dob = lb->first; dob; dob = dob->next) { - if (dob->ob->type != OB_MESH) { - continue; - } - elem = MEM_callocN(sizeof(GpBakeOb), __func__); - elem->ob = dob->ob; - BLI_addtail(list, elem); - } - - free_object_duplilist(lb); -} - -static bool gpencil_bake_ob_list(bContext *C, Depsgraph *depsgraph, Scene *scene, ListBase *list) -{ - GpBakeOb *elem = NULL; - bool simple_material = false; - - /* Add active object. In some files this could not be in selected array. */ - Object *obact = CTX_data_active_object(C); - if (obact == NULL) { - return false; - } - - if (obact->type == OB_MESH) { - elem = MEM_callocN(sizeof(GpBakeOb), __func__); - elem->ob = obact; - BLI_addtail(list, elem); - } - /* Add duplilist. */ - else if (obact->type == OB_EMPTY) { - gpencil_bake_duplilist(depsgraph, scene, obact, list); - simple_material |= true; - } - - /* Add other selected objects. */ - CTX_DATA_BEGIN (C, Object *, ob, selected_objects) { - if (ob == obact) { - continue; - } - /* Add selected meshes. */ - if (ob->type == OB_MESH) { - elem = MEM_callocN(sizeof(GpBakeOb), __func__); - elem->ob = ob; - BLI_addtail(list, elem); - } - - /* Add duplilist. */ - if (ob->type == OB_EMPTY) { - gpencil_bake_duplilist(depsgraph, scene, obact, list); - } - } - CTX_DATA_END; - - return simple_material; -} - -static void gpencil_bake_free_ob_list(ListBase *list) -{ - LISTBASE_FOREACH_MUTABLE (GpBakeOb *, elem, list) { - MEM_SAFE_FREE(elem); - } -} - -static int gpencil_bake_mesh_animation_exec(bContext *C, wmOperator *op) -{ - Main *bmain = CTX_data_main(C); - Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); - Scene *scene = CTX_data_scene(C); - View3D *v3d = CTX_wm_view3d(C); - - ListBase ob_selected_list = {NULL, NULL}; - gpencil_bake_ob_list(C, depsgraph, scene, &ob_selected_list); - - /* Cannot check this in poll because the active object changes. */ - if (ob_selected_list.first == NULL) { - BKE_report(op->reports, RPT_INFO, "No valid object selected"); - gpencil_bake_free_ob_list(&ob_selected_list); - return OPERATOR_CANCELLED; - } - - /* Grab all relevant settings. */ - const int step = RNA_int_get(op->ptr, "step"); - - const int frame_start = (scene->r.sfra > RNA_int_get(op->ptr, "frame_start")) ? - scene->r.sfra : - RNA_int_get(op->ptr, "frame_start"); - - const int frame_end = (scene->r.efra < RNA_int_get(op->ptr, "frame_end")) ? - scene->r.efra : - RNA_int_get(op->ptr, "frame_end"); - - const float angle = RNA_float_get(op->ptr, "angle"); - const int thickness = RNA_int_get(op->ptr, "thickness"); - const bool use_seams = RNA_boolean_get(op->ptr, "seams"); - const bool use_faces = RNA_boolean_get(op->ptr, "faces"); - const bool only_selected = RNA_boolean_get(op->ptr, "only_selected"); - const float offset = RNA_float_get(op->ptr, "offset"); - const int frame_offset = RNA_int_get(op->ptr, "frame_target") - frame_start; - const int project_type = RNA_enum_get(op->ptr, "project_type"); - eGP_TargetObjectMode target = RNA_enum_get(op->ptr, "target"); - - /* Create a new grease pencil object in origin or reuse selected. */ - Object *ob_gpencil = NULL; - bool newob = false; - - if (target == GP_TARGET_OB_SELECTED) { - ob_gpencil = BKE_view_layer_non_active_selected_object(CTX_data_view_layer(C), v3d); - if (ob_gpencil != NULL) { - if (ob_gpencil->type != OB_GPENCIL) { - BKE_report(op->reports, RPT_WARNING, "Target object not a grease pencil, ignoring!"); - ob_gpencil = NULL; - } - else if (BKE_object_obdata_is_libdata(ob_gpencil)) { - BKE_report(op->reports, RPT_WARNING, "Target object library-data, ignoring!"); - ob_gpencil = NULL; - } - } - } - - if (ob_gpencil == NULL) { - ushort local_view_bits = (v3d && v3d->localvd) ? v3d->local_view_uuid : 0; - const float loc[3] = {0.0f, 0.0f, 0.0f}; - ob_gpencil = ED_gpencil_add_object(C, loc, local_view_bits); - newob = true; - } - - bGPdata *gpd = (bGPdata *)ob_gpencil->data; - gpd->draw_mode = (project_type == GP_REPROJECT_KEEP) ? GP_DRAWMODE_3D : GP_DRAWMODE_2D; - - /* Set cursor to indicate working. */ - WM_cursor_wait(true); - - GP_SpaceConversion gsc = {NULL}; - SnapObjectContext *sctx = NULL; - if (project_type != GP_REPROJECT_KEEP) { - /* Init space conversion stuff. */ - gpencil_point_conversion_init(C, &gsc); - /* Move the grease pencil object to conversion data. */ - gsc.ob = ob_gpencil; - - /* Init snap context for geometry projection. */ - sctx = ED_transform_snap_object_context_create(scene, 0); - - /* Tag all existing strokes to avoid reprojections. */ - LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { - LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) { - LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) { - gps->flag |= GP_STROKE_TAG; - } - } - } - } - - /* Loop all frame range. */ - int oldframe = (int)DEG_get_ctime(depsgraph); - int key = -1; - - /* Get list of keyframes. */ - GHash *keyframe_list = BLI_ghash_int_new(__func__); - if (only_selected) { - animdata_keyframe_list_get(&ob_selected_list, only_selected, keyframe_list); - } - - for (int i = frame_start; i < frame_end + 1; i++) { - key++; - /* Jump if not step limit but include last frame always. */ - if ((key % step != 0) && (i != frame_end)) { - continue; - } - - /* Check if frame is in the list of frames to be exported. */ - if ((only_selected) && (!BLI_ghash_haskey(keyframe_list, POINTER_FROM_INT(i)))) { - continue; - } - - /* Move scene to new frame. */ - CFRA = i; - BKE_scene_graph_update_for_newframe(depsgraph); - - /* Loop all objects in the list. */ - LISTBASE_FOREACH (GpBakeOb *, elem, &ob_selected_list) { - Object *ob_eval = (Object *)DEG_get_evaluated_object(depsgraph, elem->ob); - - /* Generate strokes. */ - BKE_gpencil_convert_mesh(bmain, - depsgraph, - scene, - ob_gpencil, - elem->ob, - angle, - thickness, - offset, - ob_eval->obmat, - frame_offset, - use_seams, - use_faces, - true); - - /* Reproject all un-tagged created strokes. */ - if (project_type != GP_REPROJECT_KEEP) { - LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { - bGPDframe *gpf = gpl->actframe; - if (gpf == NULL) { - continue; - } - LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) { - if ((gps->flag & GP_STROKE_TAG) == 0) { - ED_gpencil_stroke_reproject( - depsgraph, &gsc, sctx, gpl, gpf, gps, project_type, false); - gps->flag |= GP_STROKE_TAG; - } - } - } - } - } - } - - /* Return scene frame state and DB to original state. */ - CFRA = oldframe; - BKE_scene_graph_update_for_newframe(depsgraph); - - /* Remove unused materials. */ - int actcol = ob_gpencil->actcol; - for (int slot = 1; slot <= ob_gpencil->totcol; slot++) { - while (slot <= ob_gpencil->totcol && !BKE_object_material_slot_used(ob_gpencil, slot)) { - ob_gpencil->actcol = slot; - BKE_object_material_slot_remove(CTX_data_main(C), ob_gpencil); - - if (actcol >= slot) { - actcol--; - } - } - } - ob_gpencil->actcol = actcol; - - /* Untag all strokes. */ - if (project_type != GP_REPROJECT_KEEP) { - LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { - LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) { - LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) { - gps->flag &= ~GP_STROKE_TAG; - } - } - } - } - - /* Free memory. */ - gpencil_bake_free_ob_list(&ob_selected_list); - if (sctx != NULL) { - ED_transform_snap_object_context_destroy(sctx); - } - /* Free temp hash table. */ - if (keyframe_list != NULL) { - BLI_ghash_free(keyframe_list, NULL, NULL); - } - - /* notifiers */ - if (newob) { - DEG_relations_tag_update(bmain); - } - DEG_id_tag_update(&scene->id, ID_RECALC_SELECT); - WM_event_add_notifier(C, NC_OBJECT | NA_ADDED, NULL); - WM_event_add_notifier(C, NC_SCENE | ND_OB_ACTIVE, scene); - - /* Reset cursor. */ - WM_cursor_wait(false); - - /* done */ - return OPERATOR_FINISHED; -} - -static int gpencil_bake_mesh_animation_invoke(bContext *C, - wmOperator *op, - const wmEvent *UNUSED(event)) -{ - /* Show popup dialog to allow editing. */ - /* FIXME: hard-coded dimensions here are just arbitrary. */ - return WM_operator_props_dialog_popup(C, op, 250); -} - -void GPENCIL_OT_bake_mesh_animation(wmOperatorType *ot) -{ - static const EnumPropertyItem target_object_modes[] = { - {GP_TARGET_OB_NEW, "NEW", 0, "New Object", ""}, - {GP_TARGET_OB_SELECTED, "SELECTED", 0, "Selected Object", ""}, - {0, NULL, 0, NULL, NULL}, - }; - - PropertyRNA *prop; - - /* identifiers */ - ot->name = "Bake Mesh Animation to Grease Pencil"; - ot->idname = "GPENCIL_OT_bake_mesh_animation"; - ot->description = "Bake mesh animation to grease pencil strokes"; - - /* callbacks */ - ot->invoke = gpencil_bake_mesh_animation_invoke; - ot->exec = gpencil_bake_mesh_animation_exec; - ot->poll = gpencil_bake_mesh_animation_poll; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - /* properties */ - ot->prop = RNA_def_enum(ot->srna, - "target", - target_object_modes, - GP_TARGET_OB_NEW, - "Target Object", - "Target grease pencil"); - RNA_def_property_flag(ot->prop, PROP_SKIP_SAVE); - - prop = RNA_def_int( - ot->srna, "frame_start", 1, 1, 100000, "Start Frame", "The start frame", 1, 100000); - - prop = RNA_def_int( - ot->srna, "frame_end", 250, 1, 100000, "End Frame", "The end frame of animation", 1, 100000); - RNA_def_property_update_runtime(prop, gpencil_bake_set_frame_end); - - prop = RNA_def_int(ot->srna, "step", 1, 1, 100, "Step", "Step between generated frames", 1, 100); - - RNA_def_int(ot->srna, "thickness", 1, 1, 100, "Thickness", "", 1, 100); - - prop = RNA_def_float_rotation(ot->srna, - "angle", - 0, - NULL, - 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_float_distance(ot->srna, - "offset", - 0.001f, - 0.0, - 100.0, - "Stroke Offset", - "Offset strokes from fill", - 0.0, - 100.00); - - RNA_def_boolean(ot->srna, "seams", 0, "Only Seam Edges", "Convert only seam edges"); - RNA_def_boolean(ot->srna, "faces", 1, "Export Faces", "Export faces as filled strokes"); - RNA_def_boolean( - ot->srna, "only_selected", 0, "Only Selected Keyframes", "Convert only selected keyframes"); - RNA_def_int( - ot->srna, "frame_target", 1, 1, 100000, "Target Frame", "Destination frame", 1, 100000); - - RNA_def_enum(ot->srna, - "project_type", - rna_gpencil_reproject_type_items, - GP_REPROJECT_VIEW, - "Projection Type", - ""); -} diff --git a/source/blender/editors/gpencil/gpencil_mesh.cc b/source/blender/editors/gpencil/gpencil_mesh.cc new file mode 100644 index 00000000000..aee00d4ede3 --- /dev/null +++ b/source/blender/editors/gpencil/gpencil_mesh.cc @@ -0,0 +1,471 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2008 Blender Foundation. */ + +/** \file + * \ingroup edgpencil + * Operator for converting Grease Pencil data to geometry. + */ + +#include "MEM_guardedalloc.h" + +#include "BLI_blenlib.h" +#include "BLI_ghash.h" +#include "BLI_math.h" + +#include "DNA_anim_types.h" +#include "DNA_gpencil_types.h" +#include "DNA_scene_types.h" +#include "DNA_screen_types.h" + +#include "BKE_anim_data.h" +#include "BKE_context.h" +#include "BKE_duplilist.h" +#include "BKE_gpencil_geom.h" +#include "BKE_layer.h" +#include "BKE_main.h" +#include "BKE_material.h" +#include "BKE_object.h" +#include "BKE_report.h" +#include "BKE_scene.h" + +#include "DEG_depsgraph.h" +#include "DEG_depsgraph_query.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "RNA_access.h" +#include "RNA_define.h" + +#include "ED_gpencil.h" +#include "ED_transform_snap_object_context.h" + +#include "gpencil_intern.h" + +/* Check frame_end is always > start frame! */ +static void gpencil_bake_set_frame_end(struct Main *UNUSED(main), + struct Scene *UNUSED(scene), + struct PointerRNA *ptr) +{ + int frame_start = RNA_int_get(ptr, "frame_start"); + int frame_end = RNA_int_get(ptr, "frame_end"); + + if (frame_end <= frame_start) { + RNA_int_set(ptr, "frame_end", frame_start + 1); + } +} + +/* Extract mesh animation to Grease Pencil. */ +static bool gpencil_bake_mesh_animation_poll(bContext *C) +{ + if (CTX_data_mode_enum(C) != CTX_MODE_OBJECT) { + return false; + } + + /* Only if the current view is 3D View. */ + ScrArea *area = CTX_wm_area(C); + return (area && area->spacetype); +} + +struct GpBakeOb { + struct GpBakeOb *next, *prev; + Object *ob; +}; + +/* Get list of keyframes used by selected objects. */ +static void animdata_keyframe_list_get(ListBase *ob_list, + const bool only_selected, + GHash *r_keyframes) +{ + /* Loop all objects to get the list of keyframes used. */ + LISTBASE_FOREACH (GpBakeOb *, elem, ob_list) { + Object *ob = elem->ob; + AnimData *adt = BKE_animdata_from_id(&ob->id); + if ((adt == nullptr) || (adt->action == nullptr)) { + continue; + } + LISTBASE_FOREACH (FCurve *, fcurve, &adt->action->curves) { + int i; + BezTriple *bezt; + for (i = 0, bezt = fcurve->bezt; i < fcurve->totvert; i++, bezt++) { + /* Keyframe number is x value of point. */ + if ((bezt->f2 & SELECT) || (!only_selected)) { + /* Insert only one key for each keyframe number. */ + int key = (int)bezt->vec[1][0]; + if (!BLI_ghash_haskey(r_keyframes, POINTER_FROM_INT(key))) { + BLI_ghash_insert(r_keyframes, POINTER_FROM_INT(key), POINTER_FROM_INT(key)); + } + } + } + } + } +} + +static void gpencil_bake_duplilist(Depsgraph *depsgraph, Scene *scene, Object *ob, ListBase *list) +{ + GpBakeOb *elem = nullptr; + ListBase *lb; + lb = object_duplilist(depsgraph, scene, ob); + LISTBASE_FOREACH (DupliObject *, dob, lb) { + if (dob->ob->type != OB_MESH) { + continue; + } + elem = MEM_cnew(__func__); + elem->ob = dob->ob; + BLI_addtail(list, elem); + } + + free_object_duplilist(lb); +} + +static bool gpencil_bake_ob_list(bContext *C, Depsgraph *depsgraph, Scene *scene, ListBase *list) +{ + GpBakeOb *elem = nullptr; + bool simple_material = false; + + /* Add active object. In some files this could not be in selected array. */ + Object *obact = CTX_data_active_object(C); + if (obact == nullptr) { + return false; + } + + if (obact->type == OB_MESH) { + elem = MEM_cnew(__func__); + elem->ob = obact; + BLI_addtail(list, elem); + } + /* Add duplilist. */ + else if (obact->type == OB_EMPTY) { + gpencil_bake_duplilist(depsgraph, scene, obact, list); + simple_material |= true; + } + + /* Add other selected objects. */ + CTX_DATA_BEGIN (C, Object *, ob, selected_objects) { + if (ob == obact) { + continue; + } + /* Add selected meshes. */ + if (ob->type == OB_MESH) { + elem = MEM_cnew(__func__); + elem->ob = ob; + BLI_addtail(list, elem); + } + + /* Add duplilist. */ + if (ob->type == OB_EMPTY) { + gpencil_bake_duplilist(depsgraph, scene, obact, list); + } + } + CTX_DATA_END; + + return simple_material; +} + +static void gpencil_bake_free_ob_list(ListBase *list) +{ + LISTBASE_FOREACH_MUTABLE (GpBakeOb *, elem, list) { + MEM_SAFE_FREE(elem); + } +} + +static int gpencil_bake_mesh_animation_exec(bContext *C, wmOperator *op) +{ + Main *bmain = CTX_data_main(C); + Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); + Scene *scene = CTX_data_scene(C); + View3D *v3d = CTX_wm_view3d(C); + + ListBase ob_selected_list = {nullptr, nullptr}; + gpencil_bake_ob_list(C, depsgraph, scene, &ob_selected_list); + + /* Cannot check this in poll because the active object changes. */ + if (ob_selected_list.first == nullptr) { + BKE_report(op->reports, RPT_INFO, "No valid object selected"); + gpencil_bake_free_ob_list(&ob_selected_list); + return OPERATOR_CANCELLED; + } + + /* Grab all relevant settings. */ + const int step = RNA_int_get(op->ptr, "step"); + + const int frame_start = (scene->r.sfra > RNA_int_get(op->ptr, "frame_start")) ? + scene->r.sfra : + RNA_int_get(op->ptr, "frame_start"); + + const int frame_end = (scene->r.efra < RNA_int_get(op->ptr, "frame_end")) ? + scene->r.efra : + RNA_int_get(op->ptr, "frame_end"); + + const float angle = RNA_float_get(op->ptr, "angle"); + const int thickness = RNA_int_get(op->ptr, "thickness"); + const bool use_seams = RNA_boolean_get(op->ptr, "seams"); + const bool use_faces = RNA_boolean_get(op->ptr, "faces"); + const bool only_selected = RNA_boolean_get(op->ptr, "only_selected"); + const float offset = RNA_float_get(op->ptr, "offset"); + const int frame_offset = RNA_int_get(op->ptr, "frame_target") - frame_start; + const eGP_ReprojectModes project_type = (eGP_ReprojectModes)RNA_enum_get(op->ptr, + "project_type"); + const eGP_TargetObjectMode target = (eGP_TargetObjectMode)RNA_enum_get(op->ptr, "target"); + + /* Create a new grease pencil object in origin or reuse selected. */ + Object *ob_gpencil = nullptr; + bool newob = false; + + if (target == GP_TARGET_OB_SELECTED) { + ob_gpencil = BKE_view_layer_non_active_selected_object(CTX_data_view_layer(C), v3d); + if (ob_gpencil != nullptr) { + if (ob_gpencil->type != OB_GPENCIL) { + BKE_report(op->reports, RPT_WARNING, "Target object not a grease pencil, ignoring!"); + ob_gpencil = nullptr; + } + else if (BKE_object_obdata_is_libdata(ob_gpencil)) { + BKE_report(op->reports, RPT_WARNING, "Target object library-data, ignoring!"); + ob_gpencil = nullptr; + } + } + } + + if (ob_gpencil == nullptr) { + ushort local_view_bits = (v3d && v3d->localvd) ? v3d->local_view_uuid : 0; + const float loc[3] = {0.0f, 0.0f, 0.0f}; + ob_gpencil = ED_gpencil_add_object(C, loc, local_view_bits); + newob = true; + } + + bGPdata *gpd = (bGPdata *)ob_gpencil->data; + gpd->draw_mode = (project_type == GP_REPROJECT_KEEP) ? GP_DRAWMODE_3D : GP_DRAWMODE_2D; + + /* Set cursor to indicate working. */ + WM_cursor_wait(true); + + GP_SpaceConversion gsc = {nullptr}; + SnapObjectContext *sctx = nullptr; + if (project_type != GP_REPROJECT_KEEP) { + /* Init space conversion stuff. */ + gpencil_point_conversion_init(C, &gsc); + /* Move the grease pencil object to conversion data. */ + gsc.ob = ob_gpencil; + + /* Init snap context for geometry projection. */ + sctx = ED_transform_snap_object_context_create(scene, 0); + + /* Tag all existing strokes to avoid reprojections. */ + LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { + LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) { + LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) { + gps->flag |= GP_STROKE_TAG; + } + } + } + } + + /* Loop all frame range. */ + int oldframe = (int)DEG_get_ctime(depsgraph); + int key = -1; + + /* Get list of keyframes. */ + GHash *keyframe_list = BLI_ghash_int_new(__func__); + if (only_selected) { + animdata_keyframe_list_get(&ob_selected_list, only_selected, keyframe_list); + } + + for (int i = frame_start; i < frame_end + 1; i++) { + key++; + /* Jump if not step limit but include last frame always. */ + if ((key % step != 0) && (i != frame_end)) { + continue; + } + + /* Check if frame is in the list of frames to be exported. */ + if ((only_selected) && (!BLI_ghash_haskey(keyframe_list, POINTER_FROM_INT(i)))) { + continue; + } + + /* Move scene to new frame. */ + CFRA = i; + BKE_scene_graph_update_for_newframe(depsgraph); + + /* Loop all objects in the list. */ + LISTBASE_FOREACH (GpBakeOb *, elem, &ob_selected_list) { + Object *ob_eval = (Object *)DEG_get_evaluated_object(depsgraph, elem->ob); + + /* Generate strokes. */ + BKE_gpencil_convert_mesh(bmain, + depsgraph, + scene, + ob_gpencil, + elem->ob, + angle, + thickness, + offset, + ob_eval->obmat, + frame_offset, + use_seams, + use_faces, + true); + + /* Reproject all un-tagged created strokes. */ + if (project_type != GP_REPROJECT_KEEP) { + LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { + bGPDframe *gpf = gpl->actframe; + if (gpf == nullptr) { + continue; + } + LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) { + if ((gps->flag & GP_STROKE_TAG) == 0) { + ED_gpencil_stroke_reproject( + depsgraph, &gsc, sctx, gpl, gpf, gps, project_type, false); + gps->flag |= GP_STROKE_TAG; + } + } + } + } + } + } + + /* Return scene frame state and DB to original state. */ + CFRA = oldframe; + BKE_scene_graph_update_for_newframe(depsgraph); + + /* Remove unused materials. */ + int actcol = ob_gpencil->actcol; + for (int slot = 1; slot <= ob_gpencil->totcol; slot++) { + while (slot <= ob_gpencil->totcol && !BKE_object_material_slot_used(ob_gpencil, slot)) { + ob_gpencil->actcol = slot; + BKE_object_material_slot_remove(CTX_data_main(C), ob_gpencil); + + if (actcol >= slot) { + actcol--; + } + } + } + ob_gpencil->actcol = actcol; + + /* Untag all strokes. */ + if (project_type != GP_REPROJECT_KEEP) { + LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { + LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) { + LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) { + gps->flag &= ~GP_STROKE_TAG; + } + } + } + } + + /* Free memory. */ + gpencil_bake_free_ob_list(&ob_selected_list); + if (sctx != nullptr) { + ED_transform_snap_object_context_destroy(sctx); + } + /* Free temp hash table. */ + if (keyframe_list != nullptr) { + BLI_ghash_free(keyframe_list, nullptr, nullptr); + } + + /* notifiers */ + if (newob) { + DEG_relations_tag_update(bmain); + } + DEG_id_tag_update(&scene->id, ID_RECALC_SELECT); + WM_event_add_notifier(C, NC_OBJECT | NA_ADDED, nullptr); + WM_event_add_notifier(C, NC_SCENE | ND_OB_ACTIVE, scene); + + /* Reset cursor. */ + WM_cursor_wait(false); + + /* done */ + return OPERATOR_FINISHED; +} + +static int gpencil_bake_mesh_animation_invoke(bContext *C, + wmOperator *op, + const wmEvent *UNUSED(event)) +{ + /* Show popup dialog to allow editing. */ + /* FIXME: hard-coded dimensions here are just arbitrary. */ + return WM_operator_props_dialog_popup(C, op, 250); +} + +void GPENCIL_OT_bake_mesh_animation(wmOperatorType *ot) +{ + static const EnumPropertyItem target_object_modes[] = { + {GP_TARGET_OB_NEW, "NEW", 0, "New Object", ""}, + {GP_TARGET_OB_SELECTED, "SELECTED", 0, "Selected Object", ""}, + {0, nullptr, 0, nullptr, nullptr}, + }; + + PropertyRNA *prop; + + /* identifiers */ + ot->name = "Bake Mesh Animation to Grease Pencil"; + ot->idname = "GPENCIL_OT_bake_mesh_animation"; + ot->description = "Bake mesh animation to grease pencil strokes"; + + /* callbacks */ + ot->invoke = gpencil_bake_mesh_animation_invoke; + ot->exec = gpencil_bake_mesh_animation_exec; + ot->poll = gpencil_bake_mesh_animation_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* properties */ + ot->prop = RNA_def_enum(ot->srna, + "target", + target_object_modes, + GP_TARGET_OB_NEW, + "Target Object", + "Target grease pencil"); + RNA_def_property_flag(ot->prop, PROP_SKIP_SAVE); + + prop = RNA_def_int( + ot->srna, "frame_start", 1, 1, 100000, "Start Frame", "The start frame", 1, 100000); + + prop = RNA_def_int( + ot->srna, "frame_end", 250, 1, 100000, "End Frame", "The end frame of animation", 1, 100000); + RNA_def_property_update_runtime(prop, (void *)gpencil_bake_set_frame_end); + + prop = RNA_def_int(ot->srna, "step", 1, 1, 100, "Step", "Step between generated frames", 1, 100); + + RNA_def_int(ot->srna, "thickness", 1, 1, 100, "Thickness", "", 1, 100); + + 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_float_distance(ot->srna, + "offset", + 0.001f, + 0.0, + 100.0, + "Stroke Offset", + "Offset strokes from fill", + 0.0, + 100.00); + + 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_boolean(ot->srna, + "only_selected", + false, + "Only Selected Keyframes", + "Convert only selected keyframes"); + RNA_def_int( + ot->srna, "frame_target", 1, 1, 100000, "Target Frame", "Destination frame", 1, 100000); + + RNA_def_enum(ot->srna, + "project_type", + rna_gpencil_reproject_type_items, + GP_REPROJECT_VIEW, + "Projection Type", + ""); +} -- cgit v1.2.3