[Bf-blender-cvs] [a8a92cd15a5] master: GPencil: New modules for Import and Export

Antonio Vazquez noreply at git.blender.org
Wed Mar 24 15:29:03 CET 2021


Commit: a8a92cd15a5251377474fbfdcf9ff0298a8457a9
Author: Antonio  Vazquez
Date:   Wed Mar 24 15:14:43 2021 +0100
Branches: master
https://developer.blender.org/rBa8a92cd15a5251377474fbfdcf9ff0298a8457a9

GPencil: New modules for Import and Export

This patch adds support to export and import grease pencil in several formats.

Inlude:

* Export SVG
* Export PDF (always from camera view)

* Import SVG

The import and export only support solid colors and not gradients or textures.

Requires libharu and pugixml.

For importing SVG, the NanoSVG lib is used, but this does not require installation (just a .h file embedded in the project  folder)

Example of PDF export: https://youtu.be/BMm0KeMJsI4

Reviewed By: #grease_pencil, HooglyBoogly

Maniphest Tasks: T83190, T79875, T83191, T83192

Differential Revision: https://developer.blender.org/D10482

===================================================================

M	release/scripts/startup/bl_ui/space_topbar.py
M	source/blender/blenkernel/BKE_gpencil_geom.h
M	source/blender/blenkernel/intern/gpencil_geom.c
M	source/blender/editors/gpencil/gpencil_utils.c
M	source/blender/editors/include/ED_gpencil.h
M	source/blender/editors/io/CMakeLists.txt
A	source/blender/editors/io/io_gpencil.h
A	source/blender/editors/io/io_gpencil_export.c
A	source/blender/editors/io/io_gpencil_import.c
A	source/blender/editors/io/io_gpencil_utils.c
M	source/blender/editors/io/io_ops.c
M	source/blender/editors/space_file/filelist.c
M	source/blender/io/CMakeLists.txt
A	source/blender/io/gpencil/CMakeLists.txt
A	source/blender/io/gpencil/gpencil_io.h
A	source/blender/io/gpencil/intern/gpencil_io_base.cc
A	source/blender/io/gpencil/intern/gpencil_io_base.h
A	source/blender/io/gpencil/intern/gpencil_io_capi.cc
A	source/blender/io/gpencil/intern/gpencil_io_export_base.h
A	source/blender/io/gpencil/intern/gpencil_io_export_pdf.cc
A	source/blender/io/gpencil/intern/gpencil_io_export_pdf.h
A	source/blender/io/gpencil/intern/gpencil_io_export_svg.cc
A	source/blender/io/gpencil/intern/gpencil_io_export_svg.h
A	source/blender/io/gpencil/intern/gpencil_io_import_base.cc
A	source/blender/io/gpencil/intern/gpencil_io_import_base.h
A	source/blender/io/gpencil/intern/gpencil_io_import_svg.cc
A	source/blender/io/gpencil/intern/gpencil_io_import_svg.h
A	source/blender/io/gpencil/nanosvg/nanosvg.h
M	source/blender/python/intern/CMakeLists.txt
M	source/blender/python/intern/bpy_app_build_options.c

===================================================================

diff --git a/release/scripts/startup/bl_ui/space_topbar.py b/release/scripts/startup/bl_ui/space_topbar.py
index 7219922c379..adab0b0c88a 100644
--- a/release/scripts/startup/bl_ui/space_topbar.py
+++ b/release/scripts/startup/bl_ui/space_topbar.py
@@ -469,6 +469,8 @@ class TOPBAR_MT_file_import(Menu):
         if bpy.app.build_options.alembic:
             self.layout.operator("wm.alembic_import", text="Alembic (.abc)")
 
+        self.layout.operator("wm.gpencil_import_svg", text="SVG as Grease Pencil")
+
 
 class TOPBAR_MT_file_export(Menu):
     bl_idname = "TOPBAR_MT_file_export"
@@ -485,6 +487,13 @@ class TOPBAR_MT_file_export(Menu):
             self.layout.operator(
                 "wm.usd_export", text="Universal Scene Description (.usd, .usdc, .usda)")
 
+        # Pugixml lib dependency
+        if bpy.app.build_options.pugixml:
+            self.layout.operator("wm.gpencil_export_svg", text="Grease Pencil as SVG")
+        # Haru lib dependency
+        if bpy.app.build_options.haru:
+            self.layout.operator("wm.gpencil_export_pdf", text="Grease Pencil as PDF")
+
 
 class TOPBAR_MT_file_external_data(Menu):
     bl_label = "External Data"
diff --git a/source/blender/blenkernel/BKE_gpencil_geom.h b/source/blender/blenkernel/BKE_gpencil_geom.h
index 89a794f2df3..a9bd0a524c4 100644
--- a/source/blender/blenkernel/BKE_gpencil_geom.h
+++ b/source/blender/blenkernel/BKE_gpencil_geom.h
@@ -27,10 +27,10 @@
 extern "C" {
 #endif
 
-struct BoundBox;
 struct Depsgraph;
 struct Main;
 struct Object;
+struct RegionView3D;
 struct Scene;
 struct bGPDcurve;
 struct bGPDframe;
@@ -173,6 +173,20 @@ void BKE_gpencil_stroke_uniform_subdivide(struct bGPdata *gpd,
                                           const uint32_t target_number,
                                           const bool select);
 
+void BKE_gpencil_stroke_to_view_space(struct RegionView3D *rv3d,
+                                      struct bGPDstroke *gps,
+                                      const float diff_mat[4][4]);
+void BKE_gpencil_stroke_from_view_space(struct RegionView3D *rv3d,
+                                        struct bGPDstroke *gps,
+                                        const float diff_mat[4][4]);
+struct bGPDstroke *BKE_gpencil_stroke_perimeter_from_view(struct RegionView3D *rv3d,
+                                                          struct bGPdata *gpd,
+                                                          const struct bGPDlayer *gpl,
+                                                          struct bGPDstroke *gps,
+                                                          const int subdivisions,
+                                                          const float diff_mat[4][4]);
+float BKE_gpencil_stroke_average_pressure_get(struct bGPDstroke *gps);
+bool BKE_gpencil_stroke_is_pressure_constant(struct bGPDstroke *gps);
 #ifdef __cplusplus
 }
 #endif
diff --git a/source/blender/blenkernel/intern/gpencil_geom.c b/source/blender/blenkernel/intern/gpencil_geom.c
index 8c4882854d1..5d8dd99b3ae 100644
--- a/source/blender/blenkernel/intern/gpencil_geom.c
+++ b/source/blender/blenkernel/intern/gpencil_geom.c
@@ -46,9 +46,11 @@
 #include "DNA_mesh_types.h"
 #include "DNA_meshdata_types.h"
 #include "DNA_scene_types.h"
+#include "DNA_screen_types.h"
 
 #include "BLT_translation.h"
 
+#include "BKE_context.h"
 #include "BKE_deform.h"
 #include "BKE_gpencil.h"
 #include "BKE_gpencil_curve.h"
@@ -3460,4 +3462,555 @@ void BKE_gpencil_stroke_uniform_subdivide(bGPdata *gpd,
   BKE_gpencil_stroke_geometry_update(gpd, gps);
 }
 
+/**
+ * Stroke to view space
+ * Transforms a stroke to view space. This allows for manipulations in 2D but also easy conversion
+ * back to 3D.
+ * Note: also takes care of parent space transform
+ */
+void BKE_gpencil_stroke_to_view_space(RegionView3D *rv3d,
+                                      bGPDstroke *gps,
+                                      const float diff_mat[4][4])
+{
+  for (int i = 0; i < gps->totpoints; i++) {
+    bGPDspoint *pt = &gps->points[i];
+    /* Point to parent space. */
+    mul_v3_m4v3(&pt->x, diff_mat, &pt->x);
+    /* point to view space */
+    mul_m4_v3(rv3d->viewmat, &pt->x);
+  }
+}
+
+/**
+ * Stroke from view space
+ * Transforms a stroke from view space back to world space. Inverse of
+ * BKE_gpencil_stroke_to_view_space
+ * Note: also takes care of parent space transform
+ */
+void BKE_gpencil_stroke_from_view_space(RegionView3D *rv3d,
+                                        bGPDstroke *gps,
+                                        const float diff_mat[4][4])
+{
+  float inverse_diff_mat[4][4];
+  invert_m4_m4(inverse_diff_mat, diff_mat);
+
+  for (int i = 0; i < gps->totpoints; i++) {
+    bGPDspoint *pt = &gps->points[i];
+    mul_v3_m4v3(&pt->x, rv3d->viewinv, &pt->x);
+    mul_m4_v3(inverse_diff_mat, &pt->x);
+  }
+}
+
+/* ----------------------------------------------------------------------------- */
+/* Stroke to perimeter */
+
+typedef struct tPerimeterPoint {
+  struct tPerimeterPoint *next, *prev;
+  float x, y, z;
+} tPerimeterPoint;
+
+static tPerimeterPoint *new_perimeter_point(const float pt[3])
+{
+  tPerimeterPoint *new_pt = MEM_callocN(sizeof(tPerimeterPoint), __func__);
+  copy_v3_v3(&new_pt->x, pt);
+  return new_pt;
+}
+
+static int generate_arc_from_point_to_point(ListBase *list,
+                                            tPerimeterPoint *from,
+                                            tPerimeterPoint *to,
+                                            float center_pt[3],
+                                            int subdivisions,
+                                            bool clockwise)
+{
+  float vec_from[2];
+  float vec_to[2];
+  sub_v2_v2v2(vec_from, &from->x, center_pt);
+  sub_v2_v2v2(vec_to, &to->x, center_pt);
+  if (is_zero_v2(vec_from) || is_zero_v2(vec_to)) {
+    return 0;
+  }
+
+  float dot = dot_v2v2(vec_from, vec_to);
+  float det = cross_v2v2(vec_from, vec_to);
+  float angle = clockwise ? M_PI - atan2f(-det, -dot) : atan2f(-det, -dot) + M_PI;
+
+  /* Number of points is 2^(n+1) + 1 on half a circle (n=subdivisions)
+   * so we multiply by (angle / pi) to get the right amount of
+   * points to insert. */
+  int num_points = (int)(((1 << (subdivisions + 1)) - 1) * (angle / M_PI));
+  if (num_points > 0) {
+    float angle_incr = angle / (float)num_points;
+
+    float vec_p[3];
+    float vec_t[3];
+    float tmp_angle;
+    tPerimeterPoint *last_point;
+    if (clockwise) {
+      last_point = to;
+      copy_v2_v2(vec_t, vec_to);
+    }
+    else {
+      last_point = from;
+      copy_v2_v2(vec_t, vec_from);
+    }
+
+    for (int i = 0; i < num_points - 1; i++) {
+      tmp_angle = (i + 1) * angle_incr;
+
+      rotate_v2_v2fl(vec_p, vec_t, tmp_angle);
+      add_v2_v2(vec_p, center_pt);
+      vec_p[2] = center_pt[2];
+
+      tPerimeterPoint *new_point = new_perimeter_point(vec_p);
+      if (clockwise) {
+        BLI_insertlinkbefore(list, last_point, new_point);
+      }
+      else {
+        BLI_insertlinkafter(list, last_point, new_point);
+      }
+
+      last_point = new_point;
+    }
+
+    return num_points - 1;
+  }
+
+  return 0;
+}
+
+static int generate_semi_circle_from_point_to_point(ListBase *list,
+                                                    tPerimeterPoint *from,
+                                                    tPerimeterPoint *to,
+                                                    int subdivisions)
+{
+  int num_points = (1 << (subdivisions + 1)) + 1;
+  float center_pt[3];
+  interp_v3_v3v3(center_pt, &from->x, &to->x, 0.5f);
+
+  float vec_center[2];
+  sub_v2_v2v2(vec_center, &from->x, center_pt);
+  if (is_zero_v2(vec_center)) {
+    return 0;
+  }
+
+  float vec_p[3];
+  float angle_incr = M_PI / ((float)num_points - 1);
+
+  tPerimeterPoint *last_point = from;
+  for (int i = 1; i < num_points; i++) {
+    float angle = i * angle_incr;
+
+    /* Rotate vector around point to get perimeter points. */
+    rotate_v2_v2fl(vec_p, vec_center, angle);
+    add_v2_v2(vec_p, center_pt);
+    vec_p[2] = center_pt[2];
+
+    tPerimeterPoint *new_point = new_perimeter_point(vec_p);
+    BLI_insertlinkafter(list, last_point, new_point);
+
+    last_point = new_point;
+  }
+
+  return num_points - 1;
+}
+
+static int generate_perimeter_cap(const float point[4],
+                                  const float other_point[4],
+                                  float radius,
+                                  ListBase *list,
+                                  int subdivisions,
+                                  short cap_type)
+{
+  float cap_vec[2];
+  sub_v2_v2v2(cap_vec, other_point, point);
+  normalize_v2(cap_vec);
+
+  float cap_nvec[2];
+  if (is_zero_v2(cap_vec)) {
+    cap_nvec[0] = 0;
+    cap_nvec[1] = radius;
+  }
+  else {
+    cap_nvec[0] = -cap_vec[1];
+    cap_nvec[1] = cap_vec[0];
+    mul_v2_fl(cap_nvec, radius);
+  }
+  float cap_nvec_inv[2];
+  negate_v2_v2(cap_nvec_inv, cap_nvec);
+
+  float vec_perimeter[3];
+  copy_v3_v3(vec_perimeter, point);
+  add_v2_v2(vec_perimeter, cap_nvec);
+
+  float vec_perimeter_inv[3];
+  copy_v3_v3(vec_perimeter_inv, point);
+  add_v2_v2(vec_perimeter_inv, cap_nvec_inv);
+
+  tPerimeterPoint *p_pt = new_perimeter_point(vec_perimeter);
+  tPerimeterPoint *p_pt_inv = new_perimeter_point(vec_perimeter_inv);
+
+  BLI_addtail(list, p_pt);
+  BLI_addtail(list, p_pt_inv);
+
+  int num_points = 0;
+  if (cap_type == GP_STROKE_CAP_ROUND) {
+    num_points += generate_semi_circle_from_point_to_point(list, p_pt, p_pt_inv, subdivisions);
+  }
+
+  return num_points + 2;
+}
+
+/**
+ * Calculate the perimeter (outline) of a stroke as list of tPerimeterPoint.
+ * \param subdivisions: Number of subdivions for the start and end caps
+ * \return: list of tPerimeterPoint
+ */
+static ListBase *gpencil_stroke_perimeter_ex(const bGPdata *gpd,
+                                             const bGPDlayer *gpl,
+                                             const bGPDstroke *gps,
+                                             int subdivisions,
+                                             int *r_num_perimeter_points)
+{
+  /* sanity check */
+  if (gps->totpoints < 1) {
+    retu

@@ Diff output truncated at 10240 characters. @@



More information about the Bf-blender-cvs mailing list