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:
authorAntonioya <blendergit@gmail.com>2018-07-31 11:22:19 +0300
committerAntonioya <blendergit@gmail.com>2018-07-31 11:50:43 +0300
commit66da2f537ae80ce2b31d1eaf34ad8c03d858938d (patch)
tree4776b9d2e43e4280d01d6f0b7088e6d4f417db0f /source/blender/draw/engines
parent27496cc46bbfd76e98ad3b1ccb8fea534763ffb5 (diff)
New Grease Pencil object for 2D animation
This commit merge the full development done in greasepencil-object branch and include mainly the following features. - New grease pencil object. - New drawing engine. - New grease pencil modes Draw/Sculpt/Edit and Weight Paint. - New brushes for grease pencil. - New modifiers for grease pencil. - New shaders FX. - New material system (replace old palettes and colors). - Split of annotations (old grease pencil) and new grease pencil object. - UI adapted to blender 2.8. You can get more info here: https://code.blender.org/2017/12/drawing-2d-animation-in-blender-2-8/ https://code.blender.org/2018/07/grease-pencil-status-update/ This is the result of nearly two years of development and I want thanks firstly the other members of the grease pencil team: Daniel M. Lara, Matias Mendiola and Joshua Leung for their support, ideas and to keep working in the project all the time, without them this project had been impossible. Also, I want thanks other Blender developers for their help, advices and to be there always to help me, and specially to Clément Foucault, Dalai Felinto, Pablo Vázquez and Campbell Barton.
Diffstat (limited to 'source/blender/draw/engines')
-rw-r--r--source/blender/draw/engines/gpencil/gpencil_cache_utils.c296
-rw-r--r--source/blender/draw/engines/gpencil/gpencil_draw_cache_impl.c739
-rw-r--r--source/blender/draw/engines/gpencil/gpencil_draw_utils.c1336
-rw-r--r--source/blender/draw/engines/gpencil/gpencil_engine.c794
-rw-r--r--source/blender/draw/engines/gpencil/gpencil_engine.h355
-rw-r--r--source/blender/draw/engines/gpencil/gpencil_render.c353
-rw-r--r--source/blender/draw/engines/gpencil/gpencil_shader_fx.c848
-rw-r--r--source/blender/draw/engines/gpencil/shaders/fx/gpencil_fx_blur_frag.glsl60
-rw-r--r--source/blender/draw/engines/gpencil/shaders/fx/gpencil_fx_colorize_frag.glsl86
-rw-r--r--source/blender/draw/engines/gpencil/shaders/fx/gpencil_fx_flip_frag.glsl37
-rw-r--r--source/blender/draw/engines/gpencil/shaders/fx/gpencil_fx_light_frag.glsl70
-rw-r--r--source/blender/draw/engines/gpencil/shaders/fx/gpencil_fx_pixel_frag.glsl50
-rw-r--r--source/blender/draw/engines/gpencil/shaders/fx/gpencil_fx_rim_prepare_frag.glsl64
-rw-r--r--source/blender/draw/engines/gpencil/shaders/fx/gpencil_fx_rim_resolve_frag.glsl101
-rw-r--r--source/blender/draw/engines/gpencil/shaders/fx/gpencil_fx_swirl_frag.glsl70
-rw-r--r--source/blender/draw/engines/gpencil/shaders/fx/gpencil_fx_wave_frag.glsl40
-rw-r--r--source/blender/draw/engines/gpencil/shaders/gpencil_background_frag.glsl12
-rw-r--r--source/blender/draw/engines/gpencil/shaders/gpencil_edit_point_frag.glsl17
-rw-r--r--source/blender/draw/engines/gpencil/shaders/gpencil_edit_point_geom.glsl48
-rw-r--r--source/blender/draw/engines/gpencil/shaders/gpencil_edit_point_vert.glsl15
-rw-r--r--source/blender/draw/engines/gpencil/shaders/gpencil_fill_frag.glsl140
-rw-r--r--source/blender/draw/engines/gpencil/shaders/gpencil_fill_vert.glsl14
-rw-r--r--source/blender/draw/engines/gpencil/shaders/gpencil_paper_frag.glsl9
-rw-r--r--source/blender/draw/engines/gpencil/shaders/gpencil_point_frag.glsl49
-rw-r--r--source/blender/draw/engines/gpencil/shaders/gpencil_point_geom.glsl82
-rw-r--r--source/blender/draw/engines/gpencil/shaders/gpencil_point_vert.glsl37
-rw-r--r--source/blender/draw/engines/gpencil/shaders/gpencil_simple_mix_frag.glsl15
-rw-r--r--source/blender/draw/engines/gpencil/shaders/gpencil_stroke_frag.glsl46
-rw-r--r--source/blender/draw/engines/gpencil/shaders/gpencil_stroke_geom.glsl208
-rw-r--r--source/blender/draw/engines/gpencil/shaders/gpencil_stroke_vert.glsl37
-rw-r--r--source/blender/draw/engines/gpencil/shaders/gpencil_zdepth_mix_frag.glsl45
31 files changed, 6073 insertions, 0 deletions
diff --git a/source/blender/draw/engines/gpencil/gpencil_cache_utils.c b/source/blender/draw/engines/gpencil/gpencil_cache_utils.c
new file mode 100644
index 00000000000..4e01c42d33d
--- /dev/null
+++ b/source/blender/draw/engines/gpencil/gpencil_cache_utils.c
@@ -0,0 +1,296 @@
+/*
+ * Copyright 2017, Blender Foundation.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Contributor(s): Antonio Vazquez
+ *
+ */
+
+/** \file blender/draw/engines/gpencil/gpencil_cache_utils.c
+ * \ingroup draw
+ */
+
+#include "DRW_render.h"
+
+#include "BKE_global.h"
+
+#include "ED_gpencil.h"
+#include "ED_view3d.h"
+
+#include "DNA_gpencil_types.h"
+#include "DNA_view3d_types.h"
+
+#include "gpencil_engine.h"
+
+#include "draw_cache_impl.h"
+
+ /* add a gpencil object to cache to defer drawing */
+tGPencilObjectCache *gpencil_object_cache_add(tGPencilObjectCache *cache_array, Object *ob, bool is_temp,
+ int *gp_cache_size, int *gp_cache_used)
+{
+ const DRWContextState *draw_ctx = DRW_context_state_get();
+ tGPencilObjectCache *cache_elem = NULL;
+ RegionView3D *rv3d = draw_ctx->rv3d;
+ tGPencilObjectCache *p = NULL;
+
+ /* By default a cache is created with one block with a predefined number of free slots,
+ if the size is not enough, the cache is reallocated adding a new block of free slots.
+ This is done in order to keep cache small */
+ if (*gp_cache_used + 1 > *gp_cache_size) {
+ if ((*gp_cache_size == 0) || (cache_array == NULL)) {
+ p = MEM_callocN(sizeof(struct tGPencilObjectCache) * GP_CACHE_BLOCK_SIZE, "tGPencilObjectCache");
+ *gp_cache_size = GP_CACHE_BLOCK_SIZE;
+ }
+ else {
+ *gp_cache_size += GP_CACHE_BLOCK_SIZE;
+ p = MEM_recallocN(cache_array, sizeof(struct tGPencilObjectCache) * *gp_cache_size);
+ }
+ cache_array = p;
+ }
+
+ /* zero out all pointers */
+ cache_elem = &cache_array[*gp_cache_used];
+ memset(cache_elem, 0, sizeof(*cache_elem));
+
+ /* save object */
+ cache_elem->ob = ob;
+ cache_elem->temp_ob = is_temp;
+ cache_elem->idx = *gp_cache_used;
+
+ cache_elem->init_grp = 0;
+ cache_elem->end_grp = -1;
+
+ /* calculate zdepth from point of view */
+ float zdepth = 0.0;
+ if (rv3d) {
+ if (rv3d->is_persp) {
+ zdepth = ED_view3d_calc_zfac(rv3d, ob->loc, NULL);
+ }
+ else {
+ zdepth = -dot_v3v3(rv3d->viewinv[2], ob->loc);
+ }
+ }
+ else {
+ /* In render mode, rv3d is not available, so use the distance to camera.
+ * The real distance is not important, but the relative distance to the camera plane
+ * in order to sort by z_depth of the objects
+ */
+ float vn[3] = { 0.0f, 0.0f, -1.0f }; /* always face down */
+ float plane_cam[4];
+ struct Object *camera = draw_ctx->scene->camera;
+ if (camera) {
+ mul_m4_v3(camera->obmat, vn);
+ normalize_v3(vn);
+ plane_from_point_normal_v3(plane_cam, camera->loc, vn);
+ zdepth = dist_squared_to_plane_v3(ob->loc, plane_cam);
+ }
+ }
+ cache_elem->zdepth = zdepth;
+ /* increase slots used in cache */
+ (*gp_cache_used)++;
+
+ return cache_array;
+}
+
+/* get current cache data */
+static GpencilBatchCache *gpencil_batch_get_element(Object *ob)
+{
+ bGPdata *gpd = ob->data;
+ if (gpd->runtime.batch_cache_data == NULL) {
+ gpd->runtime.batch_cache_data = BLI_ghash_str_new("GP batch cache data");
+ return NULL;
+ }
+
+ return (GpencilBatchCache *) BLI_ghash_lookup(gpd->runtime.batch_cache_data, ob->id.name);
+}
+
+/* verify if cache is valid */
+static bool gpencil_batch_cache_valid(Object *ob, bGPdata *gpd, int cfra)
+{
+ GpencilBatchCache *cache = gpencil_batch_get_element(ob);
+
+ if (cache == NULL) {
+ return false;
+ }
+
+ cache->is_editmode = GPENCIL_ANY_EDIT_MODE(gpd);
+
+ if (cfra != cache->cache_frame) {
+ return false;
+ }
+
+ if (gpd->flag & GP_DATA_CACHE_IS_DIRTY) {
+ return false;
+ }
+
+ if (cache->is_editmode) {
+ return false;
+ }
+
+ if (cache->is_dirty) {
+ return false;
+ }
+
+ return true;
+}
+
+/* resize the cache to the number of slots */
+static void gpencil_batch_cache_resize(GpencilBatchCache *cache, int slots)
+{
+ cache->cache_size = slots;
+ cache->batch_stroke = MEM_recallocN(cache->batch_stroke, sizeof(struct Gwn_Batch *) * slots);
+ cache->batch_fill = MEM_recallocN(cache->batch_fill, sizeof(struct Gwn_Batch *) * slots);
+ cache->batch_edit = MEM_recallocN(cache->batch_edit, sizeof(struct Gwn_Batch *) * slots);
+ cache->batch_edlin = MEM_recallocN(cache->batch_edlin, sizeof(struct Gwn_Batch *) * slots);
+}
+
+/* check size and increase if no free slots */
+void gpencil_batch_cache_check_free_slots(Object *ob)
+{
+ GpencilBatchCache *cache = gpencil_batch_get_element(ob);
+
+ /* the memory is reallocated by chunks, not for one slot only to improve speed */
+ if (cache->cache_idx >= cache->cache_size) {
+ cache->cache_size += GPENCIL_MIN_BATCH_SLOTS_CHUNK;
+ gpencil_batch_cache_resize(cache, cache->cache_size);
+ }
+}
+
+/* cache init */
+static void gpencil_batch_cache_init(Object *ob, int cfra)
+{
+ GpencilBatchCache *cache = gpencil_batch_get_element(ob);
+ bGPdata *gpd = ob->data;
+
+ if (G.debug_value >= 664) {
+ printf("gpencil_batch_cache_init: %s\n", ob->id.name);
+ }
+
+ if (!cache) {
+ cache = MEM_callocN(sizeof(*cache), __func__);
+ BLI_ghash_insert(gpd->runtime.batch_cache_data, ob->id.name, cache);
+ }
+ else {
+ memset(cache, 0, sizeof(*cache));
+ }
+
+ cache->cache_size = GPENCIL_MIN_BATCH_SLOTS_CHUNK;
+ cache->batch_stroke = MEM_callocN(sizeof(struct Gwn_Batch *) * cache->cache_size, "Gpencil_Batch_Stroke");
+ cache->batch_fill = MEM_callocN(sizeof(struct Gwn_Batch *) * cache->cache_size, "Gpencil_Batch_Fill");
+ cache->batch_edit = MEM_callocN(sizeof(struct Gwn_Batch *) * cache->cache_size, "Gpencil_Batch_Edit");
+ cache->batch_edlin = MEM_callocN(sizeof(struct Gwn_Batch *) * cache->cache_size, "Gpencil_Batch_Edlin");
+
+ cache->is_editmode = GPENCIL_ANY_EDIT_MODE(gpd);
+ gpd->flag &= ~GP_DATA_CACHE_IS_DIRTY;
+
+ cache->cache_idx = 0;
+ cache->is_dirty = true;
+ cache->cache_frame = cfra;
+}
+
+/* clear cache */
+static void gpencil_batch_cache_clear(GpencilBatchCache *cache, bGPdata *gpd)
+{
+ if (!cache) {
+ return;
+ }
+
+ if (cache->cache_size == 0) {
+ return;
+ }
+
+ if (G.debug_value >= 664) {
+ printf("gpencil_batch_cache_clear: %s\n", gpd->id.name);
+ }
+
+ if (cache->cache_size > 0) {
+ for (int i = 0; i < cache->cache_size; i++) {
+ GPU_BATCH_DISCARD_SAFE(cache->batch_stroke[i]);
+ GPU_BATCH_DISCARD_SAFE(cache->batch_fill[i]);
+ GPU_BATCH_DISCARD_SAFE(cache->batch_edit[i]);
+ GPU_BATCH_DISCARD_SAFE(cache->batch_edlin[i]);
+ }
+ MEM_SAFE_FREE(cache->batch_stroke);
+ MEM_SAFE_FREE(cache->batch_fill);
+ MEM_SAFE_FREE(cache->batch_edit);
+ MEM_SAFE_FREE(cache->batch_edlin);
+ }
+
+ MEM_SAFE_FREE(cache);
+}
+
+/* get cache */
+GpencilBatchCache *gpencil_batch_cache_get(Object *ob, int cfra)
+{
+ bGPdata *gpd = ob->data;
+
+ if (!gpencil_batch_cache_valid(ob, gpd, cfra)) {
+ if (G.debug_value >= 664) {
+ printf("gpencil_batch_cache: %s\n", gpd->id.name);
+ }
+
+ GpencilBatchCache *cache = gpencil_batch_get_element(ob);
+ if (cache) {
+ gpencil_batch_cache_clear(cache, gpd);
+ BLI_ghash_remove(gpd->runtime.batch_cache_data, ob->id.name, NULL, NULL);
+ }
+ gpencil_batch_cache_init(ob, cfra);
+ }
+
+ return gpencil_batch_get_element(ob);
+}
+
+/* set cache as dirty */
+void DRW_gpencil_batch_cache_dirty(bGPdata *gpd)
+{
+ if (gpd->runtime.batch_cache_data == NULL) {
+ return;
+ }
+
+ GHashIterator *ihash = BLI_ghashIterator_new(gpd->runtime.batch_cache_data);
+ while (!BLI_ghashIterator_done(ihash)) {
+ GpencilBatchCache *cache = (GpencilBatchCache *)BLI_ghashIterator_getValue(ihash);
+ if (cache) {
+ cache->is_dirty = true;
+ }
+ BLI_ghashIterator_step(ihash);
+ }
+ BLI_ghashIterator_free(ihash);
+}
+
+/* free batch cache */
+void DRW_gpencil_batch_cache_free(bGPdata *gpd)
+{
+ if (gpd->runtime.batch_cache_data == NULL) {
+ return;
+ }
+
+ GHashIterator *ihash = BLI_ghashIterator_new(gpd->runtime.batch_cache_data);
+ while (!BLI_ghashIterator_done(ihash)) {
+ GpencilBatchCache *cache = (GpencilBatchCache *)BLI_ghashIterator_getValue(ihash);
+ if (cache) {
+ gpencil_batch_cache_clear(cache, gpd);
+ }
+ BLI_ghashIterator_step(ihash);
+ }
+ BLI_ghashIterator_free(ihash);
+
+ /* free hash */
+ if (gpd->runtime.batch_cache_data) {
+ BLI_ghash_free(gpd->runtime.batch_cache_data, NULL, NULL);
+ gpd->runtime.batch_cache_data = NULL;
+ }
+}
diff --git a/source/blender/draw/engines/gpencil/gpencil_draw_cache_impl.c b/source/blender/draw/engines/gpencil/gpencil_draw_cache_impl.c
new file mode 100644
index 00000000000..dd5db85f3f4
--- /dev/null
+++ b/source/blender/draw/engines/gpencil/gpencil_draw_cache_impl.c
@@ -0,0 +1,739 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2008, Blender Foundation
+ * This is a new part of Blender
+ *
+ * Contributor(s): Antonio Vazquez
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file draw/engines/gpencil/gpencil_draw_cache_impl.c
+ * \ingroup draw
+ */
+
+#include "BLI_polyfill_2d.h"
+#include "BLI_math_color.h"
+
+#include "DNA_meshdata_types.h"
+#include "DNA_gpencil_types.h"
+#include "DNA_screen_types.h"
+#include "DNA_view3d_types.h"
+
+#include "BKE_gpencil.h"
+#include "BKE_action.h"
+
+#include "DRW_render.h"
+
+#include "GPU_immediate.h"
+#include "GPU_draw.h"
+
+#include "ED_gpencil.h"
+#include "ED_view3d.h"
+
+#include "UI_resources.h"
+
+#include "gpencil_engine.h"
+
+/* Helper to add stroke point to vbo */
+static void gpencil_set_stroke_point(
+ GPUVertBuf *vbo, float matrix[4][4], const bGPDspoint *pt, int idx,
+ uint pos_id, uint color_id,
+ uint thickness_id, uint uvdata_id, short thickness,
+ const float ink[4])
+{
+ float viewfpt[3];
+
+ float alpha = ink[3] * pt->strength;
+ CLAMP(alpha, GPENCIL_STRENGTH_MIN, 1.0f);
+ float col[4];
+ ARRAY_SET_ITEMS(col, ink[0], ink[1], ink[2], alpha);
+
+ GPU_vertbuf_attr_set(vbo, color_id, idx, col);
+
+ /* transfer both values using the same shader variable */
+ float uvdata[2] = { pt->uv_fac, pt->uv_rot };
+ GPU_vertbuf_attr_set(vbo, uvdata_id, idx, uvdata);
+
+ /* the thickness of the stroke must be affected by zoom, so a pixel scale is calculated */
+ mul_v3_m4v3(viewfpt, matrix, &pt->x);
+ float thick = max_ff(pt->pressure * thickness, 1.0f);
+ GPU_vertbuf_attr_set(vbo, thickness_id, idx, &thick);
+
+ GPU_vertbuf_attr_set(vbo, pos_id, idx, &pt->x);
+}
+
+/* Helper to add a new fill point and texture coordinates to vertex buffer */
+static void gpencil_set_fill_point(
+ GPUVertBuf *vbo, int idx, bGPDspoint *pt, const float fcolor[4], float uv[2],
+ uint pos_id, uint color_id, uint text_id)
+{
+ GPU_vertbuf_attr_set(vbo, pos_id, idx, &pt->x);
+ GPU_vertbuf_attr_set(vbo, color_id, idx, fcolor);
+ GPU_vertbuf_attr_set(vbo, text_id, idx, uv);
+}
+
+/* create batch geometry data for points stroke shader */
+GPUBatch *DRW_gpencil_get_point_geom(bGPDstroke *gps, short thickness, const float ink[4])
+{
+ static GPUVertFormat format = { 0 };
+ static uint pos_id, color_id, size_id, uvdata_id;
+ if (format.attr_len == 0) {
+ pos_id = GPU_vertformat_attr_add(&format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
+ color_id = GPU_vertformat_attr_add(&format, "color", GPU_COMP_F32, 4, GPU_FETCH_FLOAT);
+ size_id = GPU_vertformat_attr_add(&format, "thickness", GPU_COMP_F32, 1, GPU_FETCH_FLOAT);
+ uvdata_id = GPU_vertformat_attr_add(&format, "uvdata", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
+ }
+
+ GPUVertBuf *vbo = GPU_vertbuf_create_with_format(&format);
+ GPU_vertbuf_data_alloc(vbo, gps->totpoints);
+
+ /* draw stroke curve */
+ const bGPDspoint *pt = gps->points;
+ int idx = 0;
+ float alpha;
+ float col[4];
+
+ for (int i = 0; i < gps->totpoints; i++, pt++) {
+ /* set point */
+ alpha = ink[3] * pt->strength;
+ CLAMP(alpha, GPENCIL_STRENGTH_MIN, 1.0f);
+ ARRAY_SET_ITEMS(col, ink[0], ink[1], ink[2], alpha);
+
+ float thick = max_ff(pt->pressure * thickness, 1.0f);
+
+ GPU_vertbuf_attr_set(vbo, color_id, idx, col);
+ GPU_vertbuf_attr_set(vbo, size_id, idx, &thick);
+
+ /* transfer both values using the same shader variable */
+ float uvdata[2] = { pt->uv_fac, pt->uv_rot };
+ GPU_vertbuf_attr_set(vbo, uvdata_id, idx, uvdata);
+
+ GPU_vertbuf_attr_set(vbo, pos_id, idx, &pt->x);
+ idx++;
+ }
+
+ return GPU_batch_create_ex(GPU_PRIM_POINTS, vbo, NULL, GPU_BATCH_OWNS_VBO);
+}
+
+/* create batch geometry data for stroke shader */
+GPUBatch *DRW_gpencil_get_stroke_geom(bGPDframe *gpf, bGPDstroke *gps, short thickness, const float ink[4])
+{
+ bGPDspoint *points = gps->points;
+ int totpoints = gps->totpoints;
+ /* if cyclic needs more vertex */
+ int cyclic_add = (gps->flag & GP_STROKE_CYCLIC) ? 1 : 0;
+
+ static GPUVertFormat format = { 0 };
+ static uint pos_id, color_id, thickness_id, uvdata_id;
+ if (format.attr_len == 0) {
+ pos_id = GPU_vertformat_attr_add(&format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
+ color_id = GPU_vertformat_attr_add(&format, "color", GPU_COMP_F32, 4, GPU_FETCH_FLOAT);
+ thickness_id = GPU_vertformat_attr_add(&format, "thickness", GPU_COMP_F32, 1, GPU_FETCH_FLOAT);
+ uvdata_id = GPU_vertformat_attr_add(&format, "uvdata", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
+ }
+
+ GPUVertBuf *vbo = GPU_vertbuf_create_with_format(&format);
+ GPU_vertbuf_data_alloc(vbo, totpoints + cyclic_add + 2);
+
+ /* draw stroke curve */
+ const bGPDspoint *pt = points;
+ int idx = 0;
+ for (int i = 0; i < totpoints; i++, pt++) {
+ /* first point for adjacency (not drawn) */
+ if (i == 0) {
+ if (gps->flag & GP_STROKE_CYCLIC && totpoints > 2) {
+ gpencil_set_stroke_point(vbo, gpf->runtime.viewmatrix, &points[totpoints - 1], idx,
+ pos_id, color_id, thickness_id, uvdata_id, thickness, ink);
+ idx++;
+ }
+ else {
+ gpencil_set_stroke_point(vbo, gpf->runtime.viewmatrix, &points[1], idx,
+ pos_id, color_id, thickness_id, uvdata_id, thickness, ink);
+ idx++;
+ }
+ }
+ /* set point */
+ gpencil_set_stroke_point(vbo, gpf->runtime.viewmatrix, pt, idx,
+ pos_id, color_id, thickness_id, uvdata_id, thickness, ink);
+ idx++;
+ }
+
+ if (gps->flag & GP_STROKE_CYCLIC && totpoints > 2) {
+ /* draw line to first point to complete the cycle */
+ gpencil_set_stroke_point(vbo, gpf->runtime.viewmatrix, &points[0], idx,
+ pos_id, color_id, thickness_id, uvdata_id, thickness, ink);
+ idx++;
+ /* now add adjacency point (not drawn) */
+ gpencil_set_stroke_point(vbo, gpf->runtime.viewmatrix, &points[1], idx,
+ pos_id, color_id, thickness_id, uvdata_id, thickness, ink);
+ idx++;
+ }
+ /* last adjacency point (not drawn) */
+ else {
+ gpencil_set_stroke_point(vbo, gpf->runtime.viewmatrix, &points[totpoints - 2], idx,
+ pos_id, color_id, thickness_id, uvdata_id, thickness, ink);
+ }
+
+ return GPU_batch_create_ex(GPU_PRIM_LINE_STRIP_ADJ, vbo, NULL, GPU_BATCH_OWNS_VBO);
+}
+
+/* create batch geometry data for current buffer stroke shader */
+GPUBatch *DRW_gpencil_get_buffer_stroke_geom(bGPdata *gpd, float matrix[4][4], short thickness)
+{
+ const DRWContextState *draw_ctx = DRW_context_state_get();
+ Scene *scene = draw_ctx->scene;
+ View3D *v3d = draw_ctx->v3d;
+ ARegion *ar = draw_ctx->ar;
+ RegionView3D *rv3d = draw_ctx->rv3d;
+ ToolSettings *ts = scene->toolsettings;
+ Object *ob = draw_ctx->obact;
+
+ tGPspoint *points = gpd->runtime.sbuffer;
+ int totpoints = gpd->runtime.sbuffer_size;
+
+ static GPUVertFormat format = { 0 };
+ static uint pos_id, color_id, thickness_id, uvdata_id;
+ if (format.attr_len == 0) {
+ pos_id = GPU_vertformat_attr_add(&format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
+ color_id = GPU_vertformat_attr_add(&format, "color", GPU_COMP_F32, 4, GPU_FETCH_FLOAT);
+ thickness_id = GPU_vertformat_attr_add(&format, "thickness", GPU_COMP_F32, 1, GPU_FETCH_FLOAT);
+ uvdata_id = GPU_vertformat_attr_add(&format, "uvdata", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
+ }
+
+ GPUVertBuf *vbo = GPU_vertbuf_create_with_format(&format);
+ GPU_vertbuf_data_alloc(vbo, totpoints + 2);
+
+ /* draw stroke curve */
+ const tGPspoint *tpt = points;
+ bGPDspoint pt, pt2;
+ int idx = 0;
+
+ /* get origin to reproject point */
+ float origin[3];
+ bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd);
+ ED_gp_get_drawing_reference(v3d, scene, ob, gpl, ts->gpencil_v3d_align, origin);
+
+ for (int i = 0; i < totpoints; i++, tpt++) {
+ ED_gpencil_tpoint_to_point(ar, origin, tpt, &pt);
+ ED_gp_project_point_to_plane(ob, rv3d, origin, ts->gp_sculpt.lock_axis - 1, &pt);
+
+ /* first point for adjacency (not drawn) */
+ if (i == 0) {
+ if (totpoints > 1) {
+ ED_gpencil_tpoint_to_point(ar, origin, &points[1], &pt2);
+ gpencil_set_stroke_point(vbo, matrix, &pt2, idx,
+ pos_id, color_id, thickness_id, uvdata_id, thickness, gpd->runtime.scolor);
+ }
+ else {
+ gpencil_set_stroke_point(vbo, matrix, &pt, idx,
+ pos_id, color_id, thickness_id, uvdata_id, thickness, gpd->runtime.scolor);
+ }
+ idx++;
+ }
+ /* set point */
+ gpencil_set_stroke_point(vbo, matrix, &pt, idx,
+ pos_id, color_id, thickness_id, uvdata_id, thickness, gpd->runtime.scolor);
+ idx++;
+ }
+
+ /* last adjacency point (not drawn) */
+ if (totpoints > 2) {
+ ED_gpencil_tpoint_to_point(ar, origin, &points[totpoints - 2], &pt2);
+ gpencil_set_stroke_point(vbo, matrix, &pt2, idx,
+ pos_id, color_id, thickness_id, uvdata_id, thickness, gpd->runtime.scolor);
+ }
+ else {
+ gpencil_set_stroke_point(vbo, matrix, &pt, idx,
+ pos_id, color_id, thickness_id, uvdata_id, thickness, gpd->runtime.scolor);
+ }
+
+ return GPU_batch_create_ex(GPU_PRIM_LINE_STRIP_ADJ, vbo, NULL, GPU_BATCH_OWNS_VBO);
+}
+
+/* create batch geometry data for current buffer point shader */
+GPUBatch *DRW_gpencil_get_buffer_point_geom(bGPdata *gpd, float matrix[4][4], short thickness)
+{
+ const DRWContextState *draw_ctx = DRW_context_state_get();
+ Scene *scene = draw_ctx->scene;
+ View3D *v3d = draw_ctx->v3d;
+ ARegion *ar = draw_ctx->ar;
+ RegionView3D *rv3d = draw_ctx->rv3d;
+ ToolSettings *ts = scene->toolsettings;
+ Object *ob = draw_ctx->obact;
+
+ tGPspoint *points = gpd->runtime.sbuffer;
+ int totpoints = gpd->runtime.sbuffer_size;
+
+ static GPUVertFormat format = { 0 };
+ static uint pos_id, color_id, thickness_id, uvdata_id;
+ if (format.attr_len == 0) {
+ pos_id = GPU_vertformat_attr_add(&format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
+ color_id = GPU_vertformat_attr_add(&format, "color", GPU_COMP_F32, 4, GPU_FETCH_FLOAT);
+ thickness_id = GPU_vertformat_attr_add(&format, "thickness", GPU_COMP_F32, 1, GPU_FETCH_FLOAT);
+ uvdata_id = GPU_vertformat_attr_add(&format, "uvdata", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
+ }
+
+ GPUVertBuf *vbo = GPU_vertbuf_create_with_format(&format);
+ GPU_vertbuf_data_alloc(vbo, totpoints);
+
+ /* draw stroke curve */
+ const tGPspoint *tpt = points;
+ bGPDspoint pt;
+ int idx = 0;
+
+ /* get origin to reproject point */
+ float origin[3];
+ bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd);
+ ED_gp_get_drawing_reference(v3d, scene, ob, gpl, ts->gpencil_v3d_align, origin);
+
+ for (int i = 0; i < totpoints; i++, tpt++) {
+ ED_gpencil_tpoint_to_point(ar, origin, tpt, &pt);
+ ED_gp_project_point_to_plane(ob, rv3d, origin, ts->gp_sculpt.lock_axis - 1, &pt);
+
+ /* set point */
+ gpencil_set_stroke_point(vbo, matrix, &pt, idx,
+ pos_id, color_id, thickness_id, uvdata_id,
+ thickness, gpd->runtime.scolor);
+ idx++;
+ }
+
+ return GPU_batch_create_ex(GPU_PRIM_POINTS, vbo, NULL, GPU_BATCH_OWNS_VBO);
+}
+
+/* create batch geometry data for current buffer fill shader */
+GPUBatch *DRW_gpencil_get_buffer_fill_geom(bGPdata *gpd)
+{
+ if (gpd == NULL) {
+ return NULL;
+ }
+
+ const tGPspoint *points = gpd->runtime.sbuffer;
+ int totpoints = gpd->runtime.sbuffer_size;
+ if (totpoints < 3) {
+ return NULL;
+ }
+
+ const DRWContextState *draw_ctx = DRW_context_state_get();
+ Scene *scene = draw_ctx->scene;
+ View3D *v3d = draw_ctx->v3d;
+ ARegion *ar = draw_ctx->ar;
+ ToolSettings *ts = scene->toolsettings;
+ Object *ob = draw_ctx->obact;
+
+ /* get origin to reproject point */
+ float origin[3];
+ bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd);
+ ED_gp_get_drawing_reference(v3d, scene, ob, gpl, ts->gpencil_v3d_align, origin);
+
+ int tot_triangles = totpoints - 2;
+ /* allocate memory for temporary areas */
+ uint (*tmp_triangles)[3] = MEM_mallocN(sizeof(*tmp_triangles) * tot_triangles, __func__);
+ float (*points2d)[2] = MEM_mallocN(sizeof(*points2d) * totpoints, __func__);
+
+ /* Convert points to array and triangulate
+ * Here a cache is not used because while drawing the information changes all the time, so the cache
+ * would be recalculated constantly, so it is better to do direct calculation for each function call
+ */
+ for (int i = 0; i < totpoints; i++) {
+ const tGPspoint *pt = &points[i];
+ points2d[i][0] = pt->x;
+ points2d[i][1] = pt->y;
+ }
+ BLI_polyfill_calc(points2d, (uint)totpoints, 0, tmp_triangles);
+
+ static GPUVertFormat format = { 0 };
+ static uint pos_id, color_id;
+ if (format.attr_len == 0) {
+ pos_id = GPU_vertformat_attr_add(&format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
+ color_id = GPU_vertformat_attr_add(&format, "color", GPU_COMP_F32, 4, GPU_FETCH_FLOAT);
+ }
+
+ GPUVertBuf *vbo = GPU_vertbuf_create_with_format(&format);
+
+ /* draw triangulation data */
+ if (tot_triangles > 0) {
+ GPU_vertbuf_data_alloc(vbo, tot_triangles * 3);
+
+ const tGPspoint *tpt;
+ bGPDspoint pt;
+
+ int idx = 0;
+ for (int i = 0; i < tot_triangles; i++) {
+ for (int j = 0; j < 3; j++) {
+ tpt = &points[tmp_triangles[i][j]];
+ ED_gpencil_tpoint_to_point(ar, origin, tpt, &pt);
+ GPU_vertbuf_attr_set(vbo, pos_id, idx, &pt.x);
+ GPU_vertbuf_attr_set(vbo, color_id, idx, gpd->runtime.sfill);
+ idx++;
+ }
+ }
+ }
+
+ /* clear memory */
+ if (tmp_triangles) {
+ MEM_freeN(tmp_triangles);
+ }
+ if (points2d) {
+ MEM_freeN(points2d);
+ }
+
+ return GPU_batch_create_ex(GPU_PRIM_TRIS, vbo, NULL, GPU_BATCH_OWNS_VBO);
+}
+
+/* create batch geometry data for stroke shader */
+GPUBatch *DRW_gpencil_get_fill_geom(Object *ob, bGPDstroke *gps, const float color[4])
+{
+ BLI_assert(gps->totpoints >= 3);
+
+ /* Calculate triangles cache for filling area (must be done only after changes) */
+ if ((gps->flag & GP_STROKE_RECALC_CACHES) || (gps->tot_triangles == 0) || (gps->triangles == NULL)) {
+ DRW_gpencil_triangulate_stroke_fill(gps);
+ ED_gpencil_calc_stroke_uv(ob, gps);
+ }
+
+ BLI_assert(gps->tot_triangles >= 1);
+
+ static GPUVertFormat format = { 0 };
+ static uint pos_id, color_id, text_id;
+ if (format.attr_len == 0) {
+ pos_id = GPU_vertformat_attr_add(&format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
+ color_id = GPU_vertformat_attr_add(&format, "color", GPU_COMP_F32, 4, GPU_FETCH_FLOAT);
+ text_id = GPU_vertformat_attr_add(&format, "texCoord", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
+ }
+
+ GPUVertBuf *vbo = GPU_vertbuf_create_with_format(&format);
+ GPU_vertbuf_data_alloc(vbo, gps->tot_triangles * 3);
+
+ /* Draw all triangles for filling the polygon (cache must be calculated before) */
+ bGPDtriangle *stroke_triangle = gps->triangles;
+ int idx = 0;
+ for (int i = 0; i < gps->tot_triangles; i++, stroke_triangle++) {
+ for (int j = 0; j < 3; j++) {
+ gpencil_set_fill_point(
+ vbo, idx, &gps->points[stroke_triangle->verts[j]], color, stroke_triangle->uv[j],
+ pos_id, color_id, text_id);
+ idx++;
+ }
+ }
+
+ return GPU_batch_create_ex(GPU_PRIM_TRIS, vbo, NULL, GPU_BATCH_OWNS_VBO);
+}
+
+/* Draw selected verts for strokes being edited */
+GPUBatch *DRW_gpencil_get_edit_geom(bGPDstroke *gps, float alpha, short dflag)
+{
+ const DRWContextState *draw_ctx = DRW_context_state_get();
+ Object *ob = draw_ctx->obact;
+ bGPdata *gpd = ob->data;
+ bool is_weight_paint = (gpd) && (gpd->flag & GP_DATA_STROKE_WEIGHTMODE);
+
+ int vgindex = ob->actdef - 1;
+ if (!BLI_findlink(&ob->defbase, vgindex)) {
+ vgindex = -1;
+ }
+
+ /* Get size of verts:
+ * - The selected state needs to be larger than the unselected state so that
+ * they stand out more.
+ * - We use the theme setting for size of the unselected verts
+ */
+ float bsize = UI_GetThemeValuef(TH_GP_VERTEX_SIZE);
+ float vsize;
+ if ((int)bsize > 8) {
+ vsize = 10.0f;
+ bsize = 8.0f;
+ }
+ else {
+ vsize = bsize + 2;
+ }
+
+ /* for now, we assume that the base color of the points is not too close to the real color */
+ float selectColor[4];
+ UI_GetThemeColor3fv(TH_GP_VERTEX_SELECT, selectColor);
+ selectColor[3] = alpha;
+
+ static GPUVertFormat format = { 0 };
+ static uint pos_id, color_id, size_id;
+ if (format.attr_len == 0) {
+ pos_id = GPU_vertformat_attr_add(&format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
+ color_id = GPU_vertformat_attr_add(&format, "color", GPU_COMP_F32, 4, GPU_FETCH_FLOAT);
+ size_id = GPU_vertformat_attr_add(&format, "size", GPU_COMP_F32, 1, GPU_FETCH_FLOAT);
+ }
+
+ GPUVertBuf *vbo = GPU_vertbuf_create_with_format(&format);
+ GPU_vertbuf_data_alloc(vbo, gps->totpoints);
+
+ /* Draw start and end point differently if enabled stroke direction hint */
+ bool show_direction_hint = (dflag & GP_DATA_SHOW_DIRECTION) && (gps->totpoints > 1);
+
+ /* Draw all the stroke points (selected or not) */
+ bGPDspoint *pt = gps->points;
+ MDeformVert *dvert = gps->dvert;
+
+ int idx = 0;
+ float fcolor[4];
+ float fsize = 0;
+ for (int i = 0; i < gps->totpoints; i++, pt++, dvert++) {
+ /* weight paint */
+ if (is_weight_paint) {
+ float weight = BKE_gpencil_vgroup_use_index(dvert, vgindex);
+ CLAMP(weight, 0.0f, 1.0f);
+ float hue = 2.0f * (1.0f - weight) / 3.0f;
+ hsv_to_rgb(hue, 1.0f, 1.0f, &selectColor[0], &selectColor[1], &selectColor[2]);
+ selectColor[3] = 1.0f;
+ copy_v4_v4(fcolor, selectColor);
+ fsize = vsize;
+ }
+ else {
+ if (show_direction_hint && i == 0) {
+ /* start point in green bigger */
+ ARRAY_SET_ITEMS(fcolor, 0.0f, 1.0f, 0.0f, 1.0f);
+ fsize = vsize + 4;
+ }
+ else if (show_direction_hint && (i == gps->totpoints - 1)) {
+ /* end point in red smaller */
+ ARRAY_SET_ITEMS(fcolor, 1.0f, 0.0f, 0.0f, 1.0f);
+ fsize = vsize + 1;
+ }
+ else if (pt->flag & GP_SPOINT_SELECT) {
+ copy_v4_v4(fcolor, selectColor);
+ fsize = vsize;
+ }
+ else {
+ copy_v4_v4(fcolor, gps->runtime.tmp_stroke_rgba);
+ fsize = bsize;
+ }
+ }
+
+ GPU_vertbuf_attr_set(vbo, color_id, idx, fcolor);
+ GPU_vertbuf_attr_set(vbo, size_id, idx, &fsize);
+ GPU_vertbuf_attr_set(vbo, pos_id, idx, &pt->x);
+ idx++;
+ }
+
+ return GPU_batch_create_ex(GPU_PRIM_POINTS, vbo, NULL, GPU_BATCH_OWNS_VBO);
+}
+
+/* Draw lines for strokes being edited */
+GPUBatch *DRW_gpencil_get_edlin_geom(bGPDstroke *gps, float alpha, short UNUSED(dflag))
+{
+ const DRWContextState *draw_ctx = DRW_context_state_get();
+ Object *ob = draw_ctx->obact;
+ bGPdata *gpd = ob->data;
+ bool is_weight_paint = (gpd) && (gpd->flag & GP_DATA_STROKE_WEIGHTMODE);
+
+ int vgindex = ob->actdef - 1;
+ if (!BLI_findlink(&ob->defbase, vgindex)) {
+ vgindex = -1;
+ }
+
+ float selectColor[4];
+ UI_GetThemeColor3fv(TH_GP_VERTEX_SELECT, selectColor);
+ selectColor[3] = alpha;
+ float linecolor[4];
+ copy_v4_v4(linecolor, gpd->line_color);
+
+ static GPUVertFormat format = { 0 };
+ static uint pos_id, color_id;
+ if (format.attr_len == 0) {
+ pos_id = GPU_vertformat_attr_add(&format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
+ color_id = GPU_vertformat_attr_add(&format, "color", GPU_COMP_F32, 4, GPU_FETCH_FLOAT);
+ }
+
+ GPUVertBuf *vbo = GPU_vertbuf_create_with_format(&format);
+ GPU_vertbuf_data_alloc(vbo, gps->totpoints);
+
+ /* Draw all the stroke lines (selected or not) */
+ bGPDspoint *pt = gps->points;
+
+ /* GPXX: for some converted files, this struct could be null
+ * maybe we can remove this and move to versioning code after
+ * merge */
+ if (gps->dvert == NULL) {
+ gps->dvert = MEM_callocN(sizeof(MDeformVert) * gps->totpoints, "gp_stroke_weights");
+ }
+
+ MDeformVert *dvert = gps->dvert;
+
+ int idx = 0;
+ float fcolor[4];
+ for (int i = 0; i < gps->totpoints; i++, pt++, dvert++) {
+ /* weight paint */
+ if (is_weight_paint) {
+ float weight = BKE_gpencil_vgroup_use_index(dvert, vgindex);
+ CLAMP(weight, 0.0f, 1.0f);
+ float hue = 2.0f * (1.0f - weight) / 3.0f;
+ hsv_to_rgb(hue, 1.0f, 1.0f, &selectColor[0], &selectColor[1], &selectColor[2]);
+ selectColor[3] = 1.0f;
+ copy_v4_v4(fcolor, selectColor);
+ }
+ else {
+ if (pt->flag & GP_SPOINT_SELECT) {
+ copy_v4_v4(fcolor, selectColor);
+ }
+ else {
+ copy_v4_v4(fcolor, linecolor);
+ }
+ }
+
+ GPU_vertbuf_attr_set(vbo, color_id, idx, fcolor);
+ GPU_vertbuf_attr_set(vbo, pos_id, idx, &pt->x);
+ idx++;
+ }
+
+ return GPU_batch_create_ex(GPU_PRIM_LINE_STRIP, vbo, NULL, GPU_BATCH_OWNS_VBO);
+}
+
+static void set_grid_point(GPUVertBuf *vbo, int idx, float col_grid[4],
+ uint pos_id, uint color_id,
+ float v1, float v2, int axis)
+{
+ GPU_vertbuf_attr_set(vbo, color_id, idx, col_grid);
+
+ float pos[3];
+ /* Set the grid in the selected axis (default is always Y axis) */
+ if (axis & V3D_GP_GRID_AXIS_X) {
+ pos[0] = 0.0f;
+ pos[1] = v1;
+ pos[2] = v2;
+ }
+ else if (axis & V3D_GP_GRID_AXIS_Z) {
+ pos[0] = v1;
+ pos[1] = v2;
+ pos[2] = 0.0f;
+ }
+ else
+ {
+ pos[0] = v1;
+ pos[1] = 0.0f;
+ pos[2] = v2;
+ }
+
+
+
+ GPU_vertbuf_attr_set(vbo, pos_id, idx, pos);
+}
+
+/* Draw grid lines */
+GPUBatch *DRW_gpencil_get_grid(void)
+{
+ const DRWContextState *draw_ctx = DRW_context_state_get();
+ Scene *scene = draw_ctx->scene;
+ ToolSettings *ts = scene->toolsettings;
+ View3D *v3d = draw_ctx->v3d;
+
+ float col_grid[4];
+
+ /* verify we have something to draw and valid values */
+ if (v3d->overlay.gpencil_grid_lines < 1) {
+ v3d->overlay.gpencil_grid_lines = GP_DEFAULT_GRID_LINES;
+ }
+
+ if (v3d->overlay.gpencil_grid_scale == 0.0f) {
+ v3d->overlay.gpencil_grid_scale = 1.0f;
+ }
+
+ if (v3d->overlay.gpencil_grid_opacity < 0.1f) {
+ v3d->overlay.gpencil_grid_opacity = 0.1f;
+ }
+
+ UI_GetThemeColor3fv(TH_GRID, col_grid);
+ col_grid[3] = v3d->overlay.gpencil_grid_opacity;
+
+ /* if use locked axis, copy value */
+ int axis = v3d->overlay.gpencil_grid_axis;
+ if ((v3d->overlay.gpencil_grid_axis & V3D_GP_GRID_AXIS_LOCK) == 0) {
+
+ axis = v3d->overlay.gpencil_grid_axis;
+ }
+ else {
+ switch (ts->gp_sculpt.lock_axis) {
+ case GP_LOCKAXIS_X:
+ {
+ axis = V3D_GP_GRID_AXIS_X;
+ break;
+ }
+ case GP_LOCKAXIS_NONE:
+ case GP_LOCKAXIS_Y:
+ {
+ axis = V3D_GP_GRID_AXIS_Y;
+ break;
+ }
+ case GP_LOCKAXIS_Z:
+ {
+ axis = V3D_GP_GRID_AXIS_Z;
+ break;
+ }
+ }
+ }
+
+ const char *grid_unit = NULL;
+ const int gridlines = v3d->overlay.gpencil_grid_lines;
+ const float grid_scale = v3d->overlay.gpencil_grid_scale * ED_scene_grid_scale(scene, &grid_unit);
+ const float grid = grid_scale;
+ const float space = (grid_scale / gridlines);
+
+ const uint vertex_len = 2 * (gridlines * 4 + 2);
+
+ static GPUVertFormat format = { 0 };
+ static uint pos_id, color_id;
+ if (format.attr_len == 0) {
+ pos_id = GPU_vertformat_attr_add(&format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
+ color_id = GPU_vertformat_attr_add(&format, "color", GPU_COMP_F32, 4, GPU_FETCH_FLOAT);
+ }
+
+ GPUVertBuf *vbo = GPU_vertbuf_create_with_format(&format);
+ GPU_vertbuf_data_alloc(vbo, vertex_len);
+
+ int idx = 0;
+
+ for (int a = 1; a <= gridlines; a++) {
+ const float line = a * space;
+
+ set_grid_point(vbo, idx, col_grid, pos_id, color_id, -grid, -line, axis);
+ idx++;
+ set_grid_point(vbo, idx, col_grid, pos_id, color_id, +grid, -line, axis);
+ idx++;
+ set_grid_point(vbo, idx, col_grid, pos_id, color_id, -grid, +line, axis);
+ idx++;
+ set_grid_point(vbo, idx, col_grid, pos_id, color_id, +grid, +line, axis);
+ idx++;
+
+ set_grid_point(vbo, idx, col_grid, pos_id, color_id, -line, -grid, axis);
+ idx++;
+ set_grid_point(vbo, idx, col_grid, pos_id, color_id, -line, +grid, axis);
+ idx++;
+ set_grid_point(vbo, idx, col_grid, pos_id, color_id, +line, -grid, axis);
+ idx++;
+ set_grid_point(vbo, idx, col_grid, pos_id, color_id, +line, +grid, axis);
+ idx++;
+ }
+ /* center lines */
+ set_grid_point(vbo, idx, col_grid, pos_id, color_id, -grid, 0.0f, axis);
+ idx++;
+ set_grid_point(vbo, idx, col_grid, pos_id, color_id, +grid, 0.0f, axis);
+ idx++;
+
+ set_grid_point(vbo, idx, col_grid, pos_id, color_id, 0.0f, -grid, axis);
+ idx++;
+ set_grid_point(vbo, idx, col_grid, pos_id, color_id, 0.0f, +grid, axis);
+ idx++;
+
+ return GPU_batch_create_ex(GPU_PRIM_LINES, vbo, NULL, GPU_BATCH_OWNS_VBO);
+}
diff --git a/source/blender/draw/engines/gpencil/gpencil_draw_utils.c b/source/blender/draw/engines/gpencil/gpencil_draw_utils.c
new file mode 100644
index 00000000000..76cb1405a71
--- /dev/null
+++ b/source/blender/draw/engines/gpencil/gpencil_draw_utils.c
@@ -0,0 +1,1336 @@
+/*
+ * Copyright 2017, Blender Foundation.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Contributor(s): Antonio Vazquez
+ *
+ */
+
+/** \file blender/draw/engines/gpencil/gpencil_draw_utils.c
+ * \ingroup draw
+ */
+
+#include "BLI_polyfill_2d.h"
+
+#include "DRW_engine.h"
+#include "DRW_render.h"
+
+#include "BKE_brush.h"
+#include "BKE_gpencil.h"
+#include "BKE_gpencil_modifier.h"
+#include "BKE_image.h"
+#include "BKE_material.h"
+
+#include "ED_gpencil.h"
+#include "ED_view3d.h"
+
+#include "DNA_gpencil_types.h"
+#include "DNA_material_types.h"
+#include "DNA_view3d_types.h"
+#include "DNA_gpencil_modifier_types.h"
+
+ /* If builtin shaders are needed */
+#include "GPU_shader.h"
+#include "GPU_texture.h"
+
+/* For EvaluationContext... */
+#include "DEG_depsgraph.h"
+#include "DEG_depsgraph_query.h"
+
+#include "IMB_imbuf_types.h"
+
+#include "gpencil_engine.h"
+
+/* fill type to communicate to shader */
+#define SOLID 0
+#define GRADIENT 1
+#define RADIAL 2
+#define CHESS 3
+#define TEXTURE 4
+#define PATTERN 5
+
+/* Helper for doing all the checks on whether a stroke can be drawn */
+static bool gpencil_can_draw_stroke(struct MaterialGPencilStyle *gp_style, const bGPDstroke *gps,
+ const bool onion, const bool is_mat_preview)
+{
+ /* skip stroke if it doesn't have any valid data */
+ if ((gps->points == NULL) || (gps->totpoints < 1) || (gp_style == NULL))
+ return false;
+
+ /* if mat preview render always visible */
+ if (is_mat_preview) {
+ return true;
+ }
+
+ /* check if the color is visible */
+ if ((gp_style == NULL) ||
+ (gp_style->flag & GP_STYLE_COLOR_HIDE) ||
+ (onion && (gp_style->flag & GP_STYLE_COLOR_ONIONSKIN)))
+ {
+ return false;
+ }
+
+ /* stroke can be drawn */
+ return true;
+}
+
+/* calc bounding box in 2d using flat projection data */
+static void gpencil_calc_2d_bounding_box(
+ const float(*points2d)[2], int totpoints, float minv[2], float maxv[2], bool expand)
+{
+ minv[0] = points2d[0][0];
+ minv[1] = points2d[0][1];
+ maxv[0] = points2d[0][0];
+ maxv[1] = points2d[0][1];
+
+ for (int i = 1; i < totpoints; i++) {
+ /* min */
+ if (points2d[i][0] < minv[0]) {
+ minv[0] = points2d[i][0];
+ }
+ if (points2d[i][1] < minv[1]) {
+ minv[1] = points2d[i][1];
+ }
+ /* max */
+ if (points2d[i][0] > maxv[0]) {
+ maxv[0] = points2d[i][0];
+ }
+ if (points2d[i][1] > maxv[1]) {
+ maxv[1] = points2d[i][1];
+ }
+ }
+ /* If not expanded, use a perfect square */
+ if (expand == false) {
+ if (maxv[0] > maxv[1]) {
+ maxv[1] = maxv[0];
+ }
+ else {
+ maxv[0] = maxv[1];
+ }
+ }
+}
+
+/* calc texture coordinates using flat projected points */
+static void gpencil_calc_stroke_fill_uv(
+ const float(*points2d)[2], int totpoints, float minv[2], float maxv[2], float(*r_uv)[2])
+{
+ float d[2];
+ d[0] = maxv[0] - minv[0];
+ d[1] = maxv[1] - minv[1];
+ for (int i = 0; i < totpoints; i++) {
+ r_uv[i][0] = (points2d[i][0] - minv[0]) / d[0];
+ r_uv[i][1] = (points2d[i][1] - minv[1]) / d[1];
+ }
+}
+
+/* Get points of stroke always flat to view not affected by camera view or view position */
+static void gpencil_stroke_2d_flat(const bGPDspoint *points, int totpoints, float(*points2d)[2], int *r_direction)
+{
+ const bGPDspoint *pt0 = &points[0];
+ const bGPDspoint *pt1 = &points[1];
+ const bGPDspoint *pt3 = &points[(int)(totpoints * 0.75)];
+
+ float locx[3];
+ float locy[3];
+ float loc3[3];
+ float normal[3];
+
+ /* local X axis (p0 -> p1) */
+ sub_v3_v3v3(locx, &pt1->x, &pt0->x);
+
+ /* point vector at 3/4 */
+ sub_v3_v3v3(loc3, &pt3->x, &pt0->x);
+
+ /* vector orthogonal to polygon plane */
+ cross_v3_v3v3(normal, locx, loc3);
+
+ /* local Y axis (cross to normal/x axis) */
+ cross_v3_v3v3(locy, normal, locx);
+
+ /* Normalize vectors */
+ normalize_v3(locx);
+ normalize_v3(locy);
+
+ /* Get all points in local space */
+ for (int i = 0; i < totpoints; i++) {
+ const bGPDspoint *pt = &points[i];
+ float loc[3];
+
+ /* Get local space using first point as origin */
+ sub_v3_v3v3(loc, &pt->x, &pt0->x);
+
+ points2d[i][0] = dot_v3v3(loc, locx);
+ points2d[i][1] = dot_v3v3(loc, locy);
+ }
+
+ /* Concave (-1), Convex (1), or Autodetect (0)? */
+ *r_direction = (int)locy[2];
+}
+
+/* Triangulate stroke for high quality fill (this is done only if cache is null or stroke was modified) */
+void DRW_gpencil_triangulate_stroke_fill(bGPDstroke *gps)
+{
+ BLI_assert(gps->totpoints >= 3);
+
+ /* allocate memory for temporary areas */
+ gps->tot_triangles = gps->totpoints - 2;
+ uint(*tmp_triangles)[3] = MEM_mallocN(sizeof(*tmp_triangles) * gps->tot_triangles, "GP Stroke temp triangulation");
+ float(*points2d)[2] = MEM_mallocN(sizeof(*points2d) * gps->totpoints, "GP Stroke temp 2d points");
+ float(*uv)[2] = MEM_mallocN(sizeof(*uv) * gps->totpoints, "GP Stroke temp 2d uv data");
+
+ int direction = 0;
+
+ /* convert to 2d and triangulate */
+ gpencil_stroke_2d_flat(gps->points, gps->totpoints, points2d, &direction);
+ BLI_polyfill_calc(points2d, (uint)gps->totpoints, direction, tmp_triangles);
+
+ /* calc texture coordinates automatically */
+ float minv[2];
+ float maxv[2];
+ /* first needs bounding box data */
+ gpencil_calc_2d_bounding_box(points2d, gps->totpoints, minv, maxv, false);
+ /* calc uv data */
+ gpencil_calc_stroke_fill_uv(points2d, gps->totpoints, minv, maxv, uv);
+
+ /* Number of triangles */
+ gps->tot_triangles = gps->totpoints - 2;
+ /* save triangulation data in stroke cache */
+ if (gps->tot_triangles > 0) {
+ if (gps->triangles == NULL) {
+ gps->triangles = MEM_callocN(sizeof(*gps->triangles) * gps->tot_triangles, "GP Stroke triangulation");
+ }
+ else {
+ gps->triangles = MEM_recallocN(gps->triangles, sizeof(*gps->triangles) * gps->tot_triangles);
+ }
+
+ for (int i = 0; i < gps->tot_triangles; i++) {
+ bGPDtriangle *stroke_triangle = &gps->triangles[i];
+ memcpy(gps->triangles[i].verts, tmp_triangles[i], sizeof(uint[3]));
+ /* copy texture coordinates */
+ copy_v2_v2(stroke_triangle->uv[0], uv[tmp_triangles[i][0]]);
+ copy_v2_v2(stroke_triangle->uv[1], uv[tmp_triangles[i][1]]);
+ copy_v2_v2(stroke_triangle->uv[2], uv[tmp_triangles[i][2]]);
+ }
+ }
+ else {
+ /* No triangles needed - Free anything allocated previously */
+ if (gps->triangles)
+ MEM_freeN(gps->triangles);
+
+ gps->triangles = NULL;
+ }
+
+ /* disable recalculation flag */
+ if (gps->flag & GP_STROKE_RECALC_CACHES) {
+ gps->flag &= ~GP_STROKE_RECALC_CACHES;
+ }
+
+ /* clear memory */
+ MEM_SAFE_FREE(tmp_triangles);
+ MEM_SAFE_FREE(points2d);
+ MEM_SAFE_FREE(uv);
+}
+
+/* recalc the internal geometry caches for fill and uvs */
+static void DRW_gpencil_recalc_geometry_caches(Object *ob, MaterialGPencilStyle *gp_style, bGPDstroke *gps)
+{
+ if (gps->flag & GP_STROKE_RECALC_CACHES) {
+ /* Calculate triangles cache for filling area (must be done only after changes) */
+ if ((gps->tot_triangles == 0) || (gps->triangles == NULL)) {
+ if ((gps->totpoints > 2) &&
+ ((gp_style->fill_rgba[3] > GPENCIL_ALPHA_OPACITY_THRESH) || (gp_style->fill_style > 0)))
+ {
+ DRW_gpencil_triangulate_stroke_fill(gps);
+ }
+ }
+
+ /* calc uv data along the stroke */
+ ED_gpencil_calc_stroke_uv(ob, gps);
+
+ /* clear flag */
+ gps->flag &= ~GP_STROKE_RECALC_CACHES;
+ }
+}
+
+/* create shading group for filling */
+static DRWShadingGroup *DRW_gpencil_shgroup_fill_create(
+ GPENCIL_e_data *e_data, GPENCIL_Data *vedata, DRWPass *pass,
+ GPUShader *shader, bGPdata *gpd, MaterialGPencilStyle *gp_style, int id)
+{
+ GPENCIL_StorageList *stl = ((GPENCIL_Data *)vedata)->stl;
+
+ /* e_data.gpencil_fill_sh */
+ DRWShadingGroup *grp = DRW_shgroup_create(shader, pass);
+
+ DRW_shgroup_uniform_vec4(grp, "color2", gp_style->mix_rgba, 1);
+
+ /* set style type */
+ switch (gp_style->fill_style) {
+ case GP_STYLE_FILL_STYLE_SOLID:
+ stl->shgroups[id].fill_style = SOLID;
+ break;
+ case GP_STYLE_FILL_STYLE_GRADIENT:
+ if (gp_style->gradient_type == GP_STYLE_GRADIENT_LINEAR) {
+ stl->shgroups[id].fill_style = GRADIENT;
+ }
+ else {
+ stl->shgroups[id].fill_style = RADIAL;
+ }
+ break;
+ case GP_STYLE_FILL_STYLE_CHESSBOARD:
+ stl->shgroups[id].fill_style = CHESS;
+ break;
+ case GP_STYLE_FILL_STYLE_TEXTURE:
+ if (gp_style->flag & GP_STYLE_FILL_PATTERN) {
+ stl->shgroups[id].fill_style = PATTERN;
+ }
+ else {
+ stl->shgroups[id].fill_style = TEXTURE;
+ }
+ break;
+ default:
+ stl->shgroups[id].fill_style = GP_STYLE_FILL_STYLE_SOLID;
+ break;
+ }
+ DRW_shgroup_uniform_int(grp, "fill_type", &stl->shgroups[id].fill_style, 1);
+
+ DRW_shgroup_uniform_float(grp, "mix_factor", &gp_style->mix_factor, 1);
+
+ DRW_shgroup_uniform_float(grp, "gradient_angle", &gp_style->gradient_angle, 1);
+ DRW_shgroup_uniform_float(grp, "gradient_radius", &gp_style->gradient_radius, 1);
+ DRW_shgroup_uniform_float(grp, "pattern_gridsize", &gp_style->pattern_gridsize, 1);
+ DRW_shgroup_uniform_vec2(grp, "gradient_scale", gp_style->gradient_scale, 1);
+ DRW_shgroup_uniform_vec2(grp, "gradient_shift", gp_style->gradient_shift, 1);
+
+ DRW_shgroup_uniform_float(grp, "texture_angle", &gp_style->texture_angle, 1);
+ DRW_shgroup_uniform_vec2(grp, "texture_scale", gp_style->texture_scale, 1);
+ DRW_shgroup_uniform_vec2(grp, "texture_offset", gp_style->texture_offset, 1);
+ DRW_shgroup_uniform_float(grp, "texture_opacity", &gp_style->texture_opacity, 1);
+
+ stl->shgroups[id].texture_mix = gp_style->flag & GP_STYLE_COLOR_TEX_MIX ? 1 : 0;
+ DRW_shgroup_uniform_int(grp, "texture_mix", &stl->shgroups[id].texture_mix, 1);
+
+ stl->shgroups[id].texture_flip = gp_style->flag & GP_STYLE_COLOR_FLIP_FILL ? 1 : 0;
+ DRW_shgroup_uniform_int(grp, "texture_flip", &stl->shgroups[id].texture_flip, 1);
+
+ DRW_shgroup_uniform_int(grp, "xraymode", (const int *) &gpd->xray_mode, 1);
+ /* image texture */
+ if ((gp_style->flag & GP_STYLE_COLOR_TEX_MIX) ||
+ (gp_style->fill_style & GP_STYLE_FILL_STYLE_TEXTURE))
+ {
+ ImBuf *ibuf;
+ Image *image = gp_style->ima;
+ ImageUser iuser = { NULL };
+ void *lock;
+
+ iuser.ok = true;
+
+ ibuf = BKE_image_acquire_ibuf(image, &iuser, &lock);
+
+ if (ibuf == NULL || ibuf->rect == NULL) {
+ BKE_image_release_ibuf(image, ibuf, NULL);
+ }
+ else {
+ GPUTexture *texture = GPU_texture_from_blender(gp_style->ima, &iuser, GL_TEXTURE_2D, true, 0.0);
+ DRW_shgroup_uniform_texture(grp, "myTexture", texture);
+
+ stl->shgroups[id].texture_clamp = gp_style->flag & GP_STYLE_COLOR_TEX_CLAMP ? 1 : 0;
+ DRW_shgroup_uniform_int(grp, "texture_clamp", &stl->shgroups[id].texture_clamp, 1);
+
+ BKE_image_release_ibuf(image, ibuf, NULL);
+ }
+ }
+ else {
+ /* if no texture defined, need a blank texture to avoid errors in draw manager */
+ DRW_shgroup_uniform_texture(grp, "myTexture", e_data->gpencil_blank_texture);
+ stl->shgroups[id].texture_clamp = 0;
+ DRW_shgroup_uniform_int(grp, "texture_clamp", &stl->shgroups[id].texture_clamp, 1);
+ }
+
+ return grp;
+}
+
+/* create shading group for strokes */
+DRWShadingGroup *DRW_gpencil_shgroup_stroke_create(
+ GPENCIL_e_data *e_data, GPENCIL_Data *vedata, DRWPass *pass, GPUShader *shader, Object *ob,
+ bGPdata *gpd, MaterialGPencilStyle *gp_style, int id, bool onion)
+{
+ GPENCIL_StorageList *stl = ((GPENCIL_Data *)vedata)->stl;
+ const float *viewport_size = DRW_viewport_size_get();
+
+ /* e_data.gpencil_stroke_sh */
+ DRWShadingGroup *grp = DRW_shgroup_create(shader, pass);
+
+ DRW_shgroup_uniform_vec2(grp, "Viewport", viewport_size, 1);
+
+ DRW_shgroup_uniform_float(grp, "pixsize", stl->storage->pixsize, 1);
+ DRW_shgroup_uniform_float(grp, "pixelsize", &U.pixelsize, 1);
+
+ /* avoid wrong values */
+ if ((gpd) && (gpd->pixfactor == 0)) {
+ gpd->pixfactor = GP_DEFAULT_PIX_FACTOR;
+ }
+
+ /* object scale and depth */
+ if ((ob) && (id > -1)) {
+ stl->shgroups[id].obj_scale = (ob->size[0] + ob->size[1] + ob->size[2]) / 3.0f;
+ DRW_shgroup_uniform_float(grp, "objscale", &stl->shgroups[id].obj_scale, 1);
+ stl->shgroups[id].keep_size = (int)((gpd) && (gpd->flag & GP_DATA_STROKE_KEEPTHICKNESS));
+ DRW_shgroup_uniform_int(grp, "keep_size", &stl->shgroups[id].keep_size, 1);
+
+ stl->shgroups[id].stroke_style = gp_style->stroke_style;
+ stl->shgroups[id].color_type = GPENCIL_COLOR_SOLID;
+ if ((gp_style->stroke_style == GP_STYLE_STROKE_STYLE_TEXTURE) && (!onion)) {
+ stl->shgroups[id].color_type = GPENCIL_COLOR_TEXTURE;
+ if (gp_style->flag & GP_STYLE_STROKE_PATTERN) {
+ stl->shgroups[id].color_type = GPENCIL_COLOR_PATTERN;
+ }
+ }
+ DRW_shgroup_uniform_int(grp, "color_type", &stl->shgroups[id].color_type, 1);
+ DRW_shgroup_uniform_float(grp, "pixfactor", &gpd->pixfactor, 1);
+ }
+ else {
+ stl->storage->obj_scale = 1.0f;
+ stl->storage->keep_size = 0;
+ stl->storage->pixfactor = GP_DEFAULT_PIX_FACTOR;
+ DRW_shgroup_uniform_float(grp, "objscale", &stl->storage->obj_scale, 1);
+ DRW_shgroup_uniform_int(grp, "keep_size", &stl->storage->keep_size, 1);
+ DRW_shgroup_uniform_int(grp, "color_type", &stl->storage->color_type, 1);
+ if (gpd) {
+ DRW_shgroup_uniform_float(grp, "pixfactor", &gpd->pixfactor, 1);
+ }
+ else {
+ DRW_shgroup_uniform_float(grp, "pixfactor", &stl->storage->pixfactor, 1);
+ }
+ }
+
+ if ((gpd) && (id > -1)) {
+ DRW_shgroup_uniform_int(grp, "xraymode", (const int *) &gpd->xray_mode, 1);
+ }
+ else {
+ /* for drawing always on front */
+ DRW_shgroup_uniform_int(grp, "xraymode", &stl->storage->xray, 1);
+ }
+
+ /* image texture for pattern */
+ if ((gp_style) && (gp_style->stroke_style == GP_STYLE_STROKE_STYLE_TEXTURE) && (!onion)) {
+ ImBuf *ibuf;
+ Image *image = gp_style->sima;
+ ImageUser iuser = { NULL };
+ void *lock;
+
+ iuser.ok = true;
+
+ ibuf = BKE_image_acquire_ibuf(image, &iuser, &lock);
+
+ if (ibuf == NULL || ibuf->rect == NULL) {
+ BKE_image_release_ibuf(image, ibuf, NULL);
+ }
+ else {
+ GPUTexture *texture = GPU_texture_from_blender(gp_style->sima, &iuser, GL_TEXTURE_2D, true, 0.0f);
+ DRW_shgroup_uniform_texture(grp, "myTexture", texture);
+
+ BKE_image_release_ibuf(image, ibuf, NULL);
+ }
+ }
+ else {
+ /* if no texture defined, need a blank texture to avoid errors in draw manager */
+ DRW_shgroup_uniform_texture(grp, "myTexture", e_data->gpencil_blank_texture);
+ }
+
+ return grp;
+}
+
+/* create shading group for volumetrics */
+static DRWShadingGroup *DRW_gpencil_shgroup_point_create(
+ GPENCIL_e_data *e_data, GPENCIL_Data *vedata, DRWPass *pass, GPUShader *shader, Object *ob,
+ bGPdata *gpd, MaterialGPencilStyle *gp_style, int id, bool onion)
+{
+ GPENCIL_StorageList *stl = ((GPENCIL_Data *)vedata)->stl;
+ const float *viewport_size = DRW_viewport_size_get();
+
+ /* e_data.gpencil_stroke_sh */
+ DRWShadingGroup *grp = DRW_shgroup_create(shader, pass);
+
+ DRW_shgroup_uniform_vec2(grp, "Viewport", viewport_size, 1);
+ DRW_shgroup_uniform_float(grp, "pixsize", stl->storage->pixsize, 1);
+ DRW_shgroup_uniform_float(grp, "pixelsize", &U.pixelsize, 1);
+
+ /* avoid wrong values */
+ if ((gpd) && (gpd->pixfactor == 0)) {
+ gpd->pixfactor = GP_DEFAULT_PIX_FACTOR;
+ }
+
+ /* object scale and depth */
+ if ((ob) && (id > -1)) {
+ stl->shgroups[id].obj_scale = (ob->size[0] + ob->size[1] + ob->size[2]) / 3.0f;
+ DRW_shgroup_uniform_float(grp, "objscale", &stl->shgroups[id].obj_scale, 1);
+ stl->shgroups[id].keep_size = (int)((gpd) && (gpd->flag & GP_DATA_STROKE_KEEPTHICKNESS));
+ DRW_shgroup_uniform_int(grp, "keep_size", &stl->shgroups[id].keep_size, 1);
+
+ stl->shgroups[id].mode = gp_style->mode;
+ stl->shgroups[id].stroke_style = gp_style->stroke_style;
+ stl->shgroups[id].color_type = GPENCIL_COLOR_SOLID;
+ if ((gp_style->stroke_style == GP_STYLE_STROKE_STYLE_TEXTURE) && (!onion)) {
+ stl->shgroups[id].color_type = GPENCIL_COLOR_TEXTURE;
+ if (gp_style->flag & GP_STYLE_STROKE_PATTERN) {
+ stl->shgroups[id].color_type = GPENCIL_COLOR_PATTERN;
+ }
+ }
+ DRW_shgroup_uniform_int(grp, "color_type", &stl->shgroups[id].color_type, 1);
+ DRW_shgroup_uniform_int(grp, "mode", &stl->shgroups[id].mode, 1);
+ DRW_shgroup_uniform_float(grp, "pixfactor", &gpd->pixfactor, 1);
+ }
+ else {
+ stl->storage->obj_scale = 1.0f;
+ stl->storage->keep_size = 0;
+ stl->storage->pixfactor = GP_DEFAULT_PIX_FACTOR;
+ stl->storage->mode = gp_style->mode;
+ DRW_shgroup_uniform_float(grp, "objscale", &stl->storage->obj_scale, 1);
+ DRW_shgroup_uniform_int(grp, "keep_size", &stl->storage->keep_size, 1);
+ DRW_shgroup_uniform_int(grp, "color_type", &stl->storage->color_type, 1);
+ DRW_shgroup_uniform_int(grp, "mode", &stl->storage->mode, 1);
+ if (gpd) {
+ DRW_shgroup_uniform_float(grp, "pixfactor", &gpd->pixfactor, 1);
+ }
+ else {
+ DRW_shgroup_uniform_float(grp, "pixfactor", &stl->storage->pixfactor, 1);
+ }
+ }
+
+ if (gpd) {
+ DRW_shgroup_uniform_int(grp, "xraymode", (const int *)&gpd->xray_mode, 1);
+ }
+ else {
+ /* for drawing always on front */
+ DRW_shgroup_uniform_int(grp, "xraymode", &stl->storage->xray, 1);
+ }
+
+ /* image texture */
+ if ((gp_style) && (gp_style->stroke_style == GP_STYLE_STROKE_STYLE_TEXTURE) && (!onion)) {
+ ImBuf *ibuf;
+ Image *image = gp_style->sima;
+ ImageUser iuser = { NULL };
+ void *lock;
+
+ iuser.ok = true;
+
+ ibuf = BKE_image_acquire_ibuf(image, &iuser, &lock);
+
+ if (ibuf == NULL || ibuf->rect == NULL) {
+ BKE_image_release_ibuf(image, ibuf, NULL);
+ }
+ else {
+ GPUTexture *texture = GPU_texture_from_blender(gp_style->sima, &iuser, GL_TEXTURE_2D, true, 0.0f);
+ DRW_shgroup_uniform_texture(grp, "myTexture", texture);
+
+ BKE_image_release_ibuf(image, ibuf, NULL);
+ }
+ }
+ else {
+ /* if no texture defined, need a blank texture to avoid errors in draw manager */
+ DRW_shgroup_uniform_texture(grp, "myTexture", e_data->gpencil_blank_texture);
+ }
+
+ return grp;
+}
+
+/* add fill shading group to pass */
+static void gpencil_add_fill_shgroup(
+ GpencilBatchCache *cache, DRWShadingGroup *fillgrp,
+ Object *ob, bGPDlayer *gpl, bGPDframe *gpf, bGPDstroke *gps,
+ const float tintcolor[4], const bool onion, const bool custonion)
+{
+ MaterialGPencilStyle *gp_style = BKE_material_gpencil_settings_get(ob, gps->mat_nr + 1);
+ if (gps->totpoints >= 3) {
+ float tfill[4];
+ /* set color using material, tint color and opacity */
+ interp_v3_v3v3(tfill, gps->runtime.tmp_fill_rgba, tintcolor, tintcolor[3]);
+ tfill[3] = gps->runtime.tmp_fill_rgba[3] * gpl->opacity;
+ if ((tfill[3] > GPENCIL_ALPHA_OPACITY_THRESH) || (gp_style->fill_style > 0)) {
+ const float *color;
+ if (!onion) {
+ color = tfill;
+ }
+ else {
+ if (custonion) {
+ color = tintcolor;
+ }
+ else {
+ ARRAY_SET_ITEMS(tfill, UNPACK3(gps->runtime.tmp_fill_rgba), tintcolor[3]);
+ color = tfill;
+ }
+ }
+ if (cache->is_dirty) {
+ gpencil_batch_cache_check_free_slots(ob);
+ cache->batch_fill[cache->cache_idx] = DRW_gpencil_get_fill_geom(ob, gps, color);
+ }
+ DRW_shgroup_call_add(fillgrp, cache->batch_fill[cache->cache_idx], gpf->runtime.viewmatrix);
+ }
+ }
+}
+
+/* add stroke shading group to pass */
+static void gpencil_add_stroke_shgroup(GpencilBatchCache *cache, DRWShadingGroup *strokegrp,
+ Object *ob, bGPDlayer *gpl, bGPDframe *gpf, bGPDstroke *gps,
+ const float opacity, const float tintcolor[4], const bool onion, const bool custonion)
+{
+ float tcolor[4];
+ float ink[4];
+ short sthickness;
+ MaterialGPencilStyle *gp_style = BKE_material_gpencil_settings_get(ob, gps->mat_nr + 1);
+
+ /* set color using base color, tint color and opacity */
+ if (!onion) {
+ /* if special stroke, use fill color as stroke color */
+ if (gps->flag & GP_STROKE_NOFILL) {
+ interp_v3_v3v3(tcolor, gps->runtime.tmp_fill_rgba, tintcolor, tintcolor[3]);
+ tcolor[3] = gps->runtime.tmp_fill_rgba[3] * opacity;
+ }
+ else {
+ interp_v3_v3v3(tcolor, gps->runtime.tmp_stroke_rgba, tintcolor, tintcolor[3]);
+ tcolor[3] = gps->runtime.tmp_stroke_rgba[3] * opacity;
+ }
+ copy_v4_v4(ink, tcolor);
+ }
+ else {
+ if (custonion) {
+ copy_v4_v4(ink, tintcolor);
+ }
+ else {
+ ARRAY_SET_ITEMS(tcolor, UNPACK3(gps->runtime.tmp_stroke_rgba), opacity);
+ copy_v4_v4(ink, tcolor);
+ }
+ }
+
+ sthickness = gps->thickness + gpl->line_change;
+ CLAMP_MIN(sthickness, 1);
+ if (cache->is_dirty) {
+ gpencil_batch_cache_check_free_slots(ob);
+ if ((gps->totpoints > 1) && (gp_style->mode == GP_STYLE_MODE_LINE)) {
+ cache->batch_stroke[cache->cache_idx] = DRW_gpencil_get_stroke_geom(gpf, gps, sthickness, ink);
+ }
+ else {
+ cache->batch_stroke[cache->cache_idx] = DRW_gpencil_get_point_geom(gps, sthickness, ink);
+ }
+ }
+ DRW_shgroup_call_add(strokegrp, cache->batch_stroke[cache->cache_idx], gpf->runtime.viewmatrix);
+}
+
+/* add edit points shading group to pass */
+static void gpencil_add_editpoints_shgroup(
+ GPENCIL_StorageList *stl, GpencilBatchCache *cache, ToolSettings *UNUSED(ts), Object *ob,
+ bGPdata *gpd, bGPDlayer *gpl, bGPDframe *gpf, bGPDstroke *gps)
+{
+ const DRWContextState *draw_ctx = DRW_context_state_get();
+ View3D *v3d = draw_ctx->v3d;
+ MaterialGPencilStyle *gp_style = BKE_material_gpencil_settings_get(ob, gps->mat_nr + 1);
+
+ /* alpha factor for edit points/line to make them more subtle */
+ float edit_alpha = v3d->vertex_opacity;
+
+ if (GPENCIL_ANY_EDIT_MODE(gpd)) {
+ Object *obact = DRW_context_state_get()->obact;
+ if ((!obact) || (obact->type != OB_GPENCIL)) {
+ return;
+ }
+ const bool is_weight_paint = (gpd) && (gpd->flag & GP_DATA_STROKE_WEIGHTMODE);
+
+ /* line of the original stroke */
+ if (cache->is_dirty) {
+ gpencil_batch_cache_check_free_slots(ob);
+ cache->batch_edlin[cache->cache_idx] = DRW_gpencil_get_edlin_geom(gps, edit_alpha, gpd->flag);
+ }
+ if (cache->batch_edlin[cache->cache_idx]) {
+ if ((obact) && (obact == ob) &&
+ ((v3d->flag2 & V3D_RENDER_OVERRIDE) == 0) &&
+ (v3d->flag3 & V3D_GP_SHOW_EDIT_LINES))
+ {
+ DRW_shgroup_call_add(
+ stl->g_data->shgrps_edit_line,
+ cache->batch_edlin[cache->cache_idx],
+ gpf->runtime.viewmatrix);
+ }
+ }
+ /* edit points */
+ if ((gps->flag & GP_STROKE_SELECT) || (is_weight_paint)) {
+ if ((gpl->flag & GP_LAYER_UNLOCK_COLOR) || ((gp_style->flag & GP_STYLE_COLOR_LOCKED) == 0)) {
+ if (cache->is_dirty) {
+ gpencil_batch_cache_check_free_slots(ob);
+ cache->batch_edit[cache->cache_idx] = DRW_gpencil_get_edit_geom(gps, edit_alpha, gpd->flag);
+ }
+ if (cache->batch_edit[cache->cache_idx]) {
+ if ((obact) && (obact == ob)) {
+ /* edit pass */
+ DRW_shgroup_call_add(
+ stl->g_data->shgrps_edit_point,
+ cache->batch_edit[cache->cache_idx],
+ gpf->runtime.viewmatrix);
+ }
+ }
+ }
+ }
+ }
+}
+
+/* function to draw strokes for onion only */
+static void gpencil_draw_onion_strokes(
+ GpencilBatchCache *cache, GPENCIL_e_data *e_data, void *vedata, Object *ob,
+ bGPdata *gpd, bGPDlayer *gpl, bGPDframe *gpf,
+ const float opacity, const float tintcolor[4], const bool custonion)
+{
+ GPENCIL_PassList *psl = ((GPENCIL_Data *)vedata)->psl;
+ GPENCIL_StorageList *stl = ((GPENCIL_Data *)vedata)->stl;
+ Depsgraph *depsgraph = DRW_context_state_get()->depsgraph;
+
+ float viewmatrix[4][4];
+
+ /* get parent matrix and save as static data */
+ ED_gpencil_parent_location(depsgraph, ob, gpd, gpl, viewmatrix);
+ copy_m4_m4(gpf->runtime.viewmatrix, viewmatrix);
+
+ for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) {
+ MaterialGPencilStyle *gp_style = BKE_material_gpencil_settings_get(ob, gps->mat_nr + 1);
+ copy_v4_v4(gps->runtime.tmp_stroke_rgba, gp_style->stroke_rgba);
+ copy_v4_v4(gps->runtime.tmp_fill_rgba, gp_style->fill_rgba);
+
+ int id = stl->storage->shgroup_id;
+ /* check if stroke can be drawn */
+ if (gpencil_can_draw_stroke(gp_style, gps, true, false) == false) {
+ continue;
+ }
+ /* limit the number of shading groups */
+ if (id >= GPENCIL_MAX_SHGROUPS) {
+ continue;
+ }
+
+ stl->shgroups[id].shgrps_fill = NULL;
+ if ((gps->totpoints > 1) && (gp_style->mode == GP_STYLE_MODE_LINE)) {
+ stl->shgroups[id].shgrps_stroke = DRW_gpencil_shgroup_stroke_create(
+ e_data, vedata, psl->stroke_pass, e_data->gpencil_stroke_sh, ob, gpd, gp_style, id, true);
+ }
+ else {
+ stl->shgroups[id].shgrps_stroke = DRW_gpencil_shgroup_point_create(
+ e_data, vedata, psl->stroke_pass, e_data->gpencil_point_sh, ob, gpd, gp_style, id, true);
+ }
+
+ /* stroke */
+ gpencil_add_stroke_shgroup(
+ cache, stl->shgroups[id].shgrps_stroke, ob, gpl, gpf, gps, opacity, tintcolor, true, custonion);
+
+ stl->storage->shgroup_id++;
+ cache->cache_idx++;
+ }
+}
+
+
+/* main function to draw strokes */
+static void gpencil_draw_strokes(
+ GpencilBatchCache *cache, GPENCIL_e_data *e_data, void *vedata, ToolSettings *ts, Object *ob,
+ bGPdata *gpd, bGPDlayer *gpl, bGPDframe *src_gpf, bGPDframe *derived_gpf,
+ const float opacity, const float tintcolor[4], const bool custonion)
+{
+ GPENCIL_PassList *psl = ((GPENCIL_Data *)vedata)->psl;
+ GPENCIL_StorageList *stl = ((GPENCIL_Data *)vedata)->stl;
+ const DRWContextState *draw_ctx = DRW_context_state_get();
+ Scene *scene = draw_ctx->scene;
+ View3D *v3d = draw_ctx->v3d;
+ bGPDstroke *gps, *src_gps;
+ DRWShadingGroup *fillgrp;
+ DRWShadingGroup *strokegrp;
+ float viewmatrix[4][4];
+ const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd);
+ const bool playing = (bool)stl->storage->playing;
+ const bool is_render = (bool)stl->storage->is_render;
+ const bool is_mat_preview = (bool)stl->storage->is_mat_preview;
+ const bool overlay_multiedit = v3d != NULL ? (v3d->flag3 & V3D_GP_SHOW_MULTIEDIT_LINES) : true;
+
+ /* Get evaluation context */
+ /* NOTE: We must check if C is valid, otherwise we get crashes when trying to save files
+ * (i.e. the thumbnail offscreen rendering fails)
+ */
+ Depsgraph *depsgraph = DRW_context_state_get()->depsgraph;
+
+ /* get parent matrix and save as static data */
+ ED_gpencil_parent_location(depsgraph, ob, gpd, gpl, viewmatrix);
+ copy_m4_m4(derived_gpf->runtime.viewmatrix, viewmatrix);
+
+ /* apply geometry modifiers */
+ if ((cache->is_dirty) && (ob->greasepencil_modifiers.first) && (!is_multiedit)) {
+ if (!stl->storage->simplify_modif) {
+ if (BKE_gpencil_has_geometry_modifiers(ob)) {
+ BKE_gpencil_geometry_modifiers(depsgraph, ob, gpl, derived_gpf, stl->storage->is_render);
+ }
+ }
+ }
+
+ if (src_gpf) {
+ src_gps = src_gpf->strokes.first;
+ }
+ else {
+ src_gps = NULL;
+ }
+
+ for (gps = derived_gpf->strokes.first; gps; gps = gps->next) {
+ MaterialGPencilStyle *gp_style = BKE_material_gpencil_settings_get(ob, gps->mat_nr + 1);
+
+ /* check if stroke can be drawn */
+ if (gpencil_can_draw_stroke(gp_style, gps, false, is_mat_preview) == false) {
+ continue;
+ }
+ /* limit the number of shading groups */
+ if (stl->storage->shgroup_id >= GPENCIL_MAX_SHGROUPS) {
+ continue;
+ }
+
+ /* be sure recalc all chache in source stroke to avoid recalculation when frame change
+ * and improve fps */
+ if (src_gps) {
+ DRW_gpencil_recalc_geometry_caches(ob, gp_style, src_gps);
+ }
+
+ /* if the fill has any value, it's considered a fill and is not drawn if simplify fill is enabled */
+ if ((stl->storage->simplify_fill) && (scene->r.simplify_gpencil & SIMPLIFY_GPENCIL_REMOVE_FILL_LINE)) {
+ if ((gp_style->fill_rgba[3] > GPENCIL_ALPHA_OPACITY_THRESH) ||
+ (gp_style->fill_style > GP_STYLE_FILL_STYLE_SOLID))
+ {
+ continue;
+ }
+ }
+
+ if ((gpl->actframe->framenum == derived_gpf->framenum) ||
+ (!is_multiedit) || (overlay_multiedit))
+ {
+ int id = stl->storage->shgroup_id;
+ if (gps->totpoints > 0) {
+ if ((gps->totpoints > 2) && (!stl->storage->simplify_fill) &&
+ ((gp_style->fill_rgba[3] > GPENCIL_ALPHA_OPACITY_THRESH) || (gp_style->fill_style > 0)) &&
+ ((gps->flag & GP_STROKE_NOFILL) == 0))
+ {
+ stl->shgroups[id].shgrps_fill = DRW_gpencil_shgroup_fill_create(
+ e_data, vedata, psl->stroke_pass, e_data->gpencil_fill_sh, gpd, gp_style, id);
+ }
+ else {
+ stl->shgroups[id].shgrps_fill = NULL;
+ }
+ if ((gp_style->mode == GP_STYLE_MODE_LINE) && (gps->totpoints > 1)) {
+ stl->shgroups[id].shgrps_stroke = DRW_gpencil_shgroup_stroke_create(
+ e_data, vedata, psl->stroke_pass, e_data->gpencil_stroke_sh, ob, gpd, gp_style, id, false);
+ }
+ else {
+ stl->shgroups[id].shgrps_stroke = DRW_gpencil_shgroup_point_create(
+ e_data, vedata, psl->stroke_pass, e_data->gpencil_point_sh, ob, gpd, gp_style, id, false);
+ }
+ }
+ else {
+ stl->shgroups[id].shgrps_fill = NULL;
+ stl->shgroups[id].shgrps_stroke = NULL;
+ }
+ stl->storage->shgroup_id++;
+
+ fillgrp = stl->shgroups[id].shgrps_fill;
+ strokegrp = stl->shgroups[id].shgrps_stroke;
+
+ /* copy color to temp fields to apply temporal changes in the stroke */
+ copy_v4_v4(gps->runtime.tmp_stroke_rgba, gp_style->stroke_rgba);
+ copy_v4_v4(gps->runtime.tmp_fill_rgba, gp_style->fill_rgba);
+
+ /* apply modifiers (only modify geometry, but not create ) */
+ if ((cache->is_dirty) && (ob->greasepencil_modifiers.first) && (!is_multiedit)) {
+ if (!stl->storage->simplify_modif) {
+ BKE_gpencil_stroke_modifiers(depsgraph, ob, gpl, derived_gpf, gps, stl->storage->is_render);
+ }
+ }
+
+ /* fill */
+ if ((fillgrp) && (!stl->storage->simplify_fill)) {
+ gpencil_add_fill_shgroup(
+ cache, fillgrp, ob, gpl, derived_gpf, gps, tintcolor, false, custonion);
+ }
+ /* stroke */
+ if (strokegrp) {
+ gpencil_add_stroke_shgroup(
+ cache, strokegrp, ob, gpl, derived_gpf, gps, opacity, tintcolor, false, custonion);
+ }
+ }
+
+ /* edit points (only in edit mode and not play animation not render) */
+ if ((src_gps) && (!playing) && (!is_render)) {
+ if (!stl->g_data->shgrps_edit_line) {
+ stl->g_data->shgrps_edit_line = DRW_shgroup_create(e_data->gpencil_line_sh, psl->edit_pass);
+ }
+ if (!stl->g_data->shgrps_edit_point) {
+ stl->g_data->shgrps_edit_point = DRW_shgroup_create(e_data->gpencil_edit_point_sh, psl->edit_pass);
+ const float *viewport_size = DRW_viewport_size_get();
+ DRW_shgroup_uniform_vec2(stl->g_data->shgrps_edit_point, "Viewport", viewport_size, 1);
+ }
+
+ gpencil_add_editpoints_shgroup(stl, cache, ts, ob, gpd, gpl, derived_gpf, src_gps);
+ }
+
+ if (src_gps) {
+ src_gps = src_gps->next;
+ }
+
+ cache->cache_idx++;
+ }
+}
+
+ /* draw stroke in drawing buffer */
+void DRW_gpencil_populate_buffer_strokes(GPENCIL_e_data *e_data, void *vedata, ToolSettings *ts, Object *ob)
+{
+ GPENCIL_PassList *psl = ((GPENCIL_Data *)vedata)->psl;
+ GPENCIL_StorageList *stl = ((GPENCIL_Data *)vedata)->stl;
+ Brush *brush = BKE_brush_getactive_gpencil(ts);
+ bGPdata *gpd = ob->data;
+ MaterialGPencilStyle *gp_style = NULL;
+
+ float obscale = (ob->size[0] + ob->size[1] + ob->size[2]) / 3.0f;
+
+ /* use the brush material */
+ Material *ma = BKE_gpencil_get_material_from_brush(brush);
+ if (ma != NULL) {
+ gp_style = ma->gp_style;
+ }
+ /* this is not common, but avoid any special situations when brush could be without material */
+ if (gp_style == NULL) {
+ gp_style = BKE_material_gpencil_settings_get(ob, ob->actcol);
+ }
+
+ /* drawing strokes */
+ /* Check if may need to draw the active stroke cache, only if this layer is the active layer
+ * that is being edited. (Stroke buffer is currently stored in gp-data)
+ */
+ if (ED_gpencil_session_active() && (gpd->runtime.sbuffer_size > 0)) {
+ if ((gpd->runtime.sbuffer_sflag & GP_STROKE_ERASER) == 0) {
+ /* It should also be noted that sbuffer contains temporary point types
+ * i.e. tGPspoints NOT bGPDspoints
+ */
+ short lthick = brush->size * obscale;
+ /* if only one point, don't need to draw buffer because the user has no time to see it */
+ if (gpd->runtime.sbuffer_size > 1) {
+ if ((gp_style) && (gp_style->mode == GP_STYLE_MODE_LINE)) {
+ stl->g_data->shgrps_drawing_stroke = DRW_gpencil_shgroup_stroke_create(
+ e_data, vedata, psl->drawing_pass, e_data->gpencil_stroke_sh, NULL, gpd, gp_style, -1, false);
+ }
+ else {
+ stl->g_data->shgrps_drawing_stroke = DRW_gpencil_shgroup_point_create(
+ e_data, vedata, psl->drawing_pass, e_data->gpencil_point_sh, NULL, gpd, gp_style, -1, false);
+ }
+
+ /* use unit matrix because the buffer is in screen space and does not need conversion */
+ if (gpd->runtime.mode == GP_STYLE_MODE_LINE) {
+ stl->g_data->batch_buffer_stroke = DRW_gpencil_get_buffer_stroke_geom(
+ gpd, stl->storage->unit_matrix, lthick);
+ }
+ else {
+ stl->g_data->batch_buffer_stroke = DRW_gpencil_get_buffer_point_geom(
+ gpd, stl->storage->unit_matrix, lthick);
+ }
+
+ DRW_shgroup_call_add(
+ stl->g_data->shgrps_drawing_stroke,
+ stl->g_data->batch_buffer_stroke,
+ stl->storage->unit_matrix);
+
+ if ((gpd->runtime.sbuffer_size >= 3) && (gpd->runtime.sfill[3] > GPENCIL_ALPHA_OPACITY_THRESH) &&
+ ((gpd->runtime.sbuffer_sflag & GP_STROKE_NOFILL) == 0))
+ {
+ /* if not solid, fill is simulated with solid color */
+ if (gpd->runtime.bfill_style > 0) {
+ gpd->runtime.sfill[3] = 0.5f;
+ }
+ stl->g_data->shgrps_drawing_fill = DRW_shgroup_create(
+ e_data->gpencil_drawing_fill_sh, psl->drawing_pass);
+ stl->g_data->batch_buffer_fill = DRW_gpencil_get_buffer_fill_geom(gpd);
+ DRW_shgroup_call_add(
+ stl->g_data->shgrps_drawing_fill,
+ stl->g_data->batch_buffer_fill,
+ stl->storage->unit_matrix);
+ }
+ }
+ }
+ }
+}
+
+/* get alpha factor for onion strokes */
+static void gpencil_get_onion_alpha(float color[4], bGPdata *gpd)
+{
+#define MIN_ALPHA_VALUE 0.01f
+
+ /* if fade is disabled, opacity is equal in all frames */
+ if ((gpd->onion_flag & GP_ONION_FADE) == 0) {
+ color[3] = gpd->onion_factor;
+ }
+ else {
+ /* add override opacity factor */
+ color[3] += gpd->onion_factor - 0.5f;
+ }
+
+ CLAMP(color[3], MIN_ALPHA_VALUE, 1.0f);
+}
+
+/* draw onion-skinning for a layer */
+static void gpencil_draw_onionskins(
+ GpencilBatchCache *cache, GPENCIL_e_data *e_data, void *vedata,
+ Object *ob, bGPdata *gpd, bGPDlayer *gpl, bGPDframe *gpf)
+{
+
+ const float default_color[3] = { UNPACK3(U.gpencil_new_layer_col) };
+ const float alpha = 1.0f;
+ float color[4];
+ int idx;
+ float fac = 1.0f;
+ int step = 0;
+ int mode = 0;
+ bool colflag = false;
+ bGPDframe *gpf_loop = NULL;
+ int last = gpf->framenum;
+
+ colflag = (bool)gpd->onion_flag & GP_ONION_GHOST_PREVCOL;
+
+
+ /* -------------------------------
+ * 1) Draw Previous Frames First
+ * ------------------------------- */
+ step = gpd->gstep;
+ mode = gpd->onion_mode;
+
+ if (gpd->onion_flag & GP_ONION_GHOST_PREVCOL) {
+ copy_v3_v3(color, gpd->gcolor_prev);
+ }
+ else {
+ copy_v3_v3(color, default_color);
+ }
+
+ idx = 0;
+ for (bGPDframe *gf = gpf->prev; gf; gf = gf->prev) {
+ /* only selected frames */
+ if ((mode == GP_ONION_MODE_SELECTED) && ((gf->flag & GP_FRAME_SELECT) == 0)) {
+ continue;
+ }
+ /* absolute range */
+ if (mode == GP_ONION_MODE_ABSOLUTE) {
+ if ((gpf->framenum - gf->framenum) > step) {
+ break;
+ }
+ }
+ /* relative range */
+ if (mode == GP_ONION_MODE_RELATIVE) {
+ idx++;
+ if (idx > step) {
+ break;
+ }
+
+ }
+ /* alpha decreases with distance from curframe index */
+ if (mode != GP_ONION_MODE_SELECTED) {
+ if (mode == GP_ONION_MODE_ABSOLUTE) {
+ fac = 1.0f - ((float)(gpf->framenum - gf->framenum) / (float)(step + 1));
+ }
+ else {
+ fac = 1.0f - ((float)idx / (float)(step + 1));
+ }
+ color[3] = alpha * fac * 0.66f;
+ }
+ else {
+ idx++;
+ fac = alpha - ((1.1f - (1.0f / (float)idx)) * 0.66f);
+ color[3] = fac;
+ }
+
+ /* if loop option, save the frame to use later */
+ if ((mode != GP_ONION_MODE_ABSOLUTE) && (gpd->onion_flag & GP_ONION_LOOP)) {
+ gpf_loop = gf;
+ }
+
+ gpencil_get_onion_alpha(color, gpd);
+ gpencil_draw_onion_strokes(cache, e_data, vedata, ob, gpd, gpl, gf, color[3], color, colflag);
+ }
+ /* -------------------------------
+ * 2) Now draw next frames
+ * ------------------------------- */
+ step = gpd->gstep_next;
+ mode = gpd->onion_mode;
+
+ if (gpd->onion_flag & GP_ONION_GHOST_NEXTCOL) {
+ copy_v3_v3(color, gpd->gcolor_next);
+ }
+ else {
+ copy_v3_v3(color, default_color);
+ }
+
+ idx = 0;
+ for (bGPDframe *gf = gpf->next; gf; gf = gf->next) {
+ /* only selected frames */
+ if ((mode == GP_ONION_MODE_SELECTED) && ((gf->flag & GP_FRAME_SELECT) == 0)) {
+ continue;
+ }
+ /* absolute range */
+ if (mode == GP_ONION_MODE_ABSOLUTE) {
+ if ((gf->framenum - gpf->framenum) > step) {
+ break;
+ }
+ }
+ /* relative range */
+ if (mode == GP_ONION_MODE_RELATIVE) {
+ idx++;
+ if (idx > step) {
+ break;
+ }
+
+ }
+ /* alpha decreases with distance from curframe index */
+ if (mode != GP_ONION_MODE_SELECTED) {
+ if (mode == GP_ONION_MODE_ABSOLUTE) {
+ fac = 1.0f - ((float)(gf->framenum - gpf->framenum) / (float)(step + 1));
+ }
+ else {
+ fac = 1.0f - ((float)idx / (float)(step + 1));
+ }
+ color[3] = alpha * fac * 0.66f;
+ }
+ else {
+ idx++;
+ fac = alpha - ((1.1f - (1.0f / (float)idx)) * 0.66f);
+ color[3] = fac;
+ }
+
+ gpencil_get_onion_alpha(color, gpd);
+ gpencil_draw_onion_strokes(cache, e_data, vedata, ob, gpd, gpl, gf, color[3], color, colflag);
+ if (last < gf->framenum) {
+ last = gf->framenum;
+ }
+ }
+
+ /* Draw first frame in blue for loop mode */
+ if ((gpd->onion_flag & GP_ONION_LOOP) && (gpf_loop != NULL)) {
+ if ((last == gpf->framenum) || (gpf->next == NULL)) {
+ gpencil_get_onion_alpha(color, gpd);
+ gpencil_draw_onion_strokes(
+ cache, e_data, vedata, ob, gpd, gpl,
+ gpf_loop, color[3], color, colflag);
+ }
+ }
+}
+
+/* populate a datablock for multiedit (no onions, no modifiers) */
+void DRW_gpencil_populate_multiedit(GPENCIL_e_data *e_data, void *vedata, Scene *scene, Object *ob, bGPdata *gpd)
+{
+ bGPDframe *gpf = NULL;
+
+ GPENCIL_StorageList *stl = ((GPENCIL_Data *)vedata)->stl;
+ const DRWContextState *draw_ctx = DRW_context_state_get();
+ int cfra_eval = (int)DEG_get_ctime(draw_ctx->depsgraph);
+ GpencilBatchCache *cache = gpencil_batch_cache_get(ob, cfra_eval);
+ ToolSettings *ts = scene->toolsettings;
+ cache->cache_idx = 0;
+
+ /* check if playing animation */
+ bool playing = (bool)stl->storage->playing;
+
+ /* draw strokes */
+ for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) {
+ /* don't draw layer if hidden */
+ if (gpl->flag & GP_LAYER_HIDE)
+ continue;
+
+ /* list of frames to draw */
+ if (!playing) {
+ for (gpf = gpl->frames.first; gpf; gpf = gpf->next) {
+ if ((gpf == gpl->actframe) || (gpf->flag & GP_FRAME_SELECT)) {
+ gpencil_draw_strokes(
+ cache, e_data, vedata, ts, ob, gpd, gpl, gpf, gpf,
+ gpl->opacity, gpl->tintcolor, false);
+ }
+ }
+ }
+ else {
+ gpf = BKE_gpencil_layer_getframe(gpl, cfra_eval, 0);
+ if (gpf) {
+ gpencil_draw_strokes(
+ cache, e_data, vedata, ts, ob, gpd, gpl, gpf, gpf,
+ gpl->opacity, gpl->tintcolor, false);
+ }
+ }
+
+ }
+
+ cache->is_dirty = false;
+}
+
+/* helper for populate a complete grease pencil datablock */
+void DRW_gpencil_populate_datablock(GPENCIL_e_data *e_data, void *vedata, Scene *scene, Object *ob, bGPdata *gpd)
+{
+ GPENCIL_StorageList *stl = ((GPENCIL_Data *)vedata)->stl;
+ const DRWContextState *draw_ctx = DRW_context_state_get();
+ View3D *v3d = draw_ctx->v3d;
+ int cfra_eval = (int)DEG_get_ctime(draw_ctx->depsgraph);
+ ToolSettings *ts = scene->toolsettings;
+ bGPDframe *derived_gpf = NULL;
+ const bool main_onion = v3d != NULL ? ((v3d->flag3 & V3D_GP_SHOW_ONION_SKIN) == 0) : true;
+ const bool no_onion = (bool)(gpd->flag & GP_DATA_STROKE_WEIGHTMODE) || main_onion;
+ const bool overlay = v3d != NULL ? (bool)((v3d->flag2 & V3D_RENDER_OVERRIDE) == 0) : true;
+
+ /* check if playing animation */
+ bool playing = (bool)stl->storage->playing;
+
+ GpencilBatchCache *cache = gpencil_batch_cache_get(ob, cfra_eval);
+ cache->cache_idx = 0;
+
+ /* init general modifiers data */
+ if (!stl->storage->simplify_modif) {
+ if ((cache->is_dirty) && (ob->greasepencil_modifiers.first)) {
+ BKE_gpencil_lattice_init(ob);
+ }
+ }
+ /* draw normal strokes */
+ for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) {
+ /* don't draw layer if hidden */
+ if (gpl->flag & GP_LAYER_HIDE)
+ continue;
+
+ bGPDframe *gpf = BKE_gpencil_layer_getframe(gpl, cfra_eval, 0);
+ if (gpf == NULL)
+ continue;
+
+ /* create GHash if need */
+ if (gpl->runtime.derived_data == NULL) {
+ gpl->runtime.derived_data = (GHash *)BLI_ghash_str_new(gpl->info);
+ }
+
+ derived_gpf = BLI_ghash_lookup(gpl->runtime.derived_data, ob->id.name);
+ if (derived_gpf == NULL) {
+ cache->is_dirty = true;
+ }
+ if (cache->is_dirty) {
+ if (derived_gpf != NULL) {
+ /* first clear temp data */
+ BKE_gpencil_free_frame_runtime_data(derived_gpf);
+ BLI_ghash_remove(gpl->runtime.derived_data, ob->id.name, NULL, NULL);
+ }
+ /* create new data */
+ derived_gpf = BKE_gpencil_frame_duplicate(gpf);
+ BLI_ghash_insert(gpl->runtime.derived_data, ob->id.name, derived_gpf);
+ }
+
+ /* draw onion skins */
+ if ((gpd->flag & GP_DATA_SHOW_ONIONSKINS) &&
+ (!no_onion) && (overlay) &&
+ (gpl->onion_flag & GP_LAYER_ONIONSKIN) &&
+ ((!playing) || (gpd->onion_flag & GP_ONION_GHOST_ALWAYS)))
+ {
+ if ((!stl->storage->is_render) ||
+ ((stl->storage->is_render) && (gpd->onion_flag & GP_ONION_GHOST_ALWAYS)))
+ {
+ gpencil_draw_onionskins(cache, e_data, vedata, ob, gpd, gpl, gpf);
+ }
+ }
+
+ /* draw normal strokes */
+ gpencil_draw_strokes(
+ cache, e_data, vedata, ts, ob, gpd, gpl, gpf, derived_gpf,
+ gpl->opacity, gpl->tintcolor, false);
+
+ }
+
+ /* clear any lattice data */
+ if ((cache->is_dirty) && (ob->greasepencil_modifiers.first)) {
+ BKE_gpencil_lattice_clear(ob);
+ }
+
+ cache->is_dirty = false;
+}
+
+/* Helper for gpencil_instance_modifiers()
+ * See also MOD_gpencilinstance.c -> bakeModifier()
+ */
+static void gp_instance_modifier_make_instances(GPENCIL_StorageList *stl, Object *ob, InstanceGpencilModifierData *mmd)
+{
+ /* reset random */
+ mmd->rnd[0] = 1;
+
+ /* Generate instances */
+ for (int x = 0; x < mmd->count[0]; x++) {
+ for (int y = 0; y < mmd->count[1]; y++) {
+ for (int z = 0; z < mmd->count[2]; z++) {
+ Object *newob;
+
+ const int elem_idx[3] = {x, y, z};
+ float mat[4][4];
+ int sh;
+
+ /* original strokes are at index = 0,0,0 */
+ if ((x == 0) && (y == 0) && (z == 0)) {
+ continue;
+ }
+
+ /* compute transform for instance */
+ BKE_gpencil_instance_modifier_instance_tfm(mmd, elem_idx, mat);
+
+ /* add object to cache */
+ newob = MEM_dupallocN(ob);
+ mul_m4_m4m4(newob->obmat, ob->obmat, mat);
+
+ /* apply scale */
+ ARRAY_SET_ITEMS(newob->size, mat[0][0], mat[1][1], mat[2][2]);
+
+ /* apply shift */
+ sh = x;
+ if (mmd->lock_axis == GP_LOCKAXIS_Y) {
+ sh = y;
+ }
+ if (mmd->lock_axis == GP_LOCKAXIS_Z) {
+ sh = z;
+ }
+ madd_v3_v3fl(newob->obmat[3], mmd->shift, sh);
+
+ /* add temp object to cache */
+ stl->g_data->gp_object_cache = gpencil_object_cache_add(
+ stl->g_data->gp_object_cache, newob, true,
+ &stl->g_data->gp_cache_size, &stl->g_data->gp_cache_used);
+ }
+ }
+ }
+}
+
+/* create instances using instance modifiers */
+void gpencil_instance_modifiers(GPENCIL_StorageList *stl, Object *ob)
+{
+ if ((ob) && (ob->data)) {
+ bGPdata *gpd = ob->data;
+ if (GPENCIL_ANY_EDIT_MODE(gpd)) {
+ return;
+ }
+ }
+
+ for (GpencilModifierData *md = ob->greasepencil_modifiers.first; md; md = md->next) {
+ if (((md->mode & eGpencilModifierMode_Realtime) && (stl->storage->is_render == false)) ||
+ ((md->mode & eGpencilModifierMode_Render) && (stl->storage->is_render == true)))
+ {
+ if (md->type == eGpencilModifierType_Instance) {
+ InstanceGpencilModifierData *mmd = (InstanceGpencilModifierData *)md;
+
+ /* Only add instances if the "Make Objects" flag is set
+ * FIXME: This is a workaround for z-ordering weirdness when all instances are in the same object
+ */
+ if (mmd->flag & GP_INSTANCE_MAKE_OBJECTS) {
+ gp_instance_modifier_make_instances(stl, ob, mmd);
+ }
+ }
+ }
+ }
+}
diff --git a/source/blender/draw/engines/gpencil/gpencil_engine.c b/source/blender/draw/engines/gpencil/gpencil_engine.c
new file mode 100644
index 00000000000..71c99b2bcdf
--- /dev/null
+++ b/source/blender/draw/engines/gpencil/gpencil_engine.c
@@ -0,0 +1,794 @@
+/*
+ * Copyright 2017, Blender Foundation.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Contributor(s): Antonio Vazquez
+ *
+ */
+
+/** \file blender/draw/engines/gpencil/gpencil_engine.c
+ * \ingroup draw
+ */
+#include "DRW_engine.h"
+#include "DRW_render.h"
+
+#include "BKE_camera.h"
+#include "BKE_object.h"
+#include "BKE_paint.h"
+#include "BKE_gpencil.h"
+#include "BKE_shader_fx.h"
+
+#include "DNA_gpencil_types.h"
+#include "DNA_view3d_types.h"
+
+#include "draw_mode_engines.h"
+
+#include "UI_resources.h"
+
+#include "GPU_texture.h"
+
+#include "gpencil_engine.h"
+
+#include "ED_screen.h"
+#include "ED_gpencil.h"
+
+extern char datatoc_gpencil_fill_vert_glsl[];
+extern char datatoc_gpencil_fill_frag_glsl[];
+extern char datatoc_gpencil_stroke_vert_glsl[];
+extern char datatoc_gpencil_stroke_geom_glsl[];
+extern char datatoc_gpencil_stroke_frag_glsl[];
+extern char datatoc_gpencil_zdepth_mix_frag_glsl[];
+extern char datatoc_gpencil_simple_mix_frag_glsl[];
+extern char datatoc_gpencil_point_vert_glsl[];
+extern char datatoc_gpencil_point_geom_glsl[];
+extern char datatoc_gpencil_point_frag_glsl[];
+extern char datatoc_gpencil_background_frag_glsl[];
+extern char datatoc_gpencil_paper_frag_glsl[];
+extern char datatoc_gpencil_edit_point_vert_glsl[];
+extern char datatoc_gpencil_edit_point_geom_glsl[];
+extern char datatoc_gpencil_edit_point_frag_glsl[];
+
+/* *********** STATIC *********** */
+static GPENCIL_e_data e_data = {NULL}; /* Engine data */
+
+/* *********** FUNCTIONS *********** */
+
+/* create a multisample buffer if not present */
+void DRW_gpencil_multisample_ensure(GPENCIL_Data *vedata, int rect_w, int rect_h)
+{
+ GPENCIL_FramebufferList *fbl = vedata->fbl;
+ GPENCIL_StorageList *stl = ((GPENCIL_Data *)vedata)->stl;
+ GPENCIL_TextureList *txl = ((GPENCIL_Data *)vedata)->txl;
+
+ short samples = stl->storage->multisamples;
+
+ if (samples > 0) {
+ if (!fbl->multisample_fb) {
+ fbl->multisample_fb = GPU_framebuffer_create();
+ if (fbl->multisample_fb) {
+ if (txl->multisample_color == NULL) {
+ txl->multisample_color = GPU_texture_create_2D_multisample(
+ rect_w, rect_h, GPU_RGBA16F, NULL, samples, NULL);
+ }
+ if (txl->multisample_depth == NULL) {
+ txl->multisample_depth = GPU_texture_create_2D_multisample(
+ rect_w, rect_h, GPU_DEPTH24_STENCIL8, NULL, samples, NULL);
+ }
+ GPU_framebuffer_ensure_config(&fbl->multisample_fb, {
+ GPU_ATTACHMENT_TEXTURE(txl->multisample_depth),
+ GPU_ATTACHMENT_TEXTURE(txl->multisample_color)
+ });
+ if (!GPU_framebuffer_check_valid(fbl->multisample_fb, NULL)) {
+ GPU_framebuffer_free(fbl->multisample_fb);
+ }
+ }
+ }
+ }
+}
+
+static void GPENCIL_create_framebuffers(void *vedata)
+{
+ GPENCIL_FramebufferList *fbl = ((GPENCIL_Data *)vedata)->fbl;
+ GPENCIL_StorageList *stl = ((GPENCIL_Data *)vedata)->stl;
+
+ /* Go full 32bits for rendering */
+ GPUTextureFormat fb_format = DRW_state_is_image_render() ? GPU_RGBA32F : GPU_RGBA16F;
+
+ if (DRW_state_is_fbo()) {
+ const float *viewport_size = DRW_viewport_size_get();
+ const int size[2] = { (int)viewport_size[0], (int)viewport_size[1] };
+
+ /* create multiframe framebuffer for AA */
+ if (stl->storage->multisamples > 0) {
+ DRW_gpencil_multisample_ensure(vedata, size[0], size[1]);
+ }
+
+ /* temp textures */
+ e_data.temp_depth_tx_a = DRW_texture_pool_query_2D(size[0], size[1], GPU_DEPTH24_STENCIL8,
+ &draw_engine_gpencil_type);
+ e_data.temp_color_tx_a = DRW_texture_pool_query_2D(size[0], size[1], fb_format,
+ &draw_engine_gpencil_type);
+ GPU_framebuffer_ensure_config(&fbl->temp_fb_a, {
+ GPU_ATTACHMENT_TEXTURE(e_data.temp_depth_tx_a),
+ GPU_ATTACHMENT_TEXTURE(e_data.temp_color_tx_a)
+ });
+
+ e_data.temp_depth_tx_b = DRW_texture_pool_query_2D(size[0], size[1], GPU_DEPTH24_STENCIL8,
+ &draw_engine_gpencil_type);
+ e_data.temp_color_tx_b = DRW_texture_pool_query_2D(size[0], size[1], fb_format,
+ &draw_engine_gpencil_type);
+ GPU_framebuffer_ensure_config(&fbl->temp_fb_b, {
+ GPU_ATTACHMENT_TEXTURE(e_data.temp_depth_tx_b),
+ GPU_ATTACHMENT_TEXTURE(e_data.temp_color_tx_b)
+ });
+
+ /* used for rim FX effect */
+ e_data.temp_depth_tx_rim = DRW_texture_pool_query_2D(size[0], size[1], GPU_DEPTH24_STENCIL8,
+ &draw_engine_gpencil_type);
+ e_data.temp_color_tx_rim = DRW_texture_pool_query_2D(size[0], size[1], fb_format,
+ &draw_engine_gpencil_type);
+ GPU_framebuffer_ensure_config(&fbl->temp_fb_rim, {
+ GPU_ATTACHMENT_TEXTURE(e_data.temp_depth_tx_rim),
+ GPU_ATTACHMENT_TEXTURE(e_data.temp_color_tx_rim),
+ });
+
+ /* background framebuffer to speed up drawing process (always 16 bits) */
+ e_data.background_depth_tx = DRW_texture_pool_query_2D(size[0], size[1], GPU_DEPTH24_STENCIL8,
+ &draw_engine_gpencil_type);
+ e_data.background_color_tx = DRW_texture_pool_query_2D(size[0], size[1], GPU_RGBA32F,
+ &draw_engine_gpencil_type);
+ GPU_framebuffer_ensure_config(&fbl->background_fb, {
+ GPU_ATTACHMENT_TEXTURE(e_data.background_depth_tx),
+ GPU_ATTACHMENT_TEXTURE(e_data.background_color_tx)
+ });
+ }
+}
+
+static void GPENCIL_create_shaders(void)
+{
+ /* normal fill shader */
+ if (!e_data.gpencil_fill_sh) {
+ e_data.gpencil_fill_sh = DRW_shader_create(
+ datatoc_gpencil_fill_vert_glsl, NULL,
+ datatoc_gpencil_fill_frag_glsl, NULL);
+ }
+
+ /* normal stroke shader using geometry to display lines (line mode) */
+ if (!e_data.gpencil_stroke_sh) {
+ e_data.gpencil_stroke_sh = DRW_shader_create(
+ datatoc_gpencil_stroke_vert_glsl,
+ datatoc_gpencil_stroke_geom_glsl,
+ datatoc_gpencil_stroke_frag_glsl,
+ NULL);
+ }
+
+ /* dot/rectangle mode for normal strokes using geometry */
+ if (!e_data.gpencil_point_sh) {
+ e_data.gpencil_point_sh = DRW_shader_create(
+ datatoc_gpencil_point_vert_glsl,
+ datatoc_gpencil_point_geom_glsl,
+ datatoc_gpencil_point_frag_glsl,
+ NULL);
+ }
+ /* used for edit points or strokes with one point only */
+ if (!e_data.gpencil_edit_point_sh) {
+ e_data.gpencil_edit_point_sh = DRW_shader_create(
+ datatoc_gpencil_edit_point_vert_glsl,
+ datatoc_gpencil_edit_point_geom_glsl,
+ datatoc_gpencil_edit_point_frag_glsl, NULL);
+ }
+
+ /* used for edit lines for edit modes */
+ if (!e_data.gpencil_line_sh) {
+ e_data.gpencil_line_sh = GPU_shader_get_builtin_shader(GPU_SHADER_3D_FLAT_COLOR);
+ }
+
+ /* used to filling during drawing */
+ if (!e_data.gpencil_drawing_fill_sh) {
+ e_data.gpencil_drawing_fill_sh = GPU_shader_get_builtin_shader(GPU_SHADER_3D_SMOOTH_COLOR);
+ }
+
+ /* full screen for mix zdepth*/
+ if (!e_data.gpencil_fullscreen_sh) {
+ e_data.gpencil_fullscreen_sh = DRW_shader_create_fullscreen(datatoc_gpencil_zdepth_mix_frag_glsl, NULL);
+ }
+ if (!e_data.gpencil_simple_fullscreen_sh) {
+ e_data.gpencil_simple_fullscreen_sh = DRW_shader_create_fullscreen(datatoc_gpencil_simple_mix_frag_glsl, NULL);
+ }
+
+ /* shaders for use when drawing */
+ if (!e_data.gpencil_background_sh) {
+ e_data.gpencil_background_sh = DRW_shader_create_fullscreen(datatoc_gpencil_background_frag_glsl, NULL);
+ }
+ if (!e_data.gpencil_paper_sh) {
+ e_data.gpencil_paper_sh = DRW_shader_create_fullscreen(datatoc_gpencil_paper_frag_glsl, NULL);
+ }
+}
+
+void GPENCIL_engine_init(void *vedata)
+{
+ GPENCIL_StorageList *stl = ((GPENCIL_Data *)vedata)->stl;
+ /* init storage */
+ if (!stl->storage) {
+ stl->storage = MEM_callocN(sizeof(GPENCIL_Storage), "GPENCIL_Storage");
+
+ /* unit matrix */
+ unit_m4(stl->storage->unit_matrix);
+ }
+
+ stl->storage->multisamples = U.gpencil_multisamples;
+
+ /* create framebuffers */
+ GPENCIL_create_framebuffers(vedata);
+
+ /* create shaders */
+ GPENCIL_create_shaders();
+ GPENCIL_create_fx_shaders(&e_data);
+
+ /* blank texture used if no texture defined for fill shader */
+ if (!e_data.gpencil_blank_texture) {
+ float rect[16][16][4] = {{{0.0f}}};
+ e_data.gpencil_blank_texture = DRW_texture_create_2D(16, 16, GPU_RGBA8, DRW_TEX_FILTER, (float *)rect);
+ }
+}
+
+static void GPENCIL_engine_free(void)
+{
+ /* only free custom shaders, builtin shaders are freed in blender close */
+ DRW_SHADER_FREE_SAFE(e_data.gpencil_fill_sh);
+ DRW_SHADER_FREE_SAFE(e_data.gpencil_stroke_sh);
+ DRW_SHADER_FREE_SAFE(e_data.gpencil_point_sh);
+ DRW_SHADER_FREE_SAFE(e_data.gpencil_edit_point_sh);
+ DRW_SHADER_FREE_SAFE(e_data.gpencil_fullscreen_sh);
+ DRW_SHADER_FREE_SAFE(e_data.gpencil_simple_fullscreen_sh);
+ DRW_SHADER_FREE_SAFE(e_data.gpencil_background_sh);
+ DRW_SHADER_FREE_SAFE(e_data.gpencil_paper_sh);
+
+ DRW_TEXTURE_FREE_SAFE(e_data.gpencil_blank_texture);
+
+ /* effects */
+ GPENCIL_delete_fx_shaders(&e_data);
+}
+
+void GPENCIL_cache_init(void *vedata)
+{
+ GPENCIL_PassList *psl = ((GPENCIL_Data *)vedata)->psl;
+ GPENCIL_StorageList *stl = ((GPENCIL_Data *)vedata)->stl;
+ const DRWContextState *draw_ctx = DRW_context_state_get();
+ Scene *scene = draw_ctx->scene;
+ View3D *v3d = draw_ctx->v3d;
+
+ /* Special handling for when active object is GP object (e.g. for draw mode) */
+ Object *obact = draw_ctx->obact;
+ bGPdata *obact_gpd = NULL;
+ MaterialGPencilStyle *gp_style = NULL;
+
+ if (obact && (obact->type == OB_GPENCIL) && (obact->data)) {
+ obact_gpd = (bGPdata *)obact->data;
+ gp_style = BKE_material_gpencil_settings_get(obact, obact->actcol);
+ }
+
+ if (!stl->g_data) {
+ /* Alloc transient pointers */
+ stl->g_data = MEM_mallocN(sizeof(g_data), "g_data");
+ stl->storage->xray = GP_XRAY_FRONT; /* used for drawing */
+ stl->storage->stroke_style = GP_STYLE_STROKE_STYLE_SOLID; /* used for drawing */
+ }
+ stl->storage->tonemapping = 0;
+
+ stl->g_data->shgrps_edit_line = NULL;
+ stl->g_data->shgrps_edit_point = NULL;
+
+ if (!stl->shgroups) {
+ /* Alloc maximum size because count strokes is very slow and can be very complex due onion skinning.
+ I tried to allocate only one block and using realloc, increasing the size when read a new strokes
+ in cache_finish, but the realloc produce weird things on screen, so we keep as is while we found
+ a better solution
+ */
+ stl->shgroups = MEM_mallocN(sizeof(GPENCIL_shgroup) * GPENCIL_MAX_SHGROUPS, "GPENCIL_shgroup");
+ }
+
+ /* init gp objects cache */
+ stl->g_data->gp_cache_used = 0;
+ stl->g_data->gp_cache_size = 0;
+ stl->g_data->gp_object_cache = NULL;
+
+ {
+ /* Stroke pass */
+ psl->stroke_pass = DRW_pass_create(
+ "GPencil Stroke Pass",
+ DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_ALWAYS | DRW_STATE_BLEND);
+ stl->storage->shgroup_id = 0;
+
+ /* edit pass */
+ psl->edit_pass = DRW_pass_create(
+ "GPencil Edit Pass",
+ DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND);
+
+ /* detect if playing animation */
+ stl->storage->playing = 0;
+ if (draw_ctx->evil_C) {
+ stl->storage->playing = ED_screen_animation_playing(CTX_wm_manager(draw_ctx->evil_C)) != NULL ? 1 : 0;
+ }
+
+ if (obact_gpd) {
+ /* for some reason, when press play there is a delay in the animation flag check
+ * and this produces errors. To be sure, we set cache as dirty because the frame
+ * is changing.
+ */
+ if (stl->storage->playing == 1) {
+ obact_gpd->flag |= GP_DATA_CACHE_IS_DIRTY;
+ }
+ /* if render, set as dirty to update all data */
+ else if (stl->storage->is_render == true) {
+ obact_gpd->flag |= GP_DATA_CACHE_IS_DIRTY;
+ }
+ }
+
+ /* save render state */
+ stl->storage->is_render = DRW_state_is_image_render();
+ stl->storage->is_mat_preview = (bool)stl->storage->is_render && STREQ(scene->id.name + 2, "preview");
+
+ /* save simplify flags (can change while drawing, so it's better to save) */
+ stl->storage->simplify_fill = GP_SIMPLIFY_FILL(scene, stl->storage->playing);
+ stl->storage->simplify_modif = GP_SIMPLIFY_MODIF(scene, stl->storage->playing);
+
+ /* save pixsize */
+ stl->storage->pixsize = DRW_viewport_pixelsize_get();
+ if ((!DRW_state_is_opengl_render()) && (stl->storage->is_render)) {
+ stl->storage->pixsize = &stl->storage->render_pixsize;
+ }
+
+ /* detect if painting session */
+ if ((obact_gpd) &&
+ (obact_gpd->flag & GP_DATA_STROKE_PAINTMODE) &&
+ (stl->storage->playing == 0))
+ {
+ if (((obact_gpd->runtime.sbuffer_sflag & GP_STROKE_ERASER) == 0) &&
+ (obact_gpd->runtime.sbuffer_size > 1))
+ {
+ stl->g_data->session_flag = GP_DRW_PAINT_PAINTING;
+ }
+ else {
+ stl->g_data->session_flag = GP_DRW_PAINT_IDLE;
+ }
+ }
+ else {
+ /* if not drawing mode */
+ stl->g_data->session_flag = GP_DRW_PAINT_HOLD;
+ }
+
+ if (gp_style) {
+ stl->storage->stroke_style = gp_style->stroke_style;
+ stl->storage->color_type = GPENCIL_COLOR_SOLID;
+ if (gp_style->stroke_style == GP_STYLE_STROKE_STYLE_TEXTURE) {
+ stl->storage->color_type = GPENCIL_COLOR_TEXTURE;
+ if (gp_style->flag & GP_STYLE_STROKE_PATTERN) {
+ stl->storage->color_type = GPENCIL_COLOR_PATTERN;
+ }
+ }
+ }
+ else {
+ stl->storage->stroke_style = GP_STYLE_STROKE_STYLE_SOLID;
+ stl->storage->color_type = GPENCIL_COLOR_SOLID;
+ }
+
+ /* drawing buffer pass for drawing the stroke that is beeing drawing by the user. The data
+ * is stored in sbuffer
+ */
+ psl->drawing_pass = DRW_pass_create(
+ "GPencil Drawing Pass",
+ DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND | DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_ALWAYS);
+
+ /* full screen pass to combine the result with default framebuffer */
+ struct GPUBatch *quad = DRW_cache_fullscreen_quad_get();
+ psl->mix_pass = DRW_pass_create(
+ "GPencil Mix Pass",
+ DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND | DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS);
+ DRWShadingGroup *mix_shgrp = DRW_shgroup_create(e_data.gpencil_fullscreen_sh, psl->mix_pass);
+ DRW_shgroup_call_add(mix_shgrp, quad, NULL);
+ DRW_shgroup_uniform_texture_ref(mix_shgrp, "strokeColor", &e_data.input_color_tx);
+ DRW_shgroup_uniform_texture_ref(mix_shgrp, "strokeDepth", &e_data.input_depth_tx);
+ DRW_shgroup_uniform_int(mix_shgrp, "tonemapping", &stl->storage->tonemapping, 1);
+
+ /* mix pass no blend used to copy between passes. A separated pass is required
+ * because if mix_pass is used, the acumulation of blend degrade the colors.
+ *
+ * This pass is used too to take the snapshot used for background_pass. This image
+ * will be used as the background while the user is drawing.
+ */
+ psl->mix_pass_noblend = DRW_pass_create(
+ "GPencil Mix Pass no blend",
+ DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS);
+ DRWShadingGroup *mix_shgrp_noblend = DRW_shgroup_create(e_data.gpencil_fullscreen_sh, psl->mix_pass_noblend);
+ DRW_shgroup_call_add(mix_shgrp_noblend, quad, NULL);
+ DRW_shgroup_uniform_texture_ref(mix_shgrp_noblend, "strokeColor", &e_data.input_color_tx);
+ DRW_shgroup_uniform_texture_ref(mix_shgrp_noblend, "strokeDepth", &e_data.input_depth_tx);
+ DRW_shgroup_uniform_int(mix_shgrp_noblend, "tonemapping", &stl->storage->tonemapping, 1);
+
+ /* Painting session pass (used only to speedup while the user is drawing )
+ * This pass is used to show the snapshot of the current grease pencil strokes captured
+ * when the user starts to draw (see comments above).
+ * In this way, the previous strokes don't need to be redraw and the drawing process
+ * is far to agile.
+ */
+ psl->background_pass = DRW_pass_create(
+ "GPencil Background Painting Session Pass",
+ DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND | DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS);
+ DRWShadingGroup *background_shgrp = DRW_shgroup_create(e_data.gpencil_background_sh, psl->background_pass);
+ DRW_shgroup_call_add(background_shgrp, quad, NULL);
+ DRW_shgroup_uniform_texture_ref(background_shgrp, "strokeColor", &e_data.background_color_tx);
+ DRW_shgroup_uniform_texture_ref(background_shgrp, "strokeDepth", &e_data.background_depth_tx);
+
+ /* pass for drawing paper (only if viewport)
+ * In render, the v3d is null so the paper is disabled
+ * The paper is way to isolate the drawing in complex scene and to have a cleaner
+ * drawing area.
+ */
+ if (v3d) {
+ psl->paper_pass = DRW_pass_create(
+ "GPencil Paper Pass",
+ DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND);
+ DRWShadingGroup *paper_shgrp = DRW_shgroup_create(e_data.gpencil_paper_sh, psl->paper_pass);
+ DRW_shgroup_call_add(paper_shgrp, quad, NULL);
+ DRW_shgroup_uniform_vec3(paper_shgrp, "color", v3d->shading.background_color, 1);
+ DRW_shgroup_uniform_float(paper_shgrp, "opacity", &v3d->overlay.gpencil_paper_opacity, 1);
+ }
+
+ /* grid pass */
+ if (v3d) {
+ psl->grid_pass = DRW_pass_create(
+ "GPencil Grid Pass",
+ DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND | DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_ALWAYS);
+ stl->g_data->shgrps_grid = DRW_shgroup_create(e_data.gpencil_line_sh, psl->grid_pass);
+ }
+
+ /* create effects passes */
+ GPENCIL_create_fx_passes(psl);
+ }
+}
+
+void GPENCIL_cache_populate(void *vedata, Object *ob)
+{
+ /* object must be visible */
+ if (!DRW_check_object_visible_within_active_context(ob)) {
+ return;
+ }
+
+ GPENCIL_StorageList *stl = ((GPENCIL_Data *)vedata)->stl;
+ const DRWContextState *draw_ctx = DRW_context_state_get();
+ Scene *scene = draw_ctx->scene;
+ ToolSettings *ts = scene->toolsettings;
+ View3D *v3d = draw_ctx->v3d;
+
+ /* object datablock (this is not draw now) */
+ if (ob->type == OB_GPENCIL && ob->data) {
+ bGPdata *gpd = (bGPdata *)ob->data;
+ if ((stl->g_data->session_flag & GP_DRW_PAINT_READY) == 0) {
+
+ /* if render set as dirty */
+ if (stl->storage->is_render == true) {
+ gpd->flag |= GP_DATA_CACHE_IS_DIRTY;
+ }
+
+ /* allocate memory for saving gp objects for drawing later */
+ stl->g_data->gp_object_cache = gpencil_object_cache_add(stl->g_data->gp_object_cache, ob, false,
+ &stl->g_data->gp_cache_size, &stl->g_data->gp_cache_used);
+
+ /* generate instances as separate cache objects for instance modifiers
+ * with the "Make as Objects" option enabled
+ */
+ if (!stl->storage->simplify_modif) {
+ gpencil_instance_modifiers(stl, ob);
+ }
+ }
+ /* draw current painting strokes */
+ DRW_gpencil_populate_buffer_strokes(&e_data, vedata, ts, ob);
+
+ /* grid */
+ if ((v3d) &&
+ ((v3d->flag2 & V3D_RENDER_OVERRIDE) == 0) &&
+ (v3d->flag3 & V3D_GP_SHOW_GRID) &&
+ (ob->type == OB_GPENCIL) && (ob == draw_ctx->obact))
+ {
+ stl->g_data->batch_grid = DRW_gpencil_get_grid();
+ DRW_shgroup_call_add(stl->g_data->shgrps_grid,
+ stl->g_data->batch_grid,
+ ob->obmat);
+ }
+ }
+}
+
+void GPENCIL_cache_finish(void *vedata)
+{
+ GPENCIL_StorageList *stl = ((GPENCIL_Data *)vedata)->stl;
+ const DRWContextState *draw_ctx = DRW_context_state_get();
+ Scene *scene = draw_ctx->scene;
+ bool is_multiedit = false;
+
+ /* if painting session, don't need to do more */
+ if (stl->g_data->session_flag & GP_DRW_PAINT_PAINTING) {
+ return;
+ }
+
+ /* Draw all pending objects */
+ if (stl->g_data->gp_cache_used > 0) {
+ for (int i = 0; i < stl->g_data->gp_cache_used; i++) {
+ Object *ob = stl->g_data->gp_object_cache[i].ob;
+ bGPdata *gpd = ob->data;
+
+ /* save init shading group */
+ stl->g_data->gp_object_cache[i].init_grp = stl->storage->shgroup_id;
+
+ /* fill shading groups */
+ is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd);
+ if (!is_multiedit) {
+ DRW_gpencil_populate_datablock(&e_data, vedata, scene, ob, gpd);
+ }
+ else {
+ DRW_gpencil_populate_multiedit(&e_data, vedata, scene, ob, gpd);
+ }
+
+ /* save end shading group */
+ stl->g_data->gp_object_cache[i].end_grp = stl->storage->shgroup_id - 1;
+ /* if render set to dirty to refresh viewport */
+ if (stl->storage->is_render == true) {
+ gpd->flag |= GP_DATA_CACHE_IS_DIRTY;
+ }
+ /* FX passses */
+ tGPencilObjectCache *cache = &stl->g_data->gp_object_cache[i];
+ if (!is_multiedit) {
+ DRW_gpencil_fx_prepare(&e_data, vedata, cache);
+ }
+ }
+ }
+}
+
+/* helper function to sort inverse gpencil objects using qsort */
+static int gpencil_object_cache_compare_zdepth(const void *a1, const void *a2)
+{
+ const tGPencilObjectCache *ps1 = a1, *ps2 = a2;
+
+ if (ps1->zdepth < ps2->zdepth) return 1;
+ else if (ps1->zdepth > ps2->zdepth) return -1;
+
+ return 0;
+}
+
+/* prepare a texture with full viewport screenshot for fast drawing */
+static void gpencil_prepare_fast_drawing(
+ GPENCIL_StorageList *stl, DefaultFramebufferList *dfbl,
+ GPENCIL_FramebufferList *fbl, DRWPass *pass,
+ const float clearcol[4])
+{
+ if (stl->g_data->session_flag & (GP_DRW_PAINT_IDLE | GP_DRW_PAINT_FILLING)) {
+ GPU_framebuffer_bind(fbl->background_fb);
+ /* clean only in first loop cycle */
+ if (stl->g_data->session_flag & GP_DRW_PAINT_IDLE) {
+ GPU_framebuffer_clear_color_depth(fbl->background_fb, clearcol, 1.0f);
+ stl->g_data->session_flag = GP_DRW_PAINT_FILLING;
+ }
+ /* repeat pass to fill temp texture */
+ DRW_draw_pass(pass);
+ /* set default framebuffer again */
+ GPU_framebuffer_bind(dfbl->default_fb);
+ }
+}
+
+static void gpencil_free_obj_list(GPENCIL_StorageList *stl)
+{
+ /* Clear temp objects created for display instances only. These objects are created
+ * while the draw manager draw the scene, but only to hold the strokes data.
+ * see: gp_instance_modifier_make_instances()
+ *
+ * the normal objects are not freed because they are not tagged as temp objects
+ */
+ for (int i = 0; i < stl->g_data->gp_cache_used; i++) {
+ Object *ob = stl->g_data->gp_object_cache[i].ob;
+ if (stl->g_data->gp_object_cache[i].temp_ob) {
+ MEM_SAFE_FREE(ob);
+ }
+ }
+
+ /* free the cache itself */
+ MEM_SAFE_FREE(stl->g_data->gp_object_cache);
+}
+
+/* draw scene */
+void GPENCIL_draw_scene(void *ved)
+{
+ GPENCIL_Data *vedata = (GPENCIL_Data *)ved;
+ GPENCIL_StorageList *stl = ((GPENCIL_Data *)vedata)->stl;
+
+ GPENCIL_PassList *psl = ((GPENCIL_Data *)vedata)->psl;
+ GPENCIL_FramebufferList *fbl = ((GPENCIL_Data *)vedata)->fbl;
+ DefaultFramebufferList *dfbl = DRW_viewport_framebuffer_list_get();
+ GPENCIL_TextureList *txl = ((GPENCIL_Data *)vedata)->txl;
+
+ int init_grp, end_grp;
+ tGPencilObjectCache *cache;
+ const float clearcol[4] = { 0.0f, 0.0f, 0.0f, 0.0f };
+
+ const DRWContextState *draw_ctx = DRW_context_state_get();
+ View3D *v3d = draw_ctx->v3d;
+ Object *obact = draw_ctx->obact;
+ const bool playing = (bool)stl->storage->playing;
+ const bool is_render = stl->storage->is_render;
+
+ /* paper pass to display a confortable area to draw over complex scenes with geometry */
+ if ((!is_render) && (obact) && (obact->type == OB_GPENCIL)) {
+ if (((v3d->flag2 & V3D_RENDER_OVERRIDE) == 0) &&
+ (v3d->flag3 & V3D_GP_SHOW_PAPER) &&
+ (stl->g_data->gp_cache_used > 0))
+ {
+ DRW_draw_pass(psl->paper_pass);
+ }
+ }
+
+ /* if we have a painting session, we use fast viewport drawing method */
+ if ((!is_render) && (stl->g_data->session_flag & GP_DRW_PAINT_PAINTING)) {
+ GPU_framebuffer_bind(dfbl->default_fb);
+
+ MULTISAMPLE_GP_SYNC_ENABLE(stl->storage->multisamples, fbl);
+
+ DRW_draw_pass(psl->background_pass);
+ DRW_draw_pass(psl->drawing_pass);
+
+ MULTISAMPLE_GP_SYNC_DISABLE(stl->storage->multisamples, fbl, dfbl->default_fb, txl);
+
+ /* free memory */
+ gpencil_free_obj_list(stl);
+
+ /* grid pass */
+ if ((!is_render) && (obact) && (obact->type == OB_GPENCIL)) {
+ if (((v3d->flag2 & V3D_RENDER_OVERRIDE) == 0) &&
+ (v3d->flag3 & V3D_GP_SHOW_GRID))
+ {
+ DRW_draw_pass(psl->grid_pass);
+ }
+ }
+
+ return;
+ }
+
+ if (DRW_state_is_fbo()) {
+ /* attach temp textures */
+ GPU_framebuffer_texture_attach(fbl->temp_fb_a, e_data.temp_depth_tx_a, 0, 0);
+ GPU_framebuffer_texture_attach(fbl->temp_fb_a, e_data.temp_color_tx_a, 0, 0);
+ GPU_framebuffer_texture_attach(fbl->temp_fb_b, e_data.temp_depth_tx_b, 0, 0);
+ GPU_framebuffer_texture_attach(fbl->temp_fb_b, e_data.temp_color_tx_b, 0, 0);
+
+ GPU_framebuffer_texture_attach(fbl->background_fb, e_data.background_depth_tx, 0, 0);
+ GPU_framebuffer_texture_attach(fbl->background_fb, e_data.background_color_tx, 0, 0);
+
+ /* Draw all pending objects */
+ if (stl->g_data->gp_cache_used > 0) {
+
+ /* sort by zdepth */
+ qsort(stl->g_data->gp_object_cache, stl->g_data->gp_cache_used,
+ sizeof(tGPencilObjectCache), gpencil_object_cache_compare_zdepth);
+
+ for (int i = 0; i < stl->g_data->gp_cache_used; i++) {
+ cache = &stl->g_data->gp_object_cache[i];
+ Object *ob = cache->ob;
+ bGPdata *gpd = ob->data;
+ init_grp = cache->init_grp;
+ end_grp = cache->end_grp;
+ /* Render stroke in separated framebuffer */
+ GPU_framebuffer_bind(fbl->temp_fb_a);
+ GPU_framebuffer_clear_color_depth(fbl->temp_fb_a, clearcol, 1.0f);
+
+ /* Stroke Pass: DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND | DRW_STATE_WRITE_DEPTH
+ * draw only a subset that usually start with a fill and end with stroke because the
+ * shading groups are created by pairs */
+ if (end_grp >= init_grp) {
+ MULTISAMPLE_GP_SYNC_ENABLE(stl->storage->multisamples, fbl);
+
+ DRW_draw_pass_subset(
+ psl->stroke_pass,
+ stl->shgroups[init_grp].shgrps_fill != NULL ?
+ stl->shgroups[init_grp].shgrps_fill : stl->shgroups[init_grp].shgrps_stroke,
+ stl->shgroups[end_grp].shgrps_stroke);
+
+ MULTISAMPLE_GP_SYNC_DISABLE(stl->storage->multisamples, fbl, fbl->temp_fb_a, txl);
+ }
+
+ /* Current buffer drawing */
+ if ((!is_render) && (gpd->runtime.sbuffer_size > 0)) {
+ DRW_draw_pass(psl->drawing_pass);
+ }
+ /* fx passes */
+ if (BKE_shaderfx_has_gpencil(ob)) {
+ stl->storage->tonemapping = 0;
+ DRW_gpencil_fx_draw(&e_data, vedata, cache);
+ }
+
+ e_data.input_depth_tx = e_data.temp_depth_tx_a;
+ e_data.input_color_tx = e_data.temp_color_tx_a;
+
+ /* Combine with scene buffer */
+ if ((!is_render) || (fbl->main == NULL)) {
+ GPU_framebuffer_bind(dfbl->default_fb);
+ }
+ else {
+ GPU_framebuffer_bind(fbl->main);
+ }
+ /* tonemapping */
+ stl->storage->tonemapping = stl->storage->is_render ? 1 : 0;
+
+ DRW_draw_pass(psl->mix_pass);
+
+ /* prepare for fast drawing */
+ if (!is_render) {
+ gpencil_prepare_fast_drawing(stl, dfbl, fbl, psl->mix_pass_noblend, clearcol);
+ }
+ }
+ /* edit points */
+ if ((!is_render) && (!playing)) {
+ DRW_draw_pass(psl->edit_pass);
+ }
+ }
+ /* grid pass */
+ if ((!is_render) && (obact) && (obact->type == OB_GPENCIL)) {
+ if (((v3d->flag2 & V3D_RENDER_OVERRIDE) == 0) &&
+ (v3d->flag3 & V3D_GP_SHOW_GRID))
+ {
+ DRW_draw_pass(psl->grid_pass);
+ }
+ }
+ }
+ /* free memory */
+ gpencil_free_obj_list(stl);
+
+ /* detach temp textures */
+ if (DRW_state_is_fbo()) {
+ GPU_framebuffer_texture_detach(fbl->temp_fb_a, e_data.temp_depth_tx_a);
+ GPU_framebuffer_texture_detach(fbl->temp_fb_a, e_data.temp_color_tx_a);
+ GPU_framebuffer_texture_detach(fbl->temp_fb_b, e_data.temp_depth_tx_b);
+ GPU_framebuffer_texture_detach(fbl->temp_fb_b, e_data.temp_color_tx_b);
+
+ GPU_framebuffer_texture_detach(fbl->background_fb, e_data.background_depth_tx);
+ GPU_framebuffer_texture_detach(fbl->background_fb, e_data.background_color_tx);
+
+ /* attach again default framebuffer after detach textures */
+ if (!is_render) {
+ GPU_framebuffer_bind(dfbl->default_fb);
+ }
+
+ /* the temp texture is ready. Now we can use fast screen drawing */
+ if (stl->g_data->session_flag & GP_DRW_PAINT_FILLING) {
+ stl->g_data->session_flag = GP_DRW_PAINT_READY;
+ }
+ }
+}
+
+static const DrawEngineDataSize GPENCIL_data_size = DRW_VIEWPORT_DATA_SIZE(GPENCIL_Data);
+
+DrawEngineType draw_engine_gpencil_type = {
+ NULL, NULL,
+ N_("GpencilMode"),
+ &GPENCIL_data_size,
+ &GPENCIL_engine_init,
+ &GPENCIL_engine_free,
+ &GPENCIL_cache_init,
+ &GPENCIL_cache_populate,
+ &GPENCIL_cache_finish,
+ NULL,
+ &GPENCIL_draw_scene,
+ NULL,
+ NULL,
+ &GPENCIL_render_to_image,
+};
diff --git a/source/blender/draw/engines/gpencil/gpencil_engine.h b/source/blender/draw/engines/gpencil/gpencil_engine.h
new file mode 100644
index 00000000000..24a627f1012
--- /dev/null
+++ b/source/blender/draw/engines/gpencil/gpencil_engine.h
@@ -0,0 +1,355 @@
+/*
+ * Copyright 2017, Blender Foundation.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Contributor(s): Antonio Vazquez
+ *
+ */
+
+/** \file blender/draw/engines/gpencil/gpencil_engine.h
+ * \ingroup draw
+ */
+
+#ifndef __GPENCIL_ENGINE_H__
+#define __GPENCIL_ENGINE_H__
+
+#include "GPU_batch.h"
+
+struct tGPspoint;
+struct bGPDstroke;
+struct ModifierData;
+struct GPENCIL_Data;
+struct GPENCIL_StorageList;
+struct Object;
+struct MaterialGPencilStyle;
+struct RenderEngine;
+struct RenderLayer;
+
+ /* TODO: these could be system parameter in userprefs screen */
+#define GPENCIL_MAX_GP_OBJ 256
+
+#define GPENCIL_CACHE_BLOCK_SIZE 8
+#define GPENCIL_MAX_SHGROUPS 65536
+#define GPENCIL_MIN_BATCH_SLOTS_CHUNK 16
+
+#define GPENCIL_COLOR_SOLID 0
+#define GPENCIL_COLOR_TEXTURE 1
+#define GPENCIL_COLOR_PATTERN 2
+
+#define GP_SIMPLIFY(scene) ((scene->r.simplify_gpencil & SIMPLIFY_GPENCIL_ENABLE))
+#define GP_SIMPLIFY_ONPLAY(playing) (((playing == true) && (scene->r.simplify_gpencil & SIMPLIFY_GPENCIL_ON_PLAY)) || ((scene->r.simplify_gpencil & SIMPLIFY_GPENCIL_ON_PLAY) == 0))
+#define GP_SIMPLIFY_FILL(scene, playing) ((GP_SIMPLIFY_ONPLAY(playing) && (GP_SIMPLIFY(scene)) && (scene->r.simplify_gpencil & SIMPLIFY_GPENCIL_FILL)))
+#define GP_SIMPLIFY_MODIF(scene, playing) ((GP_SIMPLIFY_ONPLAY(playing) && (GP_SIMPLIFY(scene)) && (scene->r.simplify_gpencil & SIMPLIFY_GPENCIL_MODIFIER)))
+
+#define GP_IS_CAMERAVIEW ((rv3d != NULL) && (rv3d->persp == RV3D_CAMOB && v3d->camera))
+
+ /* *********** OBJECTS CACHE *********** */
+
+ /* used to save gpencil objects */
+typedef struct tGPencilObjectCache {
+ struct Object *ob;
+ int init_grp, end_grp;
+ int idx; /*original index, can change after sort */
+
+ /* effects */
+ DRWShadingGroup *fx_wave_sh;
+ DRWShadingGroup *fx_blur_sh;
+ DRWShadingGroup *fx_colorize_sh;
+ DRWShadingGroup *fx_pixel_sh;
+ DRWShadingGroup *fx_rim_sh;
+ DRWShadingGroup *fx_swirl_sh;
+ DRWShadingGroup *fx_flip_sh;
+ DRWShadingGroup *fx_light_sh;
+
+ float zdepth; /* z-depth value to sort gp object */
+ bool temp_ob; /* flag to tag temporary objects that must be removed after drawing loop */
+} tGPencilObjectCache;
+
+ /* *********** LISTS *********** */
+typedef struct GPENCIL_shgroup {
+ int s_clamp;
+ int stroke_style;
+ int color_type;
+ int mode;
+ int texture_mix;
+ int texture_flip;
+ int texture_clamp;
+ int fill_style;
+ int keep_size;
+ float obj_scale;
+ struct DRWShadingGroup *shgrps_fill;
+ struct DRWShadingGroup *shgrps_stroke;
+} GPENCIL_shgroup;
+
+typedef struct GPENCIL_Storage {
+ int shgroup_id; /* total elements */
+ float unit_matrix[4][4];
+ int stroke_style;
+ int color_type;
+ int mode;
+ int xray;
+ int keep_size;
+ float obj_scale;
+ float pixfactor;
+ int playing;
+ bool is_render;
+ bool is_mat_preview;
+ const float *pixsize;
+ float render_pixsize;
+ int tonemapping;
+ short multisamples;
+
+ /* simplify settings*/
+ bool simplify_fill;
+ bool simplify_modif;
+ bool simplify_fx;
+
+ /* Render Matrices and data */
+ float persmat[4][4], persinv[4][4];
+ float viewmat[4][4], viewinv[4][4];
+ float winmat[4][4], wininv[4][4];
+ float view_vecs[2][4]; /* vec4[2] */
+
+ Object *camera; /* camera pointer for render mode */
+} GPENCIL_Storage;
+
+typedef struct GPENCIL_StorageList {
+ struct GPENCIL_Storage *storage;
+ struct g_data *g_data;
+ struct GPENCIL_shgroup *shgroups;
+} GPENCIL_StorageList;
+
+typedef struct GPENCIL_PassList {
+ struct DRWPass *stroke_pass;
+ struct DRWPass *edit_pass;
+ struct DRWPass *drawing_pass;
+ struct DRWPass *mix_pass;
+ struct DRWPass *mix_pass_noblend;
+ struct DRWPass *background_pass;
+ struct DRWPass *paper_pass;
+ struct DRWPass *grid_pass;
+
+ /* effects */
+ struct DRWPass *fx_shader_pass;
+ struct DRWPass *fx_shader_pass_blend;
+
+} GPENCIL_PassList;
+
+typedef struct GPENCIL_FramebufferList {
+ struct GPUFrameBuffer *main;
+ struct GPUFrameBuffer *temp_fb_a;
+ struct GPUFrameBuffer *temp_fb_b;
+ struct GPUFrameBuffer *temp_fb_rim;
+ struct GPUFrameBuffer *background_fb;
+
+ struct GPUFrameBuffer *multisample_fb;
+} GPENCIL_FramebufferList;
+
+typedef struct GPENCIL_TextureList {
+ struct GPUTexture *texture;
+
+ /* multisample textures */
+ struct GPUTexture *multisample_color;
+ struct GPUTexture *multisample_depth;
+
+} GPENCIL_TextureList;
+
+typedef struct GPENCIL_Data {
+ void *engine_type; /* Required */
+ struct GPENCIL_FramebufferList *fbl;
+ struct GPENCIL_TextureList *txl;
+ struct GPENCIL_PassList *psl;
+ struct GPENCIL_StorageList *stl;
+
+ /* render textures */
+ struct GPUTexture *render_depth_tx;
+ struct GPUTexture *render_color_tx;
+
+} GPENCIL_Data;
+
+/* *********** STATIC *********** */
+typedef struct g_data {
+ struct DRWShadingGroup *shgrps_edit_point;
+ struct DRWShadingGroup *shgrps_edit_line;
+ struct DRWShadingGroup *shgrps_drawing_stroke;
+ struct DRWShadingGroup *shgrps_drawing_fill;
+ struct DRWShadingGroup *shgrps_grid;
+
+ /* for buffer only one batch is nedeed because the drawing is only of one stroke */
+ GPUBatch *batch_buffer_stroke;
+ GPUBatch *batch_buffer_fill;
+
+ /* grid geometry */
+ GPUBatch *batch_grid;
+
+ int gp_cache_used; /* total objects in cache */
+ int gp_cache_size; /* size of the cache */
+ struct tGPencilObjectCache *gp_object_cache;
+
+ int session_flag;
+
+} g_data; /* Transient data */
+
+/* flags for fast drawing support */
+typedef enum eGPsession_Flag {
+ GP_DRW_PAINT_HOLD = (1 << 0),
+ GP_DRW_PAINT_IDLE = (1 << 1),
+ GP_DRW_PAINT_FILLING = (1 << 2),
+ GP_DRW_PAINT_READY = (1 << 3),
+ GP_DRW_PAINT_PAINTING = (1 << 4),
+} eGPsession_Flag;
+
+typedef struct GPENCIL_e_data {
+ /* general drawing shaders */
+ struct GPUShader *gpencil_fill_sh;
+ struct GPUShader *gpencil_stroke_sh;
+ struct GPUShader *gpencil_point_sh;
+ struct GPUShader *gpencil_edit_point_sh;
+ struct GPUShader *gpencil_line_sh;
+ struct GPUShader *gpencil_drawing_fill_sh;
+ struct GPUShader *gpencil_fullscreen_sh;
+ struct GPUShader *gpencil_simple_fullscreen_sh;
+ struct GPUShader *gpencil_background_sh;
+ struct GPUShader *gpencil_paper_sh;
+
+ /* effects */
+ struct GPUShader *gpencil_fx_blur_sh;
+ struct GPUShader *gpencil_fx_colorize_sh;
+ struct GPUShader *gpencil_fx_flip_sh;
+ struct GPUShader *gpencil_fx_light_sh;
+ struct GPUShader *gpencil_fx_pixel_sh;
+ struct GPUShader *gpencil_fx_rim_prepare_sh;
+ struct GPUShader *gpencil_fx_rim_resolve_sh;
+ struct GPUShader *gpencil_fx_swirl_sh;
+ struct GPUShader *gpencil_fx_wave_sh;
+
+ /* textures */
+ struct GPUTexture *background_depth_tx;
+ struct GPUTexture *background_color_tx;
+
+ struct GPUTexture *gpencil_blank_texture;
+
+ /* runtime pointers texture */
+ struct GPUTexture *input_depth_tx;
+ struct GPUTexture *input_color_tx;
+
+ /* working textures */
+ struct GPUTexture *temp_color_tx_a;
+ struct GPUTexture *temp_depth_tx_a;
+
+ struct GPUTexture *temp_color_tx_b;
+ struct GPUTexture *temp_depth_tx_b;
+
+ struct GPUTexture *temp_color_tx_rim;
+ struct GPUTexture *temp_depth_tx_rim;
+} GPENCIL_e_data; /* Engine data */
+
+/* GPUBatch Cache */
+typedef struct GpencilBatchCache {
+ /* For normal strokes, a variable number of batch can be needed depending of number of strokes.
+ It could use the stroke number as total size, but when activate the onion skining, the number
+ can change, so the size is changed dinamically.
+ */
+ GPUBatch **batch_stroke;
+ GPUBatch **batch_fill;
+ GPUBatch **batch_edit;
+ GPUBatch **batch_edlin;
+
+ /* settings to determine if cache is invalid */
+ bool is_dirty;
+ bool is_editmode;
+ int cache_frame;
+
+ /* keep information about the size of the cache */
+ int cache_size; /* total batch slots available */
+ int cache_idx; /* current slot index */
+} GpencilBatchCache;
+
+/* general drawing functions */
+struct DRWShadingGroup *DRW_gpencil_shgroup_stroke_create(struct GPENCIL_e_data *e_data, struct GPENCIL_Data *vedata, struct DRWPass *pass, struct GPUShader *shader,
+ struct Object *ob, struct bGPdata *gpd, struct MaterialGPencilStyle *gp_style, int id, bool onion);
+void DRW_gpencil_populate_datablock(struct GPENCIL_e_data *e_data, void *vedata, struct Scene *scene, struct Object *ob, struct bGPdata *gpd);
+void DRW_gpencil_populate_buffer_strokes(struct GPENCIL_e_data *e_data, void *vedata, struct ToolSettings *ts, struct Object *ob);
+void DRW_gpencil_populate_multiedit(struct GPENCIL_e_data *e_data, void *vedata, struct Scene *scene, struct Object *ob, struct bGPdata *gpd);
+void DRW_gpencil_triangulate_stroke_fill(struct bGPDstroke *gps);
+
+void DRW_gpencil_multisample_ensure(struct GPENCIL_Data *vedata, int rect_w, int rect_h);
+
+/* create geometry functions */
+struct GPUBatch *DRW_gpencil_get_point_geom(struct bGPDstroke *gps, short thickness, const float ink[4]);
+struct GPUBatch *DRW_gpencil_get_stroke_geom(struct bGPDframe *gpf, struct bGPDstroke *gps, short thickness, const float ink[4]);
+struct GPUBatch *DRW_gpencil_get_fill_geom(struct Object *ob, struct bGPDstroke *gps, const float color[4]);
+struct GPUBatch *DRW_gpencil_get_edit_geom(struct bGPDstroke *gps, float alpha, short dflag);
+struct GPUBatch *DRW_gpencil_get_edlin_geom(struct bGPDstroke *gps, float alpha, short dflag);
+struct GPUBatch *DRW_gpencil_get_buffer_stroke_geom(struct bGPdata *gpd, float matrix[4][4], short thickness);
+struct GPUBatch *DRW_gpencil_get_buffer_fill_geom(struct bGPdata *gpd);
+struct GPUBatch *DRW_gpencil_get_buffer_point_geom(struct bGPdata *gpd, float matrix[4][4], short thickness);
+struct GPUBatch *DRW_gpencil_get_grid(void);
+
+/* object cache functions */
+struct tGPencilObjectCache *gpencil_object_cache_add(struct tGPencilObjectCache *cache_array, struct Object *ob,
+ bool is_temp, int *gp_cache_size, int *gp_cache_used);
+
+/* geometry batch cache functions */
+void gpencil_batch_cache_check_free_slots(struct Object *ob);
+struct GpencilBatchCache *gpencil_batch_cache_get(struct Object *ob, int cfra);
+
+/* modifier functions */
+void gpencil_instance_modifiers(struct GPENCIL_StorageList *stl, struct Object *ob);
+
+/* effects */
+void GPENCIL_create_fx_shaders(struct GPENCIL_e_data *e_data);
+void GPENCIL_delete_fx_shaders(struct GPENCIL_e_data *e_data);
+void GPENCIL_create_fx_passes(struct GPENCIL_PassList *psl);
+
+void DRW_gpencil_fx_prepare(
+ struct GPENCIL_e_data *e_data, struct GPENCIL_Data *vedata,
+ struct tGPencilObjectCache *cache);
+void DRW_gpencil_fx_draw(
+ struct GPENCIL_e_data *e_data, struct GPENCIL_Data *vedata,
+ struct tGPencilObjectCache *cache);
+
+/* main functions */
+void GPENCIL_engine_init(void *vedata);
+void GPENCIL_cache_init(void *vedata);
+void GPENCIL_cache_populate(void *vedata, struct Object *ob);
+void GPENCIL_cache_finish(void *vedata);
+void GPENCIL_draw_scene(void *vedata);
+
+/* render */
+void GPENCIL_render_init(struct GPENCIL_Data *ved, struct RenderEngine *engine, struct Depsgraph *depsgraph);
+void GPENCIL_render_to_image(void *vedata, struct RenderEngine *engine, struct RenderLayer *render_layer, const rcti *rect);
+
+/* Use of multisample framebuffers. */
+#define MULTISAMPLE_GP_SYNC_ENABLE(lvl, fbl) { \
+ if ((lvl > 0) && (fbl->multisample_fb != NULL)) { \
+ DRW_stats_query_start("GP Multisample Blit"); \
+ GPU_framebuffer_bind(fbl->multisample_fb); \
+ GPU_framebuffer_clear_color_depth(fbl->multisample_fb, (const float[4]){0.0f}, 1.0f); \
+ DRW_stats_query_end(); \
+ } \
+}
+
+#define MULTISAMPLE_GP_SYNC_DISABLE(lvl, fbl, fb, txl) { \
+ if ((lvl > 0) && (fbl->multisample_fb != NULL)) { \
+ DRW_stats_query_start("GP Multisample Resolve"); \
+ GPU_framebuffer_bind(fb); \
+ DRW_multisamples_resolve(txl->multisample_depth, txl->multisample_color, true); \
+ DRW_stats_query_end(); \
+ } \
+}
+
+#endif /* __GPENCIL_ENGINE_H__ */
diff --git a/source/blender/draw/engines/gpencil/gpencil_render.c b/source/blender/draw/engines/gpencil/gpencil_render.c
new file mode 100644
index 00000000000..d76ea56894f
--- /dev/null
+++ b/source/blender/draw/engines/gpencil/gpencil_render.c
@@ -0,0 +1,353 @@
+/*
+ * Copyright 2017, Blender Foundation.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Contributor(s): Antonio Vazquez
+ *
+ */
+
+/** \file blender/draw/engines/gpencil/gpencil_render.c
+ * \ingroup draw
+ */
+#include "BLI_rect.h"
+
+#include "DRW_engine.h"
+#include "DRW_render.h"
+
+#include "BKE_camera.h"
+
+#include "DNA_gpencil_types.h"
+
+#include "DEG_depsgraph_query.h"
+
+#include "draw_mode_engines.h"
+
+#include "RE_pipeline.h"
+
+#include "gpencil_engine.h"
+
+/* Get pixel size for render
+* This function uses the same calculation used for viewport, because if use
+* camera pixelsize, the result is not correct.
+*/
+static float get_render_pixelsize(float persmat[4][4], int winx, int winy)
+{
+ float v1[3], v2[3];
+ float len_px, len_sc;
+
+ v1[0] = persmat[0][0];
+ v1[1] = persmat[1][0];
+ v1[2] = persmat[2][0];
+
+ v2[0] = persmat[0][1];
+ v2[1] = persmat[1][1];
+ v2[2] = persmat[2][1];
+
+ len_px = 2.0f / sqrtf(min_ff(len_squared_v3(v1), len_squared_v3(v2)));
+ len_sc = (float)MAX2(winx, winy);
+
+ return len_px / len_sc;
+}
+
+/* init render data */
+void GPENCIL_render_init(GPENCIL_Data *ved, RenderEngine *engine, struct Depsgraph *depsgraph)
+{
+ GPENCIL_Data *vedata = (GPENCIL_Data *)ved;
+ GPENCIL_StorageList *stl = vedata->stl;
+ GPENCIL_FramebufferList *fbl = vedata->fbl;
+
+ Scene *scene = DEG_get_evaluated_scene(depsgraph);
+ const float *viewport_size = DRW_viewport_size_get();
+ const int size[2] = { (int)viewport_size[0], (int)viewport_size[1] };
+
+ /* In render mode the default framebuffer is not generated
+ * because there is no viewport. So we need to manually create one
+ * NOTE : use 32 bit format for precision in render mode.
+ */
+ /* create multiframe framebuffer for AA */
+ if (U.gpencil_multisamples > 0) {
+ int rect_w = (int)viewport_size[0];
+ int rect_h = (int)viewport_size[1];
+ DRW_gpencil_multisample_ensure(vedata, rect_w, rect_h);
+ }
+
+ vedata->render_depth_tx = DRW_texture_pool_query_2D(size[0], size[1], GPU_DEPTH24_STENCIL8,
+ &draw_engine_gpencil_type);
+ vedata->render_color_tx = DRW_texture_pool_query_2D(size[0], size[1], GPU_RGBA32F,
+ &draw_engine_gpencil_type);
+ GPU_framebuffer_ensure_config(&fbl->main, {
+ GPU_ATTACHMENT_TEXTURE(vedata->render_depth_tx),
+ GPU_ATTACHMENT_TEXTURE(vedata->render_color_tx)
+ });
+
+ /* Alloc transient data. */
+ if (!stl->g_data) {
+ stl->g_data = MEM_callocN(sizeof(*stl->g_data), __func__);
+ }
+
+ /* Set the pers & view matrix. */
+ struct Object *camera = DEG_get_evaluated_object(depsgraph, RE_GetCamera(engine->re));
+ float frame = BKE_scene_frame_get(scene);
+ RE_GetCameraWindow(engine->re, camera, frame, stl->storage->winmat);
+ RE_GetCameraModelMatrix(engine->re, camera, stl->storage->viewinv);
+
+ invert_m4_m4(stl->storage->viewmat, stl->storage->viewinv);
+ mul_m4_m4m4(stl->storage->persmat, stl->storage->winmat, stl->storage->viewmat);
+ invert_m4_m4(stl->storage->persinv, stl->storage->persmat);
+ invert_m4_m4(stl->storage->wininv, stl->storage->winmat);
+
+ DRW_viewport_matrix_override_set(stl->storage->persmat, DRW_MAT_PERS);
+ DRW_viewport_matrix_override_set(stl->storage->persinv, DRW_MAT_PERSINV);
+ DRW_viewport_matrix_override_set(stl->storage->winmat, DRW_MAT_WIN);
+ DRW_viewport_matrix_override_set(stl->storage->wininv, DRW_MAT_WININV);
+ DRW_viewport_matrix_override_set(stl->storage->viewmat, DRW_MAT_VIEW);
+ DRW_viewport_matrix_override_set(stl->storage->viewinv, DRW_MAT_VIEWINV);
+
+ /* calculate pixel size for render */
+ stl->storage->render_pixsize = get_render_pixelsize(stl->storage->persmat, viewport_size[0], viewport_size[1]);
+ /* INIT CACHE */
+ GPENCIL_cache_init(vedata);
+}
+
+/* render all objects and select only grease pencil */
+static void GPENCIL_render_cache(
+ void *vedata, struct Object *ob,
+ struct RenderEngine *UNUSED(engine), struct Depsgraph *UNUSED(depsgraph))
+{
+ if ((ob == NULL) || (DRW_check_object_visible_within_active_context(ob) == false)) {
+ return;
+ }
+
+ if (ob->type == OB_GPENCIL) {
+ GPENCIL_cache_populate(vedata, ob);
+ }
+}
+
+/* TODO: Reuse Eevee code in shared module instead to duplicate here */
+static void GPENCIL_render_update_viewvecs(float invproj[4][4], float winmat[4][4], float(*r_viewvecs)[4])
+{
+ /* view vectors for the corners of the view frustum.
+ * Can be used to recreate the world space position easily */
+ float view_vecs[4][4] = {
+ { -1.0f, -1.0f, -1.0f, 1.0f },
+ { 1.0f, -1.0f, -1.0f, 1.0f },
+ { -1.0f, 1.0f, -1.0f, 1.0f },
+ { -1.0f, -1.0f, 1.0f, 1.0f }
+ };
+
+ /* convert the view vectors to view space */
+ const bool is_persp = (winmat[3][3] == 0.0f);
+ for (int i = 0; i < 4; i++) {
+ mul_project_m4_v3(invproj, view_vecs[i]);
+ /* normalized trick see:
+ * http://www.derschmale.com/2014/01/26/reconstructing-positions-from-the-depth-buffer */
+ if (is_persp) {
+ /* Divide XY by Z. */
+ mul_v2_fl(view_vecs[i], 1.0f / view_vecs[i][2]);
+ }
+ }
+
+ /**
+ * If ortho : view_vecs[0] is the near-bottom-left corner of the frustum and
+ * view_vecs[1] is the vector going from the near-bottom-left corner to
+ * the far-top-right corner.
+ * If Persp : view_vecs[0].xy and view_vecs[1].xy are respectively the bottom-left corner
+ * when Z = 1, and top-left corner if Z = 1.
+ * view_vecs[0].z the near clip distance and view_vecs[1].z is the (signed)
+ * distance from the near plane to the far clip plane.
+ **/
+ copy_v4_v4(r_viewvecs[0], view_vecs[0]);
+
+ /* we need to store the differences */
+ r_viewvecs[1][0] = view_vecs[1][0] - view_vecs[0][0];
+ r_viewvecs[1][1] = view_vecs[2][1] - view_vecs[0][1];
+ r_viewvecs[1][2] = view_vecs[3][2] - view_vecs[0][2];
+}
+
+/* Update view_vecs */
+static void GPENCIL_render_update_vecs(GPENCIL_Data *vedata)
+{
+ GPENCIL_StorageList *stl = vedata->stl;
+
+ float invproj[4][4], winmat[4][4];
+ DRW_viewport_matrix_get(winmat, DRW_MAT_WIN);
+ DRW_viewport_matrix_get(invproj, DRW_MAT_WININV);
+
+ /* this is separated to keep function equal to Eevee for future reuse of same code */
+ GPENCIL_render_update_viewvecs(invproj, winmat, stl->storage->view_vecs);
+}
+
+/* read z-depth render result */
+static void GPENCIL_render_result_z(struct RenderLayer *rl, const char *viewname, GPENCIL_Data *vedata, const rcti *rect)
+{
+ const DRWContextState *draw_ctx = DRW_context_state_get();
+ ViewLayer *view_layer = draw_ctx->view_layer;
+ GPENCIL_StorageList *stl = vedata->stl;
+
+ if ((view_layer->passflag & SCE_PASS_Z) != 0) {
+ RenderPass *rp = RE_pass_find_by_name(rl, RE_PASSNAME_Z, viewname);
+
+ GPU_framebuffer_read_depth(vedata->fbl->main, rect->xmin, rect->ymin, BLI_rcti_size_x(rect), BLI_rcti_size_y(rect), rp->rect);
+
+ bool is_persp = DRW_viewport_is_persp_get();
+
+ GPENCIL_render_update_vecs(vedata);
+
+ /* Convert ogl depth [0..1] to view Z [near..far] */
+ for (int i = 0; i < BLI_rcti_size_x(rect) * BLI_rcti_size_y(rect); i++) {
+ if (rp->rect[i] == 1.0f) {
+ rp->rect[i] = 1e10f; /* Background */
+ }
+ else {
+ if (is_persp) {
+ rp->rect[i] = rp->rect[i] * 2.0f - 1.0f;
+ rp->rect[i] = stl->storage->winmat[3][2] / (rp->rect[i] + stl->storage->winmat[2][2]);
+ }
+ else {
+ rp->rect[i] = -stl->storage->view_vecs[0][2] + rp->rect[i] * -stl->storage->view_vecs[1][2];
+ }
+ }
+ }
+ }
+}
+
+/* read combined render result */
+static void GPENCIL_render_result_combined(struct RenderLayer *rl, const char *viewname, GPENCIL_Data *vedata, const rcti *rect)
+{
+ RenderPass *rp = RE_pass_find_by_name(rl, RE_PASSNAME_COMBINED, viewname);
+ GPENCIL_FramebufferList *fbl = ((GPENCIL_Data *)vedata)->fbl;
+
+ GPU_framebuffer_bind(fbl->main);
+ GPU_framebuffer_read_color(vedata->fbl->main, rect->xmin, rect->ymin, BLI_rcti_size_x(rect), BLI_rcti_size_y(rect), 4, 0, rp->rect);
+}
+
+/* helper to blend pixels */
+static void blend_pixel(float src[4], float dst[4])
+{
+ float alpha = src[3];
+
+ /* use blend: GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA */
+ dst[0] = (src[0] * alpha) + (dst[0] * (1.0f - alpha));
+ dst[1] = (src[1] * alpha) + (dst[1] * (1.0f - alpha));
+ dst[2] = (src[2] * alpha) + (dst[2] * (1.0f - alpha));
+}
+
+/* render grease pencil to image */
+void GPENCIL_render_to_image(void *vedata, RenderEngine *engine, struct RenderLayer *render_layer, const rcti *rect)
+{
+ const char *viewname = RE_GetActiveRenderView(engine->re);
+ const DRWContextState *draw_ctx = DRW_context_state_get();
+ int imgsize = BLI_rcti_size_x(rect) * BLI_rcti_size_y(rect);
+
+ /* save previous render data */
+ RenderPass *rpass_color_src = RE_pass_find_by_name(render_layer, RE_PASSNAME_COMBINED, viewname);
+ RenderPass *rpass_depth_src = RE_pass_find_by_name(render_layer, RE_PASSNAME_Z, viewname);
+ float *src_rect_color_data = NULL;
+ float *src_rect_depth_data = NULL;
+ if ((rpass_color_src) && (rpass_depth_src) && (rpass_color_src->rect) && (rpass_depth_src->rect)) {
+ src_rect_color_data = MEM_dupallocN(rpass_color_src->rect);
+ src_rect_depth_data = MEM_dupallocN(rpass_depth_src->rect);
+ }
+ else {
+ /* TODO: put this message in a better place */
+ printf("Warning: To render grease pencil, enable Combined and Z passes.\n");
+ }
+
+ GPENCIL_engine_init(vedata);
+ GPENCIL_render_init(vedata, engine, draw_ctx->depsgraph);
+
+ GPENCIL_StorageList *stl = ((GPENCIL_Data *)vedata)->stl;
+ Object *camera = DEG_get_evaluated_object(draw_ctx->depsgraph, RE_GetCamera(engine->re));
+ stl->storage->camera = camera; /* save current camera */
+
+ GPENCIL_FramebufferList *fbl = ((GPENCIL_Data *)vedata)->fbl;
+ if (fbl->main) {
+ GPU_framebuffer_texture_attach(fbl->main, ((GPENCIL_Data *)vedata)->render_depth_tx, 0, 0);
+ GPU_framebuffer_texture_attach(fbl->main, ((GPENCIL_Data *)vedata)->render_color_tx, 0, 0);
+ /* clean first time the buffer */
+ float clearcol[4] = { 0.0f, 0.0f, 0.0f, 0.0f };
+ GPU_framebuffer_bind(fbl->main);
+ GPU_framebuffer_clear_color_depth(fbl->main, clearcol, 1.0f);
+ }
+
+ /* loop all objects and draw */
+ DRW_render_object_iter(vedata, engine, draw_ctx->depsgraph, GPENCIL_render_cache);
+
+ GPENCIL_cache_finish(vedata);
+ GPENCIL_draw_scene(vedata);
+
+ /* combined data */
+ GPENCIL_render_result_combined(render_layer, viewname, vedata, rect);
+ /* z-depth data */
+ GPENCIL_render_result_z(render_layer, viewname, vedata, rect);
+
+ /* detach textures */
+ if (fbl->main) {
+ GPU_framebuffer_texture_detach(fbl->main, ((GPENCIL_Data *)vedata)->render_depth_tx);
+ GPU_framebuffer_texture_detach(fbl->main, ((GPENCIL_Data *)vedata)->render_color_tx);
+ }
+
+ /* merge previous render image with new GP image */
+ if (src_rect_color_data) {
+ RenderPass *rpass_color_gp = RE_pass_find_by_name(render_layer, RE_PASSNAME_COMBINED, viewname);
+ RenderPass *rpass_depth_gp = RE_pass_find_by_name(render_layer, RE_PASSNAME_Z, viewname);
+ float *gp_rect_color_data = rpass_color_gp->rect;
+ float *gp_rect_depth_data = rpass_depth_gp->rect;
+ float *gp_pixel_rgba;
+ float *gp_pixel_depth;
+ float *src_pixel_rgba;
+ float *src_pixel_depth;
+ float tmp[4];
+
+ for (int i = 0; i < imgsize; i++) {
+ gp_pixel_rgba = &gp_rect_color_data[i * 4];
+ gp_pixel_depth = &gp_rect_depth_data[i];
+
+ src_pixel_rgba = &src_rect_color_data[i * 4];
+ src_pixel_depth = &src_rect_depth_data[i];
+
+ /* check grease pencil render transparency */
+ if (gp_pixel_rgba[3] > 0.0f) {
+ copy_v4_v4(tmp, gp_pixel_rgba);
+ if (src_pixel_rgba[3] > 0.0f) {
+ /* copy source color on back */
+ copy_v4_v4(gp_pixel_rgba, src_pixel_rgba);
+ /* check z-depth */
+ if (gp_pixel_depth[0] > src_pixel_depth[0]) {
+ /* copy source z-depth */
+ gp_pixel_depth[0] = src_pixel_depth[0];
+ /* blend gp render */
+ blend_pixel(tmp, gp_pixel_rgba);
+ /* blend object on top */
+ blend_pixel(src_pixel_rgba, gp_pixel_rgba);
+ }
+ else {
+ /* blend gp render */
+ blend_pixel(tmp, gp_pixel_rgba);
+ }
+ }
+ }
+ else {
+ copy_v4_v4(gp_pixel_rgba, src_pixel_rgba);
+ gp_pixel_depth[0] = src_pixel_depth[0];
+ }
+ }
+
+ /* free memory */
+ MEM_SAFE_FREE(src_rect_color_data);
+ MEM_SAFE_FREE(src_rect_depth_data);
+ }
+}
diff --git a/source/blender/draw/engines/gpencil/gpencil_shader_fx.c b/source/blender/draw/engines/gpencil/gpencil_shader_fx.c
new file mode 100644
index 00000000000..e453224020d
--- /dev/null
+++ b/source/blender/draw/engines/gpencil/gpencil_shader_fx.c
@@ -0,0 +1,848 @@
+/*
+ * Copyright 2017, Blender Foundation.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Contributor(s): Antonio Vazquez
+ *
+ */
+
+/** \file blender/draw/engines/gpencil/gpencil_shader_fx.c
+ * \ingroup draw
+ */
+#include "DNA_gpencil_types.h"
+#include "DNA_shader_fx_types.h"
+#include "DNA_screen_types.h"
+#include "DNA_view3d_types.h"
+#include "DNA_camera_types.h"
+
+#include "BKE_gpencil.h"
+#include "BKE_shader_fx.h"
+
+#include "DRW_engine.h"
+#include "DRW_render.h"
+
+#include "BKE_camera.h"
+
+#include "ED_view3d.h"
+#include "ED_gpencil.h"
+
+#include "gpencil_engine.h"
+
+extern char datatoc_gpencil_fx_blur_frag_glsl[];
+extern char datatoc_gpencil_fx_colorize_frag_glsl[];
+extern char datatoc_gpencil_fx_flip_frag_glsl[];
+extern char datatoc_gpencil_fx_light_frag_glsl[];
+extern char datatoc_gpencil_fx_pixel_frag_glsl[];
+extern char datatoc_gpencil_fx_rim_prepare_frag_glsl[];
+extern char datatoc_gpencil_fx_rim_resolve_frag_glsl[];
+extern char datatoc_gpencil_fx_swirl_frag_glsl[];
+extern char datatoc_gpencil_fx_wave_frag_glsl[];
+
+/* verify if this fx is active */
+static bool effect_is_active(Object *ob, ShaderFxData *fx, bool is_render)
+{
+ if (fx == NULL) {
+ return false;
+ }
+
+ bGPdata *gpd = ob->data;
+ if (gpd == NULL) {
+ return false;
+ }
+
+ bool is_edit = GPENCIL_ANY_EDIT_MODE(gpd);
+ if (((fx->mode & eShaderFxMode_Editmode) == 0) && (is_edit)) {
+ return false;
+ }
+
+ if (((fx->mode & eShaderFxMode_Realtime) && (is_render == false)) ||
+ ((fx->mode & eShaderFxMode_Render) && (is_render == true)))
+ {
+ return true;
+ }
+
+ return false;
+}
+
+/* get normal of draw using one stroke of visible layer
+* /param gpd GP datablock
+* /param r_point Point on plane
+* /param r_normal Normal vector
+*/
+static bool get_normal_vector(bGPdata *gpd, float r_point[3], float r_normal[3])
+{
+ for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) {
+ if (gpl->flag & GP_LAYER_HIDE)
+ continue;
+
+ /* get frame */
+ bGPDframe *gpf = gpl->actframe;
+ if (gpf == NULL)
+ continue;
+ for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) {
+ if (gps->totpoints >= 3) {
+ bGPDspoint *pt = &gps->points[0];
+ BKE_gpencil_stroke_normal(gps, r_normal);
+ /* in some weird situations, the normal cannot be calculated, so try next stroke */
+ if ((r_normal[0] != 0.0f) || (r_normal[1] != 0.0f) || (r_normal[2] != 0.0f)) {
+ copy_v3_v3(r_point, &pt->x);
+ return true;
+ }
+ }
+ }
+ }
+
+ return false;
+}
+
+/* helper to get near and far depth of field values */
+static void GPENCIL_dof_nearfar(Object *camera, float coc, float nearfar[2])
+{
+ if (camera == NULL) {
+ return;
+ }
+
+ const DRWContextState *draw_ctx = DRW_context_state_get();
+ Scene *scene = draw_ctx->scene;
+ Camera *cam = (Camera *)camera->data;
+
+ float fstop = cam->gpu_dof.fstop;
+ float focus_dist = BKE_camera_object_dof_distance(camera);
+ float focal_len = cam->lens;
+
+ /* this is factor that converts to the scene scale. focal length and sensor are expressed in mm
+ * unit.scale_length is how many meters per blender unit we have. We want to convert to blender units though
+ * because the shader reads coordinates in world space, which is in blender units.
+ * Note however that focus_distance is already in blender units and shall not be scaled here (see T48157). */
+ float scale = (scene->unit.system) ? scene->unit.scale_length : 1.0f;
+ float scale_camera = 0.001f / scale;
+ /* we want radius here for the aperture number */
+ float aperture_scaled = 0.5f * scale_camera * focal_len / fstop;
+ float focal_len_scaled = scale_camera * focal_len;
+
+ float hyperfocal = (focal_len_scaled * focal_len_scaled) / (aperture_scaled * coc);
+ nearfar[0] = (hyperfocal * focus_dist) / (hyperfocal + focal_len);
+ nearfar[1] = (hyperfocal * focus_dist) / (hyperfocal - focal_len);
+}
+
+/* **************** Shader Effects ***************************** */
+
+/* Gaussian Blur FX
+ * The effect is done using two shading groups because is faster to apply horizontal
+ * and vertical in different operations.
+ */
+static void DRW_gpencil_fx_blur(
+ ShaderFxData *fx, int ob_idx, GPENCIL_e_data *e_data,
+ GPENCIL_Data *vedata, tGPencilObjectCache *cache)
+{
+ if (fx == NULL) {
+ return;
+ }
+
+ BlurShaderFxData *fxd = (BlurShaderFxData *)fx;
+
+ GPENCIL_StorageList *stl = ((GPENCIL_Data *)vedata)->stl;
+ GPENCIL_PassList *psl = ((GPENCIL_Data *)vedata)->psl;
+ const DRWContextState *draw_ctx = DRW_context_state_get();
+ View3D *v3d = draw_ctx->v3d;
+ RegionView3D *rv3d = draw_ctx->rv3d;
+ DRWShadingGroup *fx_shgrp;
+
+ Object *ob = cache->ob;
+ bGPdata *gpd = (bGPdata *)ob->data;
+
+ fxd->blur[0] = fxd->radius[0];
+ fxd->blur[1] = fxd->radius[1];
+
+ /* init weight */
+ if (fxd->flag & FX_BLUR_DOF_MODE) {
+ /* viewport and opengl render */
+ Object *camera = NULL;
+ if (rv3d) {
+ if (rv3d->persp == RV3D_CAMOB) {
+ camera = v3d->camera;
+ }
+ }
+ else {
+ camera = stl->storage->camera;
+ }
+
+ if (camera) {
+ float nearfar[2];
+ GPENCIL_dof_nearfar(camera, fxd->coc, nearfar);
+ float zdepth = stl->g_data->gp_object_cache[ob_idx].zdepth;
+ /* the object is on focus area */
+ if ((zdepth >= nearfar[0]) && (zdepth <= nearfar[1])) {
+ fxd->blur[0] = 0;
+ fxd->blur[1] = 0;
+ }
+ else {
+ float f;
+ if (zdepth < nearfar[0]) {
+ f = nearfar[0] - zdepth;
+ }
+ else {
+ f = zdepth - nearfar[1];
+ }
+ fxd->blur[0] = f;
+ fxd->blur[1] = f;
+ CLAMP2(&fxd->blur[0], 0, fxd->radius[0]);
+ }
+ }
+ else {
+ /* if not camera view, the blur is disabled */
+ fxd->blur[0] = 0;
+ fxd->blur[1] = 0;
+ }
+ }
+
+ struct GPUBatch *fxquad = DRW_cache_fullscreen_quad_get();
+
+ fx_shgrp = DRW_shgroup_create(e_data->gpencil_fx_blur_sh,
+ psl->fx_shader_pass_blend);
+ DRW_shgroup_call_add(fx_shgrp, fxquad, NULL);
+ DRW_shgroup_uniform_texture_ref(fx_shgrp, "strokeColor", &e_data->temp_color_tx_a);
+ DRW_shgroup_uniform_texture_ref(fx_shgrp, "strokeDepth", &e_data->temp_depth_tx_a);
+ DRW_shgroup_uniform_int(fx_shgrp, "blur", &fxd->blur[0], 2);
+
+ DRW_shgroup_uniform_vec3(fx_shgrp, "loc", &ob->loc[0], 1);
+ DRW_shgroup_uniform_float(fx_shgrp, "pixsize", stl->storage->pixsize, 1);
+ DRW_shgroup_uniform_float(fx_shgrp, "pixelsize", &U.pixelsize, 1);
+ DRW_shgroup_uniform_float(fx_shgrp, "pixfactor", &gpd->pixfactor, 1);
+
+ fxd->runtime.fx_sh = fx_shgrp;
+}
+
+/* Colorize FX */
+static void DRW_gpencil_fx_colorize(
+ ShaderFxData *fx, GPENCIL_e_data *e_data, GPENCIL_Data *vedata)
+{
+ if (fx == NULL) {
+ return;
+ }
+ ColorizeShaderFxData *fxd = (ColorizeShaderFxData *)fx;
+ GPENCIL_PassList *psl = ((GPENCIL_Data *)vedata)->psl;
+ DRWShadingGroup *fx_shgrp;
+
+ struct GPUBatch *fxquad = DRW_cache_fullscreen_quad_get();
+ fx_shgrp = DRW_shgroup_create(e_data->gpencil_fx_colorize_sh, psl->fx_shader_pass);
+ DRW_shgroup_call_add(fx_shgrp, fxquad, NULL);
+ DRW_shgroup_uniform_texture_ref(fx_shgrp, "strokeColor", &e_data->temp_color_tx_a);
+ DRW_shgroup_uniform_texture_ref(fx_shgrp, "strokeDepth", &e_data->temp_depth_tx_a);
+ DRW_shgroup_uniform_vec4(fx_shgrp, "low_color", &fxd->low_color[0], 1);
+ DRW_shgroup_uniform_vec4(fx_shgrp, "high_color", &fxd->high_color[0], 1);
+ DRW_shgroup_uniform_int(fx_shgrp, "mode", &fxd->mode, 1);
+ DRW_shgroup_uniform_float(fx_shgrp, "factor", &fxd->factor, 1);
+
+ fxd->runtime.fx_sh = fx_shgrp;
+}
+
+/* Flip FX */
+static void DRW_gpencil_fx_flip(
+ ShaderFxData *fx, GPENCIL_e_data *e_data, GPENCIL_Data *vedata)
+{
+ if (fx == NULL) {
+ return;
+ }
+ FlipShaderFxData *fxd = (FlipShaderFxData *)fx;
+ GPENCIL_PassList *psl = ((GPENCIL_Data *)vedata)->psl;
+ DRWShadingGroup *fx_shgrp;
+
+ fxd->flipmode = 100;
+ /* the number works as bit flag */
+ if (fxd->flag & FX_FLIP_HORIZONTAL) {
+ fxd->flipmode += 10;
+ }
+ if (fxd->flag & FX_FLIP_VERTICAL) {
+ fxd->flipmode += 1;
+ }
+
+ struct GPUBatch *fxquad = DRW_cache_fullscreen_quad_get();
+ fx_shgrp = DRW_shgroup_create(e_data->gpencil_fx_flip_sh, psl->fx_shader_pass);
+ DRW_shgroup_call_add(fx_shgrp, fxquad, NULL);
+ DRW_shgroup_uniform_texture_ref(fx_shgrp, "strokeColor", &e_data->temp_color_tx_a);
+ DRW_shgroup_uniform_texture_ref(fx_shgrp, "strokeDepth", &e_data->temp_depth_tx_a);
+ DRW_shgroup_uniform_int(fx_shgrp, "flipmode", &fxd->flipmode, 1);
+
+ DRW_shgroup_uniform_vec2(fx_shgrp, "wsize", DRW_viewport_size_get(), 1);
+
+ fxd->runtime.fx_sh = fx_shgrp;
+}
+
+/* Light FX */
+static void DRW_gpencil_fx_light(
+ ShaderFxData *fx, GPENCIL_e_data *e_data, GPENCIL_Data *vedata,
+ tGPencilObjectCache *cache)
+{
+ if (fx == NULL) {
+ return;
+ }
+ Object *ob = cache->ob;
+ LightShaderFxData *fxd = (LightShaderFxData *)fx;
+
+ if (fxd->object == NULL) {
+ return;
+ }
+ GPENCIL_StorageList *stl = ((GPENCIL_Data *)vedata)->stl;
+ GPENCIL_PassList *psl = ((GPENCIL_Data *)vedata)->psl;
+ DRWShadingGroup *fx_shgrp;
+
+ struct GPUBatch *fxquad = DRW_cache_fullscreen_quad_get();
+ fx_shgrp = DRW_shgroup_create(e_data->gpencil_fx_light_sh, psl->fx_shader_pass);
+ DRW_shgroup_call_add(fx_shgrp, fxquad, NULL);
+ DRW_shgroup_uniform_texture_ref(fx_shgrp, "strokeColor", &e_data->temp_color_tx_a);
+ DRW_shgroup_uniform_texture_ref(fx_shgrp, "strokeDepth", &e_data->temp_depth_tx_a);
+
+ DRW_shgroup_uniform_vec2(fx_shgrp, "Viewport", DRW_viewport_size_get(), 1);
+
+ /* location of the light using obj location as origin */
+ copy_v3_v3(fxd->loc, &fxd->object->loc[0]);
+
+ /* Calc distance to strokes plane
+ * The w component of location is used to transfer the distance to drawing plane
+ */
+ float r_point[3], r_normal[3];
+ float r_plane[4];
+ bGPdata *gpd = (bGPdata *)ob->data;
+ if (!get_normal_vector(gpd, r_point, r_normal)) {
+ return;
+ }
+ mul_mat3_m4_v3(ob->obmat, r_normal); /* only rotation component */
+ plane_from_point_normal_v3(r_plane, r_point, r_normal);
+ float dt = dist_to_plane_v3(fxd->object->loc, r_plane);
+ fxd->loc[3] = dt; /* use last element to save it */
+
+ DRW_shgroup_uniform_vec4(fx_shgrp, "loc", &fxd->loc[0], 1);
+
+ DRW_shgroup_uniform_float(fx_shgrp, "energy", &fxd->energy, 1);
+ DRW_shgroup_uniform_float(fx_shgrp, "ambient", &fxd->ambient, 1);
+
+ DRW_shgroup_uniform_float(fx_shgrp, "pixsize", stl->storage->pixsize, 1);
+ DRW_shgroup_uniform_float(fx_shgrp, "pixelsize", &U.pixelsize, 1);
+ DRW_shgroup_uniform_float(fx_shgrp, "pixfactor", &gpd->pixfactor, 1);
+
+ fxd->runtime.fx_sh = fx_shgrp;
+}
+
+/* Pixelate FX */
+static void DRW_gpencil_fx_pixel(
+ ShaderFxData *fx, GPENCIL_e_data *e_data, GPENCIL_Data *vedata,
+ tGPencilObjectCache *cache)
+{
+ if (fx == NULL) {
+ return;
+ }
+ Object *ob = cache->ob;
+ PixelShaderFxData *fxd = (PixelShaderFxData *)fx;
+
+ GPENCIL_StorageList *stl = ((GPENCIL_Data *)vedata)->stl;
+ GPENCIL_PassList *psl = ((GPENCIL_Data *)vedata)->psl;
+ DRWShadingGroup *fx_shgrp;
+ bGPdata *gpd = (bGPdata *)ob->data;
+
+ fxd->size[2] = (int)fxd->flag & FX_PIXEL_USE_LINES;
+
+ struct GPUBatch *fxquad = DRW_cache_fullscreen_quad_get();
+ fx_shgrp = DRW_shgroup_create(e_data->gpencil_fx_pixel_sh, psl->fx_shader_pass);
+ DRW_shgroup_call_add(fx_shgrp, fxquad, NULL);
+ DRW_shgroup_uniform_texture_ref(fx_shgrp, "strokeColor", &e_data->temp_color_tx_a);
+ DRW_shgroup_uniform_texture_ref(fx_shgrp, "strokeDepth", &e_data->temp_depth_tx_a);
+ DRW_shgroup_uniform_int(fx_shgrp, "size", &fxd->size[0], 3);
+ DRW_shgroup_uniform_vec4(fx_shgrp, "color", &fxd->rgba[0], 1);
+
+ DRW_shgroup_uniform_vec3(fx_shgrp, "loc", &ob->loc[0], 1);
+ DRW_shgroup_uniform_float(fx_shgrp, "pixsize", stl->storage->pixsize, 1);
+ DRW_shgroup_uniform_float(fx_shgrp, "pixelsize", &U.pixelsize, 1);
+ DRW_shgroup_uniform_float(fx_shgrp, "pixfactor", &gpd->pixfactor, 1);
+
+ fxd->runtime.fx_sh = fx_shgrp;
+}
+
+/* Rim FX */
+static void DRW_gpencil_fx_rim(
+ ShaderFxData *fx, GPENCIL_e_data *e_data, GPENCIL_Data *vedata,
+ tGPencilObjectCache *cache)
+{
+ if (fx == NULL) {
+ return;
+ }
+ Object *ob = cache->ob;
+ RimShaderFxData *fxd = (RimShaderFxData *)fx;
+
+ GPENCIL_StorageList *stl = ((GPENCIL_Data *)vedata)->stl;
+ GPENCIL_PassList *psl = ((GPENCIL_Data *)vedata)->psl;
+ DRWShadingGroup *fx_shgrp;
+ bGPdata *gpd = (bGPdata *)ob->data;
+
+ struct GPUBatch *fxquad = DRW_cache_fullscreen_quad_get();
+ /* prepare pass */
+ fx_shgrp = DRW_shgroup_create(e_data->gpencil_fx_rim_prepare_sh,
+ psl->fx_shader_pass_blend);
+ DRW_shgroup_call_add(fx_shgrp, fxquad, NULL);
+ DRW_shgroup_uniform_texture_ref(fx_shgrp, "strokeColor", &e_data->temp_color_tx_a);
+ DRW_shgroup_uniform_texture_ref(fx_shgrp, "strokeDepth", &e_data->temp_depth_tx_a);
+ DRW_shgroup_uniform_vec2(fx_shgrp, "Viewport", DRW_viewport_size_get(), 1);
+
+ DRW_shgroup_uniform_int(fx_shgrp, "offset", &fxd->offset[0], 2);
+ DRW_shgroup_uniform_vec3(fx_shgrp, "rim_color", &fxd->rim_rgb[0], 1);
+ DRW_shgroup_uniform_vec3(fx_shgrp, "mask_color", &fxd->mask_rgb[0], 1);
+
+ DRW_shgroup_uniform_vec3(fx_shgrp, "loc", &ob->loc[0], 1);
+ DRW_shgroup_uniform_float(fx_shgrp, "pixsize", stl->storage->pixsize, 1);
+ DRW_shgroup_uniform_float(fx_shgrp, "pixelsize", &U.pixelsize, 1);
+ DRW_shgroup_uniform_float(fx_shgrp, "pixfactor", &gpd->pixfactor, 1);
+
+ fxd->runtime.fx_sh = fx_shgrp;
+
+ /* blur pass */
+ fx_shgrp = DRW_shgroup_create(e_data->gpencil_fx_blur_sh,
+ psl->fx_shader_pass_blend);
+ DRW_shgroup_call_add(fx_shgrp, fxquad, NULL);
+ DRW_shgroup_uniform_texture_ref(fx_shgrp, "strokeColor", &e_data->temp_color_tx_rim);
+ DRW_shgroup_uniform_texture_ref(fx_shgrp, "strokeDepth", &e_data->temp_depth_tx_rim);
+ DRW_shgroup_uniform_int(fx_shgrp, "blur", &fxd->blur[0], 2);
+
+ DRW_shgroup_uniform_vec3(fx_shgrp, "loc", &ob->loc[0], 1);
+ DRW_shgroup_uniform_float(fx_shgrp, "pixsize", stl->storage->pixsize, 1);
+ DRW_shgroup_uniform_float(fx_shgrp, "pixelsize", &U.pixelsize, 1);
+ DRW_shgroup_uniform_float(fx_shgrp, "pixfactor", &gpd->pixfactor, 1);
+
+ fxd->runtime.fx_sh_b = fx_shgrp;
+
+ /* resolve pass */
+ fx_shgrp = DRW_shgroup_create(e_data->gpencil_fx_rim_resolve_sh,
+ psl->fx_shader_pass_blend);
+ DRW_shgroup_call_add(fx_shgrp, fxquad, NULL);
+ DRW_shgroup_uniform_texture_ref(fx_shgrp, "strokeColor", &e_data->temp_color_tx_a);
+ DRW_shgroup_uniform_texture_ref(fx_shgrp, "strokeDepth", &e_data->temp_depth_tx_a);
+ DRW_shgroup_uniform_texture_ref(fx_shgrp, "strokeRim", &e_data->temp_color_tx_rim);
+ DRW_shgroup_uniform_vec3(fx_shgrp, "mask_color", &fxd->mask_rgb[0], 1);
+ DRW_shgroup_uniform_int(fx_shgrp, "mode", &fxd->mode, 1);
+
+ fxd->runtime.fx_sh_c = fx_shgrp;
+}
+
+/* Swirl FX */
+static void DRW_gpencil_fx_swirl(
+ ShaderFxData *fx, GPENCIL_e_data *e_data, GPENCIL_Data *vedata,
+ tGPencilObjectCache *cache)
+{
+ if (fx == NULL) {
+ return;
+ }
+ Object *ob = cache->ob;
+ SwirlShaderFxData *fxd = (SwirlShaderFxData *)fx;
+ if (fxd->object == NULL) {
+ return;
+ }
+
+ GPENCIL_StorageList *stl = ((GPENCIL_Data *)vedata)->stl;
+ GPENCIL_PassList *psl = ((GPENCIL_Data *)vedata)->psl;
+ DRWShadingGroup *fx_shgrp;
+ bGPdata *gpd = (bGPdata *)ob->data;
+
+ fxd->transparent = (int)fxd->flag & FX_SWIRL_MAKE_TRANSPARENT;
+
+ struct GPUBatch *fxquad = DRW_cache_fullscreen_quad_get();
+ fx_shgrp = DRW_shgroup_create(e_data->gpencil_fx_swirl_sh, psl->fx_shader_pass);
+ DRW_shgroup_call_add(fx_shgrp, fxquad, NULL);
+ DRW_shgroup_uniform_texture_ref(fx_shgrp, "strokeColor", &e_data->temp_color_tx_a);
+ DRW_shgroup_uniform_texture_ref(fx_shgrp, "strokeDepth", &e_data->temp_depth_tx_a);
+
+ DRW_shgroup_uniform_vec2(fx_shgrp, "Viewport", DRW_viewport_size_get(), 1);
+
+ DRW_shgroup_uniform_vec3(fx_shgrp, "loc", &fxd->object->loc[0], 1);
+
+ DRW_shgroup_uniform_int(fx_shgrp, "radius", &fxd->radius, 1);
+ DRW_shgroup_uniform_float(fx_shgrp, "angle", &fxd->angle, 1);
+ DRW_shgroup_uniform_int(fx_shgrp, "transparent", &fxd->transparent, 1);
+
+ DRW_shgroup_uniform_float(fx_shgrp, "pixsize", stl->storage->pixsize, 1);
+ DRW_shgroup_uniform_float(fx_shgrp, "pixelsize", &U.pixelsize, 1);
+ DRW_shgroup_uniform_float(fx_shgrp, "pixfactor", &gpd->pixfactor, 1);
+
+ fxd->runtime.fx_sh = fx_shgrp;
+}
+
+/* Wave Distorsion FX */
+static void DRW_gpencil_fx_wave(
+ ShaderFxData *fx, GPENCIL_e_data *e_data, GPENCIL_Data *vedata)
+{
+ if (fx == NULL) {
+ return;
+ }
+
+ WaveShaderFxData *fxd = (WaveShaderFxData *)fx;
+
+ GPENCIL_PassList *psl = ((GPENCIL_Data *)vedata)->psl;
+ struct GPUBatch *fxquad = DRW_cache_fullscreen_quad_get();
+
+ DRWShadingGroup *fx_shgrp = DRW_shgroup_create(e_data->gpencil_fx_wave_sh, psl->fx_shader_pass);
+ DRW_shgroup_call_add(fx_shgrp, fxquad, NULL);
+ DRW_shgroup_uniform_texture_ref(fx_shgrp, "strokeColor", &e_data->temp_color_tx_a);
+ DRW_shgroup_uniform_texture_ref(fx_shgrp, "strokeDepth", &e_data->temp_depth_tx_a);
+ DRW_shgroup_uniform_float(fx_shgrp, "amplitude", &fxd->amplitude, 1);
+ DRW_shgroup_uniform_float(fx_shgrp, "period", &fxd->period, 1);
+ DRW_shgroup_uniform_float(fx_shgrp, "phase", &fxd->phase, 1);
+ DRW_shgroup_uniform_int(fx_shgrp, "orientation", &fxd->orientation, 1);
+ DRW_shgroup_uniform_vec2(fx_shgrp, "wsize", DRW_viewport_size_get(), 1);
+
+ fxd->runtime.fx_sh = fx_shgrp;
+}
+
+/* ************************************************************** */
+
+/* create all FX shaders */
+void GPENCIL_create_fx_shaders(GPENCIL_e_data *e_data)
+{
+ /* fx shaders (all in screen space) */
+ if (!e_data->gpencil_fx_blur_sh) {
+ e_data->gpencil_fx_blur_sh = DRW_shader_create_fullscreen(
+ datatoc_gpencil_fx_blur_frag_glsl, NULL);
+ }
+ if (!e_data->gpencil_fx_colorize_sh) {
+ e_data->gpencil_fx_colorize_sh = DRW_shader_create_fullscreen(
+ datatoc_gpencil_fx_colorize_frag_glsl, NULL);
+ }
+ if (!e_data->gpencil_fx_flip_sh) {
+ e_data->gpencil_fx_flip_sh = DRW_shader_create_fullscreen(
+ datatoc_gpencil_fx_flip_frag_glsl, NULL);
+ }
+ if (!e_data->gpencil_fx_light_sh) {
+ e_data->gpencil_fx_light_sh = DRW_shader_create_fullscreen(
+ datatoc_gpencil_fx_light_frag_glsl, NULL);
+ }
+ if (!e_data->gpencil_fx_pixel_sh) {
+ e_data->gpencil_fx_pixel_sh = DRW_shader_create_fullscreen(
+ datatoc_gpencil_fx_pixel_frag_glsl, NULL);
+ }
+ if (!e_data->gpencil_fx_rim_prepare_sh) {
+ e_data->gpencil_fx_rim_prepare_sh = DRW_shader_create_fullscreen(
+ datatoc_gpencil_fx_rim_prepare_frag_glsl, NULL);
+
+ e_data->gpencil_fx_rim_resolve_sh = DRW_shader_create_fullscreen(
+ datatoc_gpencil_fx_rim_resolve_frag_glsl, NULL);
+ }
+ if (!e_data->gpencil_fx_swirl_sh) {
+ e_data->gpencil_fx_swirl_sh = DRW_shader_create_fullscreen(
+ datatoc_gpencil_fx_swirl_frag_glsl, NULL);
+ }
+ if (!e_data->gpencil_fx_wave_sh) {
+ e_data->gpencil_fx_wave_sh = DRW_shader_create_fullscreen(
+ datatoc_gpencil_fx_wave_frag_glsl, NULL);
+ }
+}
+
+/* free FX shaders */
+void GPENCIL_delete_fx_shaders(GPENCIL_e_data *e_data)
+{
+ DRW_SHADER_FREE_SAFE(e_data->gpencil_fx_blur_sh);
+ DRW_SHADER_FREE_SAFE(e_data->gpencil_fx_colorize_sh);
+ DRW_SHADER_FREE_SAFE(e_data->gpencil_fx_flip_sh);
+ DRW_SHADER_FREE_SAFE(e_data->gpencil_fx_light_sh);
+ DRW_SHADER_FREE_SAFE(e_data->gpencil_fx_pixel_sh);
+ DRW_SHADER_FREE_SAFE(e_data->gpencil_fx_rim_prepare_sh);
+ DRW_SHADER_FREE_SAFE(e_data->gpencil_fx_rim_resolve_sh);
+ DRW_SHADER_FREE_SAFE(e_data->gpencil_fx_swirl_sh);
+ DRW_SHADER_FREE_SAFE(e_data->gpencil_fx_wave_sh);
+}
+
+/* create all passes used by FX */
+void GPENCIL_create_fx_passes(GPENCIL_PassList *psl)
+{
+ psl->fx_shader_pass = DRW_pass_create(
+ "GPencil Shader FX Pass",
+ DRW_STATE_WRITE_COLOR |
+ DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS);
+ psl->fx_shader_pass_blend = DRW_pass_create(
+ "GPencil Shader FX Pass",
+ DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND |
+ DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS);
+}
+
+
+/* prepare fx shading groups */
+void DRW_gpencil_fx_prepare(
+ struct GPENCIL_e_data *e_data, struct GPENCIL_Data *vedata,
+ struct tGPencilObjectCache *cache)
+{
+ GPENCIL_StorageList *stl = ((GPENCIL_Data *)vedata)->stl;
+ Object *ob = cache->ob;
+ int ob_idx = cache->idx;
+
+ if (ob->shader_fx.first == NULL) {
+ return;
+ }
+ /* loop FX */
+ for (ShaderFxData *fx = ob->shader_fx.first; fx; fx = fx->next) {
+ if (effect_is_active(ob, fx, stl->storage->is_render)) {
+ switch (fx->type) {
+ case eShaderFxType_Blur:
+ DRW_gpencil_fx_blur(fx, ob_idx, e_data, vedata, cache);
+ break;
+ case eShaderFxType_Colorize:
+ DRW_gpencil_fx_colorize(fx, e_data, vedata);
+ break;
+ case eShaderFxType_Flip:
+ DRW_gpencil_fx_flip(fx, e_data, vedata);
+ break;
+ case eShaderFxType_Light:
+ DRW_gpencil_fx_light(fx, e_data, vedata, cache);
+ break;
+ case eShaderFxType_Pixel:
+ DRW_gpencil_fx_pixel(fx, e_data, vedata, cache);
+ break;
+ case eShaderFxType_Rim:
+ DRW_gpencil_fx_rim(fx, e_data, vedata, cache);
+ break;
+ case eShaderFxType_Swirl:
+ DRW_gpencil_fx_swirl(fx, e_data, vedata, cache);
+ break;
+ case eShaderFxType_Wave:
+ DRW_gpencil_fx_wave(fx, e_data, vedata);
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+}
+
+/* helper to draw one FX pass and do ping-pong copy */
+static void gpencil_draw_fx_pass(
+ GPENCIL_e_data *e_data,
+ GPENCIL_PassList *psl,
+ GPENCIL_FramebufferList *fbl,
+ DRWShadingGroup *shgrp, bool blend)
+{
+ if (shgrp == NULL) {
+ return;
+ }
+
+ static float clearcol[4] = { 0.0f, 0.0f, 0.0f, 0.0f };
+
+ GPU_framebuffer_bind(fbl->temp_fb_b);
+ GPU_framebuffer_clear_color_depth(fbl->temp_fb_b, clearcol, 1.0f);
+
+ /* draw effect pass in temp texture (B) using as source the previous image
+ * existing in the other temp texture (A) */
+ if (!blend) {
+ DRW_draw_pass_subset(psl->fx_shader_pass, shgrp, shgrp);
+ }
+ else {
+ DRW_draw_pass_subset(psl->fx_shader_pass_blend, shgrp, shgrp);
+ }
+
+ /* copy pass from b to a for ping-pong frame buffers */
+ e_data->input_depth_tx = e_data->temp_depth_tx_b;
+ e_data->input_color_tx = e_data->temp_color_tx_b;
+
+ GPU_framebuffer_bind(fbl->temp_fb_a);
+ GPU_framebuffer_clear_color_depth(fbl->temp_fb_a, clearcol, 1.0f);
+ DRW_draw_pass(psl->mix_pass_noblend);
+}
+
+/* helper to manage gaussian blur passes */
+static void draw_gpencil_blur_passes(
+ struct GPENCIL_e_data *e_data,
+ struct GPENCIL_Data *vedata,
+ struct BlurShaderFxData *fxd)
+{
+ if (fxd->runtime.fx_sh == NULL) {
+ return;
+ }
+
+ GPENCIL_PassList *psl = ((GPENCIL_Data *)vedata)->psl;
+ GPENCIL_FramebufferList *fbl = ((GPENCIL_Data *)vedata)->fbl;
+ DRWShadingGroup *shgrp = fxd->runtime.fx_sh;
+ int samples = fxd->samples;
+
+ float bx = fxd->blur[0];
+ float by = fxd->blur[1];
+
+ /* the blur is done in two steps (Hor/Ver) because is faster and
+ * gets better result
+ *
+ * samples could be 0 and disable de blur effects because sometimes
+ * is easier animate the number of samples only, instead to animate the
+ * hide/unhide and the number of samples to make some effects.
+ */
+ for (int b = 0; b < samples; b++) {
+ /* horizontal */
+ if (bx > 0) {
+ fxd->blur[0] = bx;
+ fxd->blur[1] = 0;
+ gpencil_draw_fx_pass(e_data, psl, fbl, shgrp, true);
+ }
+ /* vertical */
+ if (by > 0) {
+ fxd->blur[0] = 0;
+ fxd->blur[1] = by;
+ gpencil_draw_fx_pass(e_data, psl, fbl, shgrp, true);
+ }
+ }
+}
+
+static void draw_gpencil_rim_blur(
+ struct GPENCIL_e_data *UNUSED(e_data),
+ struct GPENCIL_Data *vedata,
+ struct RimShaderFxData *fxd)
+{
+ GPENCIL_PassList *psl = ((GPENCIL_Data *)vedata)->psl;
+ GPENCIL_FramebufferList *fbl = ((GPENCIL_Data *)vedata)->fbl;
+ static float clearcol[4] = { 0.0f, 0.0f, 0.0f, 0.0f };
+
+ GPU_framebuffer_bind(fbl->temp_fb_b);
+ GPU_framebuffer_clear_color_depth(fbl->temp_fb_b, clearcol, 1.0f);
+ DRW_draw_pass_subset(psl->fx_shader_pass_blend,
+ fxd->runtime.fx_sh_b, fxd->runtime.fx_sh_b);
+
+ /* copy pass from b for ping-pong frame buffers */
+ GPU_framebuffer_bind(fbl->temp_fb_rim);
+ GPU_framebuffer_clear_color_depth(fbl->temp_fb_rim, clearcol, 1.0f);
+ DRW_draw_pass(psl->mix_pass_noblend);
+}
+
+/* helper to draw RIM passes */
+static void draw_gpencil_rim_passes(
+ struct GPENCIL_e_data *e_data,
+ struct GPENCIL_Data *vedata,
+ struct RimShaderFxData *fxd)
+{
+ if (fxd->runtime.fx_sh_b == NULL) {
+ return;
+ }
+
+ GPENCIL_PassList *psl = ((GPENCIL_Data *)vedata)->psl;
+ GPENCIL_FramebufferList *fbl = ((GPENCIL_Data *)vedata)->fbl;
+
+ static float clearcol[4] = { 0.0f, 0.0f, 0.0f, 0.0f };
+ int bx = fxd->blur[0];
+ int by = fxd->blur[1];
+
+ /* prepare mask */
+ GPU_framebuffer_bind(fbl->temp_fb_rim);
+ GPU_framebuffer_clear_color_depth(fbl->temp_fb_rim, clearcol, 1.0f);
+ DRW_draw_pass_subset(
+ psl->fx_shader_pass_blend,
+ fxd->runtime.fx_sh, fxd->runtime.fx_sh);
+
+ /* blur rim */
+ e_data->input_depth_tx = e_data->temp_depth_tx_b;
+ e_data->input_color_tx = e_data->temp_color_tx_b;
+
+ if ((fxd->samples > 0) && ((bx > 0) || (by > 0))) {
+ for (int x = 0; x < fxd->samples; x++) {
+
+ /* horizontal */
+ fxd->blur[0] = bx;
+ fxd->blur[1] = 0;
+ draw_gpencil_rim_blur(e_data, vedata, fxd);
+
+ /* Vertical */
+ fxd->blur[0] = 0;
+ fxd->blur[1] = by;
+ draw_gpencil_rim_blur(e_data, vedata, fxd);
+
+ fxd->blur[0] = bx;
+ fxd->blur[1] = by;
+ }
+ }
+ /* resolve */
+ GPU_framebuffer_bind(fbl->temp_fb_b);
+ GPU_framebuffer_clear_color_depth(fbl->temp_fb_b, clearcol, 1.0f);
+ DRW_draw_pass_subset(
+ psl->fx_shader_pass_blend,
+ fxd->runtime.fx_sh_c, fxd->runtime.fx_sh_c);
+
+ /* copy pass from b to a for ping-pong frame buffers */
+ e_data->input_depth_tx = e_data->temp_depth_tx_b;
+ e_data->input_color_tx = e_data->temp_color_tx_b;
+
+ GPU_framebuffer_bind(fbl->temp_fb_a);
+ GPU_framebuffer_clear_color_depth(fbl->temp_fb_a, clearcol, 1.0f);
+ DRW_draw_pass(psl->mix_pass_noblend);
+}
+
+/* apply all object fx effects */
+void DRW_gpencil_fx_draw(
+ struct GPENCIL_e_data *e_data,
+ struct GPENCIL_Data *vedata, struct tGPencilObjectCache *cache)
+{
+ GPENCIL_StorageList *stl = ((GPENCIL_Data *)vedata)->stl;
+ GPENCIL_PassList *psl = ((GPENCIL_Data *)vedata)->psl;
+ GPENCIL_FramebufferList *fbl = ((GPENCIL_Data *)vedata)->fbl;
+ Object *ob = cache->ob;
+
+ /* loop FX modifiers */
+ for (ShaderFxData *fx = ob->shader_fx.first; fx; fx = fx->next) {
+ if (effect_is_active(ob, fx, stl->storage->is_render)) {
+ switch (fx->type) {
+ case eShaderFxType_Blur:
+ {
+ BlurShaderFxData *fxd = (BlurShaderFxData *)fx;
+ draw_gpencil_blur_passes(e_data, vedata, fxd);
+ break;
+ }
+ case eShaderFxType_Colorize:
+ {
+ ColorizeShaderFxData *fxd = (ColorizeShaderFxData *)fx;
+ gpencil_draw_fx_pass(e_data, psl, fbl, fxd->runtime.fx_sh, false);
+ break;
+ }
+ case eShaderFxType_Flip:
+ {
+ FlipShaderFxData *fxd = (FlipShaderFxData *)fx;
+ gpencil_draw_fx_pass(e_data, psl, fbl, fxd->runtime.fx_sh, false);
+ break;
+ }
+ case eShaderFxType_Light:
+ {
+ LightShaderFxData *fxd = (LightShaderFxData *)fx;
+ gpencil_draw_fx_pass(e_data, psl, fbl, fxd->runtime.fx_sh, false);
+ break;
+ }
+ case eShaderFxType_Pixel:
+ {
+ PixelShaderFxData *fxd = (PixelShaderFxData *)fx;
+ gpencil_draw_fx_pass(e_data, psl, fbl, fxd->runtime.fx_sh, false);
+ break;
+ }
+ case eShaderFxType_Rim:
+ {
+ RimShaderFxData *fxd = (RimShaderFxData *)fx;
+ draw_gpencil_rim_passes(e_data, vedata, fxd);
+ break;
+ }
+ case eShaderFxType_Swirl:
+ {
+ SwirlShaderFxData *fxd = (SwirlShaderFxData *)fx;
+ gpencil_draw_fx_pass(e_data, psl, fbl, fxd->runtime.fx_sh, false);
+ break;
+ }
+ case eShaderFxType_Wave:
+ {
+ WaveShaderFxData *fxd = (WaveShaderFxData *)fx;
+ gpencil_draw_fx_pass(e_data, psl, fbl, fxd->runtime.fx_sh, false);
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ }
+}
diff --git a/source/blender/draw/engines/gpencil/shaders/fx/gpencil_fx_blur_frag.glsl b/source/blender/draw/engines/gpencil/shaders/fx/gpencil_fx_blur_frag.glsl
new file mode 100644
index 00000000000..1d66ba3d4d4
--- /dev/null
+++ b/source/blender/draw/engines/gpencil/shaders/fx/gpencil_fx_blur_frag.glsl
@@ -0,0 +1,60 @@
+uniform mat4 ProjectionMatrix;
+uniform mat4 ViewMatrix;
+
+uniform sampler2D strokeColor;
+uniform sampler2D strokeDepth;
+
+uniform int blur[2];
+
+uniform vec3 loc;
+uniform float pixsize; /* rv3d->pixsize */
+uniform float pixelsize; /* U.pixelsize */
+uniform float pixfactor;
+
+float defaultpixsize = pixsize * pixelsize * (1000.0 / pixfactor);
+vec2 noffset = vec2(blur[0], blur[1]);
+
+out vec4 FragColor;
+
+void main()
+{
+ ivec2 uv = ivec2(gl_FragCoord.xy);
+
+ vec4 nloc = ProjectionMatrix * ViewMatrix * vec4(loc.xyz, 1.0);
+
+ float dx = (ProjectionMatrix[3][3] == 0.0) ? (noffset[0] / (nloc.z * defaultpixsize)) : (noffset[0] / defaultpixsize);
+ float dy = (ProjectionMatrix[3][3] == 0.0) ? (noffset[1] / (nloc.z * defaultpixsize)) : (noffset[1] / defaultpixsize);
+
+ /* apply blurring, using a 9-tap filter with predefined gaussian weights */
+ /* depth */
+ float outdepth = 0;
+ outdepth += texelFetch(strokeDepth, ivec2(uv.x - 1.0 * dx, uv.y + 1.0 * dy), 0).r * 0.0947416;
+ outdepth += texelFetch(strokeDepth, ivec2(uv.x - 0.0 * dx, uv.y + 1.0 * dy), 0).r * 0.118318;
+ outdepth += texelFetch(strokeDepth, ivec2(uv.x + 1.0 * dx, uv.y + 1.0 * dy), 0).r * 0.0947416;
+ outdepth += texelFetch(strokeDepth, ivec2(uv.x - 1.0 * dx, uv.y + 0.0 * dy), 0).r * 0.118318;
+
+ outdepth += texelFetch(strokeDepth, ivec2(uv.x, uv.y), 0).r * 0.147761;
+
+ outdepth += texelFetch(strokeDepth, ivec2(uv.x + 1.0 * dx, uv.y + 0.0 * dy), 0).r * 0.118318;
+ outdepth += texelFetch(strokeDepth, ivec2(uv.x - 1.0 * dx, uv.y - 1.0 * dy), 0).r * 0.0947416;
+ outdepth += texelFetch(strokeDepth, ivec2(uv.x + 0.0 * dx, uv.y - 1.0 * dy), 0).r * 0.118318;
+ outdepth += texelFetch(strokeDepth, ivec2(uv.x + 1.0 * dx, uv.y - 1.0 * dy), 0).r * 0.0947416;
+
+ gl_FragDepth = outdepth;
+
+ /* color */
+ vec4 outcolor = vec4(0.0);
+ outcolor += texelFetch(strokeColor, ivec2(uv.x - 1.0 * dx, uv.y + 1.0 * dy), 0) * 0.0947416;
+ outcolor += texelFetch(strokeColor, ivec2(uv.x - 0.0 * dx, uv.y + 1.0 * dy), 0) * 0.118318;
+ outcolor += texelFetch(strokeColor, ivec2(uv.x + 1.0 * dx, uv.y + 1.0 * dy), 0) * 0.0947416;
+ outcolor += texelFetch(strokeColor, ivec2(uv.x - 1.0 * dx, uv.y + 0.0 * dy), 0) * 0.118318;
+
+ outcolor += texelFetch(strokeColor, ivec2(uv.x, uv.y), 0) * 0.147761;
+
+ outcolor += texelFetch(strokeColor, ivec2(uv.x + 1.0 * dx, uv.y + 0.0 * dy), 0) * 0.118318;
+ outcolor += texelFetch(strokeColor, ivec2(uv.x - 1.0 * dx, uv.y - 1.0 * dy), 0) * 0.0947416;
+ outcolor += texelFetch(strokeColor, ivec2(uv.x + 0.0 * dx, uv.y - 1.0 * dy), 0) * 0.118318;
+ outcolor += texelFetch(strokeColor, ivec2(uv.x + 1.0 * dx, uv.y - 1.0 * dy), 0) * 0.0947416;
+
+ FragColor = clamp(outcolor, 0, 1.0);
+}
diff --git a/source/blender/draw/engines/gpencil/shaders/fx/gpencil_fx_colorize_frag.glsl b/source/blender/draw/engines/gpencil/shaders/fx/gpencil_fx_colorize_frag.glsl
new file mode 100644
index 00000000000..7d0ce4a804e
--- /dev/null
+++ b/source/blender/draw/engines/gpencil/shaders/fx/gpencil_fx_colorize_frag.glsl
@@ -0,0 +1,86 @@
+uniform sampler2D strokeColor;
+uniform sampler2D strokeDepth;
+
+uniform vec4 low_color;
+uniform vec4 high_color;
+uniform int mode;
+uniform float factor;
+
+out vec4 FragColor;
+
+#define MODE_GRAYSCALE 0
+#define MODE_SEPIA 1
+#define MODE_BITONE 2
+#define MODE_CUSTOM 3
+#define MODE_TRANSPARENT 4
+
+float get_luminance(vec4 color)
+{
+ float lum = (color.r * 0.2126) + (color.g * 0.7152) + (color.b * 0.723);
+ return lum;
+}
+
+void main()
+{
+ ivec2 uv = ivec2(gl_FragCoord.xy);
+
+ float stroke_depth = texelFetch(strokeDepth, uv.xy, 0).r;
+ vec4 src_pixel= texelFetch(strokeColor, uv.xy, 0);
+ float luminance = get_luminance(src_pixel);
+ vec4 outcolor;
+
+ /* is transparent */
+ if (src_pixel.a == 0.0f) {
+ discard;
+ }
+
+ switch(mode) {
+ case MODE_GRAYSCALE:
+ {
+ outcolor = vec4(luminance, luminance, luminance, src_pixel.a);
+ break;
+ }
+ case MODE_SEPIA:
+ {
+ float Red = (src_pixel.r * 0.393) + (src_pixel.g * 0.769) + (src_pixel.b * 0.189);
+ float Green = (src_pixel.r * 0.349) + (src_pixel.g * 0.686) + (src_pixel.b * 0.168);
+ float Blue = (src_pixel.r * 0.272) + (src_pixel.g * 0.534) + (src_pixel.b * 0.131);
+ outcolor = vec4(Red, Green, Blue, src_pixel.a);
+ break;
+ }
+ case MODE_BITONE:
+ {
+ if (luminance <= factor) {
+ outcolor = low_color;
+ }
+ else {
+ outcolor = high_color;
+ }
+ break;
+ }
+ case MODE_CUSTOM:
+ {
+ /* if below umbral, force custom color */
+ if (luminance <= factor) {
+ outcolor = low_color;
+ }
+ else {
+ outcolor = vec4(luminance * low_color.r, luminance * low_color.b, luminance * low_color.b, src_pixel.a);
+ }
+ break;
+ }
+ case MODE_TRANSPARENT:
+ {
+ outcolor = vec4(src_pixel.rgb, src_pixel.a * factor);
+ break;
+ }
+ default:
+ {
+ outcolor = src_pixel;
+ }
+
+ }
+
+ gl_FragDepth = stroke_depth;
+ FragColor = outcolor;
+}
diff --git a/source/blender/draw/engines/gpencil/shaders/fx/gpencil_fx_flip_frag.glsl b/source/blender/draw/engines/gpencil/shaders/fx/gpencil_fx_flip_frag.glsl
new file mode 100644
index 00000000000..94fb3405c79
--- /dev/null
+++ b/source/blender/draw/engines/gpencil/shaders/fx/gpencil_fx_flip_frag.glsl
@@ -0,0 +1,37 @@
+out vec4 FragColor;
+
+uniform sampler2D strokeColor;
+uniform sampler2D strokeDepth;
+uniform vec2 wsize;
+uniform int flipmode;
+
+void main()
+{
+ vec2 mode = vec2(0,0);
+ /* horz. */
+ if (flipmode >= 110) {
+ mode[0] = 1;
+ }
+ /* vert. */
+ if ((flipmode == 101) || (flipmode == 111)) {
+ mode[1] = 1;
+ }
+
+ vec2 uv = vec2(gl_FragCoord.xy);
+ float stroke_depth;
+ vec4 outcolor;
+
+ if (mode[0] > 0) {
+ uv.x = wsize.x - uv.x;
+ }
+ if (mode[1] > 0) {
+ uv.y = wsize.y - uv.y;
+ }
+
+ ivec2 iuv = ivec2(uv.x, uv.y);
+ stroke_depth = texelFetch(strokeDepth, iuv, 0).r;
+ outcolor = texelFetch(strokeColor, iuv, 0);
+
+ gl_FragDepth = stroke_depth;
+ FragColor = outcolor;
+}
diff --git a/source/blender/draw/engines/gpencil/shaders/fx/gpencil_fx_light_frag.glsl b/source/blender/draw/engines/gpencil/shaders/fx/gpencil_fx_light_frag.glsl
new file mode 100644
index 00000000000..f3026c32fc8
--- /dev/null
+++ b/source/blender/draw/engines/gpencil/shaders/fx/gpencil_fx_light_frag.glsl
@@ -0,0 +1,70 @@
+uniform mat4 ProjectionMatrix;
+uniform mat4 ViewMatrix;
+
+uniform sampler2D strokeColor;
+uniform sampler2D strokeDepth;
+uniform vec2 Viewport;
+uniform vec4 loc;
+uniform float energy;
+uniform float ambient;
+
+uniform float pixsize; /* rv3d->pixsize */
+uniform float pixelsize; /* U.pixelsize */
+uniform float pixfactor;
+
+out vec4 FragColor;
+
+float defaultpixsize = pixsize * pixelsize * (1000.0 / pixfactor);
+
+#define height loc.w
+
+/* project 3d point to 2d on screen space */
+vec2 toScreenSpace(vec4 vertex)
+{
+ /* need to calculate ndc because this is not done by vertex shader */
+ vec3 ndc = vec3(vertex).xyz / vertex.w;
+
+ vec2 sc;
+ sc.x = ((ndc.x + 1.0) / 2.0) * Viewport.x;
+ sc.y = ((ndc.y + 1.0) / 2.0) * Viewport.y;
+
+ return sc;
+}
+
+void main()
+{
+ float stroke_depth;
+ vec4 objcolor;
+
+ vec4 light_loc = ProjectionMatrix * ViewMatrix * vec4(loc.xyz, 1.0);
+ vec2 light2d = toScreenSpace(light_loc);
+
+ /* calc pixel scale */
+ float pxscale = (ProjectionMatrix[3][3] == 0.0) ? (10.0 / (light_loc.z * defaultpixsize)) : (10.0 / defaultpixsize);
+ pxscale = max(pxscale, 0.000001);
+
+ /* the height over plane is received in the w component of the loc
+ * and needs a factor to adapt to pixels
+ */
+ float peak = height * 10.0 * pxscale;
+ vec3 light3d = vec3(light2d.x, light2d.y, peak);
+
+ vec2 uv = vec2(gl_FragCoord.xy);
+ vec3 frag_loc = vec3(uv.x, uv.y, 0);
+ vec3 norm = vec3(0, 0, 1.0); /* always z-up */
+
+ ivec2 iuv = ivec2(uv.x, uv.y);
+ stroke_depth = texelFetch(strokeDepth, iuv, 0).r;
+ objcolor = texelFetch(strokeColor, iuv, 0);
+
+ /* diffuse light */
+ vec3 lightdir = normalize(light3d - frag_loc);
+ float diff = max(dot(norm, lightdir), 0.0);
+ float dist = length(light3d - frag_loc) / pxscale;
+ float factor = diff * ((energy * 100.0) / (dist * dist));
+
+ vec3 result = factor * max(ambient, 0.1) * vec3(objcolor);
+
+ gl_FragDepth = stroke_depth;
+ FragColor = vec4(result.r, result.g, result.b, objcolor.a);
+}
diff --git a/source/blender/draw/engines/gpencil/shaders/fx/gpencil_fx_pixel_frag.glsl b/source/blender/draw/engines/gpencil/shaders/fx/gpencil_fx_pixel_frag.glsl
new file mode 100644
index 00000000000..d1a57a9a1b6
--- /dev/null
+++ b/source/blender/draw/engines/gpencil/shaders/fx/gpencil_fx_pixel_frag.glsl
@@ -0,0 +1,50 @@
+uniform mat4 ProjectionMatrix;
+uniform mat4 ViewMatrix;
+
+uniform sampler2D strokeColor;
+uniform sampler2D strokeDepth;
+
+uniform int size[3];
+uniform vec4 color;
+
+uniform vec3 loc;
+uniform float pixsize; /* rv3d->pixsize */
+uniform float pixelsize; /* U.pixelsize */
+uniform float pixfactor;
+
+out vec4 FragColor;
+
+int uselines = size[2];
+float defaultpixsize = pixsize * pixelsize * (1000.0 / pixfactor);
+vec2 nsize = max(vec2(size[0], size[1]), 3.0);
+
+/* This pixelation shader is a modified version of original Geeks3d.com code */
+void main()
+{
+ vec2 uv = vec2(gl_FragCoord.xy);
+ vec4 nloc = ProjectionMatrix * ViewMatrix * vec4(loc.xyz, 1.0);
+
+ float dx = (ProjectionMatrix[3][3] == 0.0) ? (nsize[0] / (nloc.z * defaultpixsize)) : (nsize[0] / defaultpixsize);
+ float dy = (ProjectionMatrix[3][3] == 0.0) ? (nsize[1] / (nloc.z * defaultpixsize)) : (nsize[1] / defaultpixsize);
+
+ dx = max(abs(dx), 3.0);
+ dy = max(abs(dy), 3.0);
+
+ vec2 coord = vec2(dx * floor(uv.x / dx), dy * floor(uv.y / dy));
+
+ float stroke_depth = texelFetch(strokeDepth, ivec2(coord), 0).r;
+ vec4 outcolor = texelFetch(strokeColor, ivec2(coord), 0);
+
+ if (uselines == 1) {
+ float difx = uv.x - (floor(uv.x / nsize[0]) * nsize[0]);
+ if ((difx == 0.5) && (outcolor.a > 0)) {
+ outcolor = color;
+ }
+ float dify = uv.y - (floor(uv.y / nsize[1]) * nsize[1]);
+ if ((dify == 0.5) && (outcolor.a > 0)) {
+ outcolor = color;
+ }
+ }
+ gl_FragDepth = stroke_depth;
+ FragColor = outcolor;
+}
diff --git a/source/blender/draw/engines/gpencil/shaders/fx/gpencil_fx_rim_prepare_frag.glsl b/source/blender/draw/engines/gpencil/shaders/fx/gpencil_fx_rim_prepare_frag.glsl
new file mode 100644
index 00000000000..fe35d3832e1
--- /dev/null
+++ b/source/blender/draw/engines/gpencil/shaders/fx/gpencil_fx_rim_prepare_frag.glsl
@@ -0,0 +1,64 @@
+uniform mat4 ProjectionMatrix;
+uniform mat4 ViewMatrix;
+
+/* ******************************************************************* */
+/* create rim and mask */
+/* ******************************************************************* */
+uniform sampler2D strokeColor;
+uniform sampler2D strokeDepth;
+uniform vec2 Viewport;
+
+uniform int offset[2];
+uniform vec3 rim_color;
+uniform vec3 mask_color;
+
+uniform vec3 loc;
+uniform float pixsize; /* rv3d->pixsize */
+uniform float pixelsize; /* U.pixelsize */
+uniform float pixfactor;
+
+float defaultpixsize = pixsize * pixelsize * (1000.0 / pixfactor);
+vec2 noffset = vec2(offset[0], offset[1]);
+
+out vec4 FragColor;
+
+void main()
+{
+ vec2 uv = vec2(gl_FragCoord.xy);
+ vec4 nloc = ProjectionMatrix * ViewMatrix * vec4(loc.xyz, 1.0);
+
+ float dx = (ProjectionMatrix[3][3] == 0.0) ? (noffset[0] / (nloc.z * defaultpixsize)) : (noffset[0] / defaultpixsize);
+ float dy = (ProjectionMatrix[3][3] == 0.0) ? (noffset[1] / (nloc.z * defaultpixsize)) : (noffset[1] / defaultpixsize);
+
+ float stroke_depth = texelFetch(strokeDepth, ivec2(uv.xy), 0).r;
+ vec4 src_pixel= texelFetch(strokeColor, ivec2(uv.xy), 0);
+ vec4 offset_pixel= texelFetch(strokeColor, ivec2(uv.x - dx, uv.y - dy), 0);
+ vec4 outcolor;
+
+ /* is transparent */
+ if (src_pixel.a == 0.0f) {
+ discard;
+ }
+ /* check inside viewport */
+ else if ((uv.x - dx < 0) || (uv.x - dx > Viewport[0])) {
+ discard;
+ }
+ else if ((uv.y - dy < 0) || (uv.y - dy > Viewport[1])) {
+ discard;
+ }
+ /* pixel is equal to mask color, keep */
+ else if (src_pixel.rgb == mask_color.rgb) {
+ discard;
+ }
+ else {
+ if ((src_pixel.a > 0) && (offset_pixel.a > 0)) {
+ discard;
+ }
+ else {
+ outcolor = vec4(rim_color, 1.0);
+ }
+ }
+
+ gl_FragDepth = stroke_depth;
+ FragColor = outcolor;
+}
diff --git a/source/blender/draw/engines/gpencil/shaders/fx/gpencil_fx_rim_resolve_frag.glsl b/source/blender/draw/engines/gpencil/shaders/fx/gpencil_fx_rim_resolve_frag.glsl
new file mode 100644
index 00000000000..5e5edbd8325
--- /dev/null
+++ b/source/blender/draw/engines/gpencil/shaders/fx/gpencil_fx_rim_resolve_frag.glsl
@@ -0,0 +1,101 @@
+/* ******************************************************************* */
+/* Resolve RIM pass and add blur if needed */
+/* ******************************************************************* */
+uniform sampler2D strokeColor;
+uniform sampler2D strokeDepth;
+uniform sampler2D strokeRim;
+
+uniform vec3 mask_color;
+uniform int mode;
+
+out vec4 FragColor;
+
+#define MODE_NORMAL 0
+#define MODE_OVERLAY 1
+#define MODE_ADD 2
+#define MODE_SUB 3
+#define MODE_MULTIPLY 4
+#define MODE_DIVIDE 5
+
+float overlay_color(float a, float b)
+{
+ float rtn;
+ if (a < 0.5) {
+ rtn = 2.0 * a * b;
+ }
+ else {
+ rtn = 1.0 - 2.0 * (1.0 - a) * (1.0 - b);
+ }
+
+ return rtn;
+}
+
+vec4 get_blend_color(int mode, vec4 src_color, vec4 mix_color)
+{
+ vec4 outcolor;
+ if (mode == MODE_NORMAL) {
+ outcolor = mix_color;
+ }
+ else if (mode == MODE_OVERLAY) {
+ outcolor.r = overlay_color(src_color.r, mix_color.r);
+ outcolor.g = overlay_color(src_color.g, mix_color.g);
+ outcolor.b = overlay_color(src_color.b, mix_color.b);
+ }
+ else if (mode == MODE_ADD){
+ outcolor = src_color + mix_color;
+ }
+ else if (mode == MODE_SUB){
+ outcolor = src_color - mix_color;
+ }
+ else if (mode == MODE_MULTIPLY) {
+ outcolor = src_color * mix_color;
+ }
+ else if (mode == MODE_DIVIDE) {
+ outcolor = src_color / mix_color;
+ }
+ else {
+ outcolor = mix_color;
+ }
+
+ /* use always the alpha of source color */
+
+ outcolor.a = src_color.a;
+ /* use alpha to calculate the weight of the mixed color */
+ outcolor = mix(src_color, outcolor, mix_color.a);
+
+ return outcolor;
+}
+
+void main()
+{
+ ivec2 uv = ivec2(gl_FragCoord.xy);
+
+ float stroke_depth = texelFetch(strokeDepth, uv.xy, 0).r;
+ vec4 src_pixel= texelFetch(strokeColor, uv.xy, 0);
+ vec4 rim_pixel= texelFetch(strokeRim, uv.xy, 0);
+
+ vec4 outcolor = src_pixel;
+
+ /* is transparent */
+ if (src_pixel.a == 0.0f) {
+ discard;
+ }
+ /* pixel is equal to mask color, keep */
+ else if (src_pixel.rgb == mask_color.rgb) {
+ outcolor = src_pixel;
+ }
+ else {
+ if (rim_pixel.a == 0.0f) {
+ outcolor = src_pixel;
+ }
+ else {
+ outcolor = get_blend_color(mode, src_pixel, rim_pixel);
+ }
+ }
+
+ gl_FragDepth = stroke_depth;
+ FragColor = outcolor;
+}
+
+
+
diff --git a/source/blender/draw/engines/gpencil/shaders/fx/gpencil_fx_swirl_frag.glsl b/source/blender/draw/engines/gpencil/shaders/fx/gpencil_fx_swirl_frag.glsl
new file mode 100644
index 00000000000..6ce64350b3d
--- /dev/null
+++ b/source/blender/draw/engines/gpencil/shaders/fx/gpencil_fx_swirl_frag.glsl
@@ -0,0 +1,70 @@
+uniform mat4 ProjectionMatrix;
+uniform mat4 ViewMatrix;
+
+uniform sampler2D strokeColor;
+uniform sampler2D strokeDepth;
+
+uniform vec2 Viewport;
+uniform vec3 loc;
+uniform int radius;
+uniform float angle;
+uniform int transparent;
+
+uniform float pixsize; /* rv3d->pixsize */
+uniform float pixelsize; /* U.pixelsize */
+uniform float pixfactor;
+
+out vec4 FragColor;
+
+float defaultpixsize = pixsize * pixelsize * (1000.0 / pixfactor);
+
+/* project 3d point to 2d on screen space */
+vec2 toScreenSpace(vec4 vertex)
+{
+ /* need to calculate ndc because this is not done by vertex shader */
+ vec3 ndc = vec3(vertex).xyz / vertex.w;
+
+ vec2 sc;
+ sc.x = ((ndc.x + 1.0) / 2.0) * Viewport.x;
+ sc.y = ((ndc.y + 1.0) / 2.0) * Viewport.y;
+
+ return sc;
+}
+
+/* This swirl shader is a modified version of original Geeks3d.com code */
+void main()
+{
+ vec2 uv = vec2(gl_FragCoord.xy);
+ float stroke_depth;
+ vec4 outcolor;
+
+ vec4 center3d = ProjectionMatrix * ViewMatrix * vec4(loc.xyz, 1.0);
+ vec2 center = toScreenSpace(center3d);
+ vec2 tc = uv - center;
+
+ float dist = length(tc);
+ float pxradius = (ProjectionMatrix[3][3] == 0.0) ? (radius / (loc.z * defaultpixsize)) : (radius / defaultpixsize);
+ pxradius = max(pxradius, 1);
+
+ if (dist <= pxradius) {
+ float percent = (pxradius - dist) / pxradius;
+ float theta = percent * percent * angle * 8.0;
+ float s = sin(theta);
+ float c = cos(theta);
+ tc = vec2(dot(tc, vec2(c, -s)), dot(tc, vec2(s, c)));
+ tc += center;
+
+ stroke_depth = texelFetch(strokeDepth, ivec2(tc), 0).r;
+ outcolor = texelFetch(strokeColor, ivec2(tc), 0);
+ }
+ else {
+ if (transparent == 1) {
+ discard;
+ }
+ stroke_depth = texelFetch(strokeDepth, ivec2(uv), 0).r;
+ outcolor = texelFetch(strokeColor, ivec2(uv), 0);
+ }
+
+ gl_FragDepth = stroke_depth;
+ FragColor = outcolor;
+}
diff --git a/source/blender/draw/engines/gpencil/shaders/fx/gpencil_fx_wave_frag.glsl b/source/blender/draw/engines/gpencil/shaders/fx/gpencil_fx_wave_frag.glsl
new file mode 100644
index 00000000000..882b2cf59f1
--- /dev/null
+++ b/source/blender/draw/engines/gpencil/shaders/fx/gpencil_fx_wave_frag.glsl
@@ -0,0 +1,40 @@
+
+out vec4 FragColor;
+
+uniform sampler2D strokeColor;
+uniform sampler2D strokeDepth;
+
+uniform float amplitude;
+uniform float period;
+uniform float phase;
+uniform int orientation;
+uniform vec2 wsize;
+
+#define M_PI 3.1415926535897932384626433832795
+
+#define HORIZONTAL 0
+#define VERTICAL 1
+
+void main()
+{
+ vec4 outcolor;
+ ivec2 uv = ivec2(gl_FragCoord.xy);
+ float stroke_depth;
+
+ float value;
+ if (orientation == HORIZONTAL) {
+ float pval = (uv.x * M_PI) / wsize[0];
+ value = amplitude * sin((period * pval) + phase);
+ outcolor = texelFetch(strokeColor, ivec2(uv.x, uv.y + value), 0);
+ stroke_depth = texelFetch(strokeDepth, ivec2(uv.x, uv.y + value), 0).r;
+ }
+ else {
+ float pval = (uv.y * M_PI) / wsize[1];
+ value = amplitude * sin((period * pval) + phase);
+ outcolor = texelFetch(strokeColor, ivec2(uv.x + value, uv.y), 0);
+ stroke_depth = texelFetch(strokeDepth, ivec2(uv.x + value, uv.y), 0).r;
+ }
+
+ FragColor = outcolor;
+ gl_FragDepth = stroke_depth;
+}
diff --git a/source/blender/draw/engines/gpencil/shaders/gpencil_background_frag.glsl b/source/blender/draw/engines/gpencil/shaders/gpencil_background_frag.glsl
new file mode 100644
index 00000000000..cbd7a461dd3
--- /dev/null
+++ b/source/blender/draw/engines/gpencil/shaders/gpencil_background_frag.glsl
@@ -0,0 +1,12 @@
+out vec4 FragColor;
+
+uniform sampler2D strokeColor;
+uniform sampler2D strokeDepth;
+
+void main()
+{
+ ivec2 uv = ivec2(gl_FragCoord.xy);
+
+ gl_FragDepth = texelFetch(strokeDepth, uv, 0).r;
+ FragColor = texelFetch(strokeColor, uv, 0);
+}
diff --git a/source/blender/draw/engines/gpencil/shaders/gpencil_edit_point_frag.glsl b/source/blender/draw/engines/gpencil/shaders/gpencil_edit_point_frag.glsl
new file mode 100644
index 00000000000..b3bd8e488f2
--- /dev/null
+++ b/source/blender/draw/engines/gpencil/shaders/gpencil_edit_point_frag.glsl
@@ -0,0 +1,17 @@
+in vec4 mColor;
+in vec2 mTexCoord;
+out vec4 fragColor;
+
+void main()
+{
+ vec2 centered = mTexCoord - vec2(0.5);
+ float dist_squared = dot(centered, centered);
+ const float rad_squared = 0.25;
+
+ // round point with jaggy edges
+ if (dist_squared > rad_squared) {
+ discard;
+ }
+
+ fragColor = mColor;
+}
diff --git a/source/blender/draw/engines/gpencil/shaders/gpencil_edit_point_geom.glsl b/source/blender/draw/engines/gpencil/shaders/gpencil_edit_point_geom.glsl
new file mode 100644
index 00000000000..0d2da00db66
--- /dev/null
+++ b/source/blender/draw/engines/gpencil/shaders/gpencil_edit_point_geom.glsl
@@ -0,0 +1,48 @@
+uniform mat4 ModelViewProjectionMatrix;
+uniform vec2 Viewport;
+
+layout(points) in;
+layout(triangle_strip, max_vertices = 4) out;
+
+in vec4 finalColor[1];
+in float finalThickness[1];
+
+out vec4 mColor;
+out vec2 mTexCoord;
+
+/* project 3d point to 2d on screen space */
+vec2 toScreenSpace(vec4 vertex)
+{
+ return vec2(vertex.xy / vertex.w) * Viewport;
+}
+
+void main(void)
+{
+ vec4 P0 = gl_in[0].gl_Position;
+ vec2 sp0 = toScreenSpace(P0);
+
+ float size = finalThickness[0];
+
+ /* generate the triangle strip */
+ mTexCoord = vec2(0, 1);
+ mColor = finalColor[0];
+ gl_Position = vec4(vec2(sp0.x - size, sp0.y + size) / Viewport, 0, 1.0);
+ EmitVertex();
+
+ mTexCoord = vec2(0, 0);
+ mColor = finalColor[0];
+ gl_Position = vec4(vec2(sp0.x - size, sp0.y - size) / Viewport, 0, 1.0);
+ EmitVertex();
+
+ mTexCoord = vec2(1, 1);
+ mColor = finalColor[0];
+ gl_Position = vec4(vec2(sp0.x + size, sp0.y + size) / Viewport, 0, 1.0);
+ EmitVertex();
+
+ mTexCoord = vec2(1, 0);
+ mColor = finalColor[0];
+ gl_Position = vec4(vec2(sp0.x + size, sp0.y - size) / Viewport, 0, 1.0);
+ EmitVertex();
+
+ EndPrimitive();
+}
diff --git a/source/blender/draw/engines/gpencil/shaders/gpencil_edit_point_vert.glsl b/source/blender/draw/engines/gpencil/shaders/gpencil_edit_point_vert.glsl
new file mode 100644
index 00000000000..77fdf58bea0
--- /dev/null
+++ b/source/blender/draw/engines/gpencil/shaders/gpencil_edit_point_vert.glsl
@@ -0,0 +1,15 @@
+uniform mat4 ModelViewProjectionMatrix;
+
+in vec3 pos;
+in vec4 color;
+in float size;
+
+out vec4 finalColor;
+out float finalThickness;
+
+void main()
+{
+ gl_Position = ModelViewProjectionMatrix * vec4( pos, 1.0 );
+ finalColor = color;
+ finalThickness = size;
+}
diff --git a/source/blender/draw/engines/gpencil/shaders/gpencil_fill_frag.glsl b/source/blender/draw/engines/gpencil/shaders/gpencil_fill_frag.glsl
new file mode 100644
index 00000000000..35f47d6c418
--- /dev/null
+++ b/source/blender/draw/engines/gpencil/shaders/gpencil_fill_frag.glsl
@@ -0,0 +1,140 @@
+uniform vec4 color2;
+uniform int fill_type;
+uniform float mix_factor;
+
+uniform float gradient_angle;
+uniform float gradient_radius;
+uniform float pattern_gridsize;
+uniform vec2 gradient_scale;
+uniform vec2 gradient_shift;
+
+uniform float texture_angle;
+uniform vec2 texture_scale;
+uniform vec2 texture_offset;
+uniform int texture_mix;
+uniform int texture_flip;
+uniform float texture_opacity;
+uniform int xraymode;
+
+uniform sampler2D myTexture;
+uniform int texture_clamp;
+
+/* keep this list synchronized with list in gpencil_draw_utils.c */
+#define SOLID 0
+#define GRADIENT 1
+#define RADIAL 2
+#define CHESS 3
+#define TEXTURE 4
+#define PATTERN 5
+
+#define GP_XRAY_FRONT 0
+#define GP_XRAY_3DSPACE 1
+#define GP_XRAY_BACK 2
+
+in vec4 finalColor;
+in vec2 texCoord_interp;
+out vec4 fragColor;
+#define texture2D texture
+
+void set_color(in vec4 color, in vec4 color2, in vec4 tcolor, in float mixv, in float factor,
+ in int tmix, in int flip, out vec4 ocolor)
+{
+ /* full color A */
+ if (mixv == 1.0) {
+ if (tmix == 1) {
+ ocolor = (flip == 0) ? color : tcolor;
+ }
+ else {
+ ocolor = (flip == 0) ? color : color2;
+ }
+ }
+ /* full color B */
+ else if (mixv == 0.0) {
+ if (tmix == 1) {
+ ocolor = (flip == 0) ? tcolor : color;
+ }
+ else {
+ ocolor = (flip == 0) ? color2 : color;
+ }
+ }
+ /* mix of colors */
+ else {
+ if (tmix == 1) {
+ ocolor = (flip == 0) ? mix(color, tcolor, factor) : mix(tcolor, color, factor);
+ }
+ else {
+ ocolor = (flip == 0) ? mix(color, color2, factor) : mix(color2, color, factor);
+ }
+ }
+}
+
+void main()
+{
+ vec2 t_center = vec2(0.5, 0.5);
+ mat2 matrot_tex = mat2(cos(texture_angle), -sin(texture_angle), sin(texture_angle), cos(texture_angle));
+ vec2 rot_tex = (matrot_tex * (texCoord_interp - t_center)) + t_center + texture_offset;
+ vec4 tmp_color;
+ tmp_color = (texture_clamp == 0) ? texture2D(myTexture, rot_tex * texture_scale) : texture2D(myTexture, clamp(rot_tex * texture_scale, 0.0, 1.0));
+ vec4 text_color = vec4(tmp_color[0], tmp_color[1], tmp_color[2], tmp_color[3] * texture_opacity);
+ vec4 chesscolor;
+
+ /* solid fill */
+ if (fill_type == SOLID) {
+ fragColor = finalColor;
+ }
+ else {
+ vec2 center = vec2(0.5, 0.5) + gradient_shift;
+ mat2 matrot = mat2(cos(gradient_angle), -sin(gradient_angle), sin(gradient_angle), cos(gradient_angle));
+ vec2 rot = (((matrot * (texCoord_interp - center)) + center) * gradient_scale) + gradient_shift;
+ /* gradient */
+ if (fill_type == GRADIENT) {
+ set_color(finalColor, color2, text_color, mix_factor, rot.x - mix_factor + 0.5, texture_mix, texture_flip, fragColor);
+ }
+ /* radial gradient */
+ if (fill_type == RADIAL) {
+ float in_rad = gradient_radius * mix_factor;
+ float ex_rad = gradient_radius - in_rad;
+ float intensity = 0;
+ float distance = length((center - texCoord_interp) * gradient_scale);
+ if (distance > gradient_radius) {
+ discard;
+ }
+ if (distance > in_rad) {
+ intensity = clamp(((distance - in_rad) / ex_rad), 0.0, 1.0);
+ }
+ set_color(finalColor, color2, text_color, mix_factor, intensity, texture_mix, texture_flip, fragColor);
+ }
+ /* chessboard */
+ if (fill_type == CHESS) {
+ vec2 pos = rot / pattern_gridsize;
+ if ((fract(pos.x) < 0.5 && fract(pos.y) < 0.5) || (fract(pos.x) > 0.5 && fract(pos.y) > 0.5)) {
+ chesscolor = (texture_flip == 0) ? finalColor : color2;
+ }
+ else {
+ chesscolor = (texture_flip == 0) ? color2 : finalColor;
+ }
+ /* mix with texture */
+ fragColor = (texture_mix == 1) ? mix(chesscolor, text_color, mix_factor) : chesscolor;
+ }
+ /* texture */
+ if (fill_type == TEXTURE) {
+ fragColor = (texture_mix == 1) ? mix(text_color, finalColor, mix_factor) : text_color;
+ }
+ /* pattern */
+ if (fill_type == PATTERN) {
+ fragColor = finalColor;
+ fragColor.a = min(text_color.a, finalColor.a);
+ }
+ }
+
+ /* set zdepth */
+ if (xraymode == GP_XRAY_FRONT) {
+ gl_FragDepth = 0.0;
+ }
+ if (xraymode == GP_XRAY_3DSPACE) {
+ gl_FragDepth = gl_FragCoord.z;
+ }
+ if (xraymode == GP_XRAY_BACK) {
+ gl_FragDepth = 0.999999;
+ }
+}
diff --git a/source/blender/draw/engines/gpencil/shaders/gpencil_fill_vert.glsl b/source/blender/draw/engines/gpencil/shaders/gpencil_fill_vert.glsl
new file mode 100644
index 00000000000..52da354a562
--- /dev/null
+++ b/source/blender/draw/engines/gpencil/shaders/gpencil_fill_vert.glsl
@@ -0,0 +1,14 @@
+uniform mat4 ModelViewProjectionMatrix;
+
+in vec3 pos;
+in vec4 color;
+in vec2 texCoord;
+out vec4 finalColor;
+out vec2 texCoord_interp;
+
+void main(void)
+{
+ gl_Position = ModelViewProjectionMatrix * vec4( pos, 1.0 );
+ finalColor = color;
+ texCoord_interp = texCoord;
+}
diff --git a/source/blender/draw/engines/gpencil/shaders/gpencil_paper_frag.glsl b/source/blender/draw/engines/gpencil/shaders/gpencil_paper_frag.glsl
new file mode 100644
index 00000000000..c2e3f787bec
--- /dev/null
+++ b/source/blender/draw/engines/gpencil/shaders/gpencil_paper_frag.glsl
@@ -0,0 +1,9 @@
+uniform vec3 color;
+uniform float opacity;
+
+out vec4 FragColor;
+
+void main()
+{
+ FragColor = vec4(color, opacity);
+}
diff --git a/source/blender/draw/engines/gpencil/shaders/gpencil_point_frag.glsl b/source/blender/draw/engines/gpencil/shaders/gpencil_point_frag.glsl
new file mode 100644
index 00000000000..0d6d2b22a55
--- /dev/null
+++ b/source/blender/draw/engines/gpencil/shaders/gpencil_point_frag.glsl
@@ -0,0 +1,49 @@
+uniform int color_type;
+uniform int mode;
+uniform sampler2D myTexture;
+
+in vec4 mColor;
+in vec2 mTexCoord;
+out vec4 fragColor;
+
+#define texture2D texture
+
+#define GPENCIL_MODE_LINE 0
+#define GPENCIL_MODE_DOTS 1
+#define GPENCIL_MODE_BOX 2
+
+/* keep this list synchronized with list in gpencil_engine.h */
+#define GPENCIL_COLOR_SOLID 0
+#define GPENCIL_COLOR_TEXTURE 1
+#define GPENCIL_COLOR_PATTERN 2
+
+void main()
+{
+ vec2 centered = mTexCoord - vec2(0.5);
+ float dist_squared = dot(centered, centered);
+ const float rad_squared = 0.25;
+
+ // round point with jaggy edges
+ if ((mode != GPENCIL_MODE_BOX) && (dist_squared > rad_squared))
+ discard;
+
+ vec4 tmp_color = texture2D(myTexture, mTexCoord);
+
+ /* Solid */
+ if (color_type == GPENCIL_COLOR_SOLID) {
+ fragColor = mColor;
+ }
+ /* texture */
+ if (color_type == GPENCIL_COLOR_TEXTURE) {
+ fragColor = texture2D(myTexture, mTexCoord);
+ /* mult both alpha factor to use strength factor with texture */
+ fragColor.a = min(fragColor.a * mColor.a, fragColor.a);
+ }
+ /* pattern */
+ if (color_type == GPENCIL_COLOR_PATTERN) {
+ vec4 text_color = texture2D(myTexture, mTexCoord);
+ fragColor = mColor;
+ /* mult both alpha factor to use strength factor with color alpha limit */
+ fragColor.a = min(text_color.a * mColor.a, mColor.a);
+ }
+}
diff --git a/source/blender/draw/engines/gpencil/shaders/gpencil_point_geom.glsl b/source/blender/draw/engines/gpencil/shaders/gpencil_point_geom.glsl
new file mode 100644
index 00000000000..f092149430c
--- /dev/null
+++ b/source/blender/draw/engines/gpencil/shaders/gpencil_point_geom.glsl
@@ -0,0 +1,82 @@
+uniform mat4 ModelViewProjectionMatrix;
+uniform vec2 Viewport;
+uniform int xraymode;
+
+layout(points) in;
+layout(triangle_strip, max_vertices = 4) out;
+
+in vec4 finalColor[1];
+in float finalThickness[1];
+in vec2 finaluvdata[1];
+
+out vec4 mColor;
+out vec2 mTexCoord;
+
+#define GP_XRAY_FRONT 0
+#define GP_XRAY_3DSPACE 1
+#define GP_XRAY_BACK 2
+
+/* project 3d point to 2d on screen space */
+vec2 toScreenSpace(vec4 vertex)
+{
+ return vec2(vertex.xy / vertex.w) * Viewport;
+}
+
+/* get zdepth value */
+float getZdepth(vec4 point)
+{
+ if (xraymode == GP_XRAY_FRONT) {
+ return 0.0;
+ }
+ if (xraymode == GP_XRAY_3DSPACE) {
+ return (point.z / point.w);
+ }
+ if (xraymode == GP_XRAY_BACK) {
+ return 0.999999;
+ }
+
+ /* in front by default */
+ return 0.0;
+}
+
+vec2 rotateUV(vec2 uv, float angle)
+{
+ /* translate center of rotation to the center of texture */
+ vec2 new_uv = uv - vec2(0.5f, 0.5f);
+ vec2 rot_uv;
+ rot_uv.x = new_uv.x * cos(angle) - new_uv.y * sin(angle);
+ rot_uv.y = new_uv.y * cos(angle) + new_uv.x * sin(angle);
+ return rot_uv + vec2(0.5f, 0.5f);
+}
+
+void main(void)
+{
+ /* receive 4 points */
+ vec4 P0 = gl_in[0].gl_Position;
+ vec2 sp0 = toScreenSpace(P0);
+
+ float size = finalThickness[0];
+ float aspect = 1.0;
+ /* generate the triangle strip */
+ mTexCoord = rotateUV(vec2(0, 1), finaluvdata[0].y);
+ mColor = finalColor[0];
+ gl_Position = vec4(vec2(sp0.x - size, sp0.y + size * aspect) / Viewport, getZdepth(P0), 1.0);
+ EmitVertex();
+
+ mTexCoord = rotateUV(vec2(0, 0), finaluvdata[0].y);
+ mColor = finalColor[0];
+ gl_Position = vec4(vec2(sp0.x - size, sp0.y - size * aspect) / Viewport, getZdepth(P0), 1.0);
+ EmitVertex();
+
+ mTexCoord = rotateUV(vec2(1, 1), finaluvdata[0].y);
+ mColor = finalColor[0];
+ gl_Position = vec4(vec2(sp0.x + size, sp0.y + size * aspect) / Viewport, getZdepth(P0), 1.0);
+ EmitVertex();
+
+ mTexCoord = rotateUV(vec2(1, 0), finaluvdata[0].y);
+ mColor = finalColor[0];
+ gl_Position = vec4(vec2(sp0.x + size, sp0.y - size * aspect) / Viewport, getZdepth(P0), 1.0);
+ EmitVertex();
+
+ EndPrimitive();
+}
diff --git a/source/blender/draw/engines/gpencil/shaders/gpencil_point_vert.glsl b/source/blender/draw/engines/gpencil/shaders/gpencil_point_vert.glsl
new file mode 100644
index 00000000000..5e89bf8e5ce
--- /dev/null
+++ b/source/blender/draw/engines/gpencil/shaders/gpencil_point_vert.glsl
@@ -0,0 +1,37 @@
+uniform mat4 ModelViewProjectionMatrix;
+uniform mat4 ProjectionMatrix;
+
+uniform float pixsize; /* rv3d->pixsize */
+uniform float pixelsize; /* U.pixelsize */
+uniform int keep_size;
+uniform float objscale;
+uniform float pixfactor;
+
+in vec3 pos;
+in vec4 color;
+in float thickness;
+in vec2 uvdata;
+
+out vec4 finalColor;
+out float finalThickness;
+out vec2 finaluvdata;
+
+#define TRUE 1
+
+float defaultpixsize = pixsize * pixelsize * (1000.0 / pixfactor);
+
+void main()
+{
+ gl_Position = ModelViewProjectionMatrix * vec4( pos, 1.0 );
+ finalColor = color;
+
+ if (keep_size == TRUE) {
+ finalThickness = thickness;
+ }
+ else {
+ float size = (ProjectionMatrix[3][3] == 0.0) ? (thickness / (gl_Position.z * defaultpixsize)) : (thickness / defaultpixsize);
+ finalThickness = max(size * objscale, 4.0); /* minimum 4 pixels */
+ }
+
+ finaluvdata = uvdata;
+}
diff --git a/source/blender/draw/engines/gpencil/shaders/gpencil_simple_mix_frag.glsl b/source/blender/draw/engines/gpencil/shaders/gpencil_simple_mix_frag.glsl
new file mode 100644
index 00000000000..dd54e38c3d0
--- /dev/null
+++ b/source/blender/draw/engines/gpencil/shaders/gpencil_simple_mix_frag.glsl
@@ -0,0 +1,15 @@
+in vec4 uvcoordsvar;
+
+out vec4 FragColor;
+
+uniform sampler2D strokeColor;
+uniform sampler2D strokeDepth;
+void main()
+{
+ ivec2 uv = ivec2(gl_FragCoord.xy);
+ float stroke_depth = texelFetch(strokeDepth, uv, 0).r;
+ vec4 stroke_color = texelFetch(strokeColor, uv, 0).rgba;
+
+ FragColor = stroke_color;
+ gl_FragDepth = stroke_depth;
+}
diff --git a/source/blender/draw/engines/gpencil/shaders/gpencil_stroke_frag.glsl b/source/blender/draw/engines/gpencil/shaders/gpencil_stroke_frag.glsl
new file mode 100644
index 00000000000..d57921c1629
--- /dev/null
+++ b/source/blender/draw/engines/gpencil/shaders/gpencil_stroke_frag.glsl
@@ -0,0 +1,46 @@
+uniform int color_type;
+uniform sampler2D myTexture;
+
+in vec4 mColor;
+in vec2 mTexCoord;
+in float uvfac;
+
+out vec4 fragColor;
+
+#define texture2D texture
+
+/* keep this list synchronized with list in gpencil_engine.h */
+#define GPENCIL_COLOR_SOLID 0
+#define GPENCIL_COLOR_TEXTURE 1
+#define GPENCIL_COLOR_PATTERN 2
+
+void main()
+{
+ vec4 tColor = vec4(mColor);
+ /* if alpha < 0, then encap (only solid mode ) */
+ if ((mColor.a < 0) && (color_type == GPENCIL_COLOR_SOLID)) {
+ vec2 center = vec2(uvfac, 1.0);
+ tColor.a = tColor.a * -1.0;
+ float dist = length(mTexCoord - center);
+ if (dist > 0.50) {
+ discard;
+ }
+ }
+ /* Solid */
+ if (color_type == GPENCIL_COLOR_SOLID) {
+ fragColor = tColor;
+ }
+ /* texture */
+ if (color_type == GPENCIL_COLOR_TEXTURE) {
+ fragColor = texture2D(myTexture, mTexCoord);
+ /* mult both alpha factor to use strength factor */
+ fragColor.a = min(fragColor.a * tColor.a, fragColor.a);
+ }
+ /* pattern */
+ if (color_type == GPENCIL_COLOR_PATTERN) {
+ vec4 text_color = texture2D(myTexture, mTexCoord);
+ fragColor = tColor;
+ /* mult both alpha factor to use strength factor with color alpha limit */
+ fragColor.a = min(text_color.a * tColor.a, tColor.a);
+ }
+}
diff --git a/source/blender/draw/engines/gpencil/shaders/gpencil_stroke_geom.glsl b/source/blender/draw/engines/gpencil/shaders/gpencil_stroke_geom.glsl
new file mode 100644
index 00000000000..0bcfe8cddb7
--- /dev/null
+++ b/source/blender/draw/engines/gpencil/shaders/gpencil_stroke_geom.glsl
@@ -0,0 +1,208 @@
+uniform mat4 ModelViewProjectionMatrix;
+uniform vec2 Viewport;
+uniform int xraymode;
+uniform int color_type;
+
+layout(lines_adjacency) in;
+layout(triangle_strip, max_vertices = 13) out;
+
+in vec4 finalColor[4];
+in float finalThickness[4];
+in vec2 finaluvdata[4];
+
+out vec4 mColor;
+out vec2 mTexCoord;
+out float uvfac;
+
+#define GP_XRAY_FRONT 0
+#define GP_XRAY_3DSPACE 1
+#define GP_XRAY_BACK 2
+
+/* keep this list synchronized with list in gpencil_engine.h */
+#define GPENCIL_COLOR_SOLID 0
+#define GPENCIL_COLOR_TEXTURE 1
+#define GPENCIL_COLOR_PATTERN 2
+
+/* project 3d point to 2d on screen space */
+vec2 toScreenSpace(vec4 vertex)
+{
+ return vec2(vertex.xy / vertex.w) * Viewport;
+}
+
+/* get zdepth value */
+float getZdepth(vec4 point)
+{
+ if (xraymode == GP_XRAY_FRONT) {
+ return 0.0;
+ }
+ if (xraymode == GP_XRAY_3DSPACE) {
+ return (point.z / point.w);
+ }
+ if (xraymode == GP_XRAY_BACK) {
+ return 0.999999;
+ }
+
+ /* in front by default */
+ return 0.0;
+}
+void main(void)
+{
+ float MiterLimit = 0.75;
+ uvfac = 0;
+
+ /* receive 4 points */
+ vec4 P0 = gl_in[0].gl_Position;
+ vec4 P1 = gl_in[1].gl_Position;
+ vec4 P2 = gl_in[2].gl_Position;
+ vec4 P3 = gl_in[3].gl_Position;
+
+ /* get the four vertices passed to the shader */
+ vec2 sp0 = toScreenSpace(P0); // start of previous segment
+ vec2 sp1 = toScreenSpace(P1); // end of previous segment, start of current segment
+ vec2 sp2 = toScreenSpace(P2); // end of current segment, start of next segment
+ vec2 sp3 = toScreenSpace(P3); // end of next segment
+
+ /* culling outside viewport */
+ vec2 area = Viewport * 4.0;
+ if (sp1.x < -area.x || sp1.x > area.x) return;
+ if (sp1.y < -area.y || sp1.y > area.y) return;
+ if (sp2.x < -area.x || sp2.x > area.x) return;
+ if (sp2.y < -area.y || sp2.y > area.y) return;
+
+ /* determine the direction of each of the 3 segments (previous, current, next) */
+ vec2 v0 = normalize(sp1 - sp0);
+ vec2 v1 = normalize(sp2 - sp1);
+ vec2 v2 = normalize(sp3 - sp2);
+
+ /* determine the normal of each of the 3 segments (previous, current, next) */
+ vec2 n0 = vec2(-v0.y, v0.x);
+ vec2 n1 = vec2(-v1.y, v1.x);
+ vec2 n2 = vec2(-v2.y, v2.x);
+
+ /* determine miter lines by averaging the normals of the 2 segments */
+ vec2 miter_a = normalize(n0 + n1); // miter at start of current segment
+ vec2 miter_b = normalize(n1 + n2); // miter at end of current segment
+
+ /* determine the length of the miter by projecting it onto normal and then inverse it */
+ float an1 = dot(miter_a, n1);
+ float bn1 = dot(miter_b, n2);
+ if (an1 == 0) an1 = 1;
+ if (bn1 == 0) bn1 = 1;
+ float length_a = finalThickness[1] / an1;
+ float length_b = finalThickness[2] / bn1;
+ if (length_a <= 0.0) length_a = 0.01;
+ if (length_b <= 0.0) length_b = 0.01;
+
+ /* prevent excessively long miters at sharp corners */
+ if (dot(v0, v1) < -MiterLimit) {
+ miter_a = n1;
+ length_a = finalThickness[1];
+
+ /* close the gap */
+ if (dot(v0, n1) > 0) {
+ mTexCoord = vec2(0, 0);
+ mColor = finalColor[1];
+ gl_Position = vec4((sp1 + finalThickness[1] * n0) / Viewport, getZdepth(P1), 1.0);
+ EmitVertex();
+
+ mTexCoord = vec2(0, 0);
+ mColor = finalColor[1];
+ gl_Position = vec4((sp1 + finalThickness[1] * n1) / Viewport, getZdepth(P1), 1.0);
+ EmitVertex();
+
+ mTexCoord = vec2(0, 0.5);
+ mColor = finalColor[1];
+ gl_Position = vec4(sp1 / Viewport, getZdepth(P1), 1.0);
+ EmitVertex();
+
+ EndPrimitive();
+ }
+ else {
+ mTexCoord = vec2(0, 1);
+ mColor = finalColor[1];
+ gl_Position = vec4((sp1 - finalThickness[1] * n1) / Viewport, getZdepth(P1), 1.0);
+ EmitVertex();
+
+ mTexCoord = vec2(0, 1);
+ mColor = finalColor[1];
+ gl_Position = vec4((sp1 - finalThickness[1] * n0) / Viewport, getZdepth(P1), 1.0);
+ EmitVertex();
+
+ mTexCoord = vec2(0, 0.5);
+ mColor = finalColor[1];
+ gl_Position = vec4(sp1 / Viewport, getZdepth(P1), 1.0);
+ EmitVertex();
+
+ EndPrimitive();
+ }
+ }
+
+ if (dot(v1, v2) < -MiterLimit) {
+ miter_b = n1;
+ length_b = finalThickness[2];
+ }
+
+ /* generate the start endcap (alpha < 0 used as endcap flag)*/
+ if ((P0 == P2) && (color_type == GPENCIL_COLOR_SOLID)){
+ mTexCoord = vec2(2, 1);
+ mColor = vec4(finalColor[1].rgb, finalColor[1].a * -1.0) ;
+ vec2 svn1 = normalize(sp1 - sp2) * length_a * 4.0;
+ gl_Position = vec4((sp1 + svn1) / Viewport, getZdepth(P1), 1.0);
+ EmitVertex();
+
+ mTexCoord = vec2(0, 0);
+ mColor = vec4(finalColor[1].rgb, finalColor[1].a * -1.0) ;
+ gl_Position = vec4((sp1 - (length_a * 2.0) * miter_a) / Viewport, getZdepth(P1), 1.0);
+ EmitVertex();
+
+ mTexCoord = vec2(0, 2);
+ mColor = vec4(finalColor[1].rgb, finalColor[1].a * -1.0) ;
+ gl_Position = vec4((sp1 + (length_a * 2.0) * miter_a) / Viewport, getZdepth(P1), 1.0);
+ EmitVertex();
+ }
+
+ /* generate the triangle strip */
+ mTexCoord = (color_type == GPENCIL_COLOR_SOLID) ? vec2(0, 0) : vec2(finaluvdata[1].x, 0);
+ mColor = finalColor[1];
+ gl_Position = vec4((sp1 + length_a * miter_a) / Viewport, getZdepth(P1), 1.0);
+ EmitVertex();
+
+ mTexCoord = (color_type == GPENCIL_COLOR_SOLID) ? vec2(0, 1) : vec2(finaluvdata[1].x, 1);
+ mColor = finalColor[1];
+ gl_Position = vec4((sp1 - length_a * miter_a) / Viewport, getZdepth(P1), 1.0);
+ EmitVertex();
+
+ mTexCoord = (color_type == GPENCIL_COLOR_SOLID) ? vec2(1, 0) : vec2(finaluvdata[2].x, 0);
+ mColor = finalColor[2];
+ gl_Position = vec4((sp2 + length_b * miter_b) / Viewport, getZdepth(P2), 1.0);
+ EmitVertex();
+
+ mTexCoord = (color_type == GPENCIL_COLOR_SOLID) ? vec2(1, 1) : vec2(finaluvdata[2].x, 1);
+ mColor = finalColor[2];
+ gl_Position = vec4((sp2 - length_b * miter_b) / Viewport, getZdepth(P2), 1.0);
+ EmitVertex();
+
+ /* generate the end endcap (alpha < 0 used as endcap flag)*/
+ if ((P1 == P3) && (color_type == GPENCIL_COLOR_SOLID)){
+ mTexCoord = vec2(finaluvdata[2].x, 2);
+ mColor = vec4(finalColor[2].rgb, finalColor[2].a * -1.0) ;
+ uvfac = finaluvdata[2].x;
+ gl_Position = vec4((sp2 + (length_b * 2.0) * miter_b) / Viewport, getZdepth(P2), 1.0);
+ EmitVertex();
+
+ mTexCoord = vec2(finaluvdata[2].x, 0);
+ mColor = vec4(finalColor[2].rgb, finalColor[2].a * -1.0) ;
+ uvfac = finaluvdata[2].x;
+ gl_Position = vec4((sp2 - (length_b * 2.0) * miter_b) / Viewport, getZdepth(P2), 1.0);
+ EmitVertex();
+
+ mTexCoord = vec2(finaluvdata[2].x + 2, 1);
+ mColor = vec4(finalColor[2].rgb, finalColor[2].a * -1.0) ;
+ uvfac = finaluvdata[2].x;
+ vec2 svn2 = normalize(sp2 - sp1) * length_b * 4.0;
+ gl_Position = vec4((sp2 + svn2) / Viewport, getZdepth(P2), 1.0);
+ EmitVertex();
+ }
+
+ EndPrimitive();
+}
diff --git a/source/blender/draw/engines/gpencil/shaders/gpencil_stroke_vert.glsl b/source/blender/draw/engines/gpencil/shaders/gpencil_stroke_vert.glsl
new file mode 100644
index 00000000000..2f9a105e911
--- /dev/null
+++ b/source/blender/draw/engines/gpencil/shaders/gpencil_stroke_vert.glsl
@@ -0,0 +1,37 @@
+uniform mat4 ModelViewProjectionMatrix;
+uniform mat4 ProjectionMatrix;
+
+uniform float pixsize; /* rv3d->pixsize */
+uniform float pixelsize; /* U.pixelsize */
+uniform int keep_size;
+uniform float objscale;
+uniform float pixfactor;
+
+in vec3 pos;
+in vec4 color;
+in float thickness;
+in vec2 uvdata;
+
+out vec4 finalColor;
+out float finalThickness;
+out vec2 finaluvdata;
+
+#define TRUE 1
+
+float defaultpixsize = pixsize * pixelsize * (1000.0 / pixfactor);
+
+void main(void)
+{
+ gl_Position = ModelViewProjectionMatrix * vec4( pos, 1.0 );
+ finalColor = color;
+
+ if (keep_size == TRUE) {
+ finalThickness = thickness;
+ }
+ else {
+ float size = (ProjectionMatrix[3][3] == 0.0) ? (thickness / (gl_Position.z * defaultpixsize)) : (thickness / defaultpixsize);
+ finalThickness = max(size * objscale, 1.0);
+ }
+
+ finaluvdata = uvdata;
+}
diff --git a/source/blender/draw/engines/gpencil/shaders/gpencil_zdepth_mix_frag.glsl b/source/blender/draw/engines/gpencil/shaders/gpencil_zdepth_mix_frag.glsl
new file mode 100644
index 00000000000..0983e6c4d87
--- /dev/null
+++ b/source/blender/draw/engines/gpencil/shaders/gpencil_zdepth_mix_frag.glsl
@@ -0,0 +1,45 @@
+in vec4 uvcoordsvar;
+
+out vec4 FragColor;
+
+uniform sampler2D strokeColor;
+uniform sampler2D strokeDepth;
+uniform int tonemapping;
+
+float srgb_to_linearrgb(float c)
+{
+ if (c < 0.04045)
+ return (c < 0.0) ? 0.0 : c * (1.0 / 12.92);
+ else
+ return pow((c + 0.055) * (1.0 / 1.055), 2.4);
+}
+
+float linearrgb_to_srgb(float c)
+{
+ if (c < 0.0031308)
+ return (c < 0.0) ? 0.0 : c * 12.92;
+ else
+ return 1.055 * pow(c, 1.0 / 2.4) - 0.055;
+}
+
+void main()
+{
+ ivec2 uv = ivec2(gl_FragCoord.xy);
+ float stroke_depth = texelFetch(strokeDepth, uv, 0).r;
+ vec4 stroke_color = texelFetch(strokeColor, uv, 0).rgba;
+
+ /* premult alpha factor to remove double blend effects */
+ if (stroke_color.a > 0) {
+ stroke_color = vec4(vec3(stroke_color.rgb / stroke_color.a), stroke_color.a);
+ }
+
+ /* apply color correction for render only */
+ if (tonemapping == 1) {
+ stroke_color.r = srgb_to_linearrgb(stroke_color.r);
+ stroke_color.g = srgb_to_linearrgb(stroke_color.g);
+ stroke_color.b = srgb_to_linearrgb(stroke_color.b);
+ }
+
+ FragColor = stroke_color;
+ gl_FragDepth = stroke_depth;
+}