This repository has been archived on 2023-10-09. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
blender-archive/source/blender/draw/engines/gpencil/gpencil_engine.c
Antonio Vazquez 7f29fc7415 Fix T65691: GPencil Drawing long strokes turn invisible
There was a fixed limit to the number of points available in a buffer stroke.

Now, the array is expanded as needed using a predefined number of points for each expansion, instead to add one by one. This is done to reduce the number of times the memory allocation is required.

As part of the fix, some variables have been renamed to reflect better their use.
2019-07-30 17:11:56 +02:00

1197 lines
45 KiB
C

/*
* 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.
*
* Copyright 2017, Blender Foundation.
*/
/** \file
* \ingroup draw
*/
#include "DRW_engine.h"
#include "DRW_render.h"
#include "BKE_gpencil.h"
#include "BKE_library.h"
#include "BKE_object.h"
#include "BKE_paint.h"
#include "BKE_shader_fx.h"
#include "DNA_gpencil_types.h"
#include "DNA_view3d_types.h"
#include "draw_mode_engines.h"
#include "GPU_texture.h"
#include "gpencil_engine.h"
#include "DEG_depsgraph_query.h"
#include "ED_view3d.h"
#include "ED_screen.h"
#include "UI_resources.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[];
extern char datatoc_gpencil_blend_frag_glsl[];
extern char datatoc_common_colormanagement_lib_glsl[];
extern char datatoc_common_view_lib_glsl[];
/* *********** STATIC *********** */
static GPENCIL_e_data e_data = {NULL}; /* Engine data */
/* *********** FUNCTIONS *********** */
/* create a multisample buffer if not present */
void 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)});
}
}
}
}
static void GPENCIL_create_framebuffers(void *vedata)
{
GPENCIL_FramebufferList *fbl = ((GPENCIL_Data *)vedata)->fbl;
GPENCIL_StorageList *stl = ((GPENCIL_Data *)vedata)->stl;
GPENCIL_TextureList *txl = ((GPENCIL_Data *)vedata)->txl;
/* Go full 32bits for rendering */
eGPUTextureFormat 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->framebuffer_flag & GP_FRAMEBUFFER_MULTISAMPLE) &&
(stl->storage->multisamples > 0)) {
gpencil_multisample_ensure(vedata, size[0], size[1]);
}
/* Framebufers for basic object drawing */
if (stl->storage->framebuffer_flag & GP_FRAMEBUFFER_BASIC) {
/* temp textures for ping-pong buffers */
stl->g_data->temp_depth_tx_a = DRW_texture_pool_query_2d(
size[0], size[1], GPU_DEPTH24_STENCIL8, &draw_engine_gpencil_type);
stl->g_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(stl->g_data->temp_depth_tx_a),
GPU_ATTACHMENT_TEXTURE(stl->g_data->temp_color_tx_a),
});
stl->g_data->temp_depth_tx_b = DRW_texture_pool_query_2d(
size[0], size[1], GPU_DEPTH24_STENCIL8, &draw_engine_gpencil_type);
stl->g_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(stl->g_data->temp_depth_tx_b),
GPU_ATTACHMENT_TEXTURE(stl->g_data->temp_color_tx_b),
});
/* used for FX effects and Layer blending */
stl->g_data->temp_depth_tx_fx = DRW_texture_pool_query_2d(
size[0], size[1], GPU_DEPTH24_STENCIL8, &draw_engine_gpencil_type);
stl->g_data->temp_color_tx_fx = DRW_texture_pool_query_2d(
size[0], size[1], fb_format, &draw_engine_gpencil_type);
GPU_framebuffer_ensure_config(&fbl->temp_fb_fx,
{
GPU_ATTACHMENT_TEXTURE(stl->g_data->temp_depth_tx_fx),
GPU_ATTACHMENT_TEXTURE(stl->g_data->temp_color_tx_fx),
});
}
/* background framebuffer to speed up drawing process */
if (stl->storage->framebuffer_flag & GP_FRAMEBUFFER_DRAW) {
if (txl->background_color_tx == NULL) {
stl->storage->background_ready = false;
}
DRW_texture_ensure_2d(
&txl->background_depth_tx, size[0], size[1], GPU_DEPTH_COMPONENT24, DRW_TEX_FILTER);
DRW_texture_ensure_2d(
&txl->background_color_tx, size[0], size[1], GPU_RGBA16F, DRW_TEX_FILTER);
GPU_framebuffer_ensure_config(&fbl->background_fb,
{
GPU_ATTACHMENT_TEXTURE(txl->background_depth_tx),
GPU_ATTACHMENT_TEXTURE(txl->background_color_tx),
});
}
else {
DRW_TEXTURE_FREE_SAFE(txl->background_depth_tx);
DRW_TEXTURE_FREE_SAFE(txl->background_color_tx);
}
}
}
static void GPENCIL_create_shaders(void)
{
/* normal fill shader */
if (!e_data.gpencil_fill_sh) {
e_data.gpencil_fill_sh = GPU_shader_create_from_arrays({
.vert =
(const char *[]){datatoc_common_view_lib_glsl, datatoc_gpencil_fill_vert_glsl, NULL},
.frag = (const char *[]){datatoc_common_colormanagement_lib_glsl,
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 = GPU_shader_create_from_arrays({
.vert =
(const char *[]){datatoc_common_view_lib_glsl, datatoc_gpencil_stroke_vert_glsl, NULL},
.geom = (const char *[]){datatoc_gpencil_stroke_geom_glsl, NULL},
.frag = (const char *[]){datatoc_common_colormanagement_lib_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 = GPU_shader_create_from_arrays({
.vert =
(const char *[]){datatoc_common_view_lib_glsl, datatoc_gpencil_point_vert_glsl, NULL},
.geom = (const char *[]){datatoc_gpencil_point_geom_glsl, NULL},
.frag = (const char *[]){datatoc_common_colormanagement_lib_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_with_lib(datatoc_gpencil_edit_point_vert_glsl,
datatoc_gpencil_edit_point_geom_glsl,
datatoc_gpencil_edit_point_frag_glsl,
datatoc_common_view_lib_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);
}
/* blend */
if (!e_data.gpencil_blend_fullscreen_sh) {
e_data.gpencil_blend_fullscreen_sh = DRW_shader_create_fullscreen(
datatoc_gpencil_blend_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");
stl->storage->shade_render[0] = OB_RENDER;
stl->storage->shade_render[1] = 0;
}
stl->storage->multisamples = U.gpencil_multisamples;
/* create shaders */
GPENCIL_create_shaders();
GPENCIL_create_fx_shaders(&e_data);
}
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_blend_fullscreen_sh);
DRW_SHADER_FREE_SAFE(e_data.gpencil_background_sh);
DRW_SHADER_FREE_SAFE(e_data.gpencil_paper_sh);
/* 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;
GPENCIL_TextureList *txl = ((GPENCIL_Data *)vedata)->txl;
const DRWContextState *draw_ctx = DRW_context_state_get();
Scene *scene = draw_ctx->scene;
ToolSettings *ts = scene->toolsettings;
View3D *v3d = draw_ctx->v3d;
Brush *brush = BKE_paint_brush(&ts->gp_paint->paint);
/* 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;
/* use the brush material */
Material *ma = BKE_gpencil_object_material_get_from_brush(obact, 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(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;
/* reset textures */
stl->g_data->gpencil_blank_texture = NULL;
stl->g_data->batch_buffer_stroke = NULL;
stl->g_data->batch_buffer_fill = NULL;
stl->g_data->batch_buffer_ctrlpoint = NULL;
stl->g_data->batch_grid = NULL;
/* blank texture used if no texture defined for fill shader */
float rect[1][1][4] = {{{0.0f}}};
stl->g_data->gpencil_blank_texture = DRW_texture_create_2d(
1, 1, GPU_RGBA8, DRW_TEX_FILTER, (float *)rect);
if (!stl->shgroups) {
/* Alloc maximum size because count strokes is very slow and can be very complex due onion
* skinning.
*/
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;
stl->g_data->do_instances = false;
{
/* Stroke pass 2D */
psl->stroke_pass_2d = DRW_pass_create("GPencil Stroke Pass",
DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH |
DRW_STATE_DEPTH_ALWAYS | DRW_STATE_BLEND_ALPHA |
DRW_STATE_WRITE_STENCIL | DRW_STATE_STENCIL_NEQUAL);
stl->storage->shgroup_id = 0;
/* Stroke pass 3D */
psl->stroke_pass_3d = DRW_pass_create("GPencil Stroke Pass",
DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH |
DRW_STATE_DEPTH_LESS_EQUAL | DRW_STATE_BLEND_ALPHA |
DRW_STATE_WRITE_STENCIL | DRW_STATE_STENCIL_NEQUAL);
stl->storage->shgroup_id = 0;
/* edit pass */
psl->edit_pass = DRW_pass_create("GPencil Edit Pass",
DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_ALPHA);
/* detect if playing animation */
if (draw_ctx->evil_C) {
bool playing = ED_screen_animation_playing(CTX_wm_manager(draw_ctx->evil_C)) != NULL;
if (playing != stl->storage->is_playing) {
stl->storage->reset_cache = true;
}
stl->storage->is_playing = playing;
}
else {
stl->storage->is_playing = false;
stl->storage->reset_cache = false;
}
/* 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");
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->is_playing == true) {
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 simplify flags (can change while drawing, so it's better to save) */
stl->storage->simplify_fill = GP_SIMPLIFY_FILL(scene, stl->storage->is_playing);
stl->storage->simplify_modif = GP_SIMPLIFY_MODIF(scene, stl->storage->is_playing);
stl->storage->simplify_fx = GP_SIMPLIFY_FX(scene, stl->storage->is_playing);
stl->storage->simplify_blend = GP_SIMPLIFY_BLEND(scene, stl->storage->is_playing);
/* xray mode */
if (v3d) {
stl->storage->is_xray = XRAY_ACTIVE(v3d);
}
else {
stl->storage->is_xray = 0;
}
/* 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->is_playing == false)) {
/* need the original to avoid cow overhead while drawing */
bGPdata *gpd_orig = (bGPdata *)DEG_get_original_id(&obact_gpd->id);
if (((gpd_orig->runtime.sbuffer_sflag & GP_STROKE_ERASER) == 0) &&
(gpd_orig->runtime.sbuffer_used > 0) &&
((gpd_orig->flag & GP_DATA_STROKE_POLYGON) == 0) && !DRW_state_is_depth() &&
(stl->storage->background_ready == true)) {
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 being 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_ALPHA |
DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_ALWAYS |
DRW_STATE_WRITE_STENCIL | DRW_STATE_STENCIL_NEQUAL);
/* 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_ALPHA |
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(mix_shgrp, quad, NULL);
DRW_shgroup_uniform_texture_ref(mix_shgrp, "strokeColor", &stl->g_data->input_color_tx);
DRW_shgroup_uniform_texture_ref(mix_shgrp, "strokeDepth", &stl->g_data->input_depth_tx);
DRW_shgroup_uniform_int(mix_shgrp, "tonemapping", &stl->storage->tonemapping, 1);
DRW_shgroup_uniform_int(mix_shgrp, "do_select", &stl->storage->do_select_outline, 1);
DRW_shgroup_uniform_vec4(mix_shgrp, "select_color", stl->storage->select_color, 1);
/* Mix pass no blend used to copy between passes. A separated pass is required
* because if mix_pass is used, the accumulation 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(mix_shgrp_noblend, quad, NULL);
DRW_shgroup_uniform_texture_ref(
mix_shgrp_noblend, "strokeColor", &stl->g_data->input_color_tx);
DRW_shgroup_uniform_texture_ref(
mix_shgrp_noblend, "strokeDepth", &stl->g_data->input_depth_tx);
DRW_shgroup_uniform_int(mix_shgrp_noblend, "tonemapping", &stl->storage->tonemapping, 1);
DRW_shgroup_uniform_int(mix_shgrp_noblend, "do_select", &stl->storage->do_select_outline, 1);
DRW_shgroup_uniform_vec4(mix_shgrp_noblend, "select_color", stl->storage->select_color, 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_ALPHA |
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(background_shgrp, quad, NULL);
DRW_shgroup_uniform_texture_ref(background_shgrp, "strokeColor", &txl->background_color_tx);
DRW_shgroup_uniform_texture_ref(background_shgrp, "strokeDepth", &txl->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_ALPHA);
DRWShadingGroup *paper_shgrp = DRW_shgroup_create(e_data.gpencil_paper_sh, psl->paper_pass);
DRW_shgroup_call(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_ALPHA |
DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_ALWAYS);
stl->g_data->shgrps_grid = DRW_shgroup_create(e_data.gpencil_line_sh, psl->grid_pass);
}
/* blend layers pass */
psl->blend_pass = DRW_pass_create("GPencil Blend Layers Pass",
DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_ALPHA |
DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS);
DRWShadingGroup *blend_shgrp = DRW_shgroup_create(e_data.gpencil_blend_fullscreen_sh,
psl->blend_pass);
DRW_shgroup_call(blend_shgrp, quad, NULL);
DRW_shgroup_uniform_texture_ref(blend_shgrp, "strokeColor", &stl->g_data->temp_color_tx_a);
DRW_shgroup_uniform_texture_ref(blend_shgrp, "strokeDepth", &stl->g_data->temp_depth_tx_a);
DRW_shgroup_uniform_texture_ref(blend_shgrp, "blendColor", &stl->g_data->temp_color_tx_fx);
DRW_shgroup_uniform_texture_ref(blend_shgrp, "blendDepth", &stl->g_data->temp_depth_tx_fx);
DRW_shgroup_uniform_int(blend_shgrp, "mode", &stl->storage->blend_mode, 1);
DRW_shgroup_uniform_int(blend_shgrp, "mask_layer", &stl->storage->mask_layer, 1);
DRW_shgroup_uniform_int(mix_shgrp, "tonemapping", &stl->storage->tonemapping, 1);
/* create effects passes */
if (!stl->storage->simplify_fx) {
GPENCIL_create_fx_passes(psl);
}
}
}
static void gpencil_add_draw_data(void *vedata, Object *ob)
{
GPENCIL_StorageList *stl = ((GPENCIL_Data *)vedata)->stl;
bGPdata *gpd = (bGPdata *)ob->data;
const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd);
int i = stl->g_data->gp_cache_used - 1;
tGPencilObjectCache *cache_ob = &stl->g_data->gp_object_cache[i];
if (!cache_ob->is_dup_ob) {
/* fill shading groups */
if ((!is_multiedit) || (stl->storage->is_render)) {
gpencil_populate_datablock(&e_data, vedata, ob, cache_ob);
}
else {
gpencil_populate_multiedit(&e_data, vedata, ob, cache_ob);
}
}
/* FX passses */
cache_ob->has_fx = false;
if ((!stl->storage->simplify_fx) && (!ELEM(cache_ob->shading_type[0], OB_WIRE, OB_SOLID)) &&
(BKE_shaderfx_has_gpencil(ob))) {
cache_ob->has_fx = true;
if ((!stl->storage->simplify_fx) && (!is_multiedit)) {
gpencil_fx_prepare(&e_data, vedata, cache_ob);
}
}
}
void GPENCIL_cache_populate(void *vedata, Object *ob)
{
/* object must be visible */
if (!(DRW_object_visibility_in_active_context(ob) & OB_VISIBLE_SELF)) {
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;
const View3DCursor *cursor = &scene->cursor;
float grid_matrix[4][4];
if (ob->type == OB_GPENCIL && ob->data) {
bGPdata *gpd = (bGPdata *)ob->data;
/* enable multisample and basic framebuffer creation */
stl->storage->framebuffer_flag |= GP_FRAMEBUFFER_MULTISAMPLE;
stl->storage->framebuffer_flag |= GP_FRAMEBUFFER_BASIC;
/* when start/stop animation the cache must be set as dirty to reset all data */
if (stl->storage->reset_cache) {
gpd->flag |= GP_DATA_CACHE_IS_DIRTY;
stl->storage->reset_cache = false;
}
if ((stl->g_data->session_flag & GP_DRW_PAINT_READY) == 0) {
/* bound box object are not visible, only external box*/
if (ob->dt != OB_BOUNDBOX) {
/* save gp objects for drawing later */
stl->g_data->gp_object_cache = gpencil_object_cache_add(stl->g_data->gp_object_cache,
ob,
&stl->g_data->gp_cache_size,
&stl->g_data->gp_cache_used);
/* enable instance loop */
if (!stl->g_data->do_instances) {
tGPencilObjectCache *cache_ob =
&stl->g_data->gp_object_cache[stl->g_data->gp_cache_used - 1];
stl->g_data->do_instances = cache_ob->is_dup_ob;
}
/* load drawing data */
gpencil_add_draw_data(vedata, ob);
}
}
/* draw current painting strokes
* (only if region is equal to originated paint region)
*
* Need to use original data because to use the copy of data, the paint
* operator must update depsgraph and this makes that first events of the
* mouse are missed if the datablock is very big due the time required to
* copy the datablock. The search of the original data is faster than a
* full datablock copy.
* Using the original data doesn't require a copy and the feel when drawing
* is far better.
*/
bGPdata *gpd_orig = (bGPdata *)DEG_get_original_id(&gpd->id);
if ((draw_ctx->obact == ob) &&
((gpd_orig->runtime.ar == NULL) || (gpd_orig->runtime.ar == draw_ctx->ar))) {
gpencil_populate_buffer_strokes(&e_data, vedata, ts, ob);
}
/* grid */
if ((v3d) && ((v3d->flag2 & V3D_HIDE_OVERLAYS) == 0) && (v3d->gp_flag & V3D_GP_SHOW_GRID) &&
(ob->type == OB_GPENCIL) && (ob == draw_ctx->obact) &&
((ts->gpencil_v3d_align & GP_PROJECT_DEPTH_VIEW) == 0) &&
((ts->gpencil_v3d_align & GP_PROJECT_DEPTH_STROKE) == 0)) {
stl->g_data->batch_grid = gpencil_get_grid(ob);
/* define grid orientation */
switch (ts->gp_sculpt.lock_axis) {
case GP_LOCKAXIS_VIEW: {
/* align always to view */
invert_m4_m4(grid_matrix, draw_ctx->rv3d->viewmat);
/* copy ob location */
copy_v3_v3(grid_matrix[3], ob->obmat[3]);
break;
}
case GP_LOCKAXIS_CURSOR: {
float scale[3] = {1.0f, 1.0f, 1.0f};
loc_eul_size_to_mat4(grid_matrix, cursor->location, cursor->rotation_euler, scale);
break;
}
default: {
copy_m4_m4(grid_matrix, ob->obmat);
break;
}
}
/* Move the origin to Object or Cursor */
if (ts->gpencil_v3d_align & GP_PROJECT_CURSOR) {
copy_v3_v3(grid_matrix[3], cursor->location);
}
else {
copy_v3_v3(grid_matrix[3], ob->obmat[3]);
}
DRW_shgroup_call_obmat(stl->g_data->shgrps_grid, stl->g_data->batch_grid, grid_matrix);
}
}
}
void GPENCIL_cache_finish(void *vedata)
{
GPENCIL_StorageList *stl = ((GPENCIL_Data *)vedata)->stl;
tGPencilObjectCache *cache_ob = NULL;
Object *ob = NULL;
/* create data for instances */
if (stl->g_data->do_instances) {
GHash *gh_objects = BLI_ghash_str_new(__func__);
/* create hash of real object (non duplicated) */
for (int i = 0; i < stl->g_data->gp_cache_used; i++) {
cache_ob = &stl->g_data->gp_object_cache[i];
if (!cache_ob->is_dup_ob) {
ob = cache_ob->ob;
char *name = BKE_id_to_unique_string_key(&ob->id);
BLI_ghash_insert(gh_objects, name, cache_ob->ob);
}
}
/* draw particles */
gpencil_populate_particles(&e_data, gh_objects, vedata);
/* free hash */
BLI_ghash_free(gh_objects, MEM_freeN, NULL);
}
if (stl->g_data->session_flag & (GP_DRW_PAINT_IDLE | GP_DRW_PAINT_FILLING)) {
stl->storage->framebuffer_flag |= GP_FRAMEBUFFER_DRAW;
}
/* create framebuffers (only for normal drawing) */
if (!DRW_state_is_select() || !DRW_state_is_depth()) {
GPENCIL_create_framebuffers(vedata);
}
}
/* 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_stencil(fbl->background_fb, clearcol, 1.0f, 0x0);
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);
stl->storage->background_ready = true;
}
}
void DRW_gpencil_free_runtime_data(void *ved)
{
GPENCIL_Data *vedata = (GPENCIL_Data *)ved;
GPENCIL_StorageList *stl = ((GPENCIL_Data *)vedata)->stl;
/* free gpu data */
DRW_TEXTURE_FREE_SAFE(stl->g_data->gpencil_blank_texture);
GPU_BATCH_DISCARD_SAFE(stl->g_data->batch_buffer_stroke);
MEM_SAFE_FREE(stl->g_data->batch_buffer_stroke);
GPU_BATCH_DISCARD_SAFE(stl->g_data->batch_buffer_fill);
MEM_SAFE_FREE(stl->g_data->batch_buffer_fill);
GPU_BATCH_DISCARD_SAFE(stl->g_data->batch_buffer_ctrlpoint);
MEM_SAFE_FREE(stl->g_data->batch_buffer_ctrlpoint);
GPU_BATCH_DISCARD_SAFE(stl->g_data->batch_grid);
MEM_SAFE_FREE(stl->g_data->batch_grid);
if (stl->g_data->gp_object_cache == NULL) {
return;
}
/* reset all cache flags */
for (int i = 0; i < stl->g_data->gp_cache_used; i++) {
tGPencilObjectCache *cache_ob = &stl->g_data->gp_object_cache[i];
if (cache_ob) {
bGPdata *gpd = cache_ob->gpd;
gpd->flag &= ~GP_DATA_CACHE_IS_DIRTY;
/* free shgrp array */
cache_ob->tot_layers = 0;
MEM_SAFE_FREE(cache_ob->name);
MEM_SAFE_FREE(cache_ob->shgrp_array);
}
}
/* free the cache itself */
MEM_SAFE_FREE(stl->g_data->gp_object_cache);
}
static void gpencil_draw_pass_range(GPENCIL_FramebufferList *fbl,
GPENCIL_StorageList *stl,
GPENCIL_PassList *psl,
GPENCIL_TextureList *txl,
GPUFrameBuffer *fb,
Object *ob,
bGPdata *gpd,
DRWShadingGroup *init_shgrp,
DRWShadingGroup *end_shgrp,
bool multi)
{
if (init_shgrp == NULL) {
return;
}
const bool do_antialiasing = ((!stl->storage->is_mat_preview) && (multi));
DRWShadingGroup *shgrp = init_shgrp;
DRWShadingGroup *from_shgrp = init_shgrp;
DRWShadingGroup *to_shgrp = init_shgrp;
int stencil_tot = 0;
bool do_last = true;
if (do_antialiasing) {
MULTISAMPLE_GP_SYNC_ENABLE(stl->storage->multisamples, fbl);
}
/* Loop all shading groups to separate by stencil groups. */
while ((shgrp) && (shgrp != end_shgrp)) {
do_last = true;
/* Count number of groups using stencil. */
if (DRW_shgroup_stencil_mask_get(shgrp) != 0) {
stencil_tot++;
}
/* Draw stencil group and clear stencil bit. This is required because the number of
* shading groups can be greater than the limit of 255 stencil values.
* Only count as stencil if the shading group has an stencil value assigned. This reduces
* the number of clears because Dots, Fills and some Line strokes don't need stencil.
*/
if (stencil_tot == 255) {
DRW_draw_pass_subset(GPENCIL_3D_DRAWMODE(ob, gpd) ? psl->stroke_pass_3d :
psl->stroke_pass_2d,
from_shgrp,
to_shgrp);
/* Clear Stencil and prepare for next group. */
if (do_antialiasing) {
GPU_framebuffer_clear_stencil(fbl->multisample_fb, 0x0);
}
else {
GPU_framebuffer_clear_stencil(fb, 0x0);
}
/* Set new init group and reset. */
do_last = false;
shgrp = DRW_shgroup_get_next(shgrp);
if (shgrp) {
from_shgrp = to_shgrp = shgrp;
stencil_tot = 0;
if (shgrp != end_shgrp) {
continue;
}
else {
do_last = true;
break;
}
}
else {
/* No more groups. */
break;
}
}
/* Still below stencil group limit. */
shgrp = DRW_shgroup_get_next(shgrp);
if (shgrp) {
to_shgrp = shgrp;
}
}
/* Draw last pending groups. */
if (do_last) {
DRW_draw_pass_subset(GPENCIL_3D_DRAWMODE(ob, gpd) ? psl->stroke_pass_3d : psl->stroke_pass_2d,
from_shgrp,
to_shgrp);
}
if (do_antialiasing) {
MULTISAMPLE_GP_SYNC_DISABLE(stl->storage->multisamples, fbl, fb, txl);
}
}
/* draw strokes to use for selection */
static void drw_gpencil_select_render(GPENCIL_StorageList *stl, GPENCIL_PassList *psl)
{
tGPencilObjectCache *cache_ob;
tGPencilObjectCache_shgrp *array_elm = NULL;
DRWShadingGroup *init_shgrp = NULL;
DRWShadingGroup *end_shgrp = NULL;
/* Draw all pending objects */
if ((stl->g_data->gp_cache_used > 0) && (stl->g_data->gp_object_cache)) {
/* 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_ob = &stl->g_data->gp_object_cache[i];
if (cache_ob) {
Object *ob = cache_ob->ob;
bGPdata *gpd = cache_ob->gpd;
init_shgrp = NULL;
if (cache_ob->tot_layers > 0) {
for (int e = 0; e < cache_ob->tot_layers; e++) {
array_elm = &cache_ob->shgrp_array[e];
if (init_shgrp == NULL) {
init_shgrp = array_elm->init_shgrp;
}
end_shgrp = array_elm->end_shgrp;
}
/* draw group */
DRW_draw_pass_subset(GPENCIL_3D_DRAWMODE(ob, gpd) ? psl->stroke_pass_3d :
psl->stroke_pass_2d,
init_shgrp,
end_shgrp);
}
/* the cache must be dirty for next loop */
gpd->flag |= GP_DATA_CACHE_IS_DIRTY;
}
}
}
}
/* 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;
tGPencilObjectCache *cache_ob;
tGPencilObjectCache_shgrp *array_elm = NULL;
DRWShadingGroup *init_shgrp = NULL;
DRWShadingGroup *end_shgrp = NULL;
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 = stl->storage->is_playing;
const bool is_render = stl->storage->is_render;
bGPdata *gpd_act = (obact) && (obact->type == OB_GPENCIL) ? (bGPdata *)obact->data : NULL;
const bool is_edit = GPENCIL_ANY_EDIT_MODE(gpd_act);
const bool overlay = v3d != NULL ? (bool)((v3d->flag2 & V3D_HIDE_OVERLAYS) == 0) : true;
/* if the draw is for select, do a basic drawing and return */
if (DRW_state_is_select() || DRW_state_is_depth()) {
drw_gpencil_select_render(stl, psl);
return;
}
/* paper pass to display a comfortable area to draw over complex scenes with geometry */
if ((!is_render) && (obact) && (obact->type == OB_GPENCIL)) {
if (((v3d->flag2 & V3D_HIDE_OVERLAYS) == 0) && (v3d->gp_flag & V3D_GP_SHOW_PAPER)) {
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);
if (obact->dt != OB_BOUNDBOX) {
DRW_draw_pass(psl->background_pass);
}
MULTISAMPLE_GP_SYNC_ENABLE(stl->storage->multisamples, fbl);
DRW_draw_pass(psl->drawing_pass);
MULTISAMPLE_GP_SYNC_DISABLE(stl->storage->multisamples, fbl, dfbl->default_fb, txl);
/* grid pass */
if ((!is_render) && (obact) && (obact->type == OB_GPENCIL)) {
if (((v3d->flag2 & V3D_HIDE_OVERLAYS) == 0) && (v3d->gp_flag & V3D_GP_SHOW_GRID)) {
DRW_draw_pass(psl->grid_pass);
}
}
/* free memory */
DRW_gpencil_free_runtime_data(ved);
return;
}
if (DRW_state_is_fbo()) {
/* 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_ob = &stl->g_data->gp_object_cache[i];
Object *ob = cache_ob->ob;
bGPdata *gpd = cache_ob->gpd;
init_shgrp = NULL;
/* Render stroke in separated framebuffer */
GPU_framebuffer_bind(fbl->temp_fb_a);
GPU_framebuffer_clear_color_depth_stencil(fbl->temp_fb_a, clearcol, 1.0f, 0x0);
/* Stroke Pass:
* draw only a subset that usually starts with a fill and ends with stroke
*/
bool use_blend = false;
if (cache_ob->tot_layers > 0) {
for (int e = 0; e < cache_ob->tot_layers; e++) {
bool is_last = (e == cache_ob->tot_layers - 1) ? true : false;
array_elm = &cache_ob->shgrp_array[e];
if (((array_elm->mode == eGplBlendMode_Regular) && (!use_blend) &&
(!array_elm->mask_layer)) ||
(e == 0)) {
if (init_shgrp == NULL) {
init_shgrp = array_elm->init_shgrp;
}
end_shgrp = array_elm->end_shgrp;
}
else {
use_blend = true;
/* draw pending groups */
gpencil_draw_pass_range(
fbl, stl, psl, txl, fbl->temp_fb_a, ob, gpd, init_shgrp, end_shgrp, is_last);
/* Draw current group in separated texture to blend later */
init_shgrp = array_elm->init_shgrp;
end_shgrp = array_elm->end_shgrp;
GPU_framebuffer_bind(fbl->temp_fb_fx);
GPU_framebuffer_clear_color_depth_stencil(fbl->temp_fb_fx, clearcol, 1.0f, 0x0);
gpencil_draw_pass_range(
fbl, stl, psl, txl, fbl->temp_fb_fx, ob, gpd, init_shgrp, end_shgrp, is_last);
/* Blend A texture and FX texture */
GPU_framebuffer_bind(fbl->temp_fb_b);
GPU_framebuffer_clear_color_depth_stencil(fbl->temp_fb_b, clearcol, 1.0f, 0x0);
stl->storage->blend_mode = array_elm->mode;
stl->storage->mask_layer = (int)array_elm->mask_layer;
stl->storage->tonemapping = DRW_state_do_color_management() ? 0 : 1;
DRW_draw_pass(psl->blend_pass);
stl->storage->tonemapping = 0;
/* Copy B texture to A texture to follow loop */
stl->g_data->input_depth_tx = stl->g_data->temp_depth_tx_b;
stl->g_data->input_color_tx = stl->g_data->temp_color_tx_b;
GPU_framebuffer_bind(fbl->temp_fb_a);
GPU_framebuffer_clear_color_depth_stencil(fbl->temp_fb_a, clearcol, 1.0f, 0x0);
DRW_draw_pass(psl->mix_pass_noblend);
/* prepare next group */
init_shgrp = NULL;
}
}
/* last group */
gpencil_draw_pass_range(
fbl, stl, psl, txl, fbl->temp_fb_a, ob, gpd, init_shgrp, end_shgrp, true);
}
/* Current buffer drawing */
if ((!is_render) && (cache_ob->is_dup_ob == false)) {
DRW_draw_pass(psl->drawing_pass);
}
/* fx passes */
if (cache_ob->has_fx == true) {
stl->storage->tonemapping = 0;
gpencil_fx_draw(&e_data, vedata, cache_ob);
}
stl->g_data->input_depth_tx = stl->g_data->temp_depth_tx_a;
stl->g_data->input_color_tx = stl->g_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 = DRW_state_do_color_management() ? 0 : 1;
/* active select flag and selection color */
if (!is_render) {
UI_GetThemeColorShadeAlpha4fv(
(ob == draw_ctx->obact) ? TH_ACTIVE : TH_SELECT, 0, -40, stl->storage->select_color);
}
stl->storage->do_select_outline = ((overlay) && (ob->base_flag & BASE_SELECTED) &&
(ob->mode == OB_MODE_OBJECT) && (!is_render) &&
(!playing) && (v3d->flag & V3D_SELECT_OUTLINE));
/* if active object is not object mode, disable for all objects */
if ((stl->storage->do_select_outline) && (draw_ctx->obact) &&
(draw_ctx->obact->mode != OB_MODE_OBJECT)) {
stl->storage->do_select_outline = 0;
}
/* draw mix pass */
DRW_draw_pass(psl->mix_pass);
/* disable select flag */
stl->storage->do_select_outline = 0;
/* prepare for fast drawing */
if (!is_render) {
if (!playing) {
gpencil_prepare_fast_drawing(stl, dfbl, fbl, psl->mix_pass_noblend, clearcol);
}
}
else {
/* if render, the cache must be dirty for next loop */
gpd->flag |= GP_DATA_CACHE_IS_DIRTY;
}
}
/* edit points */
if ((!is_render) && (!playing) && (is_edit)) {
DRW_draw_pass(psl->edit_pass);
}
}
/* grid pass */
if ((!is_render) && (obact) && (obact->type == OB_GPENCIL)) {
if (((v3d->flag2 & V3D_HIDE_OVERLAYS) == 0) && (v3d->gp_flag & V3D_GP_SHOW_GRID)) {
DRW_draw_pass(psl->grid_pass);
}
}
}
/* free memory */
DRW_gpencil_free_runtime_data(ved);
/* reset */
if (DRW_state_is_fbo()) {
/* attach again default framebuffer */
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,
};