Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'source/blender/editors/gpencil/gpencil_mesh.cc')
-rw-r--r--source/blender/editors/gpencil/gpencil_mesh.cc471
1 files changed, 471 insertions, 0 deletions
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<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 = 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<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_cnew<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 = {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",
+ "");
+}