diff options
Diffstat (limited to 'source/blender/draw/engines')
64 files changed, 5210 insertions, 8254 deletions
diff --git a/source/blender/draw/engines/gpencil/gpencil_antialiasing.c b/source/blender/draw/engines/gpencil/gpencil_antialiasing.c new file mode 100644 index 00000000000..e81073db4a5 --- /dev/null +++ b/source/blender/draw/engines/gpencil/gpencil_antialiasing.c @@ -0,0 +1,169 @@ +/* + * 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 2019, Blender Foundation. + */ + +/** \file + * \ingroup draw + */ + +#include "DRW_render.h" + +#include "gpencil_engine.h" + +#include "smaa_textures.h" + +void GPENCIL_antialiasing_init(struct GPENCIL_Data *vedata) +{ + GPENCIL_PrivateData *pd = vedata->stl->pd; + GPENCIL_FramebufferList *fbl = vedata->fbl; + GPENCIL_TextureList *txl = vedata->txl; + GPENCIL_PassList *psl = vedata->psl; + DRWShadingGroup *grp; + + const float *size = DRW_viewport_size_get(); + const float *sizeinv = DRW_viewport_invert_size_get(); + float metrics[4] = {sizeinv[0], sizeinv[1], size[0], size[1]}; + + if (pd->simplify_antialias) { + /* No AA fallback. */ + DRW_PASS_CREATE(psl->smaa_resolve_ps, DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_CUSTOM); + + GPUShader *sh = GPENCIL_shader_antialiasing(2); + grp = DRW_shgroup_create(sh, psl->smaa_resolve_ps); + DRW_shgroup_uniform_texture(grp, "blendTex", pd->color_tx); + DRW_shgroup_uniform_texture(grp, "colorTex", pd->color_tx); + DRW_shgroup_uniform_texture(grp, "revealTex", pd->reveal_tx); + DRW_shgroup_uniform_bool_copy(grp, "doAntiAliasing", false); + DRW_shgroup_uniform_bool_copy(grp, "onlyAlpha", pd->draw_wireframe); + DRW_shgroup_uniform_vec4_copy(grp, "viewportMetrics", metrics); + + DRW_shgroup_call_procedural_triangles(grp, NULL, 1); + return; + } + + if (txl->smaa_search_tx == NULL) { + txl->smaa_search_tx = GPU_texture_create_nD(SEARCHTEX_WIDTH, + SEARCHTEX_HEIGHT, + 0, + 2, + searchTexBytes, + GPU_R8, + GPU_DATA_UNSIGNED_BYTE, + 0, + false, + NULL); + + txl->smaa_area_tx = GPU_texture_create_nD(AREATEX_WIDTH, + AREATEX_HEIGHT, + 0, + 2, + areaTexBytes, + GPU_RG8, + GPU_DATA_UNSIGNED_BYTE, + 0, + false, + NULL); + + GPU_texture_bind(txl->smaa_search_tx, 0); + GPU_texture_filter_mode(txl->smaa_search_tx, true); + GPU_texture_unbind(txl->smaa_search_tx); + + GPU_texture_bind(txl->smaa_area_tx, 0); + GPU_texture_filter_mode(txl->smaa_area_tx, true); + GPU_texture_unbind(txl->smaa_area_tx); + } + + { + pd->smaa_edge_tx = DRW_texture_pool_query_2d( + size[0], size[1], GPU_RG8, &draw_engine_gpencil_type); + pd->smaa_weight_tx = DRW_texture_pool_query_2d( + size[0], size[1], GPU_RGBA8, &draw_engine_gpencil_type); + + GPU_framebuffer_ensure_config(&fbl->smaa_edge_fb, + { + GPU_ATTACHMENT_NONE, + GPU_ATTACHMENT_TEXTURE(pd->smaa_edge_tx), + }); + + GPU_framebuffer_ensure_config(&fbl->smaa_weight_fb, + { + GPU_ATTACHMENT_NONE, + GPU_ATTACHMENT_TEXTURE(pd->smaa_weight_tx), + }); + } + + { + /* Stage 1: Edge detection. */ + DRW_PASS_CREATE(psl->smaa_edge_ps, DRW_STATE_WRITE_COLOR); + + GPUShader *sh = GPENCIL_shader_antialiasing(0); + grp = DRW_shgroup_create(sh, psl->smaa_edge_ps); + DRW_shgroup_uniform_texture(grp, "colorTex", pd->color_tx); + DRW_shgroup_uniform_texture(grp, "revealTex", pd->reveal_tx); + DRW_shgroup_uniform_vec4_copy(grp, "viewportMetrics", metrics); + + DRW_shgroup_clear_framebuffer(grp, GPU_COLOR_BIT, 0, 0, 0, 0, 0.0f, 0x0); + DRW_shgroup_call_procedural_triangles(grp, NULL, 1); + } + { + /* Stage 2: Blend Weight/Coord. */ + DRW_PASS_CREATE(psl->smaa_weight_ps, DRW_STATE_WRITE_COLOR); + + GPUShader *sh = GPENCIL_shader_antialiasing(1); + grp = DRW_shgroup_create(sh, psl->smaa_weight_ps); + DRW_shgroup_uniform_texture(grp, "edgesTex", pd->smaa_edge_tx); + DRW_shgroup_uniform_texture(grp, "areaTex", txl->smaa_area_tx); + DRW_shgroup_uniform_texture(grp, "searchTex", txl->smaa_search_tx); + DRW_shgroup_uniform_vec4_copy(grp, "viewportMetrics", metrics); + + DRW_shgroup_clear_framebuffer(grp, GPU_COLOR_BIT, 0, 0, 0, 0, 0.0f, 0x0); + DRW_shgroup_call_procedural_triangles(grp, NULL, 1); + } + { + /* Stage 3: Resolve. */ + DRW_PASS_CREATE(psl->smaa_resolve_ps, DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_CUSTOM); + + GPUShader *sh = GPENCIL_shader_antialiasing(2); + grp = DRW_shgroup_create(sh, psl->smaa_resolve_ps); + DRW_shgroup_uniform_texture(grp, "blendTex", pd->smaa_weight_tx); + DRW_shgroup_uniform_texture(grp, "colorTex", pd->color_tx); + DRW_shgroup_uniform_texture(grp, "revealTex", pd->reveal_tx); + DRW_shgroup_uniform_bool_copy(grp, "doAntiAliasing", true); + DRW_shgroup_uniform_bool_copy(grp, "onlyAlpha", pd->draw_wireframe); + DRW_shgroup_uniform_vec4_copy(grp, "viewportMetrics", metrics); + + DRW_shgroup_call_procedural_triangles(grp, NULL, 1); + } +} + +void GPENCIL_antialiasing_draw(struct GPENCIL_Data *vedata) +{ + GPENCIL_FramebufferList *fbl = vedata->fbl; + GPENCIL_PrivateData *pd = vedata->stl->pd; + GPENCIL_PassList *psl = vedata->psl; + + if (!pd->simplify_antialias) { + GPU_framebuffer_bind(fbl->smaa_edge_fb); + DRW_draw_pass(psl->smaa_edge_ps); + + GPU_framebuffer_bind(fbl->smaa_weight_fb); + DRW_draw_pass(psl->smaa_weight_ps); + } + + GPU_framebuffer_bind(pd->scene_fb); + DRW_draw_pass(psl->smaa_resolve_ps); +} diff --git a/source/blender/draw/engines/gpencil/gpencil_cache_utils.c b/source/blender/draw/engines/gpencil/gpencil_cache_utils.c index 5357d6167be..743171b09fb 100644 --- a/source/blender/draw/engines/gpencil/gpencil_cache_utils.c +++ b/source/blender/draw/engines/gpencil/gpencil_cache_utils.c @@ -33,324 +33,368 @@ #include "BKE_gpencil.h" #include "BKE_object.h" +#include "BLI_memblock.h" +#include "BLI_link_utils.h" + #include "gpencil_engine.h" #include "draw_cache_impl.h" #include "DEG_depsgraph.h" -/* verify if exist a non instanced version of the object */ -static bool gpencil_has_noninstanced_object(Object *ob_instance) -{ - const DRWContextState *draw_ctx = DRW_context_state_get(); - const ViewLayer *view_layer = draw_ctx->view_layer; - Object *ob = NULL; - for (Base *base = view_layer->object_bases.first; base; base = base->next) { - ob = base->object; - if (ob->type != OB_GPENCIL) { - continue; - } - /* is not duplicated and the name is equals */ - if ((ob->base_flag & BASE_FROM_DUPLI) == 0) { - if (STREQ(ob->id.name, ob_instance->id.name)) { - return true; - } - } - } - - return false; -} +/* -------------------------------------------------------------------- */ +/** \name Object + * \{ */ -/* add a gpencil object to cache to defer drawing */ -tGPencilObjectCache *gpencil_object_cache_add(tGPencilObjectCache *cache_array, - Object *ob, - int *gp_cache_size, - int *gp_cache_used) +GPENCIL_tObject *gpencil_object_cache_add(GPENCIL_PrivateData *pd, Object *ob) { - const DRWContextState *draw_ctx = DRW_context_state_get(); - tGPencilObjectCache *cache_elem = NULL; - RegionView3D *rv3d = draw_ctx->rv3d; - View3D *v3d = draw_ctx->v3d; - 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; + bGPdata *gpd = (bGPdata *)ob->data; + GPENCIL_tObject *tgp_ob = BLI_memblock_alloc(pd->gp_object_pool); + + tgp_ob->layers.first = tgp_ob->layers.last = NULL; + tgp_ob->vfx.first = tgp_ob->vfx.last = NULL; + tgp_ob->camera_z = dot_v3v3(pd->camera_z_axis, ob->obmat[3]); + tgp_ob->is_drawmode3d = (gpd->draw_mode == GP_DRAWMODE_3D) || pd->draw_depth_only; + tgp_ob->object_scale = mat4_to_scale(ob->obmat); + + /* Find the normal most likely to represent the gpObject. */ + /* TODO: This does not work quite well if you use + * strokes not aligned with the object axes. Maybe we could try to + * compute the minimum axis of all strokes. But this would be more + * computationaly heavy and should go into the GPData evaluation. */ + BoundBox *bbox = BKE_object_boundbox_get(ob); + /* Convert bbox to matrix */ + float mat[4][4], size[3], center[3]; + BKE_boundbox_calc_size_aabb(bbox, size); + BKE_boundbox_calc_center_aabb(bbox, center); + unit_m4(mat); + copy_v3_v3(mat[3], center); + /* Avoid division by 0.0 later. */ + add_v3_fl(size, 1e-8f); + rescale_m4(mat, size); + /* BBox space to World. */ + mul_m4_m4m4(mat, ob->obmat, mat); + if (DRW_view_is_persp_get(NULL)) { + /* BBox center to camera vector. */ + sub_v3_v3v3(tgp_ob->plane_normal, pd->camera_pos, mat[3]); } - /* zero out all pointers */ - cache_elem = &cache_array[*gp_cache_used]; - memset(cache_elem, 0, sizeof(*cache_elem)); - - cache_elem->ob = ob; - cache_elem->gpd = (bGPdata *)ob->data; - cache_elem->name = BKE_id_to_unique_string_key(&ob->id); - - copy_v3_v3(cache_elem->loc, ob->obmat[3]); - copy_m4_m4(cache_elem->obmat, ob->obmat); - cache_elem->idx = *gp_cache_used; - - /* object is duplicated (particle) */ - if (ob->base_flag & BASE_FROM_DUPLI) { - /* Check if the original object is not in the viewlayer - * and cannot be managed as dupli. This is slower, but required to keep - * the particle drawing FPS and display instanced objects in scene - * without the original object */ - bool has_original = gpencil_has_noninstanced_object(ob); - cache_elem->is_dup_ob = (has_original) ? ob->base_flag & BASE_FROM_DUPLI : false; + else { + copy_v3_v3(tgp_ob->plane_normal, pd->camera_z_axis); + } + /* World to BBox space. */ + invert_m4(mat); + /* Normalize the vector in BBox space. */ + mul_mat3_m4_v3(mat, tgp_ob->plane_normal); + normalize_v3(tgp_ob->plane_normal); + + transpose_m4(mat); + /* mat is now a "normal" matrix which will transform + * BBox space normal to world space. */ + mul_mat3_m4_v3(mat, tgp_ob->plane_normal); + normalize_v3(tgp_ob->plane_normal); + + /* Define a matrix that will be used to render a triangle to merge the depth of the rendered + * gpencil object with the rest of the scene. */ + unit_m4(tgp_ob->plane_mat); + copy_v3_v3(tgp_ob->plane_mat[2], tgp_ob->plane_normal); + orthogonalize_m4(tgp_ob->plane_mat, 2); + mul_mat3_m4_v3(ob->obmat, size); + float radius = len_v3(size); + mul_m4_v3(ob->obmat, center); + rescale_m4(tgp_ob->plane_mat, (float[3]){radius, radius, radius}); + copy_v3_v3(tgp_ob->plane_mat[3], center); + + /* Add to corresponding list if is in front. */ + if (ob->dtx & OB_DRAWXRAY) { + BLI_LINKS_APPEND(&pd->tobjects_infront, tgp_ob); } else { - cache_elem->is_dup_ob = false; + BLI_LINKS_APPEND(&pd->tobjects, tgp_ob); } - cache_elem->scale = mat4_to_scale(ob->obmat); + return tgp_ob; +} - /* save FXs */ - cache_elem->pixfactor = cache_elem->gpd->pixfactor; - cache_elem->shader_fx = ob->shader_fx; +#define SORT_IMPL_LINKTYPE GPENCIL_tObject - /* save wire mode (object mode is always primary option) */ - if (ob->dt == OB_WIRE) { - cache_elem->shading_type[0] = (int)OB_WIRE; +#define SORT_IMPL_FUNC gpencil_tobject_sort_fn_r +#include "../../blenlib/intern/list_sort_impl.h" +#undef SORT_IMPL_FUNC + +#undef SORT_IMPL_LINKTYPE + +static int gpencil_tobject_dist_sort(const void *a, const void *b) +{ + const GPENCIL_tObject *ob_a = (const GPENCIL_tObject *)a; + const GPENCIL_tObject *ob_b = (const GPENCIL_tObject *)b; + /* Reminder, camera_z is negative in front of the camera. */ + if (ob_a->camera_z > ob_b->camera_z) { + return 1; } - else { - if (v3d) { - cache_elem->shading_type[0] = (int)v3d->shading.type; - } + else if (ob_a->camera_z < ob_b->camera_z) { + return -1; } - - /* shgrp array */ - cache_elem->tot_layers = 0; - int totgpl = BLI_listbase_count(&cache_elem->gpd->layers); - if (totgpl > 0) { - cache_elem->shgrp_array = MEM_callocN(sizeof(tGPencilObjectCache_shgrp) * totgpl, __func__); + else { + return 0; } +} - /* calculate zdepth from point of view */ - float zdepth = 0.0; - if (rv3d) { - if (rv3d->is_persp) { - zdepth = ED_view3d_calc_zfac(rv3d, ob->obmat[3], NULL); - } - else { - zdepth = -dot_v3v3(rv3d->viewinv[2], ob->obmat[3]); +void gpencil_object_cache_sort(GPENCIL_PrivateData *pd) +{ + /* Sort object by distance to the camera. */ + if (pd->tobjects.first) { + pd->tobjects.first = gpencil_tobject_sort_fn_r(pd->tobjects.first, gpencil_tobject_dist_sort); + /* Relink last pointer. */ + while (pd->tobjects.last->next) { + pd->tobjects.last = pd->tobjects.last->next; } } - 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->obmat[3], plane_cam); + if (pd->tobjects_infront.first) { + pd->tobjects_infront.first = gpencil_tobject_sort_fn_r(pd->tobjects_infront.first, + gpencil_tobject_dist_sort); + /* Relink last pointer. */ + while (pd->tobjects_infront.last->next) { + pd->tobjects_infront.last = pd->tobjects_infront.last->next; } } - cache_elem->zdepth = zdepth; - /* increase slots used in cache */ - (*gp_cache_used)++; - - return cache_array; -} -/* add a shading group to the cache to create later */ -GpencilBatchGroup *gpencil_group_cache_add(GpencilBatchGroup *cache_array, - bGPDlayer *gpl, - bGPDframe *gpf, - bGPDstroke *gps, - const short type, - const bool onion, - const int vertex_idx, - int *grp_size, - int *grp_used) -{ - GpencilBatchGroup *cache_elem = NULL; - GpencilBatchGroup *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 (*grp_used + 1 > *grp_size) { - if ((*grp_size == 0) || (cache_array == NULL)) { - p = MEM_callocN(sizeof(struct GpencilBatchGroup) * GPENCIL_GROUPS_BLOCK_SIZE, - "GpencilBatchGroup"); - *grp_size = GPENCIL_GROUPS_BLOCK_SIZE; + /* Join both lists, adding infront. */ + if (pd->tobjects_infront.first != NULL) { + if (pd->tobjects.last != NULL) { + pd->tobjects.last->next = pd->tobjects_infront.first; + pd->tobjects.last = pd->tobjects_infront.last; } else { - *grp_size += GPENCIL_GROUPS_BLOCK_SIZE; - p = MEM_recallocN(cache_array, sizeof(struct GpencilBatchGroup) * *grp_size); + /* Only in front objects. */ + pd->tobjects.first = pd->tobjects_infront.first; + pd->tobjects.last = pd->tobjects_infront.last; } - cache_array = p; } - /* zero out all data */ - cache_elem = &cache_array[*grp_used]; - memset(cache_elem, 0, sizeof(*cache_elem)); - - cache_elem->gpl = gpl; - cache_elem->gpf = gpf; - cache_elem->gps = gps; - cache_elem->type = type; - cache_elem->onion = onion; - cache_elem->vertex_idx = vertex_idx; +} - /* increase slots used in cache */ - (*grp_used)++; +/** \} */ - return cache_array; -} +/* -------------------------------------------------------------------- */ +/** \name Layer + * \{ */ -/* get current cache data */ -static GpencilBatchCache *gpencil_batch_get_element(Object *ob) +static float gpencil_layer_final_opacity_get(const GPENCIL_PrivateData *pd, + const Object *ob, + const bGPDlayer *gpl) { - return ob->runtime.gpencil_cache; + const bool is_obact = ((pd->obact) && (pd->obact == ob)); + const bool is_fade = ((pd->fade_layer_opacity > -1.0f) && (is_obact) && + ((gpl->flag & GP_LAYER_ACTIVE) == 0)); + + /* Defines layer opacity. For active object depends of layer opacity factor, and + * for no active object, depends if the fade grease pencil objects option is enabled. */ + if (!pd->is_render) { + if (is_obact && is_fade) { + return gpl->opacity * pd->fade_layer_opacity; + } + else if (!is_obact && (pd->fade_gp_object_opacity > -1.0f)) { + return gpl->opacity * pd->fade_gp_object_opacity; + } + } + return gpl->opacity; } -/* verify if cache is valid */ -static bool gpencil_batch_cache_valid(GpencilBatchCache *cache, bGPdata *gpd, int cfra) +static void gpencil_layer_final_tint_and_alpha_get(const GPENCIL_PrivateData *pd, + const bGPdata *gpd, + const bGPDlayer *gpl, + const bGPDframe *gpf, + float r_tint[4], + float *r_alpha) { - bool valid = true; - if (cache == NULL) { - return false; - } - - cache->is_editmode = GPENCIL_ANY_EDIT_MODE(gpd); - if (cfra != cache->cache_frame) { - valid = false; - } - else if (gpd->flag & GP_DATA_CACHE_IS_DIRTY) { - valid = false; - } - else if (gpd->flag & GP_DATA_PYTHON_UPDATED) { - gpd->flag &= ~GP_DATA_PYTHON_UPDATED; - valid = false; + const bool use_onion = (gpf != NULL) && (gpf->runtime.onion_id != 0.0f); + if (use_onion) { + const bool use_onion_custom_col = (gpd->onion_flag & GP_ONION_GHOST_PREVCOL) != 0; + const bool use_onion_fade = (gpd->onion_flag & GP_ONION_FADE) != 0; + const bool use_next_col = gpf->runtime.onion_id > 0.0f; + + const float *onion_col_custom = (use_onion_custom_col) ? + (use_next_col ? gpd->gcolor_next : gpd->gcolor_prev) : + U.gpencil_new_layer_col; + + copy_v4_fl4(r_tint, UNPACK3(onion_col_custom), 1.0f); + + *r_alpha = use_onion_fade ? (1.0f / abs(gpf->runtime.onion_id)) : 0.5f; + *r_alpha *= gpd->onion_factor; + *r_alpha = (gpd->onion_factor > 0.0f) ? clamp_f(*r_alpha, 0.1f, 1.0f) : + clamp_f(*r_alpha, 0.01f, 1.0f); } - else if (cache->is_editmode) { - valid = false; - } - else if (cache->is_dirty) { - valid = false; + else { + copy_v4_v4(r_tint, gpl->tintcolor); + if (GPENCIL_SIMPLIFY_TINT(pd->scene)) { + r_tint[3] = 0.0f; + } + *r_alpha = 1.0f; } - return valid; + *r_alpha *= pd->xray_alpha; } -/* cache init */ -static GpencilBatchCache *gpencil_batch_cache_init(Object *ob, int cfra) +GPENCIL_tLayer *gpencil_layer_cache_add(GPENCIL_PrivateData *pd, + const Object *ob, + const bGPDlayer *gpl, + const bGPDframe *gpf, + GPENCIL_tObject *tgp_ob) { bGPdata *gpd = (bGPdata *)ob->data; - GpencilBatchCache *cache = gpencil_batch_get_element(ob); + const bool is_in_front = (ob->dtx & OB_DRAWXRAY); + const bool is_screenspace = (gpd->flag & GP_DATA_STROKE_KEEPTHICKNESS) != 0; + const bool overide_vertcol = (pd->v3d_color_type != -1); + const bool is_vert_col_mode = (pd->v3d_color_type == V3D_SHADING_VERTEX_COLOR) || + GPENCIL_VERTEX_MODE(gpd) || pd->is_render; + bool is_masked = (gpl->flag & GP_LAYER_USE_MASK) && !BLI_listbase_is_empty(&gpl->mask_layers); + + float vert_col_opacity = (overide_vertcol) ? (is_vert_col_mode ? 1.0f : 0.0f) : + gpl->vertex_paint_opacity; + /* Negate thickness sign to tag that strokes are in screen space. + * Convert to world units (by default, 1 meter = 2000 px). */ + float thickness_scale = (is_screenspace) ? -1.0f : (gpd->pixfactor / GPENCIL_PIXEL_FACTOR); + float layer_opacity = gpencil_layer_final_opacity_get(pd, ob, gpl); + float layer_tint[4]; + float layer_alpha; + gpencil_layer_final_tint_and_alpha_get(pd, gpd, gpl, gpf, layer_tint, &layer_alpha); + + /* Create the new layer descriptor. */ + GPENCIL_tLayer *tgp_layer = BLI_memblock_alloc(pd->gp_layer_pool); + BLI_LINKS_APPEND(&tgp_ob->layers, tgp_layer); + tgp_layer->layer_id = BLI_findindex(&gpd->layers, gpl); + tgp_layer->mask_bits = NULL; + tgp_layer->mask_invert_bits = NULL; + tgp_layer->blend_ps = NULL; + + /* Masking: Go through mask list and extract valid masks in a bitmap. */ + if (is_masked) { + bool valid_mask = false; + /* Warning: only GP_MAX_MASKBITS amount of bits. + * TODO(fclem) Find a better system without any limitation. */ + tgp_layer->mask_bits = BLI_memblock_alloc(pd->gp_maskbit_pool); + tgp_layer->mask_invert_bits = BLI_memblock_alloc(pd->gp_maskbit_pool); + BLI_bitmap_set_all(tgp_layer->mask_bits, false, GP_MAX_MASKBITS); + + LISTBASE_FOREACH (bGPDlayer_Mask *, mask, &gpl->mask_layers) { + bGPDlayer *gpl_mask = BKE_gpencil_layer_named_get(gpd, mask->name); + if (gpl_mask && (gpl_mask != gpl) && ((gpl_mask->flag & GP_LAYER_HIDE) == 0) && + ((mask->flag & GP_MASK_HIDE) == 0)) { + int index = BLI_findindex(&gpd->layers, gpl_mask); + if (index < GP_MAX_MASKBITS) { + const bool invert = (mask->flag & GP_MASK_INVERT) != 0; + BLI_BITMAP_SET(tgp_layer->mask_bits, index, true); + BLI_BITMAP_SET(tgp_layer->mask_invert_bits, index, invert); + valid_mask = true; + } + } + } - if (!cache) { - cache = MEM_callocN(sizeof(*cache), __func__); - ob->runtime.gpencil_cache = cache; - } - else { - memset(cache, 0, sizeof(*cache)); + if (valid_mask) { + pd->use_mask_fb = true; + } + else { + tgp_layer->mask_bits = NULL; + } + is_masked = valid_mask; } - cache->is_editmode = GPENCIL_ANY_EDIT_MODE(gpd); - - cache->is_dirty = true; + /* Blending: Force blending for masked layer. */ + if (is_masked || (gpl->blend_mode != eGplBlendMode_Regular) || (layer_opacity < 1.0f)) { + DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_STENCIL_EQUAL; + switch (gpl->blend_mode) { + case eGplBlendMode_Regular: + state |= DRW_STATE_BLEND_ALPHA_PREMUL; + break; + case eGplBlendMode_Add: + state |= DRW_STATE_BLEND_ADD_FULL; + break; + case eGplBlendMode_Subtract: + state |= DRW_STATE_BLEND_SUB; + break; + case eGplBlendMode_Multiply: + case eGplBlendMode_Divide: + case eGplBlendMode_Overlay: + state |= DRW_STATE_BLEND_MUL; + break; + } - cache->cache_frame = cfra; + if (ELEM(gpl->blend_mode, eGplBlendMode_Subtract, eGplBlendMode_Overlay)) { + /* For these effect to propagate, we need a signed floating point buffer. */ + pd->use_signed_fb = true; + } - return cache; -} + tgp_layer->blend_ps = DRW_pass_create("GPencil Blend Layer", state); + + GPUShader *sh = GPENCIL_shader_layer_blend_get(); + DRWShadingGroup *grp = DRW_shgroup_create(sh, tgp_layer->blend_ps); + DRW_shgroup_uniform_int_copy(grp, "blendMode", gpl->blend_mode); + DRW_shgroup_uniform_float_copy(grp, "blendOpacity", layer_opacity); + DRW_shgroup_uniform_texture_ref(grp, "colorBuf", &pd->color_layer_tx); + DRW_shgroup_uniform_texture_ref(grp, "revealBuf", &pd->reveal_layer_tx); + DRW_shgroup_uniform_texture_ref(grp, "maskBuf", (is_masked) ? &pd->mask_tx : &pd->dummy_tx); + DRW_shgroup_stencil_mask(grp, 0xFF); + DRW_shgroup_call_procedural_triangles(grp, NULL, 1); + + if (gpl->blend_mode == eGplBlendMode_Overlay) { + /* We cannot do custom blending on MultiTarget framebuffers. + * Workaround by doing 2 passes. */ + grp = DRW_shgroup_create(sh, tgp_layer->blend_ps); + DRW_shgroup_state_disable(grp, DRW_STATE_BLEND_MUL); + DRW_shgroup_state_enable(grp, DRW_STATE_BLEND_ADD_FULL); + DRW_shgroup_uniform_int_copy(grp, "blendMode", 999); + DRW_shgroup_call_procedural_triangles(grp, NULL, 1); + } -/* clear cache */ -static void gpencil_batch_cache_clear(GpencilBatchCache *cache) -{ - if (!cache) { - return; + pd->use_layer_fb = true; } - GPU_BATCH_DISCARD_SAFE(cache->b_stroke.batch); - GPU_BATCH_DISCARD_SAFE(cache->b_point.batch); - GPU_BATCH_DISCARD_SAFE(cache->b_fill.batch); - GPU_BATCH_DISCARD_SAFE(cache->b_edit.batch); - GPU_BATCH_DISCARD_SAFE(cache->b_edlin.batch); - - MEM_SAFE_FREE(cache->b_stroke.batch); - MEM_SAFE_FREE(cache->b_point.batch); - MEM_SAFE_FREE(cache->b_fill.batch); - MEM_SAFE_FREE(cache->b_edit.batch); - MEM_SAFE_FREE(cache->b_edlin.batch); - - /* internal format data */ - MEM_SAFE_FREE(cache->b_stroke.format); - MEM_SAFE_FREE(cache->b_point.format); - MEM_SAFE_FREE(cache->b_fill.format); - MEM_SAFE_FREE(cache->b_edit.format); - MEM_SAFE_FREE(cache->b_edlin.format); - - MEM_SAFE_FREE(cache->grp_cache); - cache->grp_size = 0; - cache->grp_used = 0; -} - -/* get cache */ -GpencilBatchCache *gpencil_batch_cache_get(Object *ob, int cfra) -{ - bGPdata *gpd = (bGPdata *)ob->data; - - GpencilBatchCache *cache = gpencil_batch_get_element(ob); - if (!gpencil_batch_cache_valid(cache, gpd, cfra)) { - if (cache) { - gpencil_batch_cache_clear(cache); - } - return gpencil_batch_cache_init(ob, cfra); - } - else { - return cache; + /* Geometry pass */ + { + GPUTexture *depth_tex = (is_in_front) ? pd->dummy_tx : pd->scene_depth_tx; + GPUTexture **mask_tex = (is_masked) ? &pd->mask_tx : &pd->dummy_tx; + + DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH | DRW_STATE_BLEND_ALPHA_PREMUL; + /* For 2D mode, we render all strokes with uniform depth (increasing with stroke id). */ + state |= tgp_ob->is_drawmode3d ? DRW_STATE_DEPTH_LESS_EQUAL : DRW_STATE_DEPTH_GREATER; + /* Always write stencil. Only used as optimization for blending. */ + state |= DRW_STATE_WRITE_STENCIL | DRW_STATE_STENCIL_ALWAYS; + + tgp_layer->geom_ps = DRW_pass_create("GPencil Layer", state); + + struct GPUShader *sh = GPENCIL_shader_geometry_get(); + DRWShadingGroup *grp = tgp_layer->base_shgrp = DRW_shgroup_create(sh, tgp_layer->geom_ps); + + DRW_shgroup_uniform_texture_persistent(grp, "gpSceneDepthTexture", depth_tex); + DRW_shgroup_uniform_texture_ref(grp, "gpMaskTexture", mask_tex); + DRW_shgroup_uniform_vec3_copy(grp, "gpNormal", tgp_ob->plane_normal); + DRW_shgroup_uniform_bool_copy(grp, "strokeOrder3d", tgp_ob->is_drawmode3d); + DRW_shgroup_uniform_float_copy(grp, "thicknessScale", tgp_ob->object_scale); + DRW_shgroup_uniform_vec2_copy(grp, "sizeViewportInv", DRW_viewport_invert_size_get()); + DRW_shgroup_uniform_vec2_copy(grp, "sizeViewport", DRW_viewport_size_get()); + DRW_shgroup_uniform_float_copy(grp, "thicknessOffset", (float)gpl->line_change); + DRW_shgroup_uniform_float_copy(grp, "thicknessWorldScale", thickness_scale); + DRW_shgroup_uniform_float_copy(grp, "vertexColorOpacity", vert_col_opacity); + DRW_shgroup_uniform_vec4_copy(grp, "layerTint", layer_tint); + DRW_shgroup_uniform_float_copy(grp, "layerOpacity", layer_alpha); + DRW_shgroup_stencil_mask(grp, 0xFF); } -} -/* set cache as dirty */ -void DRW_gpencil_batch_cache_dirty_tag(bGPdata *gpd) -{ - gpd->flag |= GP_DATA_CACHE_IS_DIRTY; + return tgp_layer; } -/* free batch cache */ -void DRW_gpencil_batch_cache_free(bGPdata *UNUSED(gpd)) +GPENCIL_tLayer *gpencil_layer_cache_get(GPENCIL_tObject *tgp_ob, int number) { - return; -} - -/* wrapper to clear cache */ -void DRW_gpencil_freecache(struct Object *ob) -{ - if ((ob) && (ob->type == OB_GPENCIL)) { - gpencil_batch_cache_clear(ob->runtime.gpencil_cache); - MEM_SAFE_FREE(ob->runtime.gpencil_cache); - bGPdata *gpd = (bGPdata *)ob->data; - if (gpd) { - gpd->flag |= GP_DATA_CACHE_IS_DIRTY; + if (number >= 0) { + GPENCIL_tLayer *layer = tgp_ob->layers.first; + while (layer != NULL) { + if (layer->layer_id == number) { + return layer; + } + layer = layer->next; } } - - /* clear all frames evaluated data */ - for (int i = 0; i < ob->runtime.gpencil_tot_layers; i++) { - bGPDframe *gpf_eval = &ob->runtime.gpencil_evaluated_frames[i]; - BKE_gpencil_free_frame_runtime_data(gpf_eval); - } - - ob->runtime.gpencil_tot_layers = 0; - MEM_SAFE_FREE(ob->runtime.gpencil_evaluated_frames); + return 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 deleted file mode 100644 index a2016b9c1e6..00000000000 --- a/source/blender/draw/engines/gpencil/gpencil_draw_cache_impl.c +++ /dev/null @@ -1,1021 +0,0 @@ -/* - * 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 - */ - -/** \file - * \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_deform.h" -#include "BKE_gpencil.h" - -#include "DRW_render.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, - const bGPDspoint *pt, - int idx, - uint pos_id, - uint color_id, - uint thickness_id, - uint uvdata_id, - short thickness, - const float ink[4]) -{ - - 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 */ - 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 buffer_stroke point to vbo */ -static void gpencil_set_buffer_stroke_point(GPUVertBuf *vbo, - const bGPDspoint *pt, - int idx, - uint pos_id, - uint color_id, - uint thickness_id, - uint uvdata_id, - uint prev_pos_id, - const float ref_pt[3], - short thickness, - const float ink[4]) -{ - - 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 */ - 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); - /* reference point to follow drawing path */ - GPU_vertbuf_attr_set(vbo, prev_pos_id, idx, ref_pt); -} - -/* 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], - const 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); -} - -static void gpencil_vbo_ensure_size(GpencilBatchCacheElem *be, int totvertex) -{ - if (be->vbo->vertex_alloc <= be->vbo_len + totvertex) { - uint newsize = be->vbo->vertex_alloc + - (((totvertex / GPENCIL_VBO_BLOCK_SIZE) + 1) * GPENCIL_VBO_BLOCK_SIZE); - GPU_vertbuf_data_resize(be->vbo, newsize); - } -} - -static void gpencil_elem_format_ensure(GpencilBatchCacheElem *be) -{ - if (be->format == NULL) { - be->format = MEM_callocN(sizeof(GPUVertFormat), __func__); - } -} - -/* create batch geometry data for points stroke shader */ -void gpencil_get_point_geom(GpencilBatchCacheElem *be, - bGPDstroke *gps, - short thickness, - const float ink[4], - const int alignment_mode) -{ - int totvertex = gps->totpoints; - if (be->vbo == NULL) { - gpencil_elem_format_ensure(be); - be->pos_id = GPU_vertformat_attr_add(be->format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); - be->color_id = GPU_vertformat_attr_add(be->format, "color", GPU_COMP_F32, 4, GPU_FETCH_FLOAT); - be->thickness_id = GPU_vertformat_attr_add( - be->format, "thickness", GPU_COMP_F32, 1, GPU_FETCH_FLOAT); - be->uvdata_id = GPU_vertformat_attr_add( - be->format, "uvdata", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); - be->prev_pos_id = GPU_vertformat_attr_add( - be->format, "prev_pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); - - be->vbo = GPU_vertbuf_create_with_format(be->format); - GPU_vertbuf_data_alloc(be->vbo, be->tot_vertex); - be->vbo_len = 0; - } - gpencil_vbo_ensure_size(be, totvertex); - - /* draw stroke curve */ - const bGPDspoint *pt = gps->points; - 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(be->vbo, be->color_id, be->vbo_len, col); - GPU_vertbuf_attr_set(be->vbo, be->thickness_id, be->vbo_len, &thick); - - /* transfer both values using the same shader variable */ - float uvdata[2] = {pt->uv_fac, pt->uv_rot}; - GPU_vertbuf_attr_set(be->vbo, be->uvdata_id, be->vbo_len, uvdata); - - GPU_vertbuf_attr_set(be->vbo, be->pos_id, be->vbo_len, &pt->x); - - /* use previous point to determine stroke direction */ - bGPDspoint *pt2 = NULL; - float fpt[3]; - if (alignment_mode != GP_STYLE_FOLLOW_PATH) { - /* add small offset to get a vector */ - copy_v3_v3(fpt, &pt->x); - fpt[0] += 0.00001f; - fpt[1] += 0.00001f; - GPU_vertbuf_attr_set(be->vbo, be->prev_pos_id, be->vbo_len, fpt); - } - else { - if (i == 0) { - if (gps->totpoints > 1) { - /* extrapolate a point before first point */ - pt2 = &gps->points[1]; - interp_v3_v3v3(fpt, &pt2->x, &pt->x, 1.5f); - GPU_vertbuf_attr_set(be->vbo, be->prev_pos_id, be->vbo_len, fpt); - } - else { - /* add small offset to get a vector */ - copy_v3_v3(fpt, &pt->x); - fpt[0] += 0.00001f; - fpt[1] += 0.00001f; - GPU_vertbuf_attr_set(be->vbo, be->prev_pos_id, be->vbo_len, fpt); - } - } - else { - pt2 = &gps->points[i - 1]; - GPU_vertbuf_attr_set(be->vbo, be->prev_pos_id, be->vbo_len, &pt2->x); - } - } - be->vbo_len++; - } -} - -/* create batch geometry data for stroke shader */ -void gpencil_get_stroke_geom(struct GpencilBatchCacheElem *be, - 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; - int totvertex = totpoints + cyclic_add + 2; - - if (be->vbo == NULL) { - gpencil_elem_format_ensure(be); - be->pos_id = GPU_vertformat_attr_add(be->format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); - be->color_id = GPU_vertformat_attr_add(be->format, "color", GPU_COMP_F32, 4, GPU_FETCH_FLOAT); - be->thickness_id = GPU_vertformat_attr_add( - be->format, "thickness", GPU_COMP_F32, 1, GPU_FETCH_FLOAT); - be->uvdata_id = GPU_vertformat_attr_add( - be->format, "uvdata", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); - - be->vbo = GPU_vertbuf_create_with_format(be->format); - GPU_vertbuf_data_alloc(be->vbo, be->tot_vertex); - be->vbo_len = 0; - } - gpencil_vbo_ensure_size(be, totvertex); - - /* draw stroke curve */ - const bGPDspoint *pt = points; - 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(be->vbo, - &points[totpoints - 1], - be->vbo_len, - be->pos_id, - be->color_id, - be->thickness_id, - be->uvdata_id, - thickness, - ink); - be->vbo_len++; - } - else { - gpencil_set_stroke_point(be->vbo, - &points[1], - be->vbo_len, - be->pos_id, - be->color_id, - be->thickness_id, - be->uvdata_id, - thickness, - ink); - be->vbo_len++; - } - } - /* set point */ - gpencil_set_stroke_point(be->vbo, - pt, - be->vbo_len, - be->pos_id, - be->color_id, - be->thickness_id, - be->uvdata_id, - thickness, - ink); - be->vbo_len++; - } - - if (gps->flag & GP_STROKE_CYCLIC && totpoints > 2) { - /* draw line to first point to complete the cycle */ - gpencil_set_stroke_point(be->vbo, - &points[0], - be->vbo_len, - be->pos_id, - be->color_id, - be->thickness_id, - be->uvdata_id, - thickness, - ink); - be->vbo_len++; - /* now add adjacency point (not drawn) */ - gpencil_set_stroke_point(be->vbo, - &points[1], - be->vbo_len, - be->pos_id, - be->color_id, - be->thickness_id, - be->uvdata_id, - thickness, - ink); - be->vbo_len++; - } - /* last adjacency point (not drawn) */ - else { - gpencil_set_stroke_point(be->vbo, - &points[totpoints - 2], - be->vbo_len, - be->pos_id, - be->color_id, - be->thickness_id, - be->uvdata_id, - thickness, - ink); - be->vbo_len++; - } -} - -/* create batch geometry data for stroke shader */ -void gpencil_get_fill_geom(struct GpencilBatchCacheElem *be, - 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_GEOMETRY) || (gps->tot_triangles == 0) || - (gps->triangles == NULL)) { - BKE_gpencil_triangulate_stroke_fill((bGPdata *)ob->data, gps); - } - - BLI_assert(gps->tot_triangles >= 1); - int totvertex = gps->tot_triangles * 3; - - if (be->vbo == NULL) { - gpencil_elem_format_ensure(be); - be->pos_id = GPU_vertformat_attr_add(be->format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); - be->color_id = GPU_vertformat_attr_add(be->format, "color", GPU_COMP_F32, 4, GPU_FETCH_FLOAT); - be->uvdata_id = GPU_vertformat_attr_add( - be->format, "texCoord", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); - - be->vbo = GPU_vertbuf_create_with_format(be->format); - GPU_vertbuf_data_alloc(be->vbo, be->tot_vertex); - be->vbo_len = 0; - } - gpencil_vbo_ensure_size(be, totvertex); - - /* Draw all triangles for filling the polygon (cache must be calculated before) */ - bGPDtriangle *stroke_triangle = gps->triangles; - for (int i = 0; i < gps->tot_triangles; i++, stroke_triangle++) { - for (int j = 0; j < 3; j++) { - gpencil_set_fill_point(be->vbo, - be->vbo_len, - &gps->points[stroke_triangle->verts[j]], - color, - stroke_triangle->uv[j], - be->pos_id, - be->color_id, - be->uvdata_id); - be->vbo_len++; - } - } -} - -/* create batch geometry data for current buffer stroke shader */ -GPUBatch *gpencil_get_buffer_stroke_geom(bGPdata *gpd, short thickness) -{ - const DRWContextState *draw_ctx = DRW_context_state_get(); - Scene *scene = draw_ctx->scene; - ARegion *region = draw_ctx->region; - RegionView3D *rv3d = draw_ctx->rv3d; - ToolSettings *ts = scene->toolsettings; - Object *ob = draw_ctx->obact; - - tGPspoint *points = gpd->runtime.sbuffer; - int totpoints = gpd->runtime.sbuffer_used; - /* if cyclic needs more vertex */ - int cyclic_add = (gpd->runtime.sbuffer_sflag & GP_STROKE_CYCLIC) ? 1 : 0; - int totvertex = totpoints + cyclic_add + 2; - - 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, totvertex); - - /* draw stroke curve */ - const tGPspoint *tpt = points; - bGPDspoint pt, pt2, pt3; - int idx = 0; - - /* get origin to reproject point */ - float origin[3]; - bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd); - ED_gp_get_drawing_reference(scene, ob, gpl, ts->gpencil_v3d_align, origin); - - for (int i = 0; i < totpoints; i++, tpt++) { - ED_gpencil_tpoint_to_point(region, origin, tpt, &pt); - ED_gp_project_point_to_plane(scene, ob, rv3d, origin, ts->gp_sculpt.lock_axis - 1, &pt); - - /* first point for adjacency (not drawn) */ - if (i == 0) { - if (gpd->runtime.sbuffer_sflag & GP_STROKE_CYCLIC && totpoints > 2) { - ED_gpencil_tpoint_to_point(region, origin, &points[totpoints - 1], &pt2); - gpencil_set_stroke_point(vbo, - &pt2, - idx, - pos_id, - color_id, - thickness_id, - uvdata_id, - thickness, - gpd->runtime.scolor); - idx++; - } - else { - ED_gpencil_tpoint_to_point(region, origin, &points[1], &pt2); - gpencil_set_stroke_point(vbo, - &pt2, - idx, - pos_id, - color_id, - thickness_id, - uvdata_id, - thickness, - gpd->runtime.scolor); - idx++; - } - } - - /* set point */ - gpencil_set_stroke_point( - vbo, &pt, idx, pos_id, color_id, thickness_id, uvdata_id, thickness, gpd->runtime.scolor); - idx++; - } - - /* last adjacency point (not drawn) */ - if (gpd->runtime.sbuffer_sflag & GP_STROKE_CYCLIC && totpoints > 2) { - /* draw line to first point to complete the cycle */ - ED_gpencil_tpoint_to_point(region, origin, &points[0], &pt2); - gpencil_set_stroke_point( - vbo, &pt2, idx, pos_id, color_id, thickness_id, uvdata_id, thickness, gpd->runtime.scolor); - idx++; - /* now add adjacency point (not drawn) */ - ED_gpencil_tpoint_to_point(region, origin, &points[1], &pt3); - gpencil_set_stroke_point( - vbo, &pt3, idx, pos_id, color_id, thickness_id, uvdata_id, thickness, gpd->runtime.scolor); - idx++; - } - /* last adjacency point (not drawn) */ - else { - ED_gpencil_tpoint_to_point(region, origin, &points[totpoints - 2], &pt2); - gpencil_set_stroke_point( - vbo, &pt2, idx, pos_id, color_id, thickness_id, uvdata_id, thickness, gpd->runtime.scolor); - idx++; - } - - 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 *gpencil_get_buffer_point_geom(bGPdata *gpd, short thickness) -{ - const DRWContextState *draw_ctx = DRW_context_state_get(); - Scene *scene = draw_ctx->scene; - ARegion *region = draw_ctx->region; - RegionView3D *rv3d = draw_ctx->rv3d; - ToolSettings *ts = scene->toolsettings; - Object *ob = draw_ctx->obact; - - tGPspoint *points = gpd->runtime.sbuffer; - int totpoints = gpd->runtime.sbuffer_used; - - static GPUVertFormat format = {0}; - static uint pos_id, color_id, thickness_id, uvdata_id, prev_pos_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); - prev_pos_id = GPU_vertformat_attr_add(&format, "prev_pos", GPU_COMP_F32, 3, 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(scene, ob, gpl, ts->gpencil_v3d_align, origin); - - for (int i = 0; i < totpoints; i++, tpt++) { - ED_gpencil_tpoint_to_point(region, origin, tpt, &pt); - ED_gp_project_point_to_plane(scene, ob, rv3d, origin, ts->gp_sculpt.lock_axis - 1, &pt); - - /* use previous point to determine stroke direction (drawing path) */ - bGPDspoint pt2; - float ref_pt[3]; - - if (i == 0) { - if (totpoints > 1) { - /* extrapolate a point before first point */ - tGPspoint *tpt2 = &points[1]; - ED_gpencil_tpoint_to_point(region, origin, tpt2, &pt2); - ED_gp_project_point_to_plane(scene, ob, rv3d, origin, ts->gp_sculpt.lock_axis - 1, &pt2); - - interp_v3_v3v3(ref_pt, &pt2.x, &pt.x, 1.5f); - } - else { - copy_v3_v3(ref_pt, &pt.x); - } - } - else { - tGPspoint *tpt2 = &points[i - 1]; - ED_gpencil_tpoint_to_point(region, origin, tpt2, &pt2); - ED_gp_project_point_to_plane(scene, ob, rv3d, origin, ts->gp_sculpt.lock_axis - 1, &pt2); - - copy_v3_v3(ref_pt, &pt2.x); - } - - /* set point */ - gpencil_set_buffer_stroke_point(vbo, - &pt, - idx, - pos_id, - color_id, - thickness_id, - uvdata_id, - prev_pos_id, - ref_pt, - 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 control point shader */ -GPUBatch *gpencil_get_buffer_ctrlpoint_geom(bGPdata *gpd) -{ - bGPDcontrolpoint *cps = gpd->runtime.cp_points; - int totpoints = gpd->runtime.tot_cp_points; - - const DRWContextState *draw_ctx = DRW_context_state_get(); - Scene *scene = draw_ctx->scene; - ToolSettings *ts = scene->toolsettings; - - if (ts->gp_sculpt.guide.use_guide) { - totpoints++; - } - - 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); - size_id = GPU_vertformat_attr_add(&format, "size", GPU_COMP_F32, 1, 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, totpoints); - - int idx = 0; - for (int i = 0; i < gpd->runtime.tot_cp_points; i++) { - bGPDcontrolpoint *cp = &cps[i]; - - GPU_vertbuf_attr_set(vbo, color_id, idx, cp->color); - - /* scale size */ - float size = cp->size * 0.8f; - GPU_vertbuf_attr_set(vbo, size_id, idx, &size); - - GPU_vertbuf_attr_set(vbo, pos_id, idx, &cp->x); - idx++; - } - - if (ts->gp_sculpt.guide.use_guide) { - float size = 10 * 0.8f; - float color[4]; - float position[3]; - if (ts->gp_sculpt.guide.reference_point == GP_GUIDE_REF_CUSTOM) { - UI_GetThemeColor4fv(TH_GIZMO_PRIMARY, color); - copy_v3_v3(position, ts->gp_sculpt.guide.location); - } - else if (ts->gp_sculpt.guide.reference_point == GP_GUIDE_REF_OBJECT && - ts->gp_sculpt.guide.reference_object != NULL) { - UI_GetThemeColor4fv(TH_GIZMO_SECONDARY, color); - copy_v3_v3(position, ts->gp_sculpt.guide.reference_object->loc); - } - else { - UI_GetThemeColor4fv(TH_REDALERT, color); - copy_v3_v3(position, scene->cursor.location); - } - GPU_vertbuf_attr_set(vbo, pos_id, idx, position); - GPU_vertbuf_attr_set(vbo, size_id, idx, &size); - GPU_vertbuf_attr_set(vbo, color_id, idx, color); - } - - return GPU_batch_create_ex(GPU_PRIM_POINTS, vbo, NULL, GPU_BATCH_OWNS_VBO); -} - -/* create batch geometry data for current buffer fill shader */ -GPUBatch *gpencil_get_buffer_fill_geom(bGPdata *gpd) -{ - if (gpd == NULL) { - return NULL; - } - - const tGPspoint *points = gpd->runtime.sbuffer; - int totpoints = gpd->runtime.sbuffer_used; - if (totpoints < 3) { - return NULL; - } - - const DRWContextState *draw_ctx = DRW_context_state_get(); - Scene *scene = draw_ctx->scene; - ARegion *region = draw_ctx->region; - 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(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(region, 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); -} - -/* Draw selected verts for strokes being edited */ -void gpencil_get_edit_geom(struct GpencilBatchCacheElem *be, - bGPDstroke *gps, - float alpha, - short dflag) -{ - const DRWContextState *draw_ctx = DRW_context_state_get(); - Object *ob = draw_ctx->obact; - bGPdata *gpd = ob->data; - const bool is_weight_paint = (gpd) && (gpd->flag & GP_DATA_STROKE_WEIGHTMODE); - const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd); - - 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; - - float unselectColor[4]; - UI_GetThemeColor3fv(TH_GP_VERTEX, unselectColor); - unselectColor[3] = alpha; - - float linecolor[4]; - copy_v4_v4(linecolor, gpd->line_color); - - if (be->vbo == NULL) { - gpencil_elem_format_ensure(be); - be->pos_id = GPU_vertformat_attr_add(be->format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); - be->color_id = GPU_vertformat_attr_add(be->format, "color", GPU_COMP_F32, 4, GPU_FETCH_FLOAT); - be->thickness_id = GPU_vertformat_attr_add( - be->format, "size", GPU_COMP_F32, 1, GPU_FETCH_FLOAT); - - be->vbo = GPU_vertbuf_create_with_format(be->format); - GPU_vertbuf_data_alloc(be->vbo, be->tot_vertex); - be->vbo_len = 0; - } - gpencil_vbo_ensure_size(be, 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; - - float fcolor[4]; - float fsize = 0; - for (int i = 0; i < gps->totpoints; i++, pt++) { - /* weight paint */ - if (is_weight_paint) { - float weight = (dvert && dvert->dw && (vgindex > -1)) ? - BKE_defvert_find_weight(dvert, vgindex) : - 0.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 ((!is_multiedit) && (pt->runtime.pt_orig == NULL)) { - ARRAY_SET_ITEMS(fcolor, linecolor[0], linecolor[1], linecolor[2], selectColor[3]); - mul_v4_fl(fcolor, 0.9f); - copy_v4_v4(fcolor, fcolor); - fsize = vsize * 0.8f; - } - else if (pt->flag & GP_SPOINT_SELECT) { - copy_v4_v4(fcolor, selectColor); - fsize = vsize; - } - else { - copy_v4_v4(fcolor, unselectColor); - fsize = bsize; - } - } - - GPU_vertbuf_attr_set(be->vbo, be->color_id, be->vbo_len, fcolor); - GPU_vertbuf_attr_set(be->vbo, be->thickness_id, be->vbo_len, &fsize); - GPU_vertbuf_attr_set(be->vbo, be->pos_id, be->vbo_len, &pt->x); - be->vbo_len++; - if (gps->dvert != NULL) { - dvert++; - } - } -} - -/* Draw lines for strokes being edited */ -void gpencil_get_edlin_geom(struct GpencilBatchCacheElem *be, - bGPDstroke *gps, - float alpha, - const bool hide_select) -{ - const DRWContextState *draw_ctx = DRW_context_state_get(); - Object *ob = draw_ctx->obact; - bGPdata *gpd = ob->data; - const 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); - - if (be->vbo == NULL) { - gpencil_elem_format_ensure(be); - be->pos_id = GPU_vertformat_attr_add(be->format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); - be->color_id = GPU_vertformat_attr_add(be->format, "color", GPU_COMP_F32, 4, GPU_FETCH_FLOAT); - - be->vbo = GPU_vertbuf_create_with_format(be->format); - GPU_vertbuf_data_alloc(be->vbo, be->tot_vertex); - be->vbo_len = 0; - } - gpencil_vbo_ensure_size(be, gps->totpoints); - - /* Draw all the stroke lines (selected or not) */ - bGPDspoint *pt = gps->points; - MDeformVert *dvert = gps->dvert; - - float fcolor[4]; - for (int i = 0; i < gps->totpoints; i++, pt++) { - /* weight paint */ - if (is_weight_paint) { - float weight = (dvert && dvert->dw && (vgindex > -1)) ? - BKE_defvert_find_weight(dvert, vgindex) : - 0.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) && (!hide_select)) { - copy_v4_v4(fcolor, selectColor); - } - else { - copy_v4_v4(fcolor, linecolor); - } - } - - GPU_vertbuf_attr_set(be->vbo, be->color_id, be->vbo_len, fcolor); - GPU_vertbuf_attr_set(be->vbo, be->pos_id, be->vbo_len, &pt->x); - be->vbo_len++; - - if (gps->dvert != NULL) { - dvert++; - } - } -} - -static void set_grid_point(GPUVertBuf *vbo, - int idx, - const float col_grid[4], - uint pos_id, - uint color_id, - float v1, - float v2, - const int axis) -{ - GPU_vertbuf_attr_set(vbo, color_id, idx, col_grid); - - float pos[3]; - /* Set the grid in the selected axis */ - switch (axis) { - case GP_LOCKAXIS_X: { - ARRAY_SET_ITEMS(pos, 0.0f, v1, v2); - break; - } - case GP_LOCKAXIS_Y: { - ARRAY_SET_ITEMS(pos, v1, 0.0f, v2); - break; - } - case GP_LOCKAXIS_Z: - default: /* view aligned */ - { - ARRAY_SET_ITEMS(pos, v1, v2, 0.0f); - break; - } - } - - GPU_vertbuf_attr_set(vbo, pos_id, idx, pos); -} - -/* Draw grid lines */ -GPUBatch *gpencil_get_grid(Object *ob) -{ - const DRWContextState *draw_ctx = DRW_context_state_get(); - Scene *scene = draw_ctx->scene; - ToolSettings *ts = scene->toolsettings; - View3D *v3d = draw_ctx->v3d; - bGPdata *gpd = (bGPdata *)ob->data; - const bool do_center = (gpd->grid.lines <= 0) ? false : true; - - float col_grid[4]; - - /* verify we have something to draw and valid values */ - if (gpd->grid.scale[0] == 0.0f) { - gpd->grid.scale[0] = 1.0f; - } - if (gpd->grid.scale[1] == 0.0f) { - gpd->grid.scale[1] = 1.0f; - } - - if (v3d->overlay.gpencil_grid_opacity < 0.1f) { - v3d->overlay.gpencil_grid_opacity = 0.1f; - } - - /* set color */ - copy_v3_v3(col_grid, gpd->grid.color); - col_grid[3] = v3d->overlay.gpencil_grid_opacity; - - const int axis = ts->gp_sculpt.lock_axis; - - const char *grid_unit = NULL; - const int gridlines = (gpd->grid.lines <= 0) ? 1 : gpd->grid.lines; - const float grid_w = gpd->grid.scale[0] * ED_scene_grid_scale(scene, &grid_unit); - const float grid_h = gpd->grid.scale[1] * ED_scene_grid_scale(scene, &grid_unit); - const float space_w = (grid_w / gridlines); - const float space_h = (grid_h / gridlines); - const float offset[2] = {gpd->grid.offset[0], gpd->grid.offset[1]}; - - 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_w = a * space_w; - const float line_h = a * space_h; - - set_grid_point( - vbo, idx, col_grid, pos_id, color_id, -grid_w + offset[0], -line_h + offset[1], axis); - idx++; - set_grid_point( - vbo, idx, col_grid, pos_id, color_id, +grid_w + offset[0], -line_h + offset[1], axis); - idx++; - set_grid_point( - vbo, idx, col_grid, pos_id, color_id, -grid_w + offset[0], +line_h + offset[1], axis); - idx++; - set_grid_point( - vbo, idx, col_grid, pos_id, color_id, +grid_w + offset[0], +line_h + offset[1], axis); - idx++; - - set_grid_point( - vbo, idx, col_grid, pos_id, color_id, -line_w + offset[0], -grid_h + offset[1], axis); - idx++; - set_grid_point( - vbo, idx, col_grid, pos_id, color_id, -line_w + offset[0], +grid_h + offset[1], axis); - idx++; - set_grid_point( - vbo, idx, col_grid, pos_id, color_id, +line_w + offset[0], -grid_h + offset[1], axis); - idx++; - set_grid_point( - vbo, idx, col_grid, pos_id, color_id, +line_w + offset[0], +grid_h + offset[1], axis); - idx++; - } - /* center lines */ - if (do_center) { - set_grid_point( - vbo, idx, col_grid, pos_id, color_id, -grid_w + offset[0], 0.0f + offset[1], axis); - idx++; - set_grid_point( - vbo, idx, col_grid, pos_id, color_id, +grid_w + offset[0], 0.0f + offset[1], axis); - idx++; - - set_grid_point( - vbo, idx, col_grid, pos_id, color_id, 0.0f + offset[0], -grid_h + offset[1], axis); - idx++; - set_grid_point( - vbo, idx, col_grid, pos_id, color_id, 0.0f + offset[0], +grid_h + offset[1], 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_data.c b/source/blender/draw/engines/gpencil/gpencil_draw_data.c new file mode 100644 index 00000000000..77baadfc83a --- /dev/null +++ b/source/blender/draw/engines/gpencil/gpencil_draw_data.c @@ -0,0 +1,502 @@ +/* + * 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 2019, Blender Foundation. + */ + +/** \file + * \ingroup draw_engine + */ + +#include "DRW_render.h" + +#include "DNA_light_types.h" + +#include "BKE_image.h" + +#include "BLI_hash.h" +#include "BLI_math_color.h" +#include "BLI_memblock.h" + +#include "GPU_uniformbuffer.h" + +#include "IMB_imbuf_types.h" + +#include "gpencil_engine.h" + +/* -------------------------------------------------------------------- */ +/** \name Material + * \{ */ + +static GPENCIL_MaterialPool *gpencil_material_pool_add(GPENCIL_PrivateData *pd) +{ + GPENCIL_MaterialPool *matpool = BLI_memblock_alloc(pd->gp_material_pool); + matpool->next = NULL; + matpool->used_count = 0; + if (matpool->ubo == NULL) { + matpool->ubo = GPU_uniformbuffer_create(sizeof(matpool->mat_data), NULL, NULL); + } + pd->last_material_pool = matpool; + return matpool; +} + +static struct GPUTexture *gpencil_image_texture_get(Image *image, bool *r_alpha_premult) +{ + ImBuf *ibuf; + ImageUser iuser = {NULL}; + struct GPUTexture *gpu_tex = NULL; + void *lock; + + iuser.ok = true; + ibuf = BKE_image_acquire_ibuf(image, &iuser, &lock); + + if (ibuf != NULL && ibuf->rect != NULL) { + gpu_tex = GPU_texture_from_blender(image, &iuser, ibuf, GL_TEXTURE_2D); + *r_alpha_premult = (image->alpha_mode == IMA_ALPHA_PREMUL); + } + BKE_image_release_ibuf(image, ibuf, lock); + + return gpu_tex; +} + +static void gpencil_uv_transform_get(const float ofs[2], + const float scale[2], + const float rotation, + float r_uvmat[3][2]) +{ + /* OPTI this could use 3x2 matrices and reduce the number of operations drastically. */ + float mat[4][4]; + unit_m4(mat); + /* Offset to center. */ + translate_m4(mat, 0.5f, 0.5f, 0.0f); + /* Reversed order. */ + rescale_m4(mat, (float[3]){1.0f / scale[0], 1.0f / scale[1], 0.0}); + rotate_m4(mat, 'Z', -rotation); + translate_m4(mat, ofs[0], ofs[1], 0.0f); + /* Convert to 3x2 */ + copy_v2_v2(r_uvmat[0], mat[0]); + copy_v2_v2(r_uvmat[1], mat[1]); + copy_v2_v2(r_uvmat[2], mat[3]); +} + +#define HSV_SATURATION 0.5 +#define HSV_VALUE 0.8 + +static void gpencil_object_random_color_get(const Object *ob, float r_color[3]) +{ + /* Duplicated from workbench_material.c */ + uint hash = BLI_ghashutil_strhash_p_murmur(ob->id.name); + if (ob->id.lib) { + hash = (hash * 13) ^ BLI_ghashutil_strhash_p_murmur(ob->id.lib->name); + } + float hue = BLI_hash_int_01(hash); + float hsv[3] = {hue, HSV_SATURATION, HSV_VALUE}; + hsv_to_rgb_v(hsv, r_color); +} + +static void gpencil_shade_color(float color[3]) +{ + /* This is scene refered color, not gamma corrected and not per perceptual. + * So we lower the threshold a bit. (1.0 / 3.0) */ + if (color[0] + color[1] + color[2] > 1.1) { + add_v3_fl(color, -0.25f); + } + else { + add_v3_fl(color, 0.15f); + } + CLAMP3(color, 0.0f, 1.0f); +} + +/* Apply all overrides from the solid viewport mode to the GPencil material. */ +static MaterialGPencilStyle *gpencil_viewport_material_overrides(GPENCIL_PrivateData *pd, + Object *ob, + int color_type, + MaterialGPencilStyle *gp_style) +{ + static MaterialGPencilStyle gp_style_tmp; + + switch (color_type) { + case V3D_SHADING_MATERIAL_COLOR: + copy_v4_v4(gp_style_tmp.stroke_rgba, gp_style->stroke_rgba); + copy_v4_v4(gp_style_tmp.fill_rgba, gp_style->fill_rgba); + gp_style = &gp_style_tmp; + gp_style->stroke_style = GP_MATERIAL_STROKE_STYLE_SOLID; + gp_style->fill_style = GP_MATERIAL_FILL_STYLE_SOLID; + break; + case V3D_SHADING_TEXTURE_COLOR: + memcpy(&gp_style_tmp, gp_style, sizeof(*gp_style)); + gp_style = &gp_style_tmp; + if ((gp_style->stroke_style == GP_MATERIAL_STROKE_STYLE_TEXTURE) && (gp_style->sima)) { + copy_v4_fl(gp_style->stroke_rgba, 1.0f); + gp_style->mix_stroke_factor = 0.0f; + } + + if ((gp_style->fill_style == GP_MATERIAL_FILL_STYLE_TEXTURE) && (gp_style->ima)) { + copy_v4_fl(gp_style->fill_rgba, 1.0f); + gp_style->mix_factor = 0.0f; + } + else if (gp_style->fill_style == GP_MATERIAL_FILL_STYLE_GRADIENT) { + /* gp_style->fill_rgba is needed for correct gradient. */ + gp_style->mix_factor = 0.0f; + } + break; + case V3D_SHADING_RANDOM_COLOR: + gp_style = &gp_style_tmp; + gp_style->stroke_style = GP_MATERIAL_STROKE_STYLE_SOLID; + gp_style->fill_style = GP_MATERIAL_FILL_STYLE_SOLID; + gpencil_object_random_color_get(ob, gp_style->fill_rgba); + gp_style->fill_rgba[3] = 1.0f; + copy_v4_v4(gp_style->stroke_rgba, gp_style->fill_rgba); + gpencil_shade_color(gp_style->stroke_rgba); + break; + case V3D_SHADING_SINGLE_COLOR: + gp_style = &gp_style_tmp; + gp_style->stroke_style = GP_MATERIAL_STROKE_STYLE_SOLID; + gp_style->fill_style = GP_MATERIAL_FILL_STYLE_SOLID; + copy_v3_v3(gp_style->fill_rgba, pd->v3d_single_color); + gp_style->fill_rgba[3] = 1.0f; + copy_v4_v4(gp_style->stroke_rgba, gp_style->fill_rgba); + gpencil_shade_color(gp_style->stroke_rgba); + break; + case V3D_SHADING_OBJECT_COLOR: + gp_style = &gp_style_tmp; + gp_style->stroke_style = GP_MATERIAL_STROKE_STYLE_SOLID; + gp_style->fill_style = GP_MATERIAL_FILL_STYLE_SOLID; + copy_v4_v4(gp_style->fill_rgba, ob->color); + copy_v4_v4(gp_style->stroke_rgba, ob->color); + gpencil_shade_color(gp_style->stroke_rgba); + break; + case V3D_SHADING_VERTEX_COLOR: + gp_style = &gp_style_tmp; + gp_style->stroke_style = GP_MATERIAL_STROKE_STYLE_SOLID; + gp_style->fill_style = GP_MATERIAL_FILL_STYLE_SOLID; + copy_v4_fl(gp_style->fill_rgba, 1.0f); + copy_v4_fl(gp_style->stroke_rgba, 1.0f); + break; + default: + break; + } + return gp_style; +} + +/** + * Creates a linked list of material pool containing all materials assigned for a given object. + * We merge the material pools together if object does not contain a huge amount of materials. + * Also return an offset to the first material of the object in the ubo. + **/ +GPENCIL_MaterialPool *gpencil_material_pool_create(GPENCIL_PrivateData *pd, Object *ob, int *ofs) +{ + GPENCIL_MaterialPool *matpool = pd->last_material_pool; + + int mat_len = max_ii(1, ob->totcol); + + bool reuse_matpool = matpool && ((matpool->used_count + mat_len) <= GP_MATERIAL_BUFFER_LEN); + + if (reuse_matpool) { + /* Share the matpool with other objects. Return offset to first material. */ + *ofs = matpool->used_count; + } + else { + matpool = gpencil_material_pool_add(pd); + *ofs = 0; + } + + /* Force vertex color in solid mode with vertex paint mode. Same behavior as meshes. */ + bGPdata *gpd = (bGPdata *)ob->data; + int color_type = (pd->v3d_color_type != -1 && GPENCIL_VERTEX_MODE(gpd)) ? + V3D_SHADING_VERTEX_COLOR : + pd->v3d_color_type; + + GPENCIL_MaterialPool *pool = matpool; + for (int i = 0; i < mat_len; i++) { + if ((i > 0) && (pool->used_count == GP_MATERIAL_BUFFER_LEN)) { + pool->next = gpencil_material_pool_add(pd); + pool = pool->next; + } + int mat_id = pool->used_count++; + + gpMaterial *mat_data = &pool->mat_data[mat_id]; + MaterialGPencilStyle *gp_style = BKE_gpencil_material_settings(ob, i + 1); + + if (gp_style->mode == GP_MATERIAL_MODE_LINE) { + mat_data->flag = 0; + } + else { + switch (gp_style->alignment_mode) { + case GP_MATERIAL_FOLLOW_PATH: + mat_data->flag = GP_STROKE_ALIGNMENT_STROKE; + break; + case GP_MATERIAL_FOLLOW_OBJ: + mat_data->flag = GP_STROKE_ALIGNMENT_OBJECT; + break; + case GP_MATERIAL_FOLLOW_FIXED: + default: + mat_data->flag = GP_STROKE_ALIGNMENT_FIXED; + break; + } + + if (gp_style->mode == GP_MATERIAL_MODE_DOT) { + mat_data->flag |= GP_STROKE_DOTS; + } + } + + if ((gp_style->mode != GP_MATERIAL_MODE_LINE) || + (gp_style->flag & GP_MATERIAL_DISABLE_STENCIL)) { + mat_data->flag |= GP_STROKE_OVERLAP; + } + + gp_style = gpencil_viewport_material_overrides(pd, ob, color_type, gp_style); + + /* Stroke Style */ + if ((gp_style->stroke_style == GP_MATERIAL_STROKE_STYLE_TEXTURE) && (gp_style->sima)) { + bool premul; + pool->tex_stroke[mat_id] = gpencil_image_texture_get(gp_style->sima, &premul); + mat_data->flag |= pool->tex_stroke[mat_id] ? GP_STROKE_TEXTURE_USE : 0; + mat_data->flag |= premul ? GP_STROKE_TEXTURE_PREMUL : 0; + copy_v4_v4(mat_data->stroke_color, gp_style->stroke_rgba); + mat_data->stroke_texture_mix = 1.0f - gp_style->mix_stroke_factor; + mat_data->stroke_u_scale = 500.0f / gp_style->texture_pixsize; + } + else /* if (gp_style->stroke_style == GP_MATERIAL_STROKE_STYLE_SOLID) */ { + pool->tex_stroke[mat_id] = NULL; + mat_data->flag &= ~GP_STROKE_TEXTURE_USE; + copy_v4_v4(mat_data->stroke_color, gp_style->stroke_rgba); + mat_data->stroke_texture_mix = 0.0f; + } + + /* Fill Style */ + if ((gp_style->fill_style == GP_MATERIAL_FILL_STYLE_TEXTURE) && (gp_style->ima)) { + bool use_clip = (gp_style->flag & GP_MATERIAL_TEX_CLAMP) != 0; + bool premul; + pool->tex_fill[mat_id] = gpencil_image_texture_get(gp_style->ima, &premul); + mat_data->flag |= pool->tex_fill[mat_id] ? GP_FILL_TEXTURE_USE : 0; + mat_data->flag |= premul ? GP_FILL_TEXTURE_PREMUL : 0; + mat_data->flag |= use_clip ? GP_FILL_TEXTURE_CLIP : 0; + gpencil_uv_transform_get(gp_style->texture_offset, + gp_style->texture_scale, + gp_style->texture_angle, + mat_data->fill_uv_transform); + copy_v4_v4(mat_data->fill_color, gp_style->fill_rgba); + mat_data->fill_texture_mix = 1.0f - gp_style->mix_factor; + } + else if (gp_style->fill_style == GP_MATERIAL_FILL_STYLE_GRADIENT) { + bool use_radial = (gp_style->gradient_type == GP_MATERIAL_GRADIENT_RADIAL); + pool->tex_fill[mat_id] = NULL; + mat_data->flag |= GP_FILL_GRADIENT_USE; + mat_data->flag |= use_radial ? GP_FILL_GRADIENT_RADIAL : 0; + gpencil_uv_transform_get(gp_style->texture_offset, + gp_style->texture_scale, + gp_style->texture_angle, + mat_data->fill_uv_transform); + copy_v4_v4(mat_data->fill_color, gp_style->fill_rgba); + copy_v4_v4(mat_data->fill_mix_color, gp_style->mix_rgba); + mat_data->fill_texture_mix = 1.0f - gp_style->mix_factor; + if (gp_style->flag & GP_MATERIAL_FLIP_FILL) { + swap_v4_v4(mat_data->fill_color, mat_data->fill_mix_color); + } + } + else /* if (gp_style->fill_style == GP_MATERIAL_FILL_STYLE_SOLID) */ { + pool->tex_fill[mat_id] = NULL; + copy_v4_v4(mat_data->fill_color, gp_style->fill_rgba); + mat_data->fill_texture_mix = 0.0f; + } + } + + return matpool; +} + +void gpencil_material_resources_get(GPENCIL_MaterialPool *first_pool, + int mat_id, + GPUTexture **r_tex_stroke, + GPUTexture **r_tex_fill, + GPUUniformBuffer **r_ubo_mat) +{ + GPENCIL_MaterialPool *matpool = first_pool; + int pool_id = mat_id / GP_MATERIAL_BUFFER_LEN; + for (int i = 0; i < pool_id; i++) { + matpool = matpool->next; + } + mat_id = mat_id % GP_MATERIAL_BUFFER_LEN; + *r_ubo_mat = matpool->ubo; + if (r_tex_fill) { + *r_tex_fill = matpool->tex_fill[mat_id]; + } + if (r_tex_stroke) { + *r_tex_stroke = matpool->tex_stroke[mat_id]; + } +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Lights + * \{ */ + +GPENCIL_LightPool *gpencil_light_pool_add(GPENCIL_PrivateData *pd) +{ + GPENCIL_LightPool *lightpool = BLI_memblock_alloc(pd->gp_light_pool); + lightpool->light_used = 0; + /* Tag light list end. */ + lightpool->light_data[0].color[0] = -1.0; + if (lightpool->ubo == NULL) { + lightpool->ubo = GPU_uniformbuffer_create(sizeof(lightpool->light_data), NULL, NULL); + } + pd->last_light_pool = lightpool; + return lightpool; +} + +void gpencil_light_ambient_add(GPENCIL_LightPool *lightpool, const float color[3]) +{ + if (lightpool->light_used >= GPENCIL_LIGHT_BUFFER_LEN) { + return; + } + + gpLight *gp_light = &lightpool->light_data[lightpool->light_used]; + gp_light->type = GP_LIGHT_TYPE_AMBIENT; + copy_v3_v3(gp_light->color, color); + lightpool->light_used++; + + if (lightpool->light_used < GPENCIL_LIGHT_BUFFER_LEN) { + /* Tag light list end. */ + gp_light[1].color[0] = -1.0f; + } +} + +static float light_power_get(const Light *la) +{ + if (la->type == LA_AREA) { + return 1.0f / (4.0f * M_PI); + } + else if (la->type == LA_SPOT || la->type == LA_LOCAL) { + return 1.0f / (4.0f * M_PI * M_PI); + } + else { + return 1.0f / M_PI; + } +} + +void gpencil_light_pool_populate(GPENCIL_LightPool *lightpool, Object *ob) +{ + Light *la = (Light *)ob->data; + + if (lightpool->light_used >= GPENCIL_LIGHT_BUFFER_LEN) { + return; + } + + gpLight *gp_light = &lightpool->light_data[lightpool->light_used]; + float(*mat)[4] = (float(*)[4])gp_light->right; + + if (la->type == LA_SPOT) { + copy_m4_m4(mat, ob->imat); + gp_light->type = GP_LIGHT_TYPE_SPOT; + gp_light->spotsize = cosf(la->spotsize * 0.5f); + gp_light->spotblend = (1.0f - gp_light->spotsize) * la->spotblend; + } + else if (la->type == LA_AREA) { + /* Simulate area lights using a spot light. */ + normalize_m4_m4(mat, ob->obmat); + invert_m4(mat); + gp_light->type = GP_LIGHT_TYPE_SPOT; + gp_light->spotsize = cosf(M_PI * 0.5f); + gp_light->spotblend = (1.0f - gp_light->spotsize) * 1.0f; + } + else if (la->type == LA_SUN) { + normalize_v3_v3(gp_light->forward, ob->obmat[2]); + gp_light->type = GP_LIGHT_TYPE_SUN; + } + else { + gp_light->type = GP_LIGHT_TYPE_POINT; + } + copy_v4_v4(gp_light->position, ob->obmat[3]); + copy_v3_v3(gp_light->color, &la->r); + mul_v3_fl(gp_light->color, la->energy * light_power_get(la)); + + lightpool->light_used++; + + if (lightpool->light_used < GPENCIL_LIGHT_BUFFER_LEN) { + /* Tag light list end. */ + gp_light[1].color[0] = -1.0f; + } +} + +/** + * Creates a single pool containing all lights assigned (light linked) for a given object. + **/ +GPENCIL_LightPool *gpencil_light_pool_create(GPENCIL_PrivateData *pd, Object *UNUSED(ob)) +{ + GPENCIL_LightPool *lightpool = pd->last_light_pool; + + if (lightpool == NULL) { + lightpool = gpencil_light_pool_add(pd); + } + /* TODO(fclem) Light linking. */ + // gpencil_light_pool_populate(lightpool, ob); + + return lightpool; +} + +void gpencil_material_pool_free(void *storage) +{ + GPENCIL_MaterialPool *matpool = (GPENCIL_MaterialPool *)storage; + DRW_UBO_FREE_SAFE(matpool->ubo); +} + +void gpencil_light_pool_free(void *storage) +{ + GPENCIL_LightPool *lightpool = (GPENCIL_LightPool *)storage; + DRW_UBO_FREE_SAFE(lightpool->ubo); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name View Layer Data + * \{ */ + +static void gpencil_view_layer_data_free(void *storage) +{ + GPENCIL_ViewLayerData *vldata = (GPENCIL_ViewLayerData *)storage; + + BLI_memblock_destroy(vldata->gp_light_pool, gpencil_light_pool_free); + BLI_memblock_destroy(vldata->gp_material_pool, gpencil_material_pool_free); + BLI_memblock_destroy(vldata->gp_maskbit_pool, NULL); + BLI_memblock_destroy(vldata->gp_object_pool, NULL); + BLI_memblock_destroy(vldata->gp_layer_pool, NULL); + BLI_memblock_destroy(vldata->gp_vfx_pool, NULL); +} + +GPENCIL_ViewLayerData *GPENCIL_view_layer_data_ensure(void) +{ + GPENCIL_ViewLayerData **vldata = (GPENCIL_ViewLayerData **)DRW_view_layer_engine_data_ensure( + &draw_engine_gpencil_type, gpencil_view_layer_data_free); + + /* NOTE(fclem) Putting this stuff in viewlayer means it is shared by all viewports. + * For now it is ok, but in the future, it could become a problem if we implement + * the caching system. */ + if (*vldata == NULL) { + *vldata = MEM_callocN(sizeof(**vldata), "GPENCIL_ViewLayerData"); + + (*vldata)->gp_light_pool = BLI_memblock_create(sizeof(GPENCIL_LightPool)); + (*vldata)->gp_material_pool = BLI_memblock_create(sizeof(GPENCIL_MaterialPool)); + (*vldata)->gp_maskbit_pool = BLI_memblock_create(BLI_BITMAP_SIZE(GP_MAX_MASKBITS)); + (*vldata)->gp_object_pool = BLI_memblock_create(sizeof(GPENCIL_tObject)); + (*vldata)->gp_layer_pool = BLI_memblock_create(sizeof(GPENCIL_tLayer)); + (*vldata)->gp_vfx_pool = BLI_memblock_create(sizeof(GPENCIL_tVfx)); + } + + return *vldata; +} + +/** \} */ diff --git a/source/blender/draw/engines/gpencil/gpencil_draw_utils.c b/source/blender/draw/engines/gpencil/gpencil_draw_utils.c deleted file mode 100644 index d44aa5764b1..00000000000 --- a/source/blender/draw/engines/gpencil/gpencil_draw_utils.c +++ /dev/null @@ -1,2071 +0,0 @@ -/* - * 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_render.h" - -#include "BKE_gpencil.h" -#include "BKE_gpencil_modifier.h" -#include "BKE_image.h" -#include "BKE_material.h" -#include "BKE_paint.h" - -#include "BLI_hash.h" - -#include "ED_gpencil.h" - -#include "DNA_gpencil_types.h" -#include "DNA_material_types.h" -#include "DNA_view3d_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" - -#include "UI_resources.h" - -/* fill type to communicate to shader */ -#define SOLID 0 -#define GRADIENT 1 -#define RADIAL 2 -#define CHECKER 3 -#define TEXTURE 4 -#define PATTERN 5 - -/* Verify if must fade object or not. */ -static bool gpencil_fade_object_check(GPENCIL_StorageList *stl, Object *ob) -{ - const DRWContextState *draw_ctx = DRW_context_state_get(); - View3D *v3d = draw_ctx->v3d; - const bool is_overlay = (bool)((v3d) && ((v3d->flag2 & V3D_HIDE_OVERLAYS) == 0) && - (v3d->gp_flag & V3D_GP_SHOW_PAPER)); - - if ((!is_overlay) || (ob == draw_ctx->obact) || - ((v3d->gp_flag & V3D_GP_FADE_NOACTIVE_GPENCIL) == 0) || - (v3d->overlay.gpencil_paper_opacity == 1.0f)) { - return false; - } - - const bool playing = stl->storage->is_playing; - const bool is_render = (bool)stl->storage->is_render; - const bool is_mat_preview = (bool)stl->storage->is_mat_preview; - const bool is_select = (bool)(DRW_state_is_select() || DRW_state_is_depth()); - - return (bool)((!is_render) && (!playing) && (!is_mat_preview) && (!is_select)); -} - -/* Define Fade layer uniforms. */ -static void gpencil_set_fade_layer_uniforms( - GPENCIL_StorageList *stl, DRWShadingGroup *grp, Object *ob, bGPDlayer *gpl, const bool skip) -{ - const DRWContextState *draw_ctx = DRW_context_state_get(); - View3D *v3d = draw_ctx->v3d; - const bool overlay = v3d != NULL ? (bool)((v3d->flag2 & V3D_HIDE_OVERLAYS) == 0) : true; - const bool is_fade = (v3d) && (v3d->gp_flag & V3D_GP_FADE_NOACTIVE_LAYERS) && - (draw_ctx->obact) && (draw_ctx->obact == ob) && - ((gpl->flag & GP_LAYER_ACTIVE) == 0); - - const bool playing = stl->storage->is_playing; - const bool is_render = (bool)stl->storage->is_render; - const bool is_mat_preview = (bool)stl->storage->is_mat_preview; - const bool is_select = (bool)(DRW_state_is_select() || DRW_state_is_depth()); - - /* If drawing or not fading layer, skip. */ - if ((!overlay) || (skip) || (!is_fade) || (is_render) || (playing) || (is_mat_preview) || - (is_select)) { - DRW_shgroup_uniform_int_copy(grp, "fade_layer", 0); - return; - } - - /* If layer is above active, use alpha (2) if below use mix with background (1). */ - if (stl->storage->is_ontop) { - DRW_shgroup_uniform_int_copy(grp, "fade_layer", 2); - } - else { - DRW_shgroup_uniform_int_copy(grp, "fade_layer", 1); - } - if (v3d) { - DRW_shgroup_uniform_vec3(grp, "fade_color", v3d->shading.background_color, 1); - DRW_shgroup_uniform_float(grp, "fade_layer_factor", &v3d->overlay.gpencil_fade_layer, 1); - } -} - -/* Define Fade object uniforms. */ -static void gpencil_set_fade_ob_uniforms(View3D *v3d, DRWShadingGroup *grp, bool status) -{ - DRW_shgroup_uniform_bool_copy(grp, "fade_ob", status); - if (v3d) { - DRW_shgroup_uniform_vec3(grp, "fade_color", v3d->shading.background_color, 1); - DRW_shgroup_uniform_float(grp, "fade_ob_factor", &v3d->overlay.gpencil_paper_opacity, 1); - } -} - -/* Get number of vertex for using in GPU VBOs */ -static void gpencil_calc_vertex(GPENCIL_StorageList *stl, - tGPencilObjectCache *cache_ob, - GpencilBatchCache *cache, - bGPdata *gpd) -{ - if ((!cache->is_dirty) || (gpd == NULL)) { - return; - } - - Object *ob = cache_ob->ob; - const bool main_onion = stl->storage->is_main_onion; - const bool playing = stl->storage->is_playing; - const bool overlay = stl->storage->is_main_overlay; - const bool do_onion = (bool)((gpd->flag & GP_DATA_STROKE_WEIGHTMODE) == 0) && overlay && - main_onion && !playing && gpencil_onion_active(gpd); - - const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd); - - /* Onion skinning. */ - const int step = gpd->gstep; - const int mode = gpd->onion_mode; - const short onion_keytype = gpd->onion_keytype; - - cache_ob->tot_vertex = 0; - cache_ob->tot_triangles = 0; - int idx_eval = 0; - - for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { - bGPDframe *init_gpf = NULL; - const bool is_onion = ((do_onion) && (gpl->onion_flag & GP_LAYER_ONIONSKIN)); - if (gpl->flag & GP_LAYER_HIDE) { - idx_eval++; - continue; - } - - /* Relative onion mode needs to find the frame range before. */ - int frame_from = -9999; - int frame_to = 9999; - if ((is_onion) && (mode == GP_ONION_MODE_RELATIVE)) { - /* 1) Found first Frame. */ - int idx = 0; - if (gpl->actframe) { - for (bGPDframe *gf = gpl->actframe->prev; gf; gf = gf->prev) { - idx++; - frame_from = gf->framenum; - if (idx >= step) { - break; - } - } - /* 2) Found last Frame. */ - idx = 0; - for (bGPDframe *gf = gpl->actframe->next; gf; gf = gf->next) { - idx++; - frame_to = gf->framenum; - if (idx >= gpd->gstep_next) { - break; - } - } - } - } - - /* If multiedit or onion skin need to count all frames of the layer. */ - if ((is_multiedit) || (is_onion)) { - init_gpf = gpl->frames.first; - } - else { - init_gpf = &ob->runtime.gpencil_evaluated_frames[idx_eval]; - } - - if (init_gpf == NULL) { - continue; - } - - for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) { - for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { - if (!is_onion) { - if ((!is_multiedit) || - ((is_multiedit) && ((gpf == gpl->actframe) || (gpf->flag & GP_FRAME_SELECT)))) { - cache_ob->tot_vertex += gps->totpoints + 3; - cache_ob->tot_triangles += gps->totpoints - 1; - } - } - else { - bool select = ((is_multiedit) && - ((gpf == gpl->actframe) || (gpf->flag & GP_FRAME_SELECT))); - - if (!select) { - /* Only selected frames. */ - if ((mode == GP_ONION_MODE_SELECTED) && ((gpf->flag & GP_FRAME_SELECT) == 0)) { - continue; - } - /* Verify keyframe type. */ - if ((onion_keytype > -1) && (gpf->key_type != onion_keytype)) { - continue; - } - /* Absolute range. */ - if (mode == GP_ONION_MODE_ABSOLUTE) { - if ((gpl->actframe) && (abs(gpl->actframe->framenum - gpf->framenum) > step)) { - continue; - } - } - /* Relative range. */ - if (mode == GP_ONION_MODE_RELATIVE) { - if ((gpf->framenum < frame_from) || (gpf->framenum > frame_to)) { - continue; - } - } - } - - cache_ob->tot_vertex += gps->totpoints + 3; - cache_ob->tot_triangles += gps->totpoints - 1; - } - } - - /* If not multiframe nor Onion skin, don't need follow counting. */ - if ((!is_multiedit) && (!is_onion)) { - break; - } - } - idx_eval++; - } - - cache->b_fill.tot_vertex = cache_ob->tot_triangles * 3; - cache->b_stroke.tot_vertex = cache_ob->tot_vertex; - cache->b_point.tot_vertex = cache_ob->tot_vertex; - cache->b_edit.tot_vertex = cache_ob->tot_vertex; - cache->b_edlin.tot_vertex = cache_ob->tot_vertex; -} - -/* 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; -} - -/* recalc the internal geometry caches for fill and uvs */ -static void gpencil_recalc_geometry_caches(Object *ob, - bGPDlayer *gpl, - MaterialGPencilStyle *gp_style, - bGPDstroke *gps) -{ - if (gps->flag & GP_STROKE_RECALC_GEOMETRY) { - /* 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->flag & GP_STYLE_FILL_SHOW) && - ((gp_style->fill_rgba[3] > GPENCIL_ALPHA_OPACITY_THRESH) || (gp_style->fill_style > 0) || - (gpl->blend_mode != eGplBlendMode_Regular))) { - BKE_gpencil_triangulate_stroke_fill((bGPdata *)ob->data, gps); - } - } - - /* calc uv data along the stroke */ - ED_gpencil_calc_stroke_uv(ob, gps); - - /* clear flag */ - gps->flag &= ~GP_STROKE_RECALC_GEOMETRY; - } -} - -static void set_wireframe_color(Object *ob, - bGPDlayer *gpl, - View3D *v3d, - GPENCIL_StorageList *stl, - MaterialGPencilStyle *gp_style, - int id, - const bool is_fill) -{ - const DRWContextState *draw_ctx = DRW_context_state_get(); - World *world = draw_ctx->scene->world; - - float color[4]; - if (((gp_style->stroke_rgba[3] < GPENCIL_ALPHA_OPACITY_THRESH) || - (((gp_style->flag & GP_STYLE_STROKE_SHOW) == 0))) && - (gp_style->fill_rgba[3] >= GPENCIL_ALPHA_OPACITY_THRESH)) { - copy_v4_v4(color, gp_style->fill_rgba); - } - else { - copy_v4_v4(color, gp_style->stroke_rgba); - } - - /* wire color */ - if ((v3d) && (id > -1)) { - const char type = ((stl->shgroups[id].shading_type[0] == OB_WIRE) ? - v3d->shading.wire_color_type : - v3d->shading.color_type); - /* if fill and wire, use background color */ - if ((is_fill) && (stl->shgroups[id].shading_type[0] == OB_WIRE)) { - if (v3d->shading.background_type == V3D_SHADING_BACKGROUND_THEME) { - UI_GetThemeColor4fv(TH_BACK, stl->shgroups[id].wire_color); - stl->shgroups[id].wire_color[3] = 1.0f; - } - else if (v3d->shading.background_type == V3D_SHADING_BACKGROUND_WORLD) { - color[0] = world->horr; - color[1] = world->horg; - color[2] = world->horb; - color[3] = 1.0f; - copy_v4_v4(stl->shgroups[id].wire_color, color); - } - else { - copy_v3_v3(color, v3d->shading.background_color); - color[3] = 1.0f; - copy_v4_v4(stl->shgroups[id].wire_color, color); - } - return; - } - - /* strokes */ - switch (type) { - case V3D_SHADING_SINGLE_COLOR: { - if (stl->shgroups[id].shading_type[0] == OB_WIRE) { - UI_GetThemeColor4fv(TH_WIRE, color); - } - else { - copy_v3_v3(color, v3d->shading.single_color); - } - color[3] = 1.0f; - copy_v4_v4(stl->shgroups[id].wire_color, color); - break; - } - case V3D_SHADING_OBJECT_COLOR: { - copy_v4_v4(color, ob->color); - color[3] = 1.0f; - copy_v4_v4(stl->shgroups[id].wire_color, color); - break; - } - case V3D_SHADING_RANDOM_COLOR: { - uint gpl_hash = 1; - uint ob_hash = BLI_ghashutil_strhash_p_murmur(ob->id.name); - if (gpl) { - gpl_hash = BLI_ghashutil_strhash_p_murmur(gpl->info); - } - - float hue = BLI_hash_int_01(ob_hash * gpl_hash); - float hsv[3] = {hue, 0.40f, 0.8f}; - float wire_col[3]; - hsv_to_rgb_v(hsv, &wire_col[0]); - - copy_v3_v3(stl->shgroups[id].wire_color, wire_col); - stl->shgroups[id].wire_color[3] = 1.0f; - break; - } - default: { - copy_v4_v4(stl->shgroups[id].wire_color, color); - break; - } - } - } - else { - copy_v4_v4(stl->shgroups[id].wire_color, color); - } - - /* if solid, the alpha must be set to alpha */ - if (stl->shgroups[id].shading_type[0] == OB_SOLID) { - stl->shgroups[id].wire_color[3] = 1.0f; - } -} - -/* create shading group for filling */ -static DRWShadingGroup *gpencil_shgroup_fill_create(GPENCIL_e_data *e_data, - GPENCIL_Data *vedata, - DRWPass *pass, - GPUShader *shader, - Object *ob, - float (*obmat)[4], - bGPdata *gpd, - bGPDlayer *gpl, - MaterialGPencilStyle *gp_style, - int id, - const int shading_type[2]) -{ - GPENCIL_StorageList *stl = ((GPENCIL_Data *)vedata)->stl; - const DRWContextState *draw_ctx = DRW_context_state_get(); - View3D *v3d = draw_ctx->v3d; - - /* e_data.gpencil_fill_sh */ - DRWShadingGroup *grp = DRW_shgroup_create(shader, pass); - - DRW_shgroup_uniform_mat4(grp, "gpModelMatrix", obmat); - - 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_CHECKER: - stl->shgroups[id].fill_style = CHECKER; - 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); - DRW_shgroup_uniform_float(grp, "layer_opacity", &gpl->opacity, 1); - - stl->shgroups[id].texture_mix = gp_style->flag & GP_STYLE_FILL_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); - - stl->shgroups[id].xray_mode = (ob->dtx & OB_DRAWXRAY) ? GP_XRAY_FRONT : GP_XRAY_3DSPACE; - DRW_shgroup_uniform_int(grp, "xraymode", &stl->shgroups[id].xray_mode, 1); - DRW_shgroup_uniform_int(grp, "drawmode", (const int *)&gpd->draw_mode, 1); - - /* viewport x-ray */ - stl->shgroups[id].is_xray = (ob->dt == OB_WIRE) ? 1 : stl->storage->is_xray; - DRW_shgroup_uniform_int(grp, "viewport_xray", (const int *)&stl->shgroups[id].is_xray, 1); - - /* shading type */ - stl->shgroups[id].shading_type[0] = GPENCIL_USE_SOLID(stl) ? (int)OB_RENDER : shading_type[0]; - if (v3d) { - stl->shgroups[id].shading_type[1] = ((stl->shgroups[id].shading_type[0] == OB_WIRE) ? - v3d->shading.wire_color_type : - v3d->shading.color_type); - } - - DRW_shgroup_uniform_int(grp, "shading_type", &stl->shgroups[id].shading_type[0], 2); - - /* Fade layer uniforms. */ - gpencil_set_fade_layer_uniforms(stl, grp, ob, gpl, false); - - /* Fade object uniforms. */ - gpencil_set_fade_ob_uniforms(v3d, grp, gpencil_fade_object_check(stl, ob)); - - /* wire color */ - set_wireframe_color(ob, gpl, v3d, stl, gp_style, id, true); - DRW_shgroup_uniform_vec4(grp, "wire_color", stl->shgroups[id].wire_color, 1); - - /* image texture */ - if ((gp_style->flag & GP_STYLE_FILL_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, ibuf, GL_TEXTURE_2D); - DRW_shgroup_uniform_texture(grp, "myTexture", texture); - DRW_shgroup_uniform_bool_copy( - grp, "myTexturePremultiplied", (image->alpha_mode == IMA_ALPHA_PREMUL)); - - 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; -} - -/* check if some onion is enabled */ -bool gpencil_onion_active(bGPdata *gpd) -{ - for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { - if (gpl->onion_flag & GP_LAYER_ONIONSKIN) { - return true; - } - } - return false; -} - -/* create shading group for strokes */ -DRWShadingGroup *gpencil_shgroup_stroke_create(GPENCIL_e_data *e_data, - GPENCIL_Data *vedata, - DRWPass *pass, - GPUShader *shader, - Object *ob, - float (*obmat)[4], - bGPdata *gpd, - bGPDlayer *gpl, - bGPDstroke *gps, - MaterialGPencilStyle *gp_style, - int id, - bool onion, - const float scale, - const int shading_type[2]) -{ - GPENCIL_StorageList *stl = ((GPENCIL_Data *)vedata)->stl; - const float *viewport_size = DRW_viewport_size_get(); - const DRWContextState *draw_ctx = DRW_context_state_get(); - View3D *v3d = draw_ctx->v3d; - - /* 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_mat4(grp, "gpModelMatrix", obmat); - - /* avoid wrong values */ - if ((gpd) && (gpd->pixfactor == 0.0f)) { - gpd->pixfactor = GP_DEFAULT_PIX_FACTOR; - } - - /* object scale and depth */ - if ((ob) && (id > -1)) { - stl->shgroups[id].obj_scale = scale; - 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); - - stl->shgroups[id].caps_mode[0] = gps->caps[0]; - stl->shgroups[id].caps_mode[1] = gps->caps[1]; - DRW_shgroup_uniform_int(grp, "caps_mode", &stl->shgroups[id].caps_mode[0], 2); - - stl->shgroups[id].gradient_f = gps->gradient_f; - copy_v2_v2(stl->shgroups[id].gradient_s, gps->gradient_s); - DRW_shgroup_uniform_float(grp, "gradient_f", &stl->shgroups[id].gradient_f, 1); - - /* viewport x-ray */ - stl->shgroups[id].is_xray = (ob->dt == OB_WIRE) ? 1 : stl->storage->is_xray; - DRW_shgroup_uniform_int(grp, "viewport_xray", (const int *)&stl->shgroups[id].is_xray, 1); - - stl->shgroups[id].shading_type[0] = (GPENCIL_USE_SOLID(stl) || onion) ? (int)OB_RENDER : - shading_type[0]; - if (v3d) { - stl->shgroups[id].shading_type[1] = ((stl->shgroups[id].shading_type[0] == OB_WIRE) ? - v3d->shading.wire_color_type : - v3d->shading.color_type); - } - DRW_shgroup_uniform_int(grp, "shading_type", &stl->shgroups[id].shading_type[0], 2); - - /* Fade layer uniforms. */ - gpencil_set_fade_layer_uniforms(stl, grp, ob, gpl, false); - - /* Fade object uniforms. */ - gpencil_set_fade_ob_uniforms(v3d, grp, gpencil_fade_object_check(stl, ob)); - - /* wire color */ - set_wireframe_color(ob, gpl, v3d, stl, gp_style, id, false); - DRW_shgroup_uniform_vec4(grp, "wire_color", stl->shgroups[id].wire_color, 1); - - /* mix stroke factor */ - stl->shgroups[id].mix_stroke_factor = (gp_style->flag & GP_STYLE_STROKE_TEX_MIX) ? - gp_style->mix_stroke_factor : - 0.0f; - DRW_shgroup_uniform_float(grp, "mix_stroke_factor", &stl->shgroups[id].mix_stroke_factor, 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); - } - const int zero[2] = {0, 0}; - DRW_shgroup_uniform_int(grp, "caps_mode", &zero[0], 2); - - DRW_shgroup_uniform_float(grp, "gradient_f", &stl->storage->gradient_f, 1); - - /* viewport x-ray */ - DRW_shgroup_uniform_int(grp, "viewport_xray", &stl->storage->is_xray, 1); - DRW_shgroup_uniform_int(grp, "shading_type", (const int *)&stl->storage->shade_render, 2); - - /* mix stroke factor */ - stl->storage->mix_stroke_factor = (gp_style->flag & GP_STYLE_STROKE_TEX_MIX) ? - gp_style->mix_stroke_factor : - 0.0f; - DRW_shgroup_uniform_float(grp, "mix_stroke_factor", &stl->storage->mix_stroke_factor, 1); - } - - DRW_shgroup_uniform_vec4(grp, "colormix", gp_style->stroke_rgba, 1); - - if ((gpd) && (id > -1)) { - stl->shgroups[id].xray_mode = (ob->dtx & OB_DRAWXRAY) ? GP_XRAY_FRONT : GP_XRAY_3DSPACE; - DRW_shgroup_uniform_int(grp, "xraymode", &stl->shgroups[id].xray_mode, 1); - } - else { - /* for drawing always on predefined z-depth */ - DRW_shgroup_uniform_int(grp, "xraymode", &stl->storage->xray, 1); - } - - /* Fade layer uniforms. */ - gpencil_set_fade_layer_uniforms(stl, grp, ob, gpl, true); - - /* Fade object uniforms. */ - gpencil_set_fade_ob_uniforms(v3d, grp, false); - - /* 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, ibuf, GL_TEXTURE_2D); - DRW_shgroup_uniform_texture(grp, "myTexture", texture); - DRW_shgroup_uniform_bool_copy( - grp, "myTexturePremultiplied", (image->alpha_mode == IMA_ALPHA_PREMUL)); - - 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 points */ -static DRWShadingGroup *gpencil_shgroup_point_create(GPENCIL_e_data *e_data, - GPENCIL_Data *vedata, - DRWPass *pass, - GPUShader *shader, - Object *ob, - float (*obmat)[4], - bGPdata *gpd, - bGPDlayer *gpl, - bGPDstroke *gps, - MaterialGPencilStyle *gp_style, - int id, - bool onion, - const float scale, - const int shading_type[2]) -{ - GPENCIL_StorageList *stl = ((GPENCIL_Data *)vedata)->stl; - const float *viewport_size = DRW_viewport_size_get(); - const DRWContextState *draw_ctx = DRW_context_state_get(); - View3D *v3d = draw_ctx->v3d; - - /* 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_mat4(grp, "gpModelMatrix", obmat); - - /* avoid wrong values */ - if ((gpd) && (gpd->pixfactor == 0.0f)) { - gpd->pixfactor = GP_DEFAULT_PIX_FACTOR; - } - - /* object scale and depth */ - if ((ob) && (id > -1)) { - stl->shgroups[id].obj_scale = scale; - 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); - - stl->shgroups[id].gradient_f = gps->gradient_f; - copy_v2_v2(stl->shgroups[id].gradient_s, gps->gradient_s); - DRW_shgroup_uniform_float(grp, "gradient_f", &stl->shgroups[id].gradient_f, 1); - DRW_shgroup_uniform_vec2(grp, "gradient_s", stl->shgroups[id].gradient_s, 1); - - /* viewport x-ray */ - stl->shgroups[id].is_xray = (ob->dt == OB_WIRE) ? 1 : stl->storage->is_xray; - DRW_shgroup_uniform_int(grp, "viewport_xray", (const int *)&stl->shgroups[id].is_xray, 1); - - stl->shgroups[id].shading_type[0] = (GPENCIL_USE_SOLID(stl) || onion) ? (int)OB_RENDER : - shading_type[0]; - if (v3d) { - stl->shgroups[id].shading_type[1] = ((stl->shgroups[id].shading_type[0] == OB_WIRE) ? - v3d->shading.wire_color_type : - v3d->shading.color_type); - } - DRW_shgroup_uniform_int(grp, "shading_type", &stl->shgroups[id].shading_type[0], 2); - - /* Fade layer uniforms. */ - gpencil_set_fade_layer_uniforms(stl, grp, ob, gpl, false); - - /* Fade object uniforms. */ - gpencil_set_fade_ob_uniforms(v3d, grp, gpencil_fade_object_check(stl, ob)); - - /* wire color */ - set_wireframe_color(ob, gpl, v3d, stl, gp_style, id, false); - DRW_shgroup_uniform_vec4(grp, "wire_color", stl->shgroups[id].wire_color, 1); - - /* mix stroke factor */ - stl->shgroups[id].mix_stroke_factor = (gp_style->flag & GP_STYLE_STROKE_TEX_MIX) ? - gp_style->mix_stroke_factor : - 0.0f; - DRW_shgroup_uniform_float(grp, "mix_stroke_factor", &stl->shgroups[id].mix_stroke_factor, 1); - - /* lock rotation of dots and boxes */ - stl->shgroups[id].alignment_mode = gp_style->alignment_mode; - DRW_shgroup_uniform_int(grp, "alignment_mode", &stl->shgroups[id].alignment_mode, 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); - } - - DRW_shgroup_uniform_float(grp, "gradient_f", &stl->storage->gradient_f, 1); - DRW_shgroup_uniform_vec2(grp, "gradient_s", stl->storage->gradient_s, 1); - - /* viewport x-ray */ - DRW_shgroup_uniform_int(grp, "viewport_xray", &stl->storage->is_xray, 1); - DRW_shgroup_uniform_int(grp, "shading_type", (const int *)&stl->storage->shade_render, 2); - - /* mix stroke factor */ - stl->storage->mix_stroke_factor = (gp_style->flag & GP_STYLE_STROKE_TEX_MIX) ? - gp_style->mix_stroke_factor : - 0.0f; - DRW_shgroup_uniform_float(grp, "mix_stroke_factor", &stl->storage->mix_stroke_factor, 1); - - /* lock rotation of dots and boxes */ - DRW_shgroup_uniform_int(grp, "alignment_mode", &stl->storage->alignment_mode, 1); - } - - DRW_shgroup_uniform_vec4(grp, "colormix", gp_style->stroke_rgba, 1); - - if ((gpd) && (id > -1)) { - stl->shgroups[id].xray_mode = (ob->dtx & OB_DRAWXRAY) ? GP_XRAY_FRONT : GP_XRAY_3DSPACE; - DRW_shgroup_uniform_int(grp, "xraymode", (const int *)&stl->shgroups[id].xray_mode, 1); - } - else { - /* for drawing always on predefined z-depth */ - DRW_shgroup_uniform_int(grp, "xraymode", &stl->storage->xray, 1); - } - - /* Fade layer uniforms. */ - gpencil_set_fade_layer_uniforms(stl, grp, ob, gpl, true); - - /* Fade object uniforms. */ - gpencil_set_fade_ob_uniforms(v3d, grp, false); - - /* 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, ibuf, GL_TEXTURE_2D); - DRW_shgroup_uniform_texture(grp, "myTexture", texture); - DRW_shgroup_uniform_bool_copy( - grp, "myTexturePremultiplied", (image->alpha_mode == IMA_ALPHA_PREMUL)); - - 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 vertex info */ -static void gpencil_add_fill_vertexdata(GpencilBatchCache *cache, - Object *ob, - bGPDlayer *gpl, - bGPDframe *gpf, - bGPDstroke *gps, - float opacity, - const float tintcolor[4], - const bool onion, - const bool custonion) -{ - MaterialGPencilStyle *gp_style = BKE_gpencil_material_settings(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] * opacity; - if ((tfill[3] > GPENCIL_ALPHA_OPACITY_THRESH) || (gp_style->fill_style > 0) || - (gpl->blend_mode != eGplBlendMode_Regular)) { - if (cache->is_dirty) { - 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; - } - } - /* create vertex data */ - const int old_len = cache->b_fill.vbo_len; - gpencil_get_fill_geom(&cache->b_fill, ob, gps, color); - - /* add to list of groups */ - if (old_len < cache->b_fill.vbo_len) { - cache->grp_cache = gpencil_group_cache_add(cache->grp_cache, - gpl, - gpf, - gps, - eGpencilBatchGroupType_Fill, - onion, - cache->b_fill.vbo_len, - &cache->grp_size, - &cache->grp_used); - } - } - } - } -} - -/* add stroke vertex info */ -static void gpencil_add_stroke_vertexdata(GpencilBatchCache *cache, - 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_gpencil_material_settings(ob, gps->mat_nr + 1); - const int alignment_mode = (gp_style) ? gp_style->alignment_mode : GP_STYLE_FOLLOW_PATH; - - /* set color using base color, tint color and opacity */ - if (cache->is_dirty) { - 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 ((gps->totpoints > 1) && (gp_style->mode == GP_STYLE_MODE_LINE)) { - /* create vertex data */ - const int old_len = cache->b_stroke.vbo_len; - gpencil_get_stroke_geom(&cache->b_stroke, gps, sthickness, ink); - - /* add to list of groups */ - if (old_len < cache->b_stroke.vbo_len) { - cache->grp_cache = gpencil_group_cache_add(cache->grp_cache, - gpl, - gpf, - gps, - eGpencilBatchGroupType_Stroke, - onion, - cache->b_stroke.vbo_len, - &cache->grp_size, - &cache->grp_used); - } - } - else { - /* create vertex data */ - const int old_len = cache->b_point.vbo_len; - gpencil_get_point_geom(&cache->b_point, gps, sthickness, ink, alignment_mode); - - /* add to list of groups */ - if (old_len < cache->b_point.vbo_len) { - cache->grp_cache = gpencil_group_cache_add(cache->grp_cache, - gpl, - gpf, - gps, - eGpencilBatchGroupType_Point, - onion, - cache->b_point.vbo_len, - &cache->grp_size, - &cache->grp_used); - } - } - } -} - -/* add edit points vertex info */ -static void gpencil_add_editpoints_vertexdata(GpencilBatchCache *cache, - Object *ob, - bGPdata *gpd, - bGPDlayer *gpl, - bGPDframe *gpf, - bGPDstroke *gps) -{ - const DRWContextState *draw_ctx = DRW_context_state_get(); - View3D *v3d = draw_ctx->v3d; - ToolSettings *ts = draw_ctx->scene->toolsettings; - const bool use_sculpt_mask = (GPENCIL_SCULPT_MODE(gpd) && (ts->gpencil_selectmode_sculpt & - (GP_SCULPT_MASK_SELECTMODE_POINT | - GP_SCULPT_MASK_SELECTMODE_STROKE | - GP_SCULPT_MASK_SELECTMODE_SEGMENT))); - - const bool show_sculpt_points = (GPENCIL_SCULPT_MODE(gpd) && - (ts->gpencil_selectmode_sculpt & - (GP_SCULPT_MASK_SELECTMODE_POINT | - GP_SCULPT_MASK_SELECTMODE_SEGMENT))); - - MaterialGPencilStyle *gp_style = BKE_gpencil_material_settings(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); - - /* If Sculpt mode and the mask is disabled, the select must be hidden. */ - const bool hide_select = GPENCIL_SCULPT_MODE(gpd) && !use_sculpt_mask; - - /* Show Edit points if: - * Edit mode: Not in Stroke selection mode - * Sculpt mode: Not in Stroke mask mode and any other mask mode enabled - * Weight mode: Always - */ - const bool show_points = (show_sculpt_points) || (is_weight_paint) || - (GPENCIL_EDIT_MODE(gpd) && - ((ts->gpencil_selectmode_edit != GP_SELECTMODE_STROKE) || - (gps->totpoints == 1))); - - if (cache->is_dirty) { - if ((obact == ob) && ((v3d->flag2 & V3D_HIDE_OVERLAYS) == 0) && - (v3d->gp_flag & V3D_GP_SHOW_EDIT_LINES) && (gps->totpoints > 1)) { - - /* line of the original stroke */ - gpencil_get_edlin_geom(&cache->b_edlin, gps, edit_alpha, hide_select); - - /* add to list of groups */ - cache->grp_cache = gpencil_group_cache_add(cache->grp_cache, - gpl, - gpf, - gps, - eGpencilBatchGroupType_Edlin, - false, - cache->b_edlin.vbo_len, - &cache->grp_size, - &cache->grp_used); - } - - /* If the points are hidden return. */ - if ((!show_points) || (hide_select)) { - return; - } - - /* 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 (obact == ob) { - gpencil_get_edit_geom(&cache->b_edit, gps, edit_alpha, gpd->flag); - - /* add to list of groups */ - cache->grp_cache = gpencil_group_cache_add(cache->grp_cache, - gpl, - gpf, - gps, - eGpencilBatchGroupType_Edit, - false, - cache->b_edit.vbo_len, - &cache->grp_size, - &cache->grp_used); - } - } - } - } - } -} - -/* main function to draw strokes */ -static void gpencil_draw_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, - tGPencilObjectCache *cache_ob) -{ - 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; - const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd); - const bool playing = stl->storage->is_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->gp_flag & 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 */ - if ((cache_ob != NULL) && (cache_ob->is_dup_ob)) { - copy_m4_m4(gpf->runtime.parent_obmat, cache_ob->obmat); - } - else { - ED_gpencil_parent_location(depsgraph, ob, gpd, gpl, gpf->runtime.parent_obmat); - } - - for (gps = gpf->strokes.first; gps; gps = gps->next) { - MaterialGPencilStyle *gp_style = BKE_gpencil_material_settings(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; - } - - /* Copy color to temp fields. */ - if ((is_multiedit) && (gp_style)) { - copy_v4_v4(gps->runtime.tmp_stroke_rgba, gp_style->stroke_rgba); - copy_v4_v4(gps->runtime.tmp_fill_rgba, gp_style->fill_rgba); - } - - /* be sure recalc all cache in source stroke to avoid recalculation when frame change - * and improve fps */ - gpencil_recalc_geometry_caches( - ob, gpl, gp_style, (gps->runtime.gps_orig) ? gps->runtime.gps_orig : 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) || - (gpl->blend_mode != eGplBlendMode_Regular)) { - - continue; - } - } - - if ((gpl->actframe && (gpl->actframe->framenum == gpf->framenum)) || (!is_multiedit) || - (overlay_multiedit)) { - /* hide any blend layer */ - if ((!stl->storage->simplify_blend) || (gpl->blend_mode == eGplBlendMode_Regular)) { - /* fill */ - if ((gp_style->flag & GP_STYLE_FILL_SHOW) && (!stl->storage->simplify_fill) && - ((gps->flag & GP_STROKE_NOFILL) == 0)) { - gpencil_add_fill_vertexdata( - cache, ob, gpl, gpf, gps, opacity, tintcolor, false, custonion); - } - /* stroke - * No fill strokes, must show stroke always or if the total points is lower than 3, - * because the stroke cannot be filled and it would be invisible. */ - if (((gp_style->flag & GP_STYLE_STROKE_SHOW) || (gps->flag & GP_STROKE_NOFILL) || - (gps->totpoints < 3)) && - ((gp_style->stroke_rgba[3] > GPENCIL_ALPHA_OPACITY_THRESH) || - (gpl->blend_mode == eGplBlendMode_Regular))) { - /* recalc strokes uv (geometry can be changed by modifiers) */ - if (gps->flag & GP_STROKE_RECALC_GEOMETRY) { - ED_gpencil_calc_stroke_uv(ob, gps); - } - - gpencil_add_stroke_vertexdata( - cache, ob, gpl, gpf, gps, opacity, tintcolor, false, custonion); - } - } - } - - /* edit points (only in edit mode and not play animation not render) */ - if ((draw_ctx->obact == ob) && (!playing) && (!is_render) && (!cache_ob->is_dup_ob)) { - if ((gpl->flag & GP_LAYER_LOCKED) == 0) { - if (!stl->g_data->shgrps_edit_line) { - stl->g_data->shgrps_edit_line = DRW_shgroup_create(e_data->gpencil_line_sh, - psl->edit_pass); - DRW_shgroup_uniform_mat4(stl->g_data->shgrps_edit_line, "gpModelMatrix", ob->obmat); - } - 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); - DRW_shgroup_uniform_mat4(stl->g_data->shgrps_edit_point, "gpModelMatrix", ob->obmat); - } - - gpencil_add_editpoints_vertexdata(cache, ob, gpd, gpl, gpf, gps); - } - } - } -} - -/* 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); -} - -/* function to draw strokes for onion only */ -static void gpencil_draw_onion_strokes(GpencilBatchCache *cache, - void *vedata, - Object *ob, - bGPdata *gpd, - bGPDlayer *gpl, - bGPDframe *gpf, - const float opacity, - const float tintcolor[4], - const bool custonion) -{ - GPENCIL_StorageList *stl = ((GPENCIL_Data *)vedata)->stl; - Depsgraph *depsgraph = DRW_context_state_get()->depsgraph; - - /* get parent matrix and save as static data */ - ED_gpencil_parent_location(depsgraph, ob, gpd, gpl, gpf->runtime.parent_obmat); - - for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { - MaterialGPencilStyle *gp_style = BKE_gpencil_material_settings(ob, gps->mat_nr + 1); - if (gp_style == NULL) { - continue; - } - 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; - } - - /* stroke */ - gpencil_add_stroke_vertexdata(cache, ob, gpl, gpf, gps, opacity, tintcolor, true, custonion); - - stl->storage->shgroup_id++; - } -} - -/* draw onion-skinning for a layer */ -static void gpencil_draw_onionskins(GpencilBatchCache *cache, - 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; - bool colflag = false; - const int mode = gpd->onion_mode; - bGPDframe *gpf_loop = ((gpd->onion_flag & GP_ONION_LOOP) && (mode != GP_ONION_MODE_SELECTED)) ? - gpl->frames.first : - NULL; - int last = gpf->framenum; - - colflag = (gpd->onion_flag & GP_ONION_GHOST_PREVCOL) != 0; - const short onion_keytype = gpd->onion_keytype; - /* ------------------------------- - * 1) Draw Previous Frames First - * ------------------------------- */ - step = gpd->gstep; - - 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; - } - /* verify keyframe type */ - if ((onion_keytype > -1) && (gf->key_type != onion_keytype)) { - 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_SELECTED) && (gpd->onion_flag & GP_ONION_LOOP)) { - gpf_loop = gf; - } - - gpencil_get_onion_alpha(color, gpd); - gpencil_draw_onion_strokes(cache, vedata, ob, gpd, gpl, gf, color[3], color, colflag); - } - /* ------------------------------- - * 2) Now draw next frames - * ------------------------------- */ - step = gpd->gstep_next; - - 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; - } - /* verify keyframe type */ - if ((onion_keytype > -1) && (gf->key_type != onion_keytype)) { - 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, 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, vedata, ob, gpd, gpl, gpf_loop, color[3], color, colflag); - } - } -} - -/* Check if stencil is required */ -static bool gpencil_is_stencil_required(MaterialGPencilStyle *gp_style) -{ - return (bool)((gp_style->stroke_style == GP_STYLE_STROKE_STYLE_SOLID) && - ((gp_style->flag & GP_STYLE_DISABLE_STENCIL) == 0)); -} - -/* draw stroke in drawing buffer */ -void 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; - const DRWContextState *draw_ctx = DRW_context_state_get(); - View3D *v3d = draw_ctx->v3d; - const bool overlay = v3d != NULL ? (bool)((v3d->flag2 & V3D_HIDE_OVERLAYS) == 0) : true; - Brush *brush = BKE_paint_brush(&ts->gp_paint->paint); - const bool is_paint_tool = (bool)((brush) && (brush->gpencil_tool == GPAINT_TOOL_DRAW)); - bGPdata *gpd_eval = ob->data; - /* need the original to avoid cow overhead while drawing */ - bGPdata *gpd = (bGPdata *)DEG_get_original_id(&gpd_eval->id); - - MaterialGPencilStyle *gp_style = NULL; - float obscale = mat4_to_scale(ob->obmat); - - /* use the brush material */ - Material *ma = BKE_gpencil_object_material_get_from_brush(ob, 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_gpencil_material_settings(ob, ob->actcol); - } - - static float unit_mat[4][4] = { - {1.0, 0.0, 0.0, 0.0}, {0.0, 1.0, 0.0, 0.0}, {0.0, 0.0, 1.0, 0.0}, {0.0, 0.0, 0.0, 1.0}}; - - /* 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 (gpd->runtime.sbuffer_used > 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; - - /* save gradient info */ - stl->storage->gradient_f = brush->gpencil_settings->gradient_f; - copy_v2_v2(stl->storage->gradient_s, brush->gpencil_settings->gradient_s); - stl->storage->alignment_mode = (gp_style) ? gp_style->alignment_mode : GP_STYLE_FOLLOW_PATH; - - /* if only one point, don't need to draw buffer because the user has no time to see it */ - if (gpd->runtime.sbuffer_used > 1) { - if ((gp_style) && (gp_style->mode == GP_STYLE_MODE_LINE)) { - stl->g_data->shgrps_drawing_stroke = gpencil_shgroup_stroke_create( - e_data, - vedata, - psl->drawing_pass, - e_data->gpencil_stroke_sh, - NULL, - unit_mat, - gpd, - NULL, - NULL, - gp_style, - -1, - false, - 1.0f, - (const int *)stl->storage->shade_render); - - if (gpencil_is_stencil_required(gp_style)) { - DRW_shgroup_stencil_mask(stl->g_data->shgrps_drawing_stroke, 0x01); - } - else { - /* Disable stencil for this type */ - DRW_shgroup_state_disable(stl->g_data->shgrps_drawing_stroke, - DRW_STATE_WRITE_STENCIL | DRW_STATE_STENCIL_NEQUAL); - } - } - else { - stl->g_data->shgrps_drawing_stroke = gpencil_shgroup_point_create( - e_data, - vedata, - psl->drawing_pass, - e_data->gpencil_point_sh, - NULL, - unit_mat, - gpd, - NULL, - NULL, - gp_style, - -1, - false, - 1.0f, - (const int *)stl->storage->shade_render); - /* Disable stencil for this type */ - DRW_shgroup_state_disable(stl->g_data->shgrps_drawing_stroke, - DRW_STATE_WRITE_STENCIL | DRW_STATE_STENCIL_NEQUAL); - } - - /* 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 = gpencil_get_buffer_stroke_geom(gpd, lthick); - } - else { - stl->g_data->batch_buffer_stroke = gpencil_get_buffer_point_geom(gpd, lthick); - } - - /* buffer strokes, must show stroke always */ - DRW_shgroup_call( - stl->g_data->shgrps_drawing_stroke, stl->g_data->batch_buffer_stroke, NULL); - - if ((gpd->runtime.sbuffer_used >= 3) && - (gpd->runtime.sfill[3] > GPENCIL_ALPHA_OPACITY_THRESH) && - ((gpd->runtime.sbuffer_sflag & GP_STROKE_NOFILL) == 0) && - ((brush->gpencil_settings->flag & GP_BRUSH_DISSABLE_LASSO) == 0) && - (gp_style->flag & GP_STYLE_FILL_SHOW)) { - /* 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); - /* Disable stencil for this type */ - DRW_shgroup_state_disable(stl->g_data->shgrps_drawing_fill, - DRW_STATE_WRITE_STENCIL | DRW_STATE_STENCIL_NEQUAL); - - stl->g_data->batch_buffer_fill = gpencil_get_buffer_fill_geom(gpd); - DRW_shgroup_call(stl->g_data->shgrps_drawing_fill, stl->g_data->batch_buffer_fill, NULL); - } - } - } - } - - /* control points for primitives and speed guide */ - const bool is_cppoint = (gpd->runtime.tot_cp_points > 0); - const bool is_speed_guide = (ts->gp_sculpt.guide.use_guide && - (draw_ctx->object_mode == OB_MODE_PAINT_GPENCIL)); - const bool is_show_gizmo = (((v3d->gizmo_flag & V3D_GIZMO_HIDE) == 0) && - ((v3d->gizmo_flag & V3D_GIZMO_HIDE_TOOL) == 0)); - - if ((overlay) && (is_paint_tool) && (is_cppoint || is_speed_guide) && (is_show_gizmo) && - ((gpd->runtime.sbuffer_sflag & GP_STROKE_ERASER) == 0)) { - DRWShadingGroup *shgrp = DRW_shgroup_create(e_data->gpencil_edit_point_sh, psl->drawing_pass); - const float *viewport_size = DRW_viewport_size_get(); - DRW_shgroup_uniform_vec2(shgrp, "Viewport", viewport_size, 1); - DRW_shgroup_uniform_mat4(shgrp, "gpModelMatrix", unit_mat); - - /* Disable stencil for this type */ - DRW_shgroup_state_disable(shgrp, DRW_STATE_WRITE_STENCIL | DRW_STATE_STENCIL_NEQUAL); - stl->g_data->batch_buffer_ctrlpoint = gpencil_get_buffer_ctrlpoint_geom(gpd); - - DRW_shgroup_call(shgrp, stl->g_data->batch_buffer_ctrlpoint, NULL); - } -} - -/* create all missing batches */ -static void gpencil_batches_ensure(GpencilBatchCache *cache) -{ - if ((cache->b_point.vbo) && (cache->b_point.batch == NULL)) { - cache->b_point.batch = GPU_batch_create_ex( - GPU_PRIM_POINTS, cache->b_point.vbo, NULL, GPU_BATCH_OWNS_VBO); - } - if ((cache->b_stroke.vbo) && (cache->b_stroke.batch == NULL)) { - cache->b_stroke.batch = GPU_batch_create_ex( - GPU_PRIM_LINE_STRIP_ADJ, cache->b_stroke.vbo, NULL, GPU_BATCH_OWNS_VBO); - } - if ((cache->b_fill.vbo) && (cache->b_fill.batch == NULL)) { - cache->b_fill.batch = GPU_batch_create_ex( - GPU_PRIM_TRIS, cache->b_fill.vbo, NULL, GPU_BATCH_OWNS_VBO); - } - if ((cache->b_edit.vbo) && (cache->b_edit.batch == NULL)) { - cache->b_edit.batch = GPU_batch_create_ex( - GPU_PRIM_POINTS, cache->b_edit.vbo, NULL, GPU_BATCH_OWNS_VBO); - } - if ((cache->b_edlin.vbo) && (cache->b_edlin.batch == NULL)) { - cache->b_edlin.batch = GPU_batch_create_ex( - GPU_PRIM_LINE_STRIP, cache->b_edlin.vbo, NULL, GPU_BATCH_OWNS_VBO); - } -} - -/* create all shading groups */ -static void gpencil_shgroups_create(GPENCIL_e_data *e_data, - void *vedata, - Object *ob, - GpencilBatchCache *cache, - tGPencilObjectCache *cache_ob) -{ - GPENCIL_StorageList *stl = ((GPENCIL_Data *)vedata)->stl; - GPENCIL_PassList *psl = ((GPENCIL_Data *)vedata)->psl; - bGPdata *gpd = (bGPdata *)ob->data; - DRWPass *stroke_pass = GPENCIL_3D_DRAWMODE(ob, gpd) ? psl->stroke_pass_3d : psl->stroke_pass_2d; - - GpencilBatchGroup *elm = NULL; - DRWShadingGroup *shgrp = NULL; - tGPencilObjectCache_shgrp *array_elm = NULL; - - bGPDlayer *gpl = NULL; - bGPDlayer *gpl_prev = NULL; - int idx = 0; - bool tag_first = false; - - const DRWContextState *draw_ctx = DRW_context_state_get(); - const View3D *v3d = draw_ctx->v3d; - - const bool overlay = draw_ctx->v3d != NULL ? - (bool)((draw_ctx->v3d->flag2 & V3D_HIDE_OVERLAYS) == 0) : - true; - const bool screen_onion = v3d != NULL ? (v3d->gp_flag & V3D_GP_SHOW_ONION_SKIN) : true; - const bool do_onion = (bool)((gpd->flag & GP_DATA_STROKE_WEIGHTMODE) == 0) && screen_onion && - overlay && gpencil_onion_active(gpd); - - int start_stroke = 0; - int start_point = 0; - int start_fill = 0; - int start_edit = 0; - int start_edlin = 0; - - uint stencil_id = 1; - /* Flag to determine if the layer is above active layer. */ - stl->storage->is_ontop = false; - for (int i = 0; i < cache->grp_used; i++) { - elm = &cache->grp_cache[i]; - array_elm = &cache_ob->shgrp_array[idx]; - - /* Limit stencil id */ - if (stencil_id > 255) { - stencil_id = 1; - } - - /* save last group when change */ - if (gpl_prev == NULL) { - gpl_prev = elm->gpl; - tag_first = true; - } - else { - if (elm->gpl != gpl_prev) { - /* first layer is always blend Normal */ - array_elm->mode = idx == 0 ? eGplBlendMode_Regular : gpl->blend_mode; - array_elm->end_shgrp = shgrp; - gpl_prev = elm->gpl; - tag_first = true; - idx++; - } - } - - gpl = elm->gpl; - if ((!stl->storage->is_ontop) && (gpl->flag & GP_LAYER_ACTIVE)) { - stl->storage->is_ontop = true; - } - - bGPDframe *gpf = elm->gpf; - bGPDstroke *gps = elm->gps; - MaterialGPencilStyle *gp_style = BKE_gpencil_material_settings(ob, gps->mat_nr + 1); - /* if the user switch used material from data to object, - * the material could not be available */ - if (gp_style == NULL) { - break; - } - - /* limit the number of shading groups */ - if (i >= GPENCIL_MAX_SHGROUPS) { - break; - } - - const float scale = (!cache_ob->is_dup_ob) ? mat4_to_scale(gpf->runtime.parent_obmat) : - cache_ob->scale; - float(*obmat)[4] = (!cache_ob->is_dup_ob) ? gpf->runtime.parent_obmat : cache_ob->obmat; - switch (elm->type) { - case eGpencilBatchGroupType_Stroke: { - const int len = elm->vertex_idx - start_stroke; - - shgrp = gpencil_shgroup_stroke_create(e_data, - vedata, - stroke_pass, - e_data->gpencil_stroke_sh, - ob, - obmat, - gpd, - gpl, - gps, - gp_style, - stl->storage->shgroup_id, - elm->onion, - scale, - cache_ob->shading_type); - - /* set stencil mask id */ - if (gpencil_is_stencil_required(gp_style)) { - if (stencil_id == 1) { - /* Clear previous stencils. */ - DRW_shgroup_clear_framebuffer(shgrp, GPU_STENCIL_BIT, 0, 0, 0, 0, 0.0f, 0x0); - } - DRW_shgroup_stencil_mask(shgrp, stencil_id); - stencil_id++; - } - else { - /* Disable stencil for this type */ - DRW_shgroup_state_disable(shgrp, DRW_STATE_WRITE_STENCIL | DRW_STATE_STENCIL_NEQUAL); - } - - if ((do_onion) || (elm->onion == false)) { - DRW_shgroup_call_range(shgrp, cache->b_stroke.batch, start_stroke, len); - } - stl->storage->shgroup_id++; - start_stroke = elm->vertex_idx; - break; - } - case eGpencilBatchGroupType_Point: { - const int len = elm->vertex_idx - start_point; - - shgrp = gpencil_shgroup_point_create(e_data, - vedata, - stroke_pass, - e_data->gpencil_point_sh, - ob, - obmat, - gpd, - gpl, - gps, - gp_style, - stl->storage->shgroup_id, - elm->onion, - scale, - cache_ob->shading_type); - - /* Disable stencil for this type */ - DRW_shgroup_state_disable(shgrp, DRW_STATE_WRITE_STENCIL | DRW_STATE_STENCIL_NEQUAL); - - if ((do_onion) || (elm->onion == false)) { - DRW_shgroup_call_range(shgrp, cache->b_point.batch, start_point, len); - } - stl->storage->shgroup_id++; - start_point = elm->vertex_idx; - break; - } - case eGpencilBatchGroupType_Fill: { - const int len = elm->vertex_idx - start_fill; - - shgrp = gpencil_shgroup_fill_create(e_data, - vedata, - stroke_pass, - e_data->gpencil_fill_sh, - ob, - obmat, - gpd, - gpl, - gp_style, - stl->storage->shgroup_id, - cache_ob->shading_type); - - /* Disable stencil for this type */ - DRW_shgroup_state_disable(shgrp, DRW_STATE_WRITE_STENCIL | DRW_STATE_STENCIL_NEQUAL); - - if ((do_onion) || (elm->onion == false)) { - DRW_shgroup_call_range(shgrp, cache->b_fill.batch, start_fill, len); - } - stl->storage->shgroup_id++; - start_fill = elm->vertex_idx; - break; - } - case eGpencilBatchGroupType_Edit: { - if (stl->g_data->shgrps_edit_point) { - const int len = elm->vertex_idx - start_edit; - - shgrp = DRW_shgroup_create_sub(stl->g_data->shgrps_edit_point); - DRW_shgroup_uniform_mat4(shgrp, "gpModelMatrix", obmat); - /* use always the same group */ - DRW_shgroup_call_range( - stl->g_data->shgrps_edit_point, cache->b_edit.batch, start_edit, len); - - start_edit = elm->vertex_idx; - } - break; - } - case eGpencilBatchGroupType_Edlin: { - if (stl->g_data->shgrps_edit_line) { - const int len = elm->vertex_idx - start_edlin; - - shgrp = DRW_shgroup_create_sub(stl->g_data->shgrps_edit_line); - DRW_shgroup_uniform_mat4(shgrp, "gpModelMatrix", obmat); - /* use always the same group */ - DRW_shgroup_call_range( - stl->g_data->shgrps_edit_line, cache->b_edlin.batch, start_edlin, len); - - start_edlin = elm->vertex_idx; - } - break; - } - default: { - break; - } - } - /* save first group */ - if ((shgrp != NULL) && (tag_first)) { - array_elm = &cache_ob->shgrp_array[idx]; - array_elm->mode = idx == 0 ? eGplBlendMode_Regular : gpl->blend_mode; - array_elm->mask_layer = gpl->flag & GP_LAYER_USE_MASK; - array_elm->blend_opacity = gpl->opacity; - array_elm->init_shgrp = shgrp; - cache_ob->tot_layers++; - - tag_first = false; - } - } - - /* save last group */ - if (shgrp != NULL) { - array_elm->mode = idx == 0 ? eGplBlendMode_Regular : gpl->blend_mode; - array_elm->end_shgrp = shgrp; - } -} -/* populate a datablock for multiedit (no onions, no modifiers) */ -void gpencil_populate_multiedit(GPENCIL_e_data *e_data, - void *vedata, - Object *ob, - tGPencilObjectCache *cache_ob) -{ - bGPdata *gpd = (bGPdata *)ob->data; - bGPDframe *gpf = NULL; - - GPENCIL_StorageList *stl = ((GPENCIL_Data *)vedata)->stl; - const DRWContextState *draw_ctx = DRW_context_state_get(); - Scene *scene = draw_ctx->scene; - int cfra_eval = (int)DEG_get_ctime(draw_ctx->depsgraph); - GpencilBatchCache *cache = gpencil_batch_cache_get(ob, cfra_eval); - - /* check if playing animation */ - const bool playing = stl->storage->is_playing; - - /* calc max size of VBOs */ - gpencil_calc_vertex(stl, cache_ob, cache, gpd); - - /* 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; - } - const float alpha = GPENCIL_SIMPLIFY_TINT(scene, playing) ? 0.0f : gpl->tintcolor[3]; - const float tintcolor[4] = {gpl->tintcolor[0], gpl->tintcolor[1], gpl->tintcolor[2], alpha}; - - /* 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, ob, gpd, gpl, gpf, gpl->opacity, tintcolor, false, cache_ob); - } - } - } - else { - gpf = BKE_gpencil_layer_getframe(gpl, cfra_eval, GP_GETFRAME_USE_PREV); - if (gpf) { - gpencil_draw_strokes( - cache, e_data, vedata, ob, gpd, gpl, gpf, gpl->opacity, tintcolor, false, cache_ob); - } - } - } - - /* create batchs and shading groups */ - gpencil_batches_ensure(cache); - gpencil_shgroups_create(e_data, vedata, ob, cache, cache_ob); - - cache->is_dirty = false; -} - -/* helper for populate a complete grease pencil datablock */ -void gpencil_populate_datablock(GPENCIL_e_data *e_data, - void *vedata, - Object *ob, - tGPencilObjectCache *cache_ob) -{ - GPENCIL_StorageList *stl = ((GPENCIL_Data *)vedata)->stl; - const DRWContextState *draw_ctx = DRW_context_state_get(); - const ViewLayer *view_layer = DEG_get_evaluated_view_layer(draw_ctx->depsgraph); - Scene *scene = draw_ctx->scene; - - /* TODO: Review why is needed this recalc when render cycles + GP object in background. - * We need these lines to keep running the background render, but asap we get an alternative - * solution, we must remove it and keep all logic inside gpencil_modifier module. (antoniov) - */ - if (ob->runtime.gpencil_tot_layers == 0) { - BKE_gpencil_modifiers_calc(draw_ctx->depsgraph, draw_ctx->scene, ob); - } - - bGPdata *gpd = (bGPdata *)ob->data; - - /* If render mode, instead to use view switches, test if the datablock has - * the onion activated for render. */ - const bool render_onion = (gpd && gpd->onion_flag & GP_ONION_GHOST_ALWAYS); - const bool main_onion = (stl->storage->is_render) ? render_onion : stl->storage->is_main_onion; - const bool overlay = (stl->storage->is_render) ? render_onion : stl->storage->is_main_overlay; - const bool playing = stl->storage->is_playing; - const bool do_onion = (bool)((gpd->flag & GP_DATA_STROKE_WEIGHTMODE) == 0) && overlay && - main_onion && !playing && gpencil_onion_active(gpd); - - View3D *v3d = draw_ctx->v3d; - int cfra_eval = (int)DEG_get_ctime(draw_ctx->depsgraph); - - bGPDframe *gpf_eval = NULL; - const bool time_remap = BKE_gpencil_has_time_modifiers(ob); - - float opacity; - bGPDframe *gpf = NULL; - - GpencilBatchCache *cache = gpencil_batch_cache_get(ob, cfra_eval); - - /* if object is duplicate, only create shading groups */ - if (cache_ob->is_dup_ob) { - gpencil_shgroups_create(e_data, vedata, ob, cache, cache_ob); - return; - } - - /* calc max size of VBOs */ - gpencil_calc_vertex(stl, cache_ob, cache, gpd); - - /* 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; - } - - const bool is_solomode = GPENCIL_PAINT_MODE(gpd) && (!playing) && (!stl->storage->is_render) && - (gpl->flag & GP_LAYER_SOLO_MODE); - - /* filter view layer to gp layers in the same view layer (for compo) */ - if ((stl->storage->is_render) && (gpl->viewlayername[0] != '\0')) { - if (!STREQ(view_layer->name, gpl->viewlayername)) { - continue; - } - } - - /* remap time */ - int remap_cfra = cfra_eval; - if ((time_remap) && (!stl->storage->simplify_modif)) { - remap_cfra = BKE_gpencil_time_modifier( - draw_ctx->depsgraph, scene, ob, gpl, cfra_eval, stl->storage->is_render); - } - - gpf = BKE_gpencil_layer_getframe(gpl, remap_cfra, GP_GETFRAME_USE_PREV); - if (gpf == NULL) { - continue; - } - - /* if solo mode, display only frames with keyframe in the current frame */ - if ((is_solomode) && (gpf->framenum != remap_cfra)) { - continue; - } - - opacity = gpl->opacity; - /* if pose mode, maybe the overlay to fade geometry is enabled */ - if ((draw_ctx->obact) && (draw_ctx->object_mode == OB_MODE_POSE) && - (v3d->overlay.flag & V3D_OVERLAY_BONE_SELECT)) { - opacity = opacity * v3d->overlay.xray_alpha_bone; - } - - /* Get evaluated frames array data */ - int idx_eval = BLI_findindex(&gpd->layers, gpl); - gpf_eval = &ob->runtime.gpencil_evaluated_frames[idx_eval]; - - /* draw onion skins */ - if (!ID_IS_LINKED(&gpd->id)) { - if ((do_onion) && (gpl->onion_flag & GP_LAYER_ONIONSKIN) && - ((!playing) || (gpd->onion_flag & GP_ONION_GHOST_ALWAYS)) && (!cache_ob->is_dup_ob) && - (gpd->id.us <= 1)) { - if ((!stl->storage->is_render) || - ((stl->storage->is_render) && (gpd->onion_flag & GP_ONION_GHOST_ALWAYS))) { - gpencil_draw_onionskins(cache, vedata, ob, gpd, gpl, gpf); - } - } - } - /* draw normal strokes */ - const float alpha = GPENCIL_SIMPLIFY_TINT(scene, playing) ? 0.0f : gpl->tintcolor[3]; - const float tintcolor[4] = {gpl->tintcolor[0], gpl->tintcolor[1], gpl->tintcolor[2], alpha}; - gpencil_draw_strokes( - cache, e_data, vedata, ob, gpd, gpl, gpf_eval, opacity, tintcolor, false, cache_ob); - } - - /* create batchs and shading groups */ - gpencil_batches_ensure(cache); - gpencil_shgroups_create(e_data, vedata, ob, cache, cache_ob); - - cache->is_dirty = false; -} - -void gpencil_populate_particles(GPENCIL_e_data *e_data, GHash *gh_objects, void *vedata) -{ - GPENCIL_StorageList *stl = ((GPENCIL_Data *)vedata)->stl; - - /* add particles */ - 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->is_dup_ob) { - /* Reassign duplicate objects because memory for particles is not available - * and need to use the original data-block and run-time data. */ - Object *ob = (Object *)BLI_ghash_lookup(gh_objects, cache_ob->name); - if (ob) { - cache_ob->ob = ob; - cache_ob->gpd = (bGPdata *)ob->data; - GpencilBatchCache *cache = ob->runtime.gpencil_cache; - if (cache != NULL) { - gpencil_shgroups_create(e_data, vedata, ob, cache, cache_ob); - } - } - } - } -} diff --git a/source/blender/draw/engines/gpencil/gpencil_engine.c b/source/blender/draw/engines/gpencil/gpencil_engine.c index 96560d986a0..80afbb5c7a3 100644 --- a/source/blender/draw/engines/gpencil/gpencil_engine.c +++ b/source/blender/draw/engines/gpencil/gpencil_engine.c @@ -29,11 +29,19 @@ #include "BKE_paint.h" #include "BKE_shader_fx.h" +#include "BKE_camera.h" +#include "BKE_global.h" /* for G.debug */ + +#include "BLI_link_utils.h" +#include "BLI_memblock.h" + +#include "DNA_camera_types.h" #include "DNA_gpencil_types.h" #include "DNA_screen_types.h" #include "DNA_view3d_types.h" #include "GPU_texture.h" +#include "GPU_uniformbuffer.h" #include "gpencil_engine.h" @@ -44,1129 +52,904 @@ #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_gpu_shader_3D_smooth_color_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) +void GPENCIL_engine_init(void *ved) { + GPENCIL_Data *vedata = (GPENCIL_Data *)ved; + GPENCIL_StorageList *stl = vedata->stl; + GPENCIL_TextureList *txl = vedata->txl; 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)}); - } - } + DefaultTextureList *dtxl = DRW_viewport_texture_list_get(); + DefaultFramebufferList *dfbl = DRW_viewport_framebuffer_list_get(); + const DRWContextState *ctx = DRW_context_state_get(); + const View3D *v3d = ctx->v3d; + + if (!stl->pd) { + stl->pd = MEM_callocN(sizeof(GPENCIL_PrivateData), "GPENCIL_PrivateData"); + } + + if (txl->dummy_texture == NULL) { + float pixels[1][4] = {{1.0f, 0.0f, 1.0f, 1.0f}}; + txl->dummy_texture = DRW_texture_create_2d(1, 1, GPU_RGBA8, DRW_TEX_WRAP, (float *)pixels); + } + + GPENCIL_ViewLayerData *vldata = GPENCIL_view_layer_data_ensure(); + + /* Resize and reset memblocks. */ + BLI_memblock_clear(vldata->gp_light_pool, gpencil_light_pool_free); + BLI_memblock_clear(vldata->gp_material_pool, gpencil_material_pool_free); + BLI_memblock_clear(vldata->gp_object_pool, NULL); + BLI_memblock_clear(vldata->gp_layer_pool, NULL); + BLI_memblock_clear(vldata->gp_vfx_pool, NULL); + BLI_memblock_clear(vldata->gp_maskbit_pool, NULL); + + stl->pd->gp_light_pool = vldata->gp_light_pool; + stl->pd->gp_material_pool = vldata->gp_material_pool; + stl->pd->gp_maskbit_pool = vldata->gp_maskbit_pool; + stl->pd->gp_object_pool = vldata->gp_object_pool; + stl->pd->gp_layer_pool = vldata->gp_layer_pool; + stl->pd->gp_vfx_pool = vldata->gp_vfx_pool; + stl->pd->scene = ctx->scene; + stl->pd->last_light_pool = NULL; + stl->pd->last_material_pool = NULL; + stl->pd->tobjects.first = NULL; + stl->pd->tobjects.last = NULL; + stl->pd->tobjects_infront.first = NULL; + stl->pd->tobjects_infront.last = NULL; + stl->pd->sbuffer_tobjects.first = NULL; + stl->pd->sbuffer_tobjects.last = NULL; + stl->pd->dummy_tx = txl->dummy_texture; + stl->pd->draw_depth_only = !DRW_state_is_fbo(); + stl->pd->draw_wireframe = (v3d && v3d->shading.type == OB_WIRE) && !stl->pd->draw_depth_only; + stl->pd->scene_depth_tx = stl->pd->draw_depth_only ? txl->dummy_texture : dtxl->depth; + stl->pd->scene_fb = dfbl->default_fb; + stl->pd->is_render = txl->render_depth_tx || (v3d && v3d->shading.type == OB_RENDER); + stl->pd->is_viewport = (v3d != NULL); + stl->pd->global_light_pool = gpencil_light_pool_add(stl->pd); + stl->pd->shadeless_light_pool = gpencil_light_pool_add(stl->pd); + /* Small HACK: we don't want the global pool to be reused, + * so we set the last light pool to NULL. */ + stl->pd->last_light_pool = NULL; + + bool use_scene_lights = false; + bool use_scene_world = false; + + if (v3d) { + use_scene_lights = ((v3d->shading.type == OB_MATERIAL) && + (v3d->shading.flag & V3D_SHADING_SCENE_LIGHTS)) || + ((v3d->shading.type == OB_RENDER) && + (v3d->shading.flag & V3D_SHADING_SCENE_LIGHTS_RENDER)); + + use_scene_world = ((v3d->shading.type == OB_MATERIAL) && + (v3d->shading.flag & V3D_SHADING_SCENE_WORLD)) || + ((v3d->shading.type == OB_RENDER) && + (v3d->shading.flag & V3D_SHADING_SCENE_WORLD_RENDER)); + + stl->pd->v3d_color_type = (v3d->shading.type == OB_SOLID) ? v3d->shading.color_type : -1; + /* Special case: If Vertex Paint mode, use always Vertex mode. */ + if (v3d->shading.type == OB_SOLID && ctx->obact && ctx->obact->type == OB_GPENCIL && + ctx->obact->mode == OB_MODE_VERTEX_GPENCIL) { + stl->pd->v3d_color_type = V3D_SHADING_VERTEX_COLOR; + } + + copy_v3_v3(stl->pd->v3d_single_color, v3d->shading.single_color); + + /* For non active frame, use only lines in multiedit mode. */ + const bool overlays_on = (v3d->flag2 & V3D_HIDE_OVERLAYS) == 0; + stl->pd->use_multiedit_lines_only = !overlays_on || + (v3d->gp_flag & V3D_GP_SHOW_MULTIEDIT_LINES) != 0; + + const bool shmode_xray_support = v3d->shading.type <= OB_SOLID; + stl->pd->xray_alpha = (shmode_xray_support && XRAY_ENABLED(v3d)) ? XRAY_ALPHA(v3d) : 1.0f; + } + else if (stl->pd->is_render) { + use_scene_lights = true; + use_scene_world = true; + stl->pd->use_multiedit_lines_only = false; + stl->pd->xray_alpha = 1.0f; + stl->pd->v3d_color_type = -1; + } + + stl->pd->use_lighting = (v3d && v3d->shading.type > OB_SOLID) || stl->pd->is_render; + stl->pd->use_lights = use_scene_lights; + + if (txl->render_depth_tx != NULL) { + stl->pd->scene_depth_tx = txl->render_depth_tx; + stl->pd->scene_fb = fbl->render_fb; + } + + gpencil_light_ambient_add(stl->pd->shadeless_light_pool, (float[3]){1.0f, 1.0f, 1.0f}); + + World *world = ctx->scene->world; + if (world != NULL && use_scene_world) { + gpencil_light_ambient_add(stl->pd->global_light_pool, &world->horr); + } + else if (v3d) { + float world_light[3]; + copy_v3_fl(world_light, v3d->shading.studiolight_intensity); + gpencil_light_ambient_add(stl->pd->global_light_pool, world_light); + } + + float viewmatinv[4][4]; + DRW_view_viewmat_get(NULL, viewmatinv, true); + copy_v3_v3(stl->pd->camera_z_axis, viewmatinv[2]); + copy_v3_v3(stl->pd->camera_pos, viewmatinv[3]); + stl->pd->camera_z_offset = dot_v3v3(viewmatinv[3], viewmatinv[2]); + + if (ctx && ctx->rv3d && v3d) { + stl->pd->camera = (ctx->rv3d->persp == RV3D_CAMOB) ? v3d->camera : NULL; + } + else { + stl->pd->camera = NULL; } } -static void GPENCIL_create_framebuffers(void *vedata) +void GPENCIL_cache_init(void *ved) { - 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 multisample 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), - }); + GPENCIL_Data *vedata = (GPENCIL_Data *)ved; + GPENCIL_PassList *psl = vedata->psl; + GPENCIL_TextureList *txl = vedata->txl; + GPENCIL_FramebufferList *fbl = vedata->fbl; + GPENCIL_PrivateData *pd = vedata->stl->pd; + DRWShadingGroup *grp; - /* 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), - }); - } + const DRWContextState *draw_ctx = DRW_context_state_get(); + pd->cfra = (int)DEG_get_ctime(draw_ctx->depsgraph); + pd->simplify_antialias = GPENCIL_SIMPLIFY_AA(draw_ctx->scene); + pd->use_layer_fb = false; + pd->use_object_fb = false; + pd->use_mask_fb = false; + pd->use_signed_fb = false; + + if (draw_ctx->v3d) { + const bool hide_overlay = ((draw_ctx->v3d->flag2 & V3D_HIDE_OVERLAYS) != 0); + const bool show_onion = ((draw_ctx->v3d->gp_flag & V3D_GP_SHOW_ONION_SKIN) != 0); + const bool playing = (draw_ctx->evil_C != NULL) ? + ED_screen_animation_playing(CTX_wm_manager(draw_ctx->evil_C)) != + NULL : + false; + pd->do_onion = show_onion && !hide_overlay && !playing; + /* Save simplify flags (can change while drawing, so it's better to save). */ + Scene *scene = draw_ctx->scene; + pd->simplify_fill = GPENCIL_SIMPLIFY_FILL(scene, playing); + pd->simplify_fx = GPENCIL_SIMPLIFY_FX(scene, playing) || + (draw_ctx->v3d->shading.type < OB_RENDER); + + /* Fade Layer. */ + const bool is_fade_layer = ((!hide_overlay) && (!pd->is_render) && + (draw_ctx->v3d->gp_flag & V3D_GP_FADE_NOACTIVE_LAYERS)); + pd->fade_layer_opacity = (is_fade_layer) ? draw_ctx->v3d->overlay.gpencil_fade_layer : -1.0f; + /* Fade GPencil Objects. */ + const bool is_fade_object = ((!hide_overlay) && (!pd->is_render) && + (draw_ctx->v3d->gp_flag & V3D_GP_FADE_OBJECTS) && + (draw_ctx->v3d->gp_flag & V3D_GP_FADE_NOACTIVE_GPENCIL)); + pd->fade_gp_object_opacity = (is_fade_object) ? draw_ctx->v3d->overlay.gpencil_paper_opacity : + -1.0f; + pd->fade_3d_object_opacity = ((!hide_overlay) && (!pd->is_render) && + (draw_ctx->v3d->gp_flag & V3D_GP_FADE_OBJECTS)) ? + draw_ctx->v3d->overlay.gpencil_paper_opacity : + -1.0f; + } + else { + pd->do_onion = true; + pd->simplify_fill = false; + pd->simplify_fx = false; + pd->fade_layer_opacity = -1.0f; + } - /* 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; + { + pd->sbuffer_stroke = NULL; + pd->sbuffer_gpd = NULL; + pd->sbuffer_layer = NULL; + pd->stroke_batch = NULL; + pd->fill_batch = NULL; + pd->do_fast_drawing = false; + + pd->obact = draw_ctx->obact; + if (pd->obact && pd->obact->type == OB_GPENCIL) { + /* Check if active object has a temp stroke data. */ + bGPdata *gpd = (bGPdata *)pd->obact->data; + if (gpd->runtime.sbuffer_used > 0) { + pd->sbuffer_gpd = gpd; + pd->sbuffer_stroke = DRW_cache_gpencil_sbuffer_stroke_data_get(pd->obact); + pd->sbuffer_layer = BKE_gpencil_layer_active_get(pd->sbuffer_gpd); + pd->do_fast_drawing = false; /* TODO option */ } - 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) -{ - /* blank texture used if no texture defined for fill shader */ - if (!e_data.gpencil_blank_texture) { - float rect[1][1][4] = {{{0.0f}}}; - e_data.gpencil_blank_texture = DRW_texture_create_2d( - 1, 1, GPU_RGBA8, DRW_TEX_FILTER, (float *)rect); + if (pd->do_fast_drawing) { + pd->snapshot_buffer_dirty = (txl->snapshot_color_tx == NULL); + const float *size = DRW_viewport_size_get(); + DRW_texture_ensure_2d(&txl->snapshot_depth_tx, size[0], size[1], GPU_DEPTH24_STENCIL8, 0); + DRW_texture_ensure_2d(&txl->snapshot_color_tx, size[0], size[1], GPU_R11F_G11F_B10F, 0); + DRW_texture_ensure_2d(&txl->snapshot_reveal_tx, size[0], size[1], GPU_R11F_G11F_B10F, 0); + + GPU_framebuffer_ensure_config(&fbl->snapshot_fb, + { + GPU_ATTACHMENT_TEXTURE(txl->snapshot_depth_tx), + GPU_ATTACHMENT_TEXTURE(txl->snapshot_color_tx), + GPU_ATTACHMENT_TEXTURE(txl->snapshot_reveal_tx), + }); } - /* 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}, - }); + else { + /* Free uneeded buffers. */ + GPU_FRAMEBUFFER_FREE_SAFE(fbl->snapshot_fb); + DRW_TEXTURE_FREE_SAFE(txl->snapshot_depth_tx); + DRW_TEXTURE_FREE_SAFE(txl->snapshot_color_tx); + DRW_TEXTURE_FREE_SAFE(txl->snapshot_reveal_tx); } - /* 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}, - }); - } + { + DRWState state = DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS; + DRW_PASS_CREATE(psl->merge_depth_ps, state); - /* 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); + GPUShader *sh = GPENCIL_shader_depth_merge_get(); + grp = DRW_shgroup_create(sh, psl->merge_depth_ps); + DRW_shgroup_uniform_texture_ref(grp, "depthBuf", &pd->depth_tx); + DRW_shgroup_uniform_bool(grp, "strokeOrder3d", &pd->is_stroke_order_3d, 1); + DRW_shgroup_uniform_vec4(grp, "gpModelMatrix[0]", pd->object_bound_mat[0], 4); + DRW_shgroup_call_procedural_triangles(grp, NULL, 1); } + { + DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_LOGIC_INVERT; + DRW_PASS_CREATE(psl->mask_invert_ps, state); - /* used for edit lines for edit modes */ - if (!e_data.gpencil_line_sh) { - e_data.gpencil_line_sh = DRW_shader_create_with_lib( - datatoc_gpencil_edit_point_vert_glsl, - NULL, - datatoc_gpu_shader_3D_smooth_color_frag_glsl, - datatoc_common_view_lib_glsl, - NULL); + GPUShader *sh = GPENCIL_shader_mask_invert_get(); + grp = DRW_shgroup_create(sh, psl->mask_invert_ps); + DRW_shgroup_call_procedural_triangles(grp, NULL, 1); } - /* 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); - } + Camera *cam = (pd->camera != NULL) ? pd->camera->data : NULL; - /* 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); - } + /* Pseudo DOF setup. */ + if (cam && (cam->dof.flag & CAM_DOF_ENABLED)) { + const float *vp_size = DRW_viewport_size_get(); + float fstop = cam->dof.aperture_fstop; + float sensor = BKE_camera_sensor_size(cam->sensor_fit, cam->sensor_x, cam->sensor_y); + float focus_dist = BKE_camera_object_dof_distance(pd->camera); + float focal_len = cam->lens; - /* blend */ - if (!e_data.gpencil_blend_fullscreen_sh) { - e_data.gpencil_blend_fullscreen_sh = DRW_shader_create_fullscreen( - datatoc_gpencil_blend_frag_glsl, NULL); - } + const float scale_camera = 0.001f; + /* we want radius here for the aperture number */ + float aperture = 0.5f * scale_camera * focal_len / fstop; + float focal_len_scaled = scale_camera * focal_len; + float sensor_scaled = scale_camera * sensor; - /* 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 (draw_ctx->rv3d != NULL) { + sensor_scaled *= draw_ctx->rv3d->viewcamtexcofac[0]; + } + + pd->dof_params[1] = aperture * fabsf(focal_len_scaled / (focus_dist - focal_len_scaled)); + pd->dof_params[1] *= vp_size[0] / sensor_scaled; + pd->dof_params[0] = -focus_dist * pd->dof_params[1]; } - if (!e_data.gpencil_paper_sh) { - e_data.gpencil_paper_sh = DRW_shader_create_fullscreen(datatoc_gpencil_paper_frag_glsl, NULL); + else { + /* Disable DoF blur scalling. */ + pd->camera = NULL; } } -void GPENCIL_engine_init(void *vedata) +#define DRAW_NOW 2 + +typedef struct gpIterPopulateData { + Object *ob; + GPENCIL_tObject *tgp_ob; + GPENCIL_PrivateData *pd; + GPENCIL_MaterialPool *matpool; + DRWShadingGroup *grp; + /* Last material UBO bound. Used to avoid uneeded buffer binding. */ + GPUUniformBuffer *ubo_mat; + GPUUniformBuffer *ubo_lights; + /* Last texture bound. */ + GPUTexture *tex_fill; + GPUTexture *tex_stroke; + /* Offset in the material pool to the first material of this object. */ + int mat_ofs; + /* Is the sbuffer call need to be issued. */ + int do_sbuffer_call; + /* Indices to do correct insertion of the sbuffer stroke. */ + int stroke_index_last; + int stroke_index_offset; + /* Infos for call batching. */ + struct GPUBatch *geom; + bool instancing; + int vfirst, vcount; +} gpIterPopulateData; + +#define DISABLE_BATCHING 0 + +static void gpencil_drawcall_flush(gpIterPopulateData *iter) { - 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; +#if !DISABLE_BATCHING + if (iter->geom != NULL) { + if (iter->instancing) { + DRW_shgroup_call_instance_range(iter->grp, iter->ob, iter->geom, iter->vfirst, iter->vcount); + } + else { + DRW_shgroup_call_range(iter->grp, iter->ob, iter->geom, iter->vfirst, iter->vcount); + } } +#endif - /* Not supported anymore. */ - stl->storage->multisamples = 0; - - /* create shaders */ - GPENCIL_create_shaders(); - GPENCIL_create_fx_shaders(&e_data); + iter->geom = NULL; + iter->vfirst = -1; + iter->vcount = 0; } -static void GPENCIL_engine_free(void) +/* Group drawcalls that are consecutive and with the same type. Reduces GPU driver overhead. */ +static void gp_drawcall_add( + gpIterPopulateData *iter, struct GPUBatch *geom, bool instancing, int v_first, int v_count) { - /* 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_line_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); - - DRW_TEXTURE_FREE_SAFE(e_data.gpencil_blank_texture); - - /* effects */ - GPENCIL_delete_fx_shaders(&e_data); +#if DISABLE_BATCHING + if (instancing) { + DRW_shgroup_call_instance_range(iter->grp, iter->ob, geom, v_first, v_count); + } + else { + DRW_shgroup_call_range(iter->grp, iter->ob, geom, v_first, v_count); + } +#endif + + int last = iter->vfirst + iter->vcount; + /* Interupt drawcall grouping if the sequence is not consecutive. */ + if ((geom != iter->geom) || (v_first - last > 3)) { + gpencil_drawcall_flush(iter); + } + iter->geom = geom; + iter->instancing = instancing; + if (iter->vfirst == -1) { + iter->vfirst = v_first; + } + iter->vcount = v_first + v_count - iter->vfirst; } -/* Helper: Check if the main overlay and onion switches are enabled in any screen. - * - * This is required to generate the onion skin and limit the times the cache is updated because the - * cache is generated only in the first screen and if the first screen has the onion disabled the - * cache for onion skin is not generated. The loop adds time, but always is faster than regenerate - * the cache all the times. - */ -static void gpencil_check_screen_switches(const DRWContextState *draw_ctx, - GPENCIL_StorageList *stl) +static void gpencil_stroke_cache_populate(bGPDlayer *gpl, + bGPDframe *gpf, + bGPDstroke *gps, + void *thunk); + +static void gp_sbuffer_cache_populate(gpIterPopulateData *iter) { - stl->storage->is_main_overlay = false; - stl->storage->is_main_onion = false; - /* Check if main onion switch is enabled in any screen. */ - Main *bmain = CTX_data_main(draw_ctx->evil_C); - - for (bScreen *screen = bmain->screens.first; screen; screen = screen->id.next) { - for (const ScrArea *sa = screen->areabase.first; sa; sa = sa->next) { - if (sa && sa->spacetype == SPACE_VIEW3D) { - View3D *v3d = sa->spacedata.first; - if (v3d == NULL) { - continue; - } - if ((v3d->flag2 & V3D_HIDE_OVERLAYS) == 0) { - stl->storage->is_main_overlay = true; - } - if (v3d->gp_flag & V3D_GP_SHOW_ONION_SKIN) { - stl->storage->is_main_onion = true; - } - } - /* If found, don't need loop more. */ - if ((stl->storage->is_main_overlay) && (stl->storage->is_main_onion)) { - return; - } - } - } + iter->do_sbuffer_call = DRAW_NOW; + /* In order to draw the sbuffer stroke correctly mixed with other strokes, + * we need to offset the stroke index of the sbuffer stroke and the subsequent strokes. + * Remember, sbuffer stroke indices start from 0. So we add last index to avoid + * masking issues. */ + iter->grp = DRW_shgroup_create_sub(iter->grp); + DRW_shgroup_uniform_block(iter->grp, "gpMaterialBlock", iter->ubo_mat); + DRW_shgroup_uniform_float_copy(iter->grp, "strokeIndexOffset", iter->stroke_index_last); + + const DRWContextState *ctx = DRW_context_state_get(); + ToolSettings *ts = ctx->scene->toolsettings; + if (ts->gpencil_v3d_align & (GP_PROJECT_DEPTH_VIEW | GP_PROJECT_DEPTH_STROKE)) { + /* In this case we can't do correct projection during stroke. We just disable depth test. */ + DRW_shgroup_uniform_texture(iter->grp, "gpSceneDepthTexture", iter->pd->dummy_tx); + } + + gpencil_stroke_cache_populate(NULL, NULL, iter->pd->sbuffer_stroke, iter); + gpencil_drawcall_flush(iter); + + iter->stroke_index_offset = iter->pd->sbuffer_stroke->totpoints + 1; + iter->do_sbuffer_call = 0; } -void GPENCIL_cache_init(void *vedata) +static void gpencil_layer_cache_populate(bGPDlayer *gpl, + bGPDframe *gpf, + bGPDstroke *UNUSED(gps), + void *thunk) { - 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); - const View3DCursor *cursor = &scene->cursor; - - /* 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_gpencil_material_settings(obact, obact->actcol); - } - } + gpIterPopulateData *iter = (gpIterPopulateData *)thunk; + GPENCIL_PrivateData *pd = iter->pd; + bGPdata *gpd = (bGPdata *)iter->ob->data; - 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 */ + gpencil_drawcall_flush(iter); + + if (iter->do_sbuffer_call) { + gp_sbuffer_cache_populate(iter); } - stl->storage->tonemapping = 0; - - stl->g_data->shgrps_edit_line = NULL; - stl->g_data->shgrps_edit_point = NULL; - - /* reset textures */ - 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; - - 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"); + else { + iter->do_sbuffer_call = !pd->do_fast_drawing && (gpd == pd->sbuffer_gpd) && + (gpl == pd->sbuffer_layer) && + (gpf == NULL || gpf->runtime.onion_id == 0.0f); } - /* 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; + GPENCIL_tLayer *tgp_layer = gpencil_layer_cache_add(pd, iter->ob, gpl, gpf, iter->tgp_ob); - { - /* 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; + const bool use_lights = pd->use_lighting && ((gpl->flag & GP_LAYER_USE_LIGHTS) != 0) && + (iter->ob->dtx & OB_USE_GPENCIL_LIGHTS); - /* Found if main overlay and onion switches are enabled in any screen. */ - gpencil_check_screen_switches(draw_ctx, stl); - } - else { - stl->storage->is_playing = false; - stl->storage->reset_cache = false; - stl->storage->is_main_overlay = false; - stl->storage->is_main_onion = false; - } - /* save render state */ - stl->storage->is_render = DRW_state_is_scene_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; - } - } + iter->ubo_lights = (use_lights) ? pd->global_light_pool->ubo : pd->shadeless_light_pool->ubo; - /* save simplify flags (can change while drawing, so it's better to save) */ - stl->storage->simplify_fill = GPENCIL_SIMPLIFY_FILL(scene, stl->storage->is_playing); - stl->storage->simplify_modif = GPENCIL_SIMPLIFY_MODIF(scene, stl->storage->is_playing); - stl->storage->simplify_fx = GPENCIL_SIMPLIFY_FX(scene, stl->storage->is_playing); - stl->storage->simplify_blend = GPENCIL_SIMPLIFY_BLEND(scene, stl->storage->is_playing); + gpencil_material_resources_get(iter->matpool, 0, NULL, NULL, &iter->ubo_mat); - /* xray mode */ - if (v3d) { - stl->storage->is_xray = XRAY_ACTIVE(v3d); - } - else { - stl->storage->is_xray = 0; - } + /* Iterator dependent uniforms. */ + DRWShadingGroup *grp = iter->grp = tgp_layer->base_shgrp; + DRW_shgroup_uniform_block_persistent(grp, "gpLightBlock", iter->ubo_lights); + DRW_shgroup_uniform_block(grp, "gpMaterialBlock", iter->ubo_mat); + DRW_shgroup_uniform_texture(grp, "gpFillTexture", iter->tex_fill); + DRW_shgroup_uniform_texture(grp, "gpStrokeTexture", iter->tex_stroke); + DRW_shgroup_uniform_int_copy(grp, "gpMaterialOffset", iter->mat_ofs); + DRW_shgroup_uniform_float_copy(grp, "strokeIndexOffset", iter->stroke_index_offset); +} - /* 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; - } +static void gpencil_stroke_cache_populate(bGPDlayer *gpl, + bGPDframe *gpf, + bGPDstroke *gps, + void *thunk) +{ + gpIterPopulateData *iter = (gpIterPopulateData *)thunk; - /* 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; - } + MaterialGPencilStyle *gp_style = BKE_gpencil_material_settings(iter->ob, gps->mat_nr + 1); - 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; - } - } + bool hide_material = (gp_style->flag & GP_MATERIAL_HIDE) != 0; + bool show_stroke = (gp_style->flag & GP_MATERIAL_STROKE_SHOW) != 0; + bool show_fill = (gps->tot_triangles > 0) && ((gp_style->flag & GP_MATERIAL_FILL_SHOW) != 0) && + (!iter->pd->simplify_fill) && ((gps->flag & GP_STROKE_NOFILL) == 0); + + bool only_lines = gpl && gpf && gpl->actframe != gpf && iter->pd->use_multiedit_lines_only; + + if (hide_material || (!show_stroke && !show_fill) || only_lines) { + return; + } + + GPUUniformBuffer *ubo_mat; + GPUTexture *tex_stroke, *tex_fill; + gpencil_material_resources_get( + iter->matpool, iter->mat_ofs + gps->mat_nr, &tex_stroke, &tex_fill, &ubo_mat); + + bool resource_changed = (iter->ubo_mat != ubo_mat) || + (tex_fill && (iter->tex_fill != tex_fill)) || + (tex_stroke && (iter->tex_stroke != tex_stroke)); + + if (resource_changed) { + gpencil_drawcall_flush(iter); + + iter->grp = DRW_shgroup_create_sub(iter->grp); + if (iter->ubo_mat != ubo_mat) { + DRW_shgroup_uniform_block(iter->grp, "gpMaterialBlock", ubo_mat); + iter->ubo_mat = ubo_mat; } - else { - stl->storage->stroke_style = GP_STYLE_STROKE_STYLE_SOLID; - stl->storage->color_type = GPENCIL_COLOR_SOLID; + if (tex_fill) { + DRW_shgroup_uniform_texture(iter->grp, "gpFillTexture", tex_fill); + iter->tex_fill = tex_fill; } - - /* 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); + if (tex_stroke) { + DRW_shgroup_uniform_texture(iter->grp, "gpStrokeTexture", tex_stroke); + iter->tex_stroke = tex_stroke; } - /* grid pass */ - if ((v3d) && (obact) && (obact->type == OB_GPENCIL)) { - 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); - - /* define grid orientation */ - switch (ts->gp_sculpt.lock_axis) { - case GP_LOCKAXIS_VIEW: { - /* align always to view */ - invert_m4_m4(stl->storage->grid_matrix, draw_ctx->rv3d->viewmat); - /* copy ob location */ - copy_v3_v3(stl->storage->grid_matrix[3], obact->obmat[3]); - break; - } - case GP_LOCKAXIS_CURSOR: { - float scale[3] = {1.0f, 1.0f, 1.0f}; - loc_eul_size_to_mat4( - stl->storage->grid_matrix, cursor->location, cursor->rotation_euler, scale); - break; - } - default: { - copy_m4_m4(stl->storage->grid_matrix, obact->obmat); - break; - } - } + /* TODO(fclem): This is a quick workaround but + * ideally we should have this as a permanent bind. */ + const bool is_masked = iter->tgp_ob->layers.last->mask_bits != NULL; + GPUTexture **mask_tex = (is_masked) ? &iter->pd->mask_tx : &iter->pd->dummy_tx; + DRW_shgroup_uniform_texture_ref(iter->grp, "gpMaskTexture", mask_tex); + } - /* Move the origin to Object or Cursor */ - if (ts->gpencil_v3d_align & GP_PROJECT_CURSOR) { - copy_v3_v3(stl->storage->grid_matrix[3], cursor->location); - } - else { - copy_v3_v3(stl->storage->grid_matrix[3], obact->obmat[3]); - } - DRW_shgroup_uniform_mat4( - stl->g_data->shgrps_grid, "gpModelMatrix", stl->storage->grid_matrix); - } + bool do_sbuffer = (iter->do_sbuffer_call == DRAW_NOW); - /* 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); - } + if (show_fill) { + GPUBatch *geom = do_sbuffer ? DRW_cache_gpencil_sbuffer_fill_get(iter->ob) : + DRW_cache_gpencil_fills_get(iter->ob, iter->pd->cfra); + int vfirst = gps->runtime.fill_start * 3; + int vcount = gps->tot_triangles * 3; + gp_drawcall_add(iter, geom, false, vfirst, vcount); } + + if (show_stroke) { + GPUBatch *geom = do_sbuffer ? DRW_cache_gpencil_sbuffer_stroke_get(iter->ob) : + DRW_cache_gpencil_strokes_get(iter->ob, iter->pd->cfra); + /* Start one vert before to have gl_InstanceID > 0 (see shader). */ + int vfirst = gps->runtime.stroke_start - 1; + /* Include "potential" cyclic vertex and start adj vertex (see shader). */ + int vcount = gps->totpoints + 1 + 1; + gp_drawcall_add(iter, geom, true, vfirst, vcount); + } + + iter->stroke_index_last = gps->runtime.stroke_start + gps->totpoints + 1; } -static void gpencil_add_draw_data(void *vedata, Object *ob) +static void gp_sbuffer_cache_populate_fast(GPENCIL_Data *vedata, gpIterPopulateData *iter) { - GPENCIL_StorageList *stl = ((GPENCIL_Data *)vedata)->stl; - bGPdata *gpd = (bGPdata *)ob->data; - const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd); - const DRWContextState *draw_ctx = DRW_context_state_get(); - const View3D *v3d = draw_ctx->v3d; + bGPdata *gpd = (bGPdata *)iter->ob->data; + if (gpd != iter->pd->sbuffer_gpd) { + return; + } - int i = stl->g_data->gp_cache_used - 1; - tGPencilObjectCache *cache_ob = &stl->g_data->gp_object_cache[i]; + GPENCIL_TextureList *txl = vedata->txl; + GPUTexture *depth_texture = iter->pd->scene_depth_tx; + GPENCIL_tObject *last_tgp_ob = iter->pd->tobjects.last; + /* Create another temp object that only contain the stroke. */ + iter->tgp_ob = gpencil_object_cache_add(iter->pd, iter->ob); + /* Remove from the main list. */ + iter->pd->tobjects.last = last_tgp_ob; + last_tgp_ob->next = NULL; + /* Add to sbuffer tgpobject list. */ + BLI_LINKS_APPEND(&iter->pd->sbuffer_tobjects, iter->tgp_ob); + /* Remove depth test with scene (avoid self occlusion). */ + iter->pd->scene_depth_tx = txl->dummy_texture; - 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); - } - } + gpencil_layer_cache_populate( + iter->pd->sbuffer_layer, iter->pd->sbuffer_layer->actframe, NULL, iter); - /* FX passses */ - cache_ob->has_fx = false; - if ((!stl->storage->simplify_fx) && - ((!ELEM(cache_ob->shading_type[0], OB_WIRE, OB_SOLID)) || - ((v3d->spacetype != SPACE_VIEW3D))) && - (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); - } + const DRWContextState *ctx = DRW_context_state_get(); + ToolSettings *ts = ctx->scene->toolsettings; + if (ts->gpencil_v3d_align & (GP_PROJECT_DEPTH_VIEW | GP_PROJECT_DEPTH_STROKE)) { + /* In this case we can't do correct projection during stroke. We just disable depth test. */ + DRW_shgroup_uniform_texture(iter->grp, "gpSceneDepthTexture", iter->pd->dummy_tx); } + + iter->do_sbuffer_call = DRAW_NOW; + gpencil_stroke_cache_populate(NULL, NULL, iter->pd->sbuffer_stroke, iter); + gpencil_drawcall_flush(iter); + + gpencil_vfx_cache_populate(vedata, iter->ob, iter->tgp_ob); + + /* Restore state. */ + iter->do_sbuffer_call = 0; + iter->pd->scene_depth_tx = depth_texture; } -void GPENCIL_cache_populate(void *vedata, Object *ob) +void GPENCIL_cache_populate(void *ved, Object *ob) { + GPENCIL_Data *vedata = (GPENCIL_Data *)ved; + GPENCIL_PrivateData *pd = vedata->stl->pd; + GPENCIL_TextureList *txl = vedata->txl; + /* 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; + if (ob->data && (ob->type == OB_GPENCIL) && (ob->dt >= OB_SOLID)) { + gpIterPopulateData iter = {0}; + iter.ob = ob; + iter.pd = pd; + iter.tgp_ob = gpencil_object_cache_add(pd, ob); + iter.matpool = gpencil_material_pool_create(pd, ob, &iter.mat_ofs); + iter.tex_fill = txl->dummy_texture; + iter.tex_stroke = txl->dummy_texture; - if (ob->type == OB_GPENCIL && ob->data) { + /* Special case for rendering onion skin. */ bGPdata *gpd = (bGPdata *)ob->data; + bool do_onion = (!pd->is_render) ? pd->do_onion : (gpd->onion_flag & GP_ONION_GHOST_ALWAYS); - /* 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; - } + BKE_gpencil_visible_stroke_iter(ob, + gpencil_layer_cache_populate, + gpencil_stroke_cache_populate, + &iter, + do_onion, + pd->cfra); - 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); - } - } + gpencil_drawcall_flush(&iter); - /* 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->region))) { - gpencil_populate_buffer_strokes(&e_data, vedata, ts, ob); + if (iter.do_sbuffer_call) { + gp_sbuffer_cache_populate(&iter); } - /* 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)) { + gpencil_vfx_cache_populate(vedata, ob, iter.tgp_ob); - stl->g_data->batch_grid = gpencil_get_grid(ob); - DRW_shgroup_call(stl->g_data->shgrps_grid, stl->g_data->batch_grid, NULL); + if (pd->do_fast_drawing) { + gp_sbuffer_cache_populate_fast(vedata, &iter); } } + + if (ob->type == OB_LAMP && pd->use_lights) { + gpencil_light_pool_populate(pd->global_light_pool, ob); + } } -void GPENCIL_cache_finish(void *vedata) +void GPENCIL_cache_finish(void *ved) { - 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); + GPENCIL_Data *vedata = (GPENCIL_Data *)ved; + GPENCIL_PrivateData *pd = vedata->stl->pd; + GPENCIL_FramebufferList *fbl = vedata->fbl; - /* free hash */ - BLI_ghash_free(gh_objects, MEM_freeN, NULL); + /* Upload UBO data. */ + BLI_memblock_iter iter; + BLI_memblock_iternew(pd->gp_material_pool, &iter); + GPENCIL_MaterialPool *pool; + while ((pool = (GPENCIL_MaterialPool *)BLI_memblock_iterstep(&iter))) { + GPU_uniformbuffer_update(pool->ubo, pool->mat_data); } - if (stl->g_data->session_flag & (GP_DRW_PAINT_IDLE | GP_DRW_PAINT_FILLING)) { - stl->storage->framebuffer_flag |= GP_FRAMEBUFFER_DRAW; + BLI_memblock_iternew(pd->gp_light_pool, &iter); + GPENCIL_LightPool *lpool; + while ((lpool = (GPENCIL_LightPool *)BLI_memblock_iterstep(&iter))) { + GPU_uniformbuffer_update(lpool->ubo, lpool->light_data); } - /* create framebuffers (only for normal drawing) */ - if (!DRW_state_is_select() || !DRW_state_is_depth()) { - GPENCIL_create_framebuffers(vedata); + /* Sort object by decreasing Z to avoid most of alpha ordering issues. */ + gpencil_object_cache_sort(pd); + + /* Create framebuffers only if needed. */ + if (pd->tobjects.first) { + eGPUTextureFormat format = pd->use_signed_fb ? GPU_RGBA16F : GPU_R11F_G11F_B10F; + + const float *size = DRW_viewport_size_get(); + pd->depth_tx = DRW_texture_pool_query_2d( + size[0], size[1], GPU_DEPTH24_STENCIL8, &draw_engine_gpencil_type); + pd->color_tx = DRW_texture_pool_query_2d(size[0], size[1], format, &draw_engine_gpencil_type); + pd->reveal_tx = DRW_texture_pool_query_2d(size[0], size[1], format, &draw_engine_gpencil_type); + + GPU_framebuffer_ensure_config(&fbl->gpencil_fb, + { + GPU_ATTACHMENT_TEXTURE(pd->depth_tx), + GPU_ATTACHMENT_TEXTURE(pd->color_tx), + GPU_ATTACHMENT_TEXTURE(pd->reveal_tx), + }); + + if (pd->use_layer_fb) { + pd->color_layer_tx = DRW_texture_pool_query_2d( + size[0], size[1], format, &draw_engine_gpencil_type); + pd->reveal_layer_tx = DRW_texture_pool_query_2d( + size[0], size[1], format, &draw_engine_gpencil_type); + + GPU_framebuffer_ensure_config(&fbl->layer_fb, + { + GPU_ATTACHMENT_TEXTURE(pd->depth_tx), + GPU_ATTACHMENT_TEXTURE(pd->color_layer_tx), + GPU_ATTACHMENT_TEXTURE(pd->reveal_layer_tx), + }); + }; + + if (pd->use_object_fb) { + pd->color_object_tx = DRW_texture_pool_query_2d( + size[0], size[1], format, &draw_engine_gpencil_type); + pd->reveal_object_tx = DRW_texture_pool_query_2d( + size[0], size[1], format, &draw_engine_gpencil_type); + + GPU_framebuffer_ensure_config(&fbl->object_fb, + { + GPU_ATTACHMENT_TEXTURE(pd->depth_tx), + GPU_ATTACHMENT_TEXTURE(pd->color_object_tx), + GPU_ATTACHMENT_TEXTURE(pd->reveal_object_tx), + }); + } + + if (pd->use_mask_fb) { + /* We need an extra depth to not disturb the normal drawing. + * The color_tx is needed for framebuffer cmpleteness. */ + GPUTexture *color_tx, *depth_tx; + depth_tx = DRW_texture_pool_query_2d( + size[0], size[1], GPU_DEPTH24_STENCIL8, &draw_engine_gpencil_type); + color_tx = DRW_texture_pool_query_2d(size[0], size[1], GPU_R8, &draw_engine_gpencil_type); + /* Use high quality format for render. */ + eGPUTextureFormat mask_format = pd->is_render ? GPU_R16 : GPU_R8; + pd->mask_tx = DRW_texture_pool_query_2d( + size[0], size[1], mask_format, &draw_engine_gpencil_type); + + GPU_framebuffer_ensure_config(&fbl->mask_fb, + { + GPU_ATTACHMENT_TEXTURE(depth_tx), + GPU_ATTACHMENT_TEXTURE(color_tx), + GPU_ATTACHMENT_TEXTURE(pd->mask_tx), + }); + } + + GPENCIL_antialiasing_init(vedata); } } -/* helper function to sort inverse gpencil objects using qsort */ -static int gpencil_object_cache_compare_zdepth(const void *a1, const void *a2) +static void GPENCIL_draw_scene_depth_only(void *ved) { - const tGPencilObjectCache *ps1 = a1, *ps2 = a2; + GPENCIL_Data *vedata = (GPENCIL_Data *)ved; + GPENCIL_PrivateData *pd = vedata->stl->pd; + DefaultFramebufferList *dfbl = DRW_viewport_framebuffer_list_get(); - if (ps1->zdepth < ps2->zdepth) { - return 1; - } - else if (ps1->zdepth > ps2->zdepth) { - return -1; + if (DRW_state_is_fbo()) { + GPU_framebuffer_bind(dfbl->depth_only_fb); } - 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; + for (GPENCIL_tObject *ob = pd->tobjects.first; ob; ob = ob->next) { + for (GPENCIL_tLayer *layer = ob->layers.first; layer; layer = layer->next) { + DRW_draw_pass(layer->geom_ps); } - /* repeat pass to fill temp texture */ - DRW_draw_pass(pass); - /* set default framebuffer again */ + } + + if (DRW_state_is_fbo()) { GPU_framebuffer_bind(dfbl->default_fb); + } + + pd->gp_object_pool = pd->gp_layer_pool = pd->gp_vfx_pool = pd->gp_maskbit_pool = NULL; - stl->storage->background_ready = true; + /* Free temp stroke buffers. */ + if (pd->sbuffer_gpd) { + DRW_cache_gpencil_sbuffer_clear(pd->obact); } } -void DRW_gpencil_free_runtime_data(void *ved) +static void gpencil_draw_mask(GPENCIL_Data *vedata, GPENCIL_tObject *ob, GPENCIL_tLayer *layer) { - GPENCIL_Data *vedata = (GPENCIL_Data *)ved; - GPENCIL_StorageList *stl = ((GPENCIL_Data *)vedata)->stl; + GPENCIL_PassList *psl = vedata->psl; + GPENCIL_FramebufferList *fbl = vedata->fbl; + float clear_col[4] = {1.0f, 1.0f, 1.0f, 1.0f}; + float clear_depth = ob->is_drawmode3d ? 1.0f : 0.0f; + bool inverted = false; + /* OPTI(fclem) we could optimize by only clearing if the new mask_bits does not contain all + * the masks already rendered in the buffer, and drawing only the layers not already drawn. */ + bool cleared = false; - /* free gpu data */ - GPU_BATCH_DISCARD_SAFE(stl->g_data->batch_buffer_stroke); - MEM_SAFE_FREE(stl->g_data->batch_buffer_stroke); + DRW_stats_group_start("GPencil Mask"); - GPU_BATCH_DISCARD_SAFE(stl->g_data->batch_buffer_fill); - MEM_SAFE_FREE(stl->g_data->batch_buffer_fill); + GPU_framebuffer_bind(fbl->mask_fb); - GPU_BATCH_DISCARD_SAFE(stl->g_data->batch_buffer_ctrlpoint); - MEM_SAFE_FREE(stl->g_data->batch_buffer_ctrlpoint); + for (int i = 0; i < GP_MAX_MASKBITS; i++) { + if (!BLI_BITMAP_TEST(layer->mask_bits, i)) { + continue; + } - GPU_BATCH_DISCARD_SAFE(stl->g_data->batch_grid); - MEM_SAFE_FREE(stl->g_data->batch_grid); + if (BLI_BITMAP_TEST_BOOL(layer->mask_invert_bits, i) != inverted) { + if (cleared) { + DRW_draw_pass(psl->mask_invert_ps); + } + inverted = !inverted; + } - if (stl->g_data->gp_object_cache == NULL) { - return; + if (!cleared) { + cleared = true; + GPU_framebuffer_clear_color_depth(fbl->mask_fb, clear_col, clear_depth); + } + + GPENCIL_tLayer *mask_layer = gpencil_layer_cache_get(ob, i); + BLI_assert(mask_layer); + + DRW_draw_pass(mask_layer->geom_ps); } - /* 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); - } + if (!inverted) { + /* Blend shader expect an opacity mask not a reavealage buffer. */ + DRW_draw_pass(psl->mask_invert_ps); } - /* free the cache itself */ - MEM_SAFE_FREE(stl->g_data->gp_object_cache); + DRW_stats_group_end(); } -static void gpencil_draw_pass_range(GPENCIL_FramebufferList *fbl, - GPENCIL_StorageList *stl, - GPENCIL_PassList *psl, - GPUFrameBuffer *fb, - Object *ob, - bGPdata *gpd, - DRWShadingGroup *init_shgrp, - DRWShadingGroup *end_shgrp, - bool multi) +static void GPENCIL_draw_object(GPENCIL_Data *vedata, GPENCIL_tObject *ob) { - if (init_shgrp == NULL) { - return; + GPENCIL_PassList *psl = vedata->psl; + GPENCIL_PrivateData *pd = vedata->stl->pd; + GPENCIL_FramebufferList *fbl = vedata->fbl; + float clear_cols[2][4] = {{0.0f, 0.0f, 0.0f, 0.0f}, {1.0f, 1.0f, 1.0f, 1.0f}}; + + DRW_stats_group_start("GPencil Object"); + + GPUFrameBuffer *fb_object = (ob->vfx.first) ? fbl->object_fb : fbl->gpencil_fb; + + GPU_framebuffer_bind(fb_object); + GPU_framebuffer_clear_depth_stencil(fb_object, ob->is_drawmode3d ? 1.0f : 0.0f, 0x00); + + if (ob->vfx.first) { + GPU_framebuffer_multi_clear(fb_object, clear_cols); } - const bool do_antialiasing = ((!stl->storage->is_mat_preview) && (multi)); + for (GPENCIL_tLayer *layer = ob->layers.first; layer; layer = layer->next) { + if (layer->mask_bits) { + gpencil_draw_mask(vedata, ob, layer); + } + + if (layer->blend_ps) { + GPU_framebuffer_bind(fbl->layer_fb); + GPU_framebuffer_multi_clear(fbl->layer_fb, clear_cols); + } + else { + GPU_framebuffer_bind(fb_object); + } + + DRW_draw_pass(layer->geom_ps); - if (do_antialiasing) { - MULTISAMPLE_GP_SYNC_ENABLE(stl->storage->multisamples, fbl); + if (layer->blend_ps) { + GPU_framebuffer_bind(fb_object); + DRW_draw_pass(layer->blend_ps); + } + } + + for (GPENCIL_tVfx *vfx = ob->vfx.first; vfx; vfx = vfx->next) { + GPU_framebuffer_bind(*(vfx->target_fb)); + DRW_draw_pass(vfx->vfx_ps); } - DRW_draw_pass_subset(GPENCIL_3D_DRAWMODE(ob, gpd) ? psl->stroke_pass_3d : psl->stroke_pass_2d, - init_shgrp, - end_shgrp); + copy_m4_m4(pd->object_bound_mat, ob->plane_mat); + pd->is_stroke_order_3d = ob->is_drawmode3d; - if (do_antialiasing) { - MULTISAMPLE_GP_SYNC_DISABLE(stl->storage->multisamples, fbl, fb, txl); + if (pd->scene_fb) { + GPU_framebuffer_bind(pd->scene_fb); + DRW_draw_pass(psl->merge_depth_ps); } + + DRW_stats_group_end(); } -/* draw strokes to use for selection */ -static void drw_gpencil_select_render(GPENCIL_StorageList *stl, GPENCIL_PassList *psl) +static void GPENCIL_fast_draw_start(GPENCIL_Data *vedata) { - 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; - } - } + GPENCIL_PrivateData *pd = vedata->stl->pd; + GPENCIL_FramebufferList *fbl = vedata->fbl; + DefaultFramebufferList *dfbl = DRW_viewport_framebuffer_list_get(); + + if (!pd->snapshot_buffer_dirty) { + /* Copy back cached render. */ + GPU_framebuffer_blit(fbl->snapshot_fb, 0, dfbl->default_fb, 0, GPU_DEPTH_BIT); + GPU_framebuffer_blit(fbl->snapshot_fb, 0, fbl->gpencil_fb, 0, GPU_COLOR_BIT); + GPU_framebuffer_blit(fbl->snapshot_fb, 1, fbl->gpencil_fb, 1, GPU_COLOR_BIT); + /* Bypass drawing. */ + pd->tobjects.first = pd->tobjects.last = NULL; } } -/* draw scene */ -void GPENCIL_draw_scene(void *ved) +static void GPENCIL_fast_draw_end(GPENCIL_Data *vedata) { - 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; + GPENCIL_PrivateData *pd = vedata->stl->pd; + GPENCIL_FramebufferList *fbl = vedata->fbl; DefaultFramebufferList *dfbl = DRW_viewport_framebuffer_list_get(); - 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; + if (pd->snapshot_buffer_dirty) { + /* Save to snapshot buffer. */ + GPU_framebuffer_blit(dfbl->default_fb, 0, fbl->snapshot_fb, 0, GPU_DEPTH_BIT); + GPU_framebuffer_blit(fbl->gpencil_fb, 0, fbl->snapshot_fb, 0, GPU_COLOR_BIT); + GPU_framebuffer_blit(fbl->gpencil_fb, 1, fbl->snapshot_fb, 1, GPU_COLOR_BIT); + pd->snapshot_buffer_dirty = false; } - - /* 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); - } + /* Draw the sbuffer stroke(s). */ + for (GPENCIL_tObject *ob = pd->sbuffer_tobjects.first; ob; ob = ob->next) { + GPENCIL_draw_object(vedata, ob); } +} - /* 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); - } +void GPENCIL_draw_scene(void *ved) +{ + GPENCIL_Data *vedata = (GPENCIL_Data *)ved; + GPENCIL_PrivateData *pd = vedata->stl->pd; + GPENCIL_FramebufferList *fbl = vedata->fbl; + float clear_cols[2][4] = {{0.0f, 0.0f, 0.0f, 0.0f}, {1.0f, 1.0f, 1.0f, 1.0f}}; - MULTISAMPLE_GP_SYNC_ENABLE(stl->storage->multisamples, fbl); + /* Fade 3D objects. */ + if ((!pd->is_render) && (pd->fade_3d_object_opacity > -1.0f)) { + mul_v4_fl(clear_cols[1], pd->fade_3d_object_opacity); + } - DRW_draw_pass(psl->drawing_pass); + if (pd->draw_depth_only) { + GPENCIL_draw_scene_depth_only(vedata); + return; + } - MULTISAMPLE_GP_SYNC_DISABLE(stl->storage->multisamples, fbl, dfbl->default_fb, txl); + if (pd->tobjects.first == NULL) { + return; + } - /* 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); - } - } + if (pd->do_fast_drawing) { + GPENCIL_fast_draw_start(vedata); + } - /* free memory */ - DRW_gpencil_free_runtime_data(ved); + if (pd->tobjects.first) { + GPU_framebuffer_bind(fbl->gpencil_fb); + GPU_framebuffer_multi_clear(fbl->gpencil_fb, clear_cols); + } - return; + for (GPENCIL_tObject *ob = pd->tobjects.first; ob; ob = ob->next) { + GPENCIL_draw_object(vedata, ob); } - if (DRW_state_is_fbo()) { + if (pd->do_fast_drawing) { + GPENCIL_fast_draw_end(vedata); + } - /* 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, 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, 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 = 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, 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 = 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); - } - } + if (pd->scene_fb) { + GPENCIL_antialiasing_draw(vedata); } - /* 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); - } + pd->gp_object_pool = pd->gp_layer_pool = pd->gp_vfx_pool = pd->gp_maskbit_pool = NULL; - /* 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; - } + /* Free temp stroke buffers. */ + if (pd->sbuffer_gpd) { + DRW_cache_gpencil_sbuffer_clear(pd->obact); } } +static void GPENCIL_engine_free(void) +{ + GPENCIL_shader_free(); +} + static const DrawEngineDataSize GPENCIL_data_size = DRW_VIEWPORT_DATA_SIZE(GPENCIL_Data); DrawEngineType draw_engine_gpencil_type = { diff --git a/source/blender/draw/engines/gpencil/gpencil_engine.h b/source/blender/draw/engines/gpencil/gpencil_engine.h index 44a30260343..4b6059f1474 100644 --- a/source/blender/draw/engines/gpencil/gpencil_engine.h +++ b/source/blender/draw/engines/gpencil/gpencil_engine.h @@ -23,6 +23,10 @@ #ifndef __GPENCIL_ENGINE_H__ #define __GPENCIL_ENGINE_H__ +#include "DNA_gpencil_types.h" + +#include "BLI_bitmap.h" + #include "GPU_batch.h" extern DrawEngineType draw_engine_gpencil_type; @@ -34,204 +38,201 @@ struct Object; struct RenderEngine; struct RenderLayer; struct bGPDstroke; - +struct View3D; +struct GpencilBatchCache; struct GPUBatch; struct GPUVertBuf; struct GPUVertFormat; -#define GPENCIL_MAX_SHGROUPS 65536 -#define GPENCIL_GROUPS_BLOCK_SIZE 1024 +/* used to convert pixel scale. */ +#define GPENCIL_PIXEL_FACTOR 2000.0f /* used to expand VBOs. Size has a big impact in the speed */ #define GPENCIL_VBO_BLOCK_SIZE 128 -#define GPENCIL_COLOR_SOLID 0 -#define GPENCIL_COLOR_TEXTURE 1 -#define GPENCIL_COLOR_PATTERN 2 - -/* *********** OBJECTS CACHE *********** */ -typedef struct tGPencilObjectCache_shgrp { - /** type of blend (regular, add, mult, etc...) */ - int mode; - /** flag to enable the layer clamping */ - bool mask_layer; - /** factor to define the opacity of the layer */ - float blend_opacity; - DRWShadingGroup *init_shgrp; - DRWShadingGroup *end_shgrp; -} tGPencilObjectCache_shgrp; - -/* used to save gpencil object data for drawing */ -typedef struct tGPencilObjectCache { - struct Object *ob; - struct bGPdata *gpd; - int idx; /*original index, can change after sort */ - char *name; - - /* effects */ - bool has_fx; - ListBase shader_fx; - float pixfactor; - DRWShadingGroup *fx_wave_sh; - DRWShadingGroup *fx_blur_sh; - DRWShadingGroup *fx_colorize_sh; - DRWShadingGroup *fx_pixel_sh; - DRWShadingGroup *fx_rim_sh; - DRWShadingGroup *fx_shadow_sh; - DRWShadingGroup *fx_glow_sh; - DRWShadingGroup *fx_swirl_sh; - DRWShadingGroup *fx_flip_sh; - DRWShadingGroup *fx_light_sh; - - float loc[3]; - float obmat[4][4]; - float zdepth; /* z-depth value to sort gp object */ - bool is_dup_ob; /* flag to tag duplicate objects */ - float scale; - - /* shading type */ - int shading_type[2]; - - /* GPU data size */ - int tot_vertex; - int tot_triangles; - - /* Save shader groups by layer */ - int tot_layers; - tGPencilObjectCache_shgrp *shgrp_array; - -} tGPencilObjectCache; +#define GP_MAX_MASKBITS 256 + +/* UBO structure. Watch out for padding. Must match GLSL declaration. */ +typedef struct gpMaterial { + float stroke_color[4]; + float fill_color[4]; + float fill_mix_color[4]; + float fill_uv_transform[3][2], _pad0[2]; + float stroke_texture_mix; + float stroke_u_scale; + float fill_texture_mix; + int flag; +} gpMaterial; + +/* gpMaterial->flag */ +/* WATCH Keep in sync with GLSL declaration. */ +#define GP_STROKE_ALIGNMENT_STROKE 1 +#define GP_STROKE_ALIGNMENT_OBJECT 2 +#define GP_STROKE_ALIGNMENT_FIXED 3 +#define GP_STROKE_ALIGNMENT 0x3 +#define GP_STROKE_OVERLAP (1 << 2) +#define GP_STROKE_TEXTURE_USE (1 << 3) +#define GP_STROKE_TEXTURE_STENCIL (1 << 4) +#define GP_STROKE_TEXTURE_PREMUL (1 << 5) +#define GP_STROKE_DOTS (1 << 6) +#define GP_FILL_TEXTURE_USE (1 << 10) +#define GP_FILL_TEXTURE_PREMUL (1 << 11) +#define GP_FILL_TEXTURE_CLIP (1 << 12) +#define GP_FILL_GRADIENT_USE (1 << 13) +#define GP_FILL_GRADIENT_RADIAL (1 << 14) + +#define GPENCIL_LIGHT_BUFFER_LEN 128 + +/* UBO structure. Watch out for padding. Must match GLSL declaration. */ +typedef struct gpLight { + float color[3], type; + float right[3], spotsize; + float up[3], spotblend; + float forward[4]; + float position[4]; +} gpLight; + +/* gpLight->type */ +/* WATCH Keep in sync with GLSL declaration. */ +#define GP_LIGHT_TYPE_POINT 0.0 +#define GP_LIGHT_TYPE_SPOT 1.0 +#define GP_LIGHT_TYPE_SUN 2.0 +#define GP_LIGHT_TYPE_AMBIENT 3.0 + +BLI_STATIC_ASSERT_ALIGN(gpMaterial, 16) +BLI_STATIC_ASSERT_ALIGN(gpLight, 16) + +/* *********** Draw Datas *********** */ +typedef struct GPENCIL_MaterialPool { + /* Linklist. */ + struct GPENCIL_MaterialPool *next; + /* GPU representatin of materials. */ + gpMaterial mat_data[GP_MATERIAL_BUFFER_LEN]; + /* Matching ubo. */ + struct GPUUniformBuffer *ubo; + /* Texture per material. NULL means none. */ + struct GPUTexture *tex_fill[GP_MATERIAL_BUFFER_LEN]; + struct GPUTexture *tex_stroke[GP_MATERIAL_BUFFER_LEN]; + /* Number of material used in this pool. */ + int used_count; +} GPENCIL_MaterialPool; + +typedef struct GPENCIL_LightPool { + /* GPU representatin of materials. */ + gpLight light_data[GPENCIL_LIGHT_BUFFER_LEN]; + /* Matching ubo. */ + struct GPUUniformBuffer *ubo; + /* Number of light in the pool. */ + int light_used; +} GPENCIL_LightPool; + +typedef struct GPENCIL_ViewLayerData { + /* GPENCIL_tObject */ + struct BLI_memblock *gp_object_pool; + /* GPENCIL_tLayer */ + struct BLI_memblock *gp_layer_pool; + /* GPENCIL_tVfx */ + struct BLI_memblock *gp_vfx_pool; + /* GPENCIL_MaterialPool */ + struct BLI_memblock *gp_material_pool; + /* GPENCIL_LightPool */ + struct BLI_memblock *gp_light_pool; + /* BLI_bitmap */ + struct BLI_memblock *gp_maskbit_pool; +} GPENCIL_ViewLayerData; + +/* *********** GPencil *********** */ + +typedef struct GPENCIL_tVfx { + /** Linklist */ + struct GPENCIL_tVfx *next; + DRWPass *vfx_ps; + /* Framebuffer reference since it may not be allocated yet. */ + GPUFrameBuffer **target_fb; +} GPENCIL_tVfx; + +typedef struct GPENCIL_tLayer { + /** Linklist */ + struct GPENCIL_tLayer *next; + /** Geometry pass (draw all strokes). */ + DRWPass *geom_ps; + /** Blend pass to composite onto the target buffer (blends modes). NULL if not needed. */ + DRWPass *blend_ps; + /** First shading group created for this layer. Contains all uniforms. */ + DRWShadingGroup *base_shgrp; + /** Layer id of the mask. */ + BLI_bitmap *mask_bits; + BLI_bitmap *mask_invert_bits; + /** Index in the layer list. Used as id for masking. */ + int layer_id; +} GPENCIL_tLayer; + +typedef struct GPENCIL_tObject { + /** Linklist */ + struct GPENCIL_tObject *next; + + struct { + GPENCIL_tLayer *first, *last; + } layers; + + struct { + GPENCIL_tVfx *first, *last; + } vfx; + + /* Distance to camera. Used for sorting. */ + float camera_z; + /* Used for stroke thickness scaling. */ + float object_scale; + /* Normal used for shading. Based on view angle. */ + float plane_normal[3]; + /* Used for drawing depth merge pass. */ + float plane_mat[4][4]; + + bool is_drawmode3d; +} GPENCIL_tObject; /* *********** 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; - int caps_mode[2]; - float obj_scale; - int xray_mode; - int alignment_mode; - - float gradient_f; - float gradient_s[2]; - - float mix_stroke_factor; - - /* color of the wireframe */ - float wire_color[4]; - /* shading type and mode */ - int shading_type[2]; - int is_xray; -} GPENCIL_shgroup; - -typedef struct GPENCIL_Storage { - int shgroup_id; /* total elements */ - int stroke_style; - int color_type; - int mode; - int xray; - int keep_size; - float obj_scale; - float pixfactor; - bool is_playing; - bool is_render; - bool is_mat_preview; - bool is_main_overlay; - bool is_main_onion; - bool background_ready; - int is_xray; - bool is_ontop; - bool reset_cache; - const float *pixsize; - float render_pixsize; - int tonemapping; - int do_select_outline; - float select_color[4]; - short multisamples; - float grid_matrix[4][4]; - - short framebuffer_flag; /* flag what framebuffer need to create */ - - int blend_mode; - int mask_layer; - - /* simplify settings*/ - bool simplify_fill; - bool simplify_modif; - bool simplify_fx; - bool simplify_blend; - - float gradient_f; - float gradient_s[2]; - int alignment_mode; - - float mix_stroke_factor; - - /* Render Matrices and data */ - float view_vecs[2][4]; /* vec4[2] */ - - int shade_render[2]; - - Object *camera; /* camera pointer for render mode */ -} GPENCIL_Storage; - -typedef enum eGpencilFramebuffer_Flag { - GP_FRAMEBUFFER_MULTISAMPLE = (1 << 0), - GP_FRAMEBUFFER_BASIC = (1 << 1), - GP_FRAMEBUFFER_DRAW = (1 << 2), -} eGpencilFramebuffer_Flag; - typedef struct GPENCIL_StorageList { - struct GPENCIL_Storage *storage; - struct g_data *g_data; - struct GPENCIL_shgroup *shgroups; + struct GPENCIL_PrivateData *pd; } GPENCIL_StorageList; typedef struct GPENCIL_PassList { - struct DRWPass *stroke_pass_2d; - struct DRWPass *stroke_pass_3d; - 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; - struct DRWPass *blend_pass; - - /* effects */ - struct DRWPass *fx_shader_pass; - struct DRWPass *fx_shader_pass_blend; - + /* Composite the main GPencil buffer onto the rendered image. */ + struct DRWPass *composite_ps; + /* Composite the object depth to the default depth buffer to occlude overlays. */ + struct DRWPass *merge_depth_ps; + /* Invert mask buffer content. */ + struct DRWPass *mask_invert_ps; + /* Anti-Aliasing. */ + struct DRWPass *smaa_edge_ps; + struct DRWPass *smaa_weight_ps; + struct DRWPass *smaa_resolve_ps; } GPENCIL_PassList; typedef struct GPENCIL_FramebufferList { - struct GPUFrameBuffer *main; - struct GPUFrameBuffer *temp_fb_a; - struct GPUFrameBuffer *temp_fb_b; - struct GPUFrameBuffer *temp_fb_fx; - struct GPUFrameBuffer *background_fb; - - struct GPUFrameBuffer *multisample_fb; + struct GPUFrameBuffer *render_fb; + struct GPUFrameBuffer *gpencil_fb; + struct GPUFrameBuffer *snapshot_fb; + struct GPUFrameBuffer *layer_fb; + struct GPUFrameBuffer *object_fb; + struct GPUFrameBuffer *mask_fb; + struct GPUFrameBuffer *smaa_edge_fb; + struct GPUFrameBuffer *smaa_weight_fb; } GPENCIL_FramebufferList; typedef struct GPENCIL_TextureList { - struct GPUTexture *texture; - - /* multisample textures */ - struct GPUTexture *multisample_color; - struct GPUTexture *multisample_depth; - - /* Background textures for speed-up drawing. */ - struct GPUTexture *background_depth_tx; - struct GPUTexture *background_color_tx; - + /* Dummy texture to avoid errors cause by empty sampler. */ + struct GPUTexture *dummy_texture; + /* Snapshot for smoother drawing. */ + struct GPUTexture *snapshot_depth_tx; + struct GPUTexture *snapshot_color_tx; + struct GPUTexture *snapshot_reveal_tx; + /* Textures used by Antialiasing. */ + struct GPUTexture *smaa_area_tx; + struct GPUTexture *smaa_search_tx; + /* Textures used during render. Containing underlying rendered scene. */ + struct GPUTexture *render_depth_tx; + struct GPUTexture *render_color_tx; } GPENCIL_TextureList; typedef struct GPENCIL_Data { @@ -240,248 +241,175 @@ typedef struct GPENCIL_Data { 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; - - int gp_cache_used; /* total objects in cache */ - int gp_cache_size; /* size of the cache */ - struct tGPencilObjectCache *gp_object_cache; - - /* for buffer only one batch is nedeed because the drawing is only of one stroke */ - GPUBatch *batch_buffer_stroke; - GPUBatch *batch_buffer_fill; - GPUBatch *batch_buffer_ctrlpoint; - - /* grid geometry */ - GPUBatch *batch_grid; - - /* 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_fx; - struct GPUTexture *temp_depth_tx_fx; - - int session_flag; - bool do_instances; - -} 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 { - /* textures */ - struct GPUTexture *gpencil_blank_texture; - - /* 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_blend_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_glow_prepare_sh; - struct GPUShader *gpencil_fx_glow_resolve_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_shadow_prepare_sh; - struct GPUShader *gpencil_fx_shadow_resolve_sh; - struct GPUShader *gpencil_fx_swirl_sh; - struct GPUShader *gpencil_fx_wave_sh; - -} GPENCIL_e_data; /* Engine data */ - -/* GPUBatch Cache Element */ -typedef struct GpencilBatchCacheElem { - GPUBatch *batch; - GPUVertBuf *vbo; - int vbo_len; - /* attr ids */ - GPUVertFormat *format; - uint pos_id; - uint color_id; - uint thickness_id; - uint uvdata_id; - uint prev_pos_id; - - /* size for VBO alloc */ - int tot_vertex; -} GpencilBatchCacheElem; - -/* Defines each batch group to define later the shgroup */ -typedef struct GpencilBatchGroup { - struct bGPDlayer *gpl; /* reference to original layer */ - struct bGPDframe *gpf; /* reference to original frame */ - struct bGPDstroke *gps; /* reference to original stroke */ - short type; /* type of element */ - bool onion; /* the group is part of onion skin */ - int vertex_idx; /* index of vertex data */ -} GpencilBatchGroup; - -typedef enum GpencilBatchGroup_Type { - eGpencilBatchGroupType_Stroke = 1, - eGpencilBatchGroupType_Point = 2, - eGpencilBatchGroupType_Fill = 3, - eGpencilBatchGroupType_Edit = 4, - eGpencilBatchGroupType_Edlin = 5, -} GpencilBatchGroup_Type; - -/* Runtime data for GPU and evaluated frames after applying modifiers */ -typedef struct GpencilBatchCache { - GpencilBatchCacheElem b_stroke; - GpencilBatchCacheElem b_point; - GpencilBatchCacheElem b_fill; - GpencilBatchCacheElem b_edit; - GpencilBatchCacheElem b_edlin; - - /** Cache is dirty */ - bool is_dirty; - /** Edit mode flag */ - bool is_editmode; - /** Last cache frame */ - int cache_frame; - - /** Total groups in arrays */ - int grp_used; - /** Max size of the array */ - int grp_size; - /** Array of cache elements */ - struct GpencilBatchGroup *grp_cache; -} GpencilBatchCache; - -/* general drawing functions */ -struct DRWShadingGroup *gpencil_shgroup_stroke_create(struct GPENCIL_e_data *e_data, - struct GPENCIL_Data *vedata, - struct DRWPass *pass, - struct GPUShader *shader, - struct Object *ob, - float (*obmat)[4], - struct bGPdata *gpd, - struct bGPDlayer *gpl, - struct bGPDstroke *gps, - struct MaterialGPencilStyle *gp_style, - int id, - bool onion, - const float scale, - const int shading_type[2]); -void gpencil_populate_datablock(struct GPENCIL_e_data *e_data, - void *vedata, - struct Object *ob, - struct tGPencilObjectCache *cache_ob); -void gpencil_populate_buffer_strokes(struct GPENCIL_e_data *e_data, - void *vedata, - struct ToolSettings *ts, - struct Object *ob); -void gpencil_populate_multiedit(struct GPENCIL_e_data *e_data, - void *vedata, - struct Object *ob, - struct tGPencilObjectCache *cache_ob); -void gpencil_populate_particles(struct GPENCIL_e_data *e_data, - struct GHash *gh_objects, - void *vedata); - -void gpencil_multisample_ensure(struct GPENCIL_Data *vedata, int rect_w, int rect_h); - -/* create geometry functions */ -void gpencil_get_point_geom(struct GpencilBatchCacheElem *be, - struct bGPDstroke *gps, - short thickness, - const float ink[4], - const int follow_mode); -void gpencil_get_stroke_geom(struct GpencilBatchCacheElem *be, - struct bGPDstroke *gps, - short thickness, - const float ink[4]); -void gpencil_get_fill_geom(struct GpencilBatchCacheElem *be, - struct Object *ob, - struct bGPDstroke *gps, - const float color[4]); -void gpencil_get_edit_geom(struct GpencilBatchCacheElem *be, - struct bGPDstroke *gps, - float alpha, - short dflag); -void gpencil_get_edlin_geom(struct GpencilBatchCacheElem *be, - struct bGPDstroke *gps, - float alpha, - const bool hide_select); - -struct GPUBatch *gpencil_get_buffer_stroke_geom(struct bGPdata *gpd, short thickness); -struct GPUBatch *gpencil_get_buffer_fill_geom(struct bGPdata *gpd); -struct GPUBatch *gpencil_get_buffer_point_geom(struct bGPdata *gpd, short thickness); -struct GPUBatch *gpencil_get_buffer_ctrlpoint_geom(struct bGPdata *gpd); -struct GPUBatch *gpencil_get_grid(Object *ob); - -/* object cache functions */ -struct tGPencilObjectCache *gpencil_object_cache_add(struct tGPencilObjectCache *cache_array, - struct Object *ob, - int *gp_cache_size, - int *gp_cache_used); - -bool gpencil_onion_active(struct bGPdata *gpd); - -/* shading groups cache functions */ -struct GpencilBatchGroup *gpencil_group_cache_add(struct GpencilBatchGroup *cache_array, - struct bGPDlayer *gpl, - struct bGPDframe *gpf, - struct bGPDstroke *gps, - const short type, - const bool onion, - const int vertex_idx, - int *grp_size, - int *grp_used); +typedef struct GPENCIL_PrivateData { + /* Pointers copied from GPENCIL_ViewLayerData. */ + struct BLI_memblock *gp_object_pool; + struct BLI_memblock *gp_layer_pool; + struct BLI_memblock *gp_vfx_pool; + struct BLI_memblock *gp_material_pool; + struct BLI_memblock *gp_light_pool; + struct BLI_memblock *gp_maskbit_pool; + /* Last used material pool. */ + GPENCIL_MaterialPool *last_material_pool; + /* Last used light pool. */ + GPENCIL_LightPool *last_light_pool; + /* Common lightpool containing all lights in the scene. */ + GPENCIL_LightPool *global_light_pool; + /* Common lightpool containing one ambient white light. */ + GPENCIL_LightPool *shadeless_light_pool; + /* Linked list of tObjects. */ + struct { + GPENCIL_tObject *first, *last; + } tobjects, tobjects_infront; + /* Temp Textures (shared with other engines). */ + GPUTexture *depth_tx; + GPUTexture *color_tx; + GPUTexture *color_layer_tx; + GPUTexture *color_object_tx; + /* Revealage is 1 - alpha */ + GPUTexture *reveal_tx; + GPUTexture *reveal_layer_tx; + GPUTexture *reveal_object_tx; + /* Mask texture */ + GPUTexture *mask_tx; + /* Anti-Aliasing. */ + GPUTexture *smaa_edge_tx; + GPUTexture *smaa_weight_tx; + /* Pointer to dtxl->depth */ + GPUTexture *scene_depth_tx; + GPUFrameBuffer *scene_fb; + /* Copy of txl->dummy_tx */ + GPUTexture *dummy_tx; + /* Copy of v3d->shading.single_color. */ + float v3d_single_color[3]; + /* Copy of v3d->shading.color_type or -1 to ignore. */ + int v3d_color_type; + /* Current frame */ + int cfra; + /* If we are rendering for final render (F12). */ + bool is_render; + /* If we are in viewport display (used for VFX). */ + bool is_viewport; + /* True in selection and auto_depth drawing */ + bool draw_depth_only; + /* Is shading set to wireframe. */ + bool draw_wireframe; + /* Used by the depth merge step. */ + int is_stroke_order_3d; + float object_bound_mat[4][4]; + /* Used for computing object distance to camera. */ + float camera_z_axis[3], camera_z_offset; + float camera_pos[3]; + /* Pseudo depth of field parameter. Used to scale blur radius. */ + float dof_params[2]; + /* Used for DoF Setup. */ + Object *camera; + /* Copy of draw_ctx->scene for convenience. */ + struct Scene *scene; + + /* Active object. */ + Object *obact; + /* Object being in draw mode. */ + struct bGPdata *sbuffer_gpd; + /* Layer to append the temp stroke to. */ + struct bGPDlayer *sbuffer_layer; + /* Temporary stroke currently being drawn. */ + struct bGPDstroke *sbuffer_stroke; + /* List of temp objects containing the stroke. */ + struct { + GPENCIL_tObject *first, *last; + } sbuffer_tobjects; + /* Batches containing the temp stroke. */ + GPUBatch *stroke_batch; + GPUBatch *fill_batch; + bool do_fast_drawing; + bool snapshot_buffer_dirty; + + /* Display onion skinning */ + bool do_onion; + /* simplify settings */ + bool simplify_fill; + bool simplify_fx; + bool simplify_antialias; + /* Use scene lighting or flat shading (global setting). */ + bool use_lighting; + /* Use physical lights or just ambient lighting. */ + bool use_lights; + /* Do we need additional framebuffers? */ + bool use_layer_fb; + bool use_object_fb; + bool use_mask_fb; + /* Some blend mode needs to add negative values. + * This is only supported if target texture is signed. */ + bool use_signed_fb; + /* Use only lines for multiedit and not active frame. */ + bool use_multiedit_lines_only; + /* Layer opacity for fading. */ + float fade_layer_opacity; + /* Opacity for fading gpencil objects. */ + float fade_gp_object_opacity; + /* Opacity for fading 3D objects. */ + float fade_3d_object_opacity; + /* Mask opacity uniform. */ + float mask_opacity; + /* Xray transparency in solid mode. */ + float xray_alpha; + /* Mask invert uniform. */ + int mask_invert; +} GPENCIL_PrivateData; /* geometry batch cache functions */ struct GpencilBatchCache *gpencil_batch_cache_get(struct Object *ob, int cfra); -/* 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); +GPENCIL_tObject *gpencil_object_cache_add(GPENCIL_PrivateData *pd, Object *ob); +void gpencil_object_cache_sort(GPENCIL_PrivateData *pd); -void gpencil_fx_prepare(struct GPENCIL_e_data *e_data, - struct GPENCIL_Data *vedata, - struct tGPencilObjectCache *cache_ob); -void gpencil_fx_draw(struct GPENCIL_e_data *e_data, - struct GPENCIL_Data *vedata, - struct tGPencilObjectCache *cache_ob); +GPENCIL_tLayer *gpencil_layer_cache_add(GPENCIL_PrivateData *pd, + const Object *ob, + const bGPDlayer *gpl, + const bGPDframe *gpf, + GPENCIL_tObject *tgp_ob); +GPENCIL_tLayer *gpencil_layer_cache_get(GPENCIL_tObject *tgp_ob, int number); + +GPENCIL_MaterialPool *gpencil_material_pool_create(GPENCIL_PrivateData *pd, Object *ob, int *ofs); +void gpencil_material_resources_get(GPENCIL_MaterialPool *first_pool, + int mat_id, + struct GPUTexture **r_tex_stroke, + struct GPUTexture **r_tex_fill, + struct GPUUniformBuffer **r_ubo_mat); + +void gpencil_light_ambient_add(GPENCIL_LightPool *lightpool, const float color[3]); +void gpencil_light_pool_populate(GPENCIL_LightPool *matpool, Object *ob); +GPENCIL_LightPool *gpencil_light_pool_add(GPENCIL_PrivateData *pd); +GPENCIL_LightPool *gpencil_light_pool_create(GPENCIL_PrivateData *pd, Object *ob); + +/* effects */ +void gpencil_vfx_cache_populate(GPENCIL_Data *vedata, Object *ob, GPENCIL_tObject *tgp_ob); + +/* Shaders */ +struct GPUShader *GPENCIL_shader_antialiasing(int stage); +struct GPUShader *GPENCIL_shader_geometry_get(void); +struct GPUShader *GPENCIL_shader_composite_get(void); +struct GPUShader *GPENCIL_shader_layer_blend_get(void); +struct GPUShader *GPENCIL_shader_mask_invert_get(void); +struct GPUShader *GPENCIL_shader_depth_merge_get(void); +struct GPUShader *GPENCIL_shader_fx_blur_get(void); +struct GPUShader *GPENCIL_shader_fx_colorize_get(void); +struct GPUShader *GPENCIL_shader_fx_composite_get(void); +struct GPUShader *GPENCIL_shader_fx_transform_get(void); +struct GPUShader *GPENCIL_shader_fx_glow_get(void); +struct GPUShader *GPENCIL_shader_fx_pixelize_get(void); +struct GPUShader *GPENCIL_shader_fx_rim_get(void); +struct GPUShader *GPENCIL_shader_fx_shadow_get(void); + +void GPENCIL_shader_free(void); + +/* Antialiasing */ +void GPENCIL_antialiasing_init(struct GPENCIL_Data *vedata); +void GPENCIL_antialiasing_draw(struct GPENCIL_Data *vedata); /* main functions */ void GPENCIL_engine_init(void *vedata); @@ -493,43 +421,17 @@ void GPENCIL_draw_scene(void *vedata); /* render */ void GPENCIL_render_init(struct GPENCIL_Data *ved, struct RenderEngine *engine, - struct Depsgraph *depsgraph); + struct RenderLayer *render_layer, + const struct Depsgraph *depsgraph, + const rcti *rect); void GPENCIL_render_to_image(void *vedata, struct RenderEngine *engine, struct RenderLayer *render_layer, const rcti *rect); -/* TODO: GPXX workaround function to call free memory from draw manager while draw manager support - * scene finish callback. */ -void DRW_gpencil_free_runtime_data(void *ved); - -/* Use of multisample framebuffers. */ -#define MULTISAMPLE_GP_SYNC_ENABLE(lvl, fbl) \ - { \ - if ((lvl > 0) && (fbl->multisample_fb != NULL) && (DRW_state_is_fbo())) { \ - DRW_stats_query_start("GP Multisample Blit"); \ - GPU_framebuffer_bind(fbl->multisample_fb); \ - GPU_framebuffer_clear_color_depth_stencil( \ - fbl->multisample_fb, (const float[4]){0.0f}, 1.0f, 0x0); \ - DRW_stats_query_end(); \ - } \ - } \ - ((void)0) - -#define MULTISAMPLE_GP_SYNC_DISABLE(lvl, fbl, fb, txl) \ - { \ - if ((lvl > 0) && (fbl->multisample_fb != NULL) && (DRW_state_is_fbo())) { \ - DRW_stats_query_start("GP Multisample Resolve"); \ - GPU_framebuffer_bind(fb); \ - DRW_stats_query_end(); \ - } \ - } \ - ((void)0) - -#define GPENCIL_3D_DRAWMODE(ob, gpd) \ - ((gpd) && (gpd->draw_mode == GP_DRAWMODE_3D) && ((ob->dtx & OB_DRAWXRAY) == 0)) - -#define GPENCIL_USE_SOLID(stl) \ - ((stl) && ((stl->storage->is_render) || (stl->storage->is_mat_preview))) +/* Draw Data. */ +void gpencil_light_pool_free(void *storage); +void gpencil_material_pool_free(void *storage); +GPENCIL_ViewLayerData *GPENCIL_view_layer_data_ensure(void); #endif /* __GPENCIL_ENGINE_H__ */ diff --git a/source/blender/draw/engines/gpencil/gpencil_render.c b/source/blender/draw/engines/gpencil/gpencil_render.c index 8c126310ea2..083b04c0600 100644 --- a/source/blender/draw/engines/gpencil/gpencil_render.c +++ b/source/blender/draw/engines/gpencil/gpencil_render.c @@ -33,66 +33,22 @@ #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) +void GPENCIL_render_init(GPENCIL_Data *vedata, + RenderEngine *engine, + struct RenderLayer *render_layer, + const Depsgraph *depsgraph, + const rcti *rect) { - GPENCIL_Data *vedata = (GPENCIL_Data *)ved; - GPENCIL_StorageList *stl = vedata->stl; GPENCIL_FramebufferList *fbl = vedata->fbl; + GPENCIL_TextureList *txl = vedata->txl; 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 multisample framebuffer for AA */ - if (U.gpencil_multisamples > 0) { - int rect_w = (int)viewport_size[0]; - int rect_h = (int)viewport_size[1]; - gpencil_multisample_ensure(vedata, rect_w, rect_h); - } - - vedata->render_depth_tx = DRW_texture_pool_query_2d( - size[0], size[1], GPU_DEPTH_COMPONENT24, &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. */ - float winmat[4][4], viewmat[4][4], viewinv[4][4], persmat[4][4]; + float winmat[4][4], viewmat[4][4], viewinv[4][4]; struct Object *camera = DEG_get_evaluated_object(depsgraph, RE_GetCamera(engine->re)); float frame = BKE_scene_frame_get(scene); @@ -105,85 +61,101 @@ void GPENCIL_render_init(GPENCIL_Data *ved, RenderEngine *engine, struct Depsgra DRW_view_default_set(view); DRW_view_set_active(view); - DRW_view_persmat_get(NULL, persmat, false); + /* Create depth texture & color texture from render result. */ + const char *viewname = RE_GetActiveRenderView(engine->re); + RenderPass *rpass_z_src = RE_pass_find_by_name(render_layer, RE_PASSNAME_Z, viewname); + RenderPass *rpass_col_src = RE_pass_find_by_name(render_layer, RE_PASSNAME_COMBINED, viewname); - /* calculate pixel size for render */ - stl->storage->render_pixsize = get_render_pixelsize(persmat, viewport_size[0], viewport_size[1]); + float *pix_z = (rpass_z_src) ? rpass_z_src->rect : NULL; + float *pix_col = (rpass_col_src) ? rpass_col_src->rect : NULL; - /* INIT CACHE */ - GPENCIL_cache_init(vedata); + if (!pix_z || !pix_col) { + RE_engine_set_error_message(engine, + "Warning: To render grease pencil, enable Combined and Z passes."); + } + + if (pix_z) { + /* Depth need to be remapped to [0..1] range. */ + pix_z = MEM_dupallocN(pix_z); + + int pix_ct = rpass_z_src->rectx * rpass_z_src->recty; + + if (DRW_view_is_persp_get(view)) { + for (int i = 0; i < pix_ct; i++) { + pix_z[i] = (-winmat[3][2] / -pix_z[i]) - winmat[2][2]; + pix_z[i] = clamp_f(pix_z[i] * 0.5f + 0.5f, 0.0f, 1.0f); + } + } + else { + /* Keep in mind, near and far distance are negatives. */ + float near = DRW_view_near_distance_get(view); + float far = DRW_view_far_distance_get(view); + float range_inv = 1.0f / fabsf(far - near); + for (int i = 0; i < pix_ct; i++) { + pix_z[i] = (pix_z[i] + near) * range_inv; + pix_z[i] = clamp_f(pix_z[i], 0.0f, 1.0f); + } + } + } + + const bool do_region = (scene->r.mode & R_BORDER) != 0; + const bool do_clear_z = !pix_z || do_region; + const bool do_clear_col = !pix_col || do_region; + + /* FIXME(fclem): we have a precision loss in the depth buffer because of this reupload. + * Find where it comes from! */ + txl->render_depth_tx = DRW_texture_create_2d( + size[0], size[1], GPU_DEPTH_COMPONENT24, 0, do_region ? NULL : pix_z); + txl->render_color_tx = DRW_texture_create_2d( + size[0], size[1], GPU_RGBA16F, 0, do_region ? NULL : pix_col); + + GPU_framebuffer_ensure_config(&fbl->render_fb, + { + GPU_ATTACHMENT_TEXTURE(txl->render_depth_tx), + GPU_ATTACHMENT_TEXTURE(txl->render_color_tx), + }); + + if (do_clear_z || do_clear_col) { + /* To avoid unpredictable result, clear buffers that have not be initialized. */ + GPU_framebuffer_bind(fbl->render_fb); + if (do_clear_col) { + float clear_col[4] = {0.0f, 0.0f, 0.0f, 0.0f}; + GPU_framebuffer_clear_color(fbl->render_fb, clear_col); + } + if (do_clear_z) { + GPU_framebuffer_clear_depth(fbl->render_fb, 1.0f); + } + } + + if (do_region) { + int x = rect->xmin; + int y = rect->ymin; + int w = BLI_rcti_size_x(rect); + int h = BLI_rcti_size_y(rect); + if (pix_col) { + GPU_texture_update_sub(txl->render_color_tx, GPU_DATA_FLOAT, pix_col, x, y, 0, w, h, 0); + } + if (pix_z) { + GPU_texture_update_sub(txl->render_depth_tx, GPU_DATA_FLOAT, pix_z, x, y, 0, w, h, 0); + } + } + + MEM_SAFE_FREE(pix_z); } /* 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)) + Depsgraph *UNUSED(depsgraph)) { - if (ob && ob->type == OB_GPENCIL) { + if (ob && ELEM(ob->type, OB_GPENCIL, OB_LAMP)) { if (DRW_object_visibility_in_active_context(ob) & OB_VISIBLE_SELF) { 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], - const 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_view_winmat_get(NULL, winmat, false); - DRW_view_winmat_get(NULL, invproj, true); - - /* 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, @@ -191,45 +163,52 @@ static void GPENCIL_render_result_z(struct RenderLayer *rl, { 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, + GPU_framebuffer_read_depth(vedata->fbl->render_fb, rect->xmin, rect->ymin, BLI_rcti_size_x(rect), BLI_rcti_size_y(rect), rp->rect); - bool is_persp = DRW_view_is_persp_get(NULL); - - GPENCIL_render_update_vecs(vedata); - float winmat[4][4]; DRW_view_winmat_get(NULL, winmat, false); + int pix_ct = BLI_rcti_size_x(rect) * BLI_rcti_size_y(rect); + /* 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) { + if (DRW_view_is_persp_get(NULL)) { + for (int i = 0; i < pix_ct; i++) { + if (rp->rect[i] == 1.0f) { + rp->rect[i] = 1e10f; /* Background */ + } + else { rp->rect[i] = rp->rect[i] * 2.0f - 1.0f; rp->rect[i] = winmat[3][2] / (rp->rect[i] + winmat[2][2]); } + } + } + else { + /* Keep in mind, near and far distance are negatives. */ + float near = DRW_view_near_distance_get(NULL); + float far = DRW_view_far_distance_get(NULL); + float range = fabsf(far - near); + + for (int i = 0; i < pix_ct; i++) { + if (rp->rect[i] == 1.0f) { + rp->rect[i] = 1e10f; /* Background */ + } else { - rp->rect[i] = -stl->storage->view_vecs[0][2] + - rp->rect[i] * -stl->storage->view_vecs[1][2]; + rp->rect[i] = -rp->rect[i] * range + near; } } } } } -/* read combined render result */ static void GPENCIL_render_result_combined(struct RenderLayer *rl, const char *viewname, GPENCIL_Data *vedata, @@ -238,8 +217,8 @@ static void GPENCIL_render_result_combined(struct RenderLayer *rl, 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, + GPU_framebuffer_bind(fbl->render_fb); + GPU_framebuffer_read_color(vedata->fbl->render_fb, rect->xmin, rect->ymin, BLI_rcti_size_x(rect), @@ -249,135 +228,31 @@ static void GPENCIL_render_result_combined(struct RenderLayer *rl, rp->rect); } -/* helper to blend pixels */ -static void blend_pixel(float top_color[4], float bottom_color[4], float dst_color[4]) -{ - float alpha = top_color[3]; - - /* use blend: GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA */ - dst_color[0] = (top_color[0] * alpha) + (bottom_color[0] * (1.0f - alpha)); - dst_color[1] = (top_color[1] * alpha) + (bottom_color[1] * (1.0f - alpha)); - dst_color[2] = (top_color[2] * alpha) + (bottom_color[2] * (1.0f - alpha)); -} - -/* render grease pencil to image */ -void GPENCIL_render_to_image(void *vedata, +void GPENCIL_render_to_image(void *ved, RenderEngine *engine, struct RenderLayer *render_layer, const rcti *rect) { + GPENCIL_Data *vedata = (GPENCIL_Data *)ved; 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"); - } + Depsgraph *depsgraph = draw_ctx->depsgraph; + GPENCIL_render_init(vedata, engine, render_layer, depsgraph, rect); 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); + vedata->stl->pd->camera = DEG_get_evaluated_object(depsgraph, RE_GetCamera(engine->re)); + /* Loop over all objects and create draw structure. */ + GPENCIL_cache_init(vedata); + DRW_render_object_iter(vedata, engine, depsgraph, GPENCIL_render_cache); GPENCIL_cache_finish(vedata); DRW_render_instance_buffer_finish(); + /* Render the gpencil object and merge the result to the underlying render. */ 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; - - 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) { - if (src_pixel_rgba[3] > 0.0f) { - /* 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 object on top */ - if (src_pixel_rgba[3] < 1.0f) { - blend_pixel(src_pixel_rgba, gp_pixel_rgba, gp_pixel_rgba); - } - else { - copy_v4_v4(gp_pixel_rgba, src_pixel_rgba); - } - } - else { - /* blend gp render */ - if (gp_pixel_rgba[3] < 1.0f) { - /* premult alpha factor to remove double blend effects */ - mul_v3_fl(gp_pixel_rgba, 1.0f / gp_pixel_rgba[3]); - - blend_pixel(gp_pixel_rgba, src_pixel_rgba, gp_pixel_rgba); - - gp_pixel_rgba[3] = gp_pixel_rgba[3] > src_pixel_rgba[3] ? gp_pixel_rgba[3] : - src_pixel_rgba[3]; - } - } - } - } - 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.c b/source/blender/draw/engines/gpencil/gpencil_shader.c new file mode 100644 index 00000000000..8c7ba42a70e --- /dev/null +++ b/source/blender/draw/engines/gpencil/gpencil_shader.c @@ -0,0 +1,311 @@ +/* + * 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 2019, Blender Foundation. + */ + +/** \file + * \ingroup draw + */ +#include "DRW_render.h" + +#include "gpencil_engine.h" + +extern char datatoc_gpencil_common_lib_glsl[]; +extern char datatoc_gpencil_frag_glsl[]; +extern char datatoc_gpencil_vert_glsl[]; +extern char datatoc_gpencil_antialiasing_frag_glsl[]; +extern char datatoc_gpencil_antialiasing_vert_glsl[]; +extern char datatoc_gpencil_layer_blend_frag_glsl[]; +extern char datatoc_gpencil_mask_invert_frag_glsl[]; +extern char datatoc_gpencil_depth_merge_frag_glsl[]; +extern char datatoc_gpencil_depth_merge_vert_glsl[]; +extern char datatoc_gpencil_vfx_frag_glsl[]; + +extern char datatoc_common_colormanagement_lib_glsl[]; +extern char datatoc_common_fullscreen_vert_glsl[]; +extern char datatoc_common_smaa_lib_glsl[]; +extern char datatoc_common_view_lib_glsl[]; + +static struct { + /* SMAA antialiasing */ + GPUShader *antialiasing_sh[3]; + /* GPencil Object rendering */ + GPUShader *gpencil_sh; + /* Final Compositing over rendered background. */ + GPUShader *composite_sh; + /* All layer blend types in one shader! */ + GPUShader *layer_blend_sh; + /* Merge the final object depth to the depth buffer. */ + GPUShader *depth_merge_sh; + /* Invert the content of the mask buffer. */ + GPUShader *mask_invert_sh; + /* Effects. */ + GPUShader *fx_composite_sh; + GPUShader *fx_colorize_sh; + GPUShader *fx_blur_sh; + GPUShader *fx_glow_sh; + GPUShader *fx_pixel_sh; + GPUShader *fx_rim_sh; + GPUShader *fx_shadow_sh; + GPUShader *fx_transform_sh; + /* general drawing shaders */ + GPUShader *gpencil_fill_sh; + GPUShader *gpencil_stroke_sh; + GPUShader *gpencil_point_sh; + GPUShader *gpencil_edit_point_sh; + GPUShader *gpencil_line_sh; + GPUShader *gpencil_drawing_fill_sh; + GPUShader *gpencil_fullscreen_sh; + GPUShader *gpencil_simple_fullscreen_sh; + GPUShader *gpencil_blend_fullscreen_sh; + GPUShader *gpencil_background_sh; + GPUShader *gpencil_paper_sh; +} g_shaders = {{NULL}}; + +void GPENCIL_shader_free(void) +{ + GPUShader **sh_data_as_array = (GPUShader **)&g_shaders; + for (int i = 0; i < (sizeof(g_shaders) / sizeof(GPUShader *)); i++) { + DRW_SHADER_FREE_SAFE(sh_data_as_array[i]); + } +} + +GPUShader *GPENCIL_shader_antialiasing(int stage) +{ + BLI_assert(stage < 3); + + if (!g_shaders.antialiasing_sh[stage]) { + char stage_define[32]; + BLI_snprintf(stage_define, sizeof(stage_define), "#define SMAA_STAGE %d\n", stage); + + g_shaders.antialiasing_sh[stage] = GPU_shader_create_from_arrays({ + .vert = + (const char *[]){ + "#define SMAA_INCLUDE_VS 1\n", + "#define SMAA_INCLUDE_PS 0\n", + "uniform vec4 viewportMetrics;\n", + datatoc_common_smaa_lib_glsl, + datatoc_gpencil_antialiasing_vert_glsl, + NULL, + }, + .frag = + (const char *[]){ + "#define SMAA_INCLUDE_VS 0\n", + "#define SMAA_INCLUDE_PS 1\n", + "uniform vec4 viewportMetrics;\n", + datatoc_common_smaa_lib_glsl, + datatoc_gpencil_antialiasing_frag_glsl, + NULL, + }, + .defs = + (const char *[]){ + "#define SMAA_GLSL_3\n", + "#define SMAA_RT_METRICS viewportMetrics\n", + "#define SMAA_PRESET_HIGH\n", + "#define SMAA_LUMA_WEIGHT float4(1.0, 1.0, 1.0, 0.0)\n", + "#define SMAA_NO_DISCARD\n", + stage_define, + NULL, + }, + }); + } + return g_shaders.antialiasing_sh[stage]; +} + +GPUShader *GPENCIL_shader_geometry_get(void) +{ + if (!g_shaders.gpencil_sh) { + g_shaders.gpencil_sh = GPU_shader_create_from_arrays({ + .vert = + (const char *[]){ + datatoc_common_view_lib_glsl, + datatoc_gpencil_common_lib_glsl, + datatoc_gpencil_vert_glsl, + NULL, + }, + .frag = + (const char *[]){ + datatoc_common_colormanagement_lib_glsl, + datatoc_gpencil_common_lib_glsl, + datatoc_gpencil_frag_glsl, + NULL, + }, + .defs = + (const char *[]){ + "#define GP_MATERIAL_BUFFER_LEN " STRINGIFY(GP_MATERIAL_BUFFER_LEN) "\n", + "#define GPENCIL_LIGHT_BUFFER_LEN " STRINGIFY(GPENCIL_LIGHT_BUFFER_LEN) "\n", + "#define UNIFORM_RESOURCE_ID\n", + NULL, + }, + }); + } + return g_shaders.gpencil_sh; +} + +GPUShader *GPENCIL_shader_layer_blend_get(void) +{ + if (!g_shaders.layer_blend_sh) { + g_shaders.layer_blend_sh = GPU_shader_create_from_arrays({ + .vert = + (const char *[]){ + datatoc_common_fullscreen_vert_glsl, + NULL, + }, + .frag = + (const char *[]){ + datatoc_gpencil_common_lib_glsl, + datatoc_gpencil_layer_blend_frag_glsl, + NULL, + }, + }); + } + return g_shaders.layer_blend_sh; +} + +GPUShader *GPENCIL_shader_mask_invert_get(void) +{ + if (!g_shaders.mask_invert_sh) { + g_shaders.mask_invert_sh = DRW_shader_create_fullscreen(datatoc_gpencil_mask_invert_frag_glsl, + NULL); + } + return g_shaders.mask_invert_sh; +} + +GPUShader *GPENCIL_shader_depth_merge_get(void) +{ + if (!g_shaders.depth_merge_sh) { + g_shaders.depth_merge_sh = GPU_shader_create_from_arrays({ + .vert = + (const char *[]){ + datatoc_common_view_lib_glsl, + datatoc_gpencil_depth_merge_vert_glsl, + NULL, + }, + .frag = + (const char *[]){ + datatoc_gpencil_depth_merge_frag_glsl, + NULL, + }, + }); + } + return g_shaders.depth_merge_sh; +} + +/* ------- FX Shaders --------- */ + +GPUShader *GPENCIL_shader_fx_blur_get(void) +{ + if (!g_shaders.fx_blur_sh) { + g_shaders.fx_blur_sh = DRW_shader_create_fullscreen(datatoc_gpencil_vfx_frag_glsl, + "#define BLUR\n"); + } + return g_shaders.fx_blur_sh; +} + +GPUShader *GPENCIL_shader_fx_colorize_get(void) +{ + if (!g_shaders.fx_colorize_sh) { + g_shaders.fx_colorize_sh = DRW_shader_create_fullscreen(datatoc_gpencil_vfx_frag_glsl, + "#define COLORIZE\n"); + } + return g_shaders.fx_colorize_sh; +} + +GPUShader *GPENCIL_shader_fx_composite_get(void) +{ + if (!g_shaders.fx_composite_sh) { + g_shaders.fx_composite_sh = DRW_shader_create_fullscreen(datatoc_gpencil_vfx_frag_glsl, + "#define COMPOSITE\n"); + } + return g_shaders.fx_composite_sh; +} + +GPUShader *GPENCIL_shader_fx_glow_get(void) +{ + if (!g_shaders.fx_glow_sh) { + g_shaders.fx_glow_sh = GPU_shader_create_from_arrays({ + .vert = + (const char *[]){ + datatoc_common_fullscreen_vert_glsl, + NULL, + }, + .frag = + (const char *[]){ + datatoc_gpencil_common_lib_glsl, + datatoc_gpencil_vfx_frag_glsl, + NULL, + }, + .defs = + (const char *[]){ + "#define GLOW\n", + NULL, + }, + }); + } + return g_shaders.fx_glow_sh; +} + +GPUShader *GPENCIL_shader_fx_pixelize_get(void) +{ + if (!g_shaders.fx_pixel_sh) { + g_shaders.fx_pixel_sh = DRW_shader_create_fullscreen(datatoc_gpencil_vfx_frag_glsl, + "#define PIXELIZE\n"); + } + return g_shaders.fx_pixel_sh; +} + +GPUShader *GPENCIL_shader_fx_rim_get(void) +{ + if (!g_shaders.fx_rim_sh) { + g_shaders.fx_rim_sh = GPU_shader_create_from_arrays({ + .vert = + (const char *[]){ + datatoc_common_fullscreen_vert_glsl, + NULL, + }, + .frag = + (const char *[]){ + datatoc_gpencil_common_lib_glsl, + datatoc_gpencil_vfx_frag_glsl, + NULL, + }, + .defs = + (const char *[]){ + "#define RIM\n", + NULL, + }, + }); + } + return g_shaders.fx_rim_sh; +} + +GPUShader *GPENCIL_shader_fx_shadow_get(void) +{ + if (!g_shaders.fx_shadow_sh) { + g_shaders.fx_shadow_sh = DRW_shader_create_fullscreen(datatoc_gpencil_vfx_frag_glsl, + "#define SHADOW\n"); + } + return g_shaders.fx_shadow_sh; +} + +GPUShader *GPENCIL_shader_fx_transform_get(void) +{ + if (!g_shaders.fx_transform_sh) { + g_shaders.fx_transform_sh = DRW_shader_create_fullscreen(datatoc_gpencil_vfx_frag_glsl, + "#define TRANSFORM\n"); + } + return g_shaders.fx_transform_sh; +} diff --git a/source/blender/draw/engines/gpencil/gpencil_shader_fx.c b/source/blender/draw/engines/gpencil/gpencil_shader_fx.c index f6a62e0d472..9467efcd18c 100644 --- a/source/blender/draw/engines/gpencil/gpencil_shader_fx.c +++ b/source/blender/draw/engines/gpencil/gpencil_shader_fx.c @@ -26,28 +26,17 @@ #include "BKE_gpencil.h" +#include "BLI_link_utils.h" +#include "BLI_memblock.h" + #include "DRW_render.h" #include "BKE_camera.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_shadow_prepare_frag_glsl[]; -extern char datatoc_gpencil_fx_shadow_resolve_frag_glsl[]; -extern char datatoc_gpencil_fx_glow_prepare_frag_glsl[]; -extern char datatoc_gpencil_fx_glow_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(bGPdata *gpd, ShaderFxData *fx, bool is_render) +static bool effect_is_active(bGPdata *gpd, ShaderFxData *fx, bool is_viewport) { if (fx == NULL) { return false; @@ -58,1007 +47,613 @@ static bool effect_is_active(bGPdata *gpd, ShaderFxData *fx, bool is_render) } bool is_edit = GPENCIL_ANY_EDIT_MODE(gpd); - if (((fx->mode & eShaderFxMode_Editmode) == 0) && (is_edit) && (!is_render)) { + if (((fx->mode & eShaderFxMode_Editmode) == 0) && (is_edit) && (is_viewport)) { return false; } - if (((fx->mode & eShaderFxMode_Realtime) && (is_render == false)) || - ((fx->mode & eShaderFxMode_Render) && (is_render == true))) { + if (((fx->mode & eShaderFxMode_Realtime) && (is_viewport == true)) || + ((fx->mode & eShaderFxMode_Render) && (is_viewport == false))) { 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]) +typedef struct gpIterVfxData { + GPENCIL_PrivateData *pd; + GPENCIL_tObject *tgp_ob; + GPUFrameBuffer **target_fb; + GPUFrameBuffer **source_fb; + GPUTexture **target_color_tx; + GPUTexture **source_color_tx; + GPUTexture **target_reveal_tx; + GPUTexture **source_reveal_tx; +} gpIterVfxData; + +static DRWShadingGroup *gpencil_vfx_pass_create(const char *name, + DRWState state, + gpIterVfxData *iter, + GPUShader *sh) { - for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { - if (gpl->flag & GP_LAYER_HIDE) { - continue; - } + DRWPass *pass = DRW_pass_create(name, state); + DRWShadingGroup *grp = DRW_shgroup_create(sh, pass); + DRW_shgroup_uniform_texture_ref(grp, "colorBuf", iter->source_color_tx); + DRW_shgroup_uniform_texture_ref(grp, "revealBuf", iter->source_reveal_tx); - /* 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; - } - Camera *cam = camera->data; + GPENCIL_tVfx *tgp_vfx = BLI_memblock_alloc(iter->pd->gp_vfx_pool); + tgp_vfx->target_fb = iter->target_fb; + tgp_vfx->vfx_ps = pass; - float fstop = cam->dof.aperture_fstop; - float focus_dist = BKE_camera_object_dof_distance(camera); - float focal_len = cam->lens; + SWAP(GPUFrameBuffer **, iter->target_fb, iter->source_fb); + SWAP(GPUTexture **, iter->target_color_tx, iter->source_color_tx); + SWAP(GPUTexture **, iter->target_reveal_tx, iter->source_reveal_tx); - const float scale_camera = 0.001f; - /* 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; + BLI_LINKS_APPEND(&iter->tgp_ob->vfx, tgp_vfx); - 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); + return grp; } -/* **************** 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 gpencil_fx_blur(ShaderFxData *fx, - int ob_idx, - GPENCIL_e_data *e_data, - GPENCIL_Data *vedata, - tGPencilObjectCache *cache) +static void gpencil_vfx_blur(BlurShaderFxData *fx, Object *ob, gpIterVfxData *iter) { - if (fx == NULL) { + if (fx->radius[0] == 0.0f && fx->radius[1] == 0.0f) { 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; - bGPdata *gpd = cache->gpd; - copy_v3_v3(fxd->runtime.loc, cache->loc); - - 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; - } - } - - GPUBatch *fxquad = DRW_cache_fullscreen_quad_get(); + DRWShadingGroup *grp; + const float s = sin(fx->rotation); + const float c = cos(fx->rotation); - fx_shgrp = DRW_shgroup_create(e_data->gpencil_fx_blur_sh, psl->fx_shader_pass_blend); - DRW_shgroup_call(fx_shgrp, fxquad, NULL); - DRW_shgroup_uniform_texture_ref(fx_shgrp, "strokeColor", &stl->g_data->temp_color_tx_a); - DRW_shgroup_uniform_texture_ref(fx_shgrp, "strokeDepth", &stl->g_data->temp_depth_tx_a); - DRW_shgroup_uniform_vec2(fx_shgrp, "Viewport", DRW_viewport_size_get(), 1); - DRW_shgroup_uniform_int(fx_shgrp, "blur", &fxd->blur[0], 2); - - DRW_shgroup_uniform_vec3(fx_shgrp, "loc", fxd->runtime.loc, 1); - DRW_shgroup_uniform_float(fx_shgrp, "pixsize", stl->storage->pixsize, 1); - DRW_shgroup_uniform_float(fx_shgrp, "pixfactor", &gpd->pixfactor, 1); - - fxd->runtime.fx_sh = fx_shgrp; -} + float winmat[4][4], persmat[4][4]; + float blur_size[2] = {fx->radius[0], fx->radius[1]}; + DRW_view_persmat_get(NULL, persmat, false); + const float w = fabsf(mul_project_m4_v3_zfac(persmat, ob->obmat[3])); -/* Colorize FX */ -static void gpencil_fx_colorize(ShaderFxData *fx, GPENCIL_e_data *e_data, GPENCIL_Data *vedata) -{ - if (fx == NULL) { - return; + if ((fx->flag & FX_BLUR_DOF_MODE) && iter->pd->camera != NULL) { + /* Compute circle of confusion size. */ + float coc = (iter->pd->dof_params[0] / -w) - iter->pd->dof_params[1]; + copy_v2_fl(blur_size, fabsf(coc)); + } + else { + /* Modify by distance to camera and object scale. */ + DRW_view_winmat_get(NULL, winmat, false); + const float *vp_size = DRW_viewport_size_get(); + float world_pixel_scale = 1.0f / GPENCIL_PIXEL_FACTOR; + float scale = mat4_to_scale(ob->obmat); + float distance_factor = world_pixel_scale * scale * winmat[1][1] * vp_size[1] / w; + mul_v2_fl(blur_size, distance_factor); + } + + GPUShader *sh = GPENCIL_shader_fx_blur_get(); + + DRWState state = DRW_STATE_WRITE_COLOR; + if (blur_size[0] > 0.0f) { + grp = gpencil_vfx_pass_create("Fx Blur H", state, iter, sh); + DRW_shgroup_uniform_vec2_copy(grp, "offset", (float[2]){blur_size[0] * c, blur_size[0] * s}); + DRW_shgroup_uniform_int_copy(grp, "sampCount", max_ii(1, min_ii(fx->samples, blur_size[0]))); + DRW_shgroup_call_procedural_triangles(grp, NULL, 1); + } + if (blur_size[1] > 0.0f) { + grp = gpencil_vfx_pass_create("Fx Blur V", state, iter, sh); + DRW_shgroup_uniform_vec2_copy(grp, "offset", (float[2]){-blur_size[1] * s, blur_size[1] * c}); + DRW_shgroup_uniform_int_copy(grp, "sampCount", max_ii(1, min_ii(fx->samples, blur_size[1]))); + DRW_shgroup_call_procedural_triangles(grp, NULL, 1); } - GPENCIL_StorageList *stl = ((GPENCIL_Data *)vedata)->stl; - ColorizeShaderFxData *fxd = (ColorizeShaderFxData *)fx; - GPENCIL_PassList *psl = ((GPENCIL_Data *)vedata)->psl; - DRWShadingGroup *fx_shgrp; - - 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(fx_shgrp, fxquad, NULL); - DRW_shgroup_uniform_texture_ref(fx_shgrp, "strokeColor", &stl->g_data->temp_color_tx_a); - DRW_shgroup_uniform_texture_ref(fx_shgrp, "strokeDepth", &stl->g_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 gpencil_fx_flip(ShaderFxData *fx, GPENCIL_e_data *e_data, GPENCIL_Data *vedata) +static void gpencil_vfx_colorize(ColorizeShaderFxData *fx, Object *UNUSED(ob), gpIterVfxData *iter) { - if (fx == NULL) { - return; - } - GPENCIL_StorageList *stl = ((GPENCIL_Data *)vedata)->stl; - 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; - } + DRWShadingGroup *grp; - 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(fx_shgrp, fxquad, NULL); - DRW_shgroup_uniform_texture_ref(fx_shgrp, "strokeColor", &stl->g_data->temp_color_tx_a); - DRW_shgroup_uniform_texture_ref(fx_shgrp, "strokeDepth", &stl->g_data->temp_depth_tx_a); - DRW_shgroup_uniform_int(fx_shgrp, "flipmode", &fxd->flipmode, 1); + GPUShader *sh = GPENCIL_shader_fx_colorize_get(); - DRW_shgroup_uniform_vec2(fx_shgrp, "wsize", DRW_viewport_size_get(), 1); - - fxd->runtime.fx_sh = fx_shgrp; + DRWState state = DRW_STATE_WRITE_COLOR; + grp = gpencil_vfx_pass_create("Fx Colorize", state, iter, sh); + DRW_shgroup_uniform_vec3_copy(grp, "lowColor", fx->low_color); + DRW_shgroup_uniform_vec3_copy(grp, "highColor", fx->high_color); + DRW_shgroup_uniform_float_copy(grp, "factor", fx->factor); + DRW_shgroup_uniform_int_copy(grp, "mode", fx->mode); + DRW_shgroup_call_procedural_triangles(grp, NULL, 1); } -/* Light FX */ -static void gpencil_fx_light(ShaderFxData *fx, - GPENCIL_e_data *e_data, - GPENCIL_Data *vedata, - tGPencilObjectCache *cache) +static void gpencil_vfx_flip(FlipShaderFxData *fx, Object *UNUSED(ob), gpIterVfxData *iter) { - if (fx == NULL) { - return; - } - 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; - - 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(fx_shgrp, fxquad, NULL); - DRW_shgroup_uniform_texture_ref(fx_shgrp, "strokeColor", &stl->g_data->temp_color_tx_a); - DRW_shgroup_uniform_texture_ref(fx_shgrp, "strokeDepth", &stl->g_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->obmat[3]); - - /* 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 = cache->gpd; - if (!get_normal_vector(gpd, r_point, r_normal)) { - return; - } - mul_mat3_m4_v3(cache->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->obmat[3], r_plane); - fxd->loc[3] = dt; /* use last element to save it */ + DRWShadingGroup *grp; - DRW_shgroup_uniform_vec4(fx_shgrp, "loc", &fxd->loc[0], 1); + float axis_flip[2]; + axis_flip[0] = (fx->flag & FX_FLIP_HORIZONTAL) ? -1.0f : 1.0f; + axis_flip[1] = (fx->flag & FX_FLIP_VERTICAL) ? -1.0f : 1.0f; - DRW_shgroup_uniform_float(fx_shgrp, "energy", &fxd->energy, 1); - DRW_shgroup_uniform_float(fx_shgrp, "ambient", &fxd->ambient, 1); + GPUShader *sh = GPENCIL_shader_fx_transform_get(); - DRW_shgroup_uniform_float(fx_shgrp, "pixsize", stl->storage->pixsize, 1); - DRW_shgroup_uniform_float(fx_shgrp, "pixfactor", &gpd->pixfactor, 1); - - fxd->runtime.fx_sh = fx_shgrp; + DRWState state = DRW_STATE_WRITE_COLOR; + grp = gpencil_vfx_pass_create("Fx Flip", state, iter, sh); + DRW_shgroup_uniform_vec2_copy(grp, "axisFlip", axis_flip); + DRW_shgroup_uniform_vec2_copy(grp, "waveOffset", (float[2]){0.0f, 0.0f}); + DRW_shgroup_uniform_float_copy(grp, "swirlRadius", 0.0f); + DRW_shgroup_call_procedural_triangles(grp, NULL, 1); } -/* Pixelate FX */ -static void gpencil_fx_pixel(ShaderFxData *fx, - GPENCIL_e_data *e_data, - GPENCIL_Data *vedata, - tGPencilObjectCache *cache) +static void gpencil_vfx_rim(RimShaderFxData *fx, Object *ob, gpIterVfxData *iter) { - if (fx == NULL) { - return; + DRWShadingGroup *grp; + + float winmat[4][4], persmat[4][4]; + float offset[2] = {fx->offset[0], fx->offset[1]}; + float blur_size[2] = {fx->blur[0], fx->blur[1]}; + DRW_view_winmat_get(NULL, winmat, false); + DRW_view_persmat_get(NULL, persmat, false); + const float *vp_size = DRW_viewport_size_get(); + const float *vp_size_inv = DRW_viewport_invert_size_get(); + + const float w = fabsf(mul_project_m4_v3_zfac(persmat, ob->obmat[3])); + + /* Modify by distance to camera and object scale. */ + float world_pixel_scale = 1.0f / GPENCIL_PIXEL_FACTOR; + float scale = mat4_to_scale(ob->obmat); + float distance_factor = (world_pixel_scale * scale * winmat[1][1] * vp_size[1]) / w; + mul_v2_fl(offset, distance_factor); + mul_v2_v2(offset, vp_size_inv); + mul_v2_fl(blur_size, distance_factor); + + GPUShader *sh = GPENCIL_shader_fx_rim_get(); + + DRWState state = DRW_STATE_WRITE_COLOR; + grp = gpencil_vfx_pass_create("Fx Rim H", state, iter, sh); + DRW_shgroup_uniform_vec2_copy(grp, "blurDir", (float[2]){blur_size[0] * vp_size_inv[0], 0.0f}); + DRW_shgroup_uniform_vec2_copy(grp, "uvOffset", offset); + DRW_shgroup_uniform_int_copy(grp, "sampCount", max_ii(1, min_ii(fx->samples, blur_size[0]))); + DRW_shgroup_uniform_vec3_copy(grp, "maskColor", fx->mask_rgb); + DRW_shgroup_uniform_bool_copy(grp, "isFirstPass", true); + DRW_shgroup_call_procedural_triangles(grp, NULL, 1); + + switch (fx->mode) { + case eShaderFxRimMode_Normal: + state |= DRW_STATE_BLEND_ALPHA_PREMUL; + break; + case eShaderFxRimMode_Add: + state |= DRW_STATE_BLEND_ADD_FULL; + break; + case eShaderFxRimMode_Subtract: + state |= DRW_STATE_BLEND_SUB; + break; + case eShaderFxRimMode_Multiply: + case eShaderFxRimMode_Divide: + case eShaderFxRimMode_Overlay: + state |= DRW_STATE_BLEND_MUL; + break; + } + + zero_v2(offset); + + grp = gpencil_vfx_pass_create("Fx Rim V", state, iter, sh); + DRW_shgroup_uniform_vec2_copy(grp, "blurDir", (float[2]){0.0f, blur_size[1] * vp_size_inv[1]}); + DRW_shgroup_uniform_vec2_copy(grp, "uvOffset", offset); + DRW_shgroup_uniform_vec3_copy(grp, "rimColor", fx->rim_rgb); + DRW_shgroup_uniform_int_copy(grp, "sampCount", max_ii(1, min_ii(fx->samples, blur_size[1]))); + DRW_shgroup_uniform_int_copy(grp, "blendMode", fx->mode); + DRW_shgroup_uniform_bool_copy(grp, "isFirstPass", false); + DRW_shgroup_call_procedural_triangles(grp, NULL, 1); + + if (fx->mode == eShaderFxRimMode_Overlay) { + /* We cannot do custom blending on MultiTarget framebuffers. + * Workaround by doing 2 passes. */ + grp = DRW_shgroup_create_sub(grp); + DRW_shgroup_state_disable(grp, DRW_STATE_BLEND_MUL); + DRW_shgroup_state_enable(grp, DRW_STATE_BLEND_ADD_FULL); + DRW_shgroup_uniform_int_copy(grp, "blendMode", 999); + DRW_shgroup_call_procedural_triangles(grp, NULL, 1); } - PixelShaderFxData *fxd = (PixelShaderFxData *)fx; - - GPENCIL_StorageList *stl = ((GPENCIL_Data *)vedata)->stl; - GPENCIL_PassList *psl = ((GPENCIL_Data *)vedata)->psl; - DRWShadingGroup *fx_shgrp; - bGPdata *gpd = cache->gpd; - copy_v3_v3(fxd->runtime.loc, cache->loc); - - fxd->size[2] = (int)fxd->flag & FX_PIXEL_USE_LINES; - - 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(fx_shgrp, fxquad, NULL); - DRW_shgroup_uniform_texture_ref(fx_shgrp, "strokeColor", &stl->g_data->temp_color_tx_a); - DRW_shgroup_uniform_texture_ref(fx_shgrp, "strokeDepth", &stl->g_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", fxd->runtime.loc, 1); - DRW_shgroup_uniform_float(fx_shgrp, "pixsize", stl->storage->pixsize, 1); - DRW_shgroup_uniform_float(fx_shgrp, "pixfactor", &gpd->pixfactor, 1); - - fxd->runtime.fx_sh = fx_shgrp; } -/* Rim FX */ -static void gpencil_fx_rim(ShaderFxData *fx, - GPENCIL_e_data *e_data, - GPENCIL_Data *vedata, - tGPencilObjectCache *cache) +static void gpencil_vfx_pixelize(PixelShaderFxData *fx, Object *ob, gpIterVfxData *iter) { - if (fx == NULL) { - return; + DRWShadingGroup *grp; + + float persmat[4][4], winmat[4][4], ob_center[3], pixsize_uniform[2]; + DRW_view_winmat_get(NULL, winmat, false); + DRW_view_persmat_get(NULL, persmat, false); + const float *vp_size = DRW_viewport_size_get(); + const float *vp_size_inv = DRW_viewport_invert_size_get(); + float pixel_size[2] = {fx->size[0], fx->size[1]}; + mul_v2_v2(pixel_size, vp_size_inv); + + /* Fixed pixelisation center from object center. */ + const float w = fabsf(mul_project_m4_v3_zfac(persmat, ob->obmat[3])); + mul_v3_m4v3(ob_center, persmat, ob->obmat[3]); + mul_v3_fl(ob_center, 1.0f / w); + + /* Convert to uvs. */ + mul_v2_fl(ob_center, 0.5f); + add_v2_fl(ob_center, 0.5f); + + /* Modify by distance to camera and object scale. */ + float world_pixel_scale = 1.0f / GPENCIL_PIXEL_FACTOR; + float scale = mat4_to_scale(ob->obmat); + mul_v2_fl(pixel_size, (world_pixel_scale * scale * winmat[1][1] * vp_size[1]) / w); + + /* Center to texel */ + madd_v2_v2fl(ob_center, pixel_size, -0.5f); + + GPUShader *sh = GPENCIL_shader_fx_pixelize_get(); + + DRWState state = DRW_STATE_WRITE_COLOR; + + /* Only if pixelated effect is bigger than 1px. */ + if (pixel_size[0] > vp_size_inv[0]) { + copy_v2_fl2(pixsize_uniform, pixel_size[0], vp_size_inv[1]); + grp = gpencil_vfx_pass_create("Fx Pixelize X", state, iter, sh); + DRW_shgroup_uniform_vec2_copy(grp, "targetPixelSize", pixsize_uniform); + DRW_shgroup_uniform_vec2_copy(grp, "targetPixelOffset", ob_center); + DRW_shgroup_uniform_vec2_copy(grp, "accumOffset", (float[2]){pixel_size[0], 0.0f}); + DRW_shgroup_uniform_int_copy(grp, "sampCount", (pixel_size[0] / vp_size_inv[0] > 3.0) ? 2 : 1); + DRW_shgroup_call_procedural_triangles(grp, NULL, 1); + } + + if (pixel_size[1] > vp_size_inv[1]) { + copy_v2_fl2(pixsize_uniform, vp_size_inv[0], pixel_size[1]); + grp = gpencil_vfx_pass_create("Fx Pixelize Y", state, iter, sh); + DRW_shgroup_uniform_vec2_copy(grp, "targetPixelSize", pixsize_uniform); + DRW_shgroup_uniform_vec2_copy(grp, "accumOffset", (float[2]){0.0f, pixel_size[1]}); + DRW_shgroup_uniform_int_copy(grp, "sampCount", (pixel_size[1] / vp_size_inv[1] > 3.0) ? 2 : 1); + DRW_shgroup_call_procedural_triangles(grp, NULL, 1); } - RimShaderFxData *fxd = (RimShaderFxData *)fx; - bGPdata *gpd = cache->gpd; - - GPENCIL_StorageList *stl = ((GPENCIL_Data *)vedata)->stl; - GPENCIL_PassList *psl = ((GPENCIL_Data *)vedata)->psl; - DRWShadingGroup *fx_shgrp; - - GPUBatch *fxquad = DRW_cache_fullscreen_quad_get(); - copy_v3_v3(fxd->runtime.loc, cache->loc); - - /* prepare pass */ - fx_shgrp = DRW_shgroup_create(e_data->gpencil_fx_rim_prepare_sh, psl->fx_shader_pass_blend); - DRW_shgroup_call(fx_shgrp, fxquad, NULL); - DRW_shgroup_uniform_texture_ref(fx_shgrp, "strokeColor", &stl->g_data->temp_color_tx_a); - DRW_shgroup_uniform_texture_ref(fx_shgrp, "strokeDepth", &stl->g_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", fxd->runtime.loc, 1); - DRW_shgroup_uniform_float(fx_shgrp, "pixsize", stl->storage->pixsize, 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(fx_shgrp, fxquad, NULL); - DRW_shgroup_uniform_texture_ref(fx_shgrp, "strokeColor", &stl->g_data->temp_color_tx_fx); - DRW_shgroup_uniform_texture_ref(fx_shgrp, "strokeDepth", &stl->g_data->temp_depth_tx_fx); - DRW_shgroup_uniform_vec2(fx_shgrp, "Viewport", DRW_viewport_size_get(), 1); - DRW_shgroup_uniform_int(fx_shgrp, "blur", &fxd->blur[0], 2); - - DRW_shgroup_uniform_vec3(fx_shgrp, "loc", fxd->runtime.loc, 1); - DRW_shgroup_uniform_float(fx_shgrp, "pixsize", stl->storage->pixsize, 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(fx_shgrp, fxquad, NULL); - DRW_shgroup_uniform_texture_ref(fx_shgrp, "strokeColor", &stl->g_data->temp_color_tx_a); - DRW_shgroup_uniform_texture_ref(fx_shgrp, "strokeDepth", &stl->g_data->temp_depth_tx_a); - DRW_shgroup_uniform_texture_ref(fx_shgrp, "strokeRim", &stl->g_data->temp_color_tx_fx); - 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; } -/* Shadow FX */ -static void gpencil_fx_shadow(ShaderFxData *fx, - GPENCIL_e_data *e_data, - GPENCIL_Data *vedata, - tGPencilObjectCache *cache) +static void gpencil_vfx_shadow(ShadowShaderFxData *fx, Object *ob, gpIterVfxData *iter) { - if (fx == NULL) { - return; - } - ShadowShaderFxData *fxd = (ShadowShaderFxData *)fx; - if ((!fxd->object) && (fxd->flag & FX_SHADOW_USE_OBJECT)) { - fxd->runtime.fx_sh = NULL; - fxd->runtime.fx_sh_b = NULL; - fxd->runtime.fx_sh_c = NULL; - return; - } - - GPENCIL_StorageList *stl = ((GPENCIL_Data *)vedata)->stl; - GPENCIL_PassList *psl = ((GPENCIL_Data *)vedata)->psl; - DRWShadingGroup *fx_shgrp; - bGPdata *gpd = cache->gpd; - copy_v3_v3(fxd->runtime.loc, cache->loc); - - GPUBatch *fxquad = DRW_cache_fullscreen_quad_get(); - /* prepare pass */ - fx_shgrp = DRW_shgroup_create(e_data->gpencil_fx_shadow_prepare_sh, psl->fx_shader_pass_blend); - DRW_shgroup_call(fx_shgrp, fxquad, NULL); - DRW_shgroup_uniform_texture_ref(fx_shgrp, "strokeColor", &stl->g_data->temp_color_tx_a); - DRW_shgroup_uniform_texture_ref(fx_shgrp, "strokeDepth", &stl->g_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_float(fx_shgrp, "scale", &fxd->scale[0], 2); - DRW_shgroup_uniform_float(fx_shgrp, "rotation", &fxd->rotation, 1); - DRW_shgroup_uniform_vec4(fx_shgrp, "shadow_color", &fxd->shadow_rgba[0], 1); - - if ((fxd->object) && (fxd->flag & FX_SHADOW_USE_OBJECT)) { - DRW_shgroup_uniform_vec3(fx_shgrp, "loc", fxd->object->obmat[3], 1); - } - else { - DRW_shgroup_uniform_vec3(fx_shgrp, "loc", fxd->runtime.loc, 1); - } - - if (fxd->flag & FX_SHADOW_USE_WAVE) { - DRW_shgroup_uniform_int(fx_shgrp, "orientation", &fxd->orientation, 1); + DRWShadingGroup *grp; + + const bool use_obj_pivot = (fx->flag & FX_SHADOW_USE_OBJECT) != 0; + const bool use_wave = (fx->flag & FX_SHADOW_USE_WAVE) != 0; + + float uv_mat[4][4], winmat[4][4], persmat[4][4], rot_center[3]; + float wave_ofs[3], wave_dir[3], wave_phase, blur_dir[2], tmp[2]; + float offset[2] = {fx->offset[0], fx->offset[1]}; + float blur_size[2] = {fx->blur[0], fx->blur[1]}; + DRW_view_winmat_get(NULL, winmat, false); + DRW_view_persmat_get(NULL, persmat, false); + const float *vp_size = DRW_viewport_size_get(); + const float *vp_size_inv = DRW_viewport_invert_size_get(); + const float ratio = vp_size_inv[1] / vp_size_inv[0]; + + copy_v3_v3(rot_center, (use_obj_pivot && fx->object) ? fx->object->obmat[3] : ob->obmat[3]); + + const float w = fabsf(mul_project_m4_v3_zfac(persmat, rot_center)); + mul_v3_m4v3(rot_center, persmat, rot_center); + mul_v3_fl(rot_center, 1.0f / w); + + /* Modify by distance to camera and object scale. */ + float world_pixel_scale = 1.0f / GPENCIL_PIXEL_FACTOR; + float scale = mat4_to_scale(ob->obmat); + float distance_factor = (world_pixel_scale * scale * winmat[1][1] * vp_size[1]) / w; + mul_v2_fl(offset, distance_factor); + mul_v2_v2(offset, vp_size_inv); + mul_v2_fl(blur_size, distance_factor); + + rot_center[0] = rot_center[0] * 0.5f + 0.5f; + rot_center[1] = rot_center[1] * 0.5f + 0.5f; + + /* UV transform matrix. (loc, rot, scale) Sent to shader as 2x3 matrix. */ + unit_m4(uv_mat); + translate_m4(uv_mat, rot_center[0], rot_center[1], 0.0f); + rescale_m4(uv_mat, (float[3]){1.0f / fx->scale[0], 1.0f / fx->scale[1], 1.0f}); + translate_m4(uv_mat, -offset[0], -offset[1], 0.0f); + rescale_m4(uv_mat, (float[3]){1.0f / ratio, 1.0f, 1.0f}); + rotate_m4(uv_mat, 'Z', fx->rotation); + rescale_m4(uv_mat, (float[3]){ratio, 1.0f, 1.0f}); + translate_m4(uv_mat, -rot_center[0], -rot_center[1], 0.0f); + + if (use_wave) { + float dir[2]; + if (fx->orientation == 0) { + /* Horizontal */ + copy_v2_fl2(dir, 1.0f, 0.0f); + } + else { + /* Vertical */ + copy_v2_fl2(dir, 0.0f, 1.0f); + } + /* This is applied after rotation. Counter the rotation to keep aligned with global axis. */ + rotate_v2_v2fl(wave_dir, dir, fx->rotation); + /* Rotate 90°. */ + copy_v2_v2(wave_ofs, wave_dir); + SWAP(float, wave_ofs[0], wave_ofs[1]); + wave_ofs[1] *= -1.0f; + /* Keep world space scalling and aspect ratio. */ + mul_v2_fl(wave_dir, 1.0f / (max_ff(1e-8f, fx->period) * distance_factor)); + mul_v2_v2(wave_dir, vp_size); + mul_v2_fl(wave_ofs, fx->amplitude * distance_factor); + mul_v2_v2(wave_ofs, vp_size_inv); + /* Phase start at shadow center. */ + wave_phase = fx->phase - dot_v2v2(rot_center, wave_dir); } else { - DRW_shgroup_uniform_int_copy(fx_shgrp, "orientation", -1); - } - 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_float(fx_shgrp, "pixsize", stl->storage->pixsize, 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(fx_shgrp, fxquad, NULL); - DRW_shgroup_uniform_texture_ref(fx_shgrp, "strokeColor", &stl->g_data->temp_color_tx_fx); - DRW_shgroup_uniform_texture_ref(fx_shgrp, "strokeDepth", &stl->g_data->temp_depth_tx_fx); - DRW_shgroup_uniform_vec2(fx_shgrp, "Viewport", DRW_viewport_size_get(), 1); - DRW_shgroup_uniform_int(fx_shgrp, "blur", &fxd->blur[0], 2); - - DRW_shgroup_uniform_vec3(fx_shgrp, "loc", fxd->runtime.loc, 1); - DRW_shgroup_uniform_float(fx_shgrp, "pixsize", stl->storage->pixsize, 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_shadow_resolve_sh, psl->fx_shader_pass_blend); - DRW_shgroup_call(fx_shgrp, fxquad, NULL); - DRW_shgroup_uniform_texture_ref(fx_shgrp, "strokeColor", &stl->g_data->temp_color_tx_a); - DRW_shgroup_uniform_texture_ref(fx_shgrp, "strokeDepth", &stl->g_data->temp_depth_tx_a); - DRW_shgroup_uniform_texture_ref(fx_shgrp, "shadowColor", &stl->g_data->temp_color_tx_fx); - DRW_shgroup_uniform_texture_ref(fx_shgrp, "shadowDepth", &stl->g_data->temp_depth_tx_fx); - - fxd->runtime.fx_sh_c = fx_shgrp; + zero_v2(wave_dir); + zero_v2(wave_ofs); + wave_phase = 0.0f; + } + + GPUShader *sh = GPENCIL_shader_fx_shadow_get(); + + copy_v2_fl2(blur_dir, blur_size[0] * vp_size_inv[0], 0.0f); + + DRWState state = DRW_STATE_WRITE_COLOR; + grp = gpencil_vfx_pass_create("Fx Shadow H", state, iter, sh); + DRW_shgroup_uniform_vec2_copy(grp, "blurDir", blur_dir); + DRW_shgroup_uniform_vec2_copy(grp, "waveDir", wave_dir); + DRW_shgroup_uniform_vec2_copy(grp, "waveOffset", wave_ofs); + DRW_shgroup_uniform_float_copy(grp, "wavePhase", wave_phase); + DRW_shgroup_uniform_vec2_copy(grp, "uvRotX", uv_mat[0]); + DRW_shgroup_uniform_vec2_copy(grp, "uvRotY", uv_mat[1]); + DRW_shgroup_uniform_vec2_copy(grp, "uvOffset", uv_mat[3]); + DRW_shgroup_uniform_int_copy(grp, "sampCount", max_ii(1, min_ii(fx->samples, blur_size[0]))); + DRW_shgroup_uniform_bool_copy(grp, "isFirstPass", true); + DRW_shgroup_call_procedural_triangles(grp, NULL, 1); + + unit_m4(uv_mat); + zero_v2(wave_ofs); + + /* We reseted the uv_mat so we need to accound for the rotation in the */ + copy_v2_fl2(tmp, 0.0f, blur_size[1]); + rotate_v2_v2fl(blur_dir, tmp, -fx->rotation); + mul_v2_v2(blur_dir, vp_size_inv); + + state = DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_ALPHA_PREMUL; + grp = gpencil_vfx_pass_create("Fx Shadow V", state, iter, sh); + DRW_shgroup_uniform_vec4_copy(grp, "shadowColor", fx->shadow_rgba); + DRW_shgroup_uniform_vec2_copy(grp, "blurDir", blur_dir); + DRW_shgroup_uniform_vec2_copy(grp, "waveOffset", wave_ofs); + DRW_shgroup_uniform_vec2_copy(grp, "uvRotX", uv_mat[0]); + DRW_shgroup_uniform_vec2_copy(grp, "uvRotY", uv_mat[1]); + DRW_shgroup_uniform_vec2_copy(grp, "uvOffset", uv_mat[3]); + DRW_shgroup_uniform_int_copy(grp, "sampCount", max_ii(1, min_ii(fx->samples, blur_size[1]))); + DRW_shgroup_uniform_bool_copy(grp, "isFirstPass", false); + DRW_shgroup_call_procedural_triangles(grp, NULL, 1); } -/* Glow FX */ -static void gpencil_fx_glow(ShaderFxData *fx, - GPENCIL_e_data *e_data, - GPENCIL_Data *vedata, - tGPencilObjectCache *cache) +static void gpencil_vfx_glow(GlowShaderFxData *fx, Object *UNUSED(ob), gpIterVfxData *iter) { - if (fx == NULL) { - return; - } - GlowShaderFxData *fxd = (GlowShaderFxData *)fx; - bGPdata *gpd = cache->gpd; - copy_v3_v3(fxd->runtime.loc, cache->loc); - - GPENCIL_StorageList *stl = ((GPENCIL_Data *)vedata)->stl; - GPENCIL_PassList *psl = ((GPENCIL_Data *)vedata)->psl; - DRWShadingGroup *fx_shgrp; - - GPUBatch *fxquad = DRW_cache_fullscreen_quad_get(); - /* prepare pass */ - fx_shgrp = DRW_shgroup_create(e_data->gpencil_fx_glow_prepare_sh, psl->fx_shader_pass_blend); - DRW_shgroup_call(fx_shgrp, fxquad, NULL); - DRW_shgroup_uniform_texture_ref(fx_shgrp, "strokeColor", &stl->g_data->temp_color_tx_a); - DRW_shgroup_uniform_texture_ref(fx_shgrp, "strokeDepth", &stl->g_data->temp_depth_tx_a); - - DRW_shgroup_uniform_vec3(fx_shgrp, "glow_color", &fxd->glow_color[0], 1); - DRW_shgroup_uniform_vec3(fx_shgrp, "select_color", &fxd->select_color[0], 1); - DRW_shgroup_uniform_int(fx_shgrp, "mode", &fxd->mode, 1); - DRW_shgroup_uniform_float(fx_shgrp, "threshold", &fxd->threshold, 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(fx_shgrp, fxquad, NULL); - DRW_shgroup_uniform_texture_ref(fx_shgrp, "strokeColor", &stl->g_data->temp_color_tx_fx); - DRW_shgroup_uniform_texture_ref(fx_shgrp, "strokeDepth", &stl->g_data->temp_depth_tx_fx); - DRW_shgroup_uniform_vec2(fx_shgrp, "Viewport", DRW_viewport_size_get(), 1); - DRW_shgroup_uniform_int(fx_shgrp, "blur", &fxd->blur[0], 2); - - DRW_shgroup_uniform_vec3(fx_shgrp, "loc", fxd->runtime.loc, 1); - DRW_shgroup_uniform_float(fx_shgrp, "pixsize", stl->storage->pixsize, 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_glow_resolve_sh, psl->fx_shader_pass_blend); - DRW_shgroup_call(fx_shgrp, fxquad, NULL); - DRW_shgroup_uniform_texture_ref(fx_shgrp, "strokeColor", &stl->g_data->temp_color_tx_a); - DRW_shgroup_uniform_texture_ref(fx_shgrp, "strokeDepth", &stl->g_data->temp_depth_tx_a); - DRW_shgroup_uniform_texture_ref(fx_shgrp, "glowColor", &stl->g_data->temp_color_tx_fx); - DRW_shgroup_uniform_texture_ref(fx_shgrp, "glowDepth", &stl->g_data->temp_depth_tx_fx); - - /* reuse field */ - DRW_shgroup_uniform_int(fx_shgrp, "alpha_mode", &fxd->blur[1], 1); - - fxd->runtime.fx_sh_c = fx_shgrp; -} + const bool use_glow_under = (fx->flag & FX_GLOW_USE_ALPHA) != 0; + DRWShadingGroup *grp; + const float s = sin(fx->rotation); + const float c = cos(fx->rotation); -/* Swirl FX */ -static void gpencil_fx_swirl(ShaderFxData *fx, - GPENCIL_e_data *e_data, - GPENCIL_Data *vedata, - tGPencilObjectCache *cache) -{ - if (fx == NULL) { - return; - } - SwirlShaderFxData *fxd = (SwirlShaderFxData *)fx; - if (fxd->object == NULL) { - return; - } + GPUShader *sh = GPENCIL_shader_fx_glow_get(); - GPENCIL_StorageList *stl = ((GPENCIL_Data *)vedata)->stl; - GPENCIL_PassList *psl = ((GPENCIL_Data *)vedata)->psl; - DRWShadingGroup *fx_shgrp; - bGPdata *gpd = cache->gpd; + float ref_col[3]; - fxd->transparent = (int)fxd->flag & FX_SWIRL_MAKE_TRANSPARENT; + if (fx->mode == eShaderFxGlowMode_Luminance) { + ref_col[0] = fx->threshold; + ref_col[1] = -1.0f; + ref_col[2] = -1.0f; + } + else { + copy_v3_v3(ref_col, fx->select_color); + } + + DRWState state = DRW_STATE_WRITE_COLOR; + grp = gpencil_vfx_pass_create("Fx Glow H", state, iter, sh); + DRW_shgroup_uniform_vec2_copy(grp, "offset", (float[2]){fx->blur[0] * c, fx->blur[0] * s}); + DRW_shgroup_uniform_int_copy(grp, "sampCount", max_ii(1, min_ii(fx->samples, fx->blur[0]))); + DRW_shgroup_uniform_vec3_copy(grp, "threshold", ref_col); + DRW_shgroup_uniform_vec4_copy(grp, "glowColor", fx->glow_color); + DRW_shgroup_uniform_bool_copy(grp, "glowUnder", use_glow_under); + DRW_shgroup_uniform_bool_copy(grp, "firstPass", true); + DRW_shgroup_call_procedural_triangles(grp, NULL, 1); + + state = DRW_STATE_WRITE_COLOR; + /* Blending: Force blending. */ + switch (fx->blend_mode) { + case eGplBlendMode_Regular: + state |= DRW_STATE_BLEND_ALPHA_PREMUL; + break; + case eGplBlendMode_Add: + state |= DRW_STATE_BLEND_ADD_FULL; + break; + case eGplBlendMode_Subtract: + state |= DRW_STATE_BLEND_SUB; + break; + case eGplBlendMode_Multiply: + case eGplBlendMode_Divide: + state |= DRW_STATE_BLEND_MUL; + break; + } + + /* Small Hack: We ask for RGBA16F buffer if using use_glow_under to store original + * revealage in alpha channel. */ + if (fx->blend_mode == eGplBlendMode_Subtract || use_glow_under) { + /* For this effect to propagate, we need a signed floating point buffer. */ + iter->pd->use_signed_fb = true; + } + + grp = gpencil_vfx_pass_create("Fx Glow V", state, iter, sh); + DRW_shgroup_uniform_vec2_copy(grp, "offset", (float[2]){-fx->blur[1] * s, fx->blur[1] * c}); + DRW_shgroup_uniform_int_copy(grp, "sampCount", max_ii(1, min_ii(fx->samples, fx->blur[0]))); + DRW_shgroup_uniform_vec3_copy(grp, "threshold", (float[3]){-1.0f, -1.0f, -1.0f}); + DRW_shgroup_uniform_vec4_copy(grp, "glowColor", (float[4]){1.0f, 1.0f, 1.0f, fx->glow_color[3]}); + DRW_shgroup_uniform_bool_copy(grp, "firstPass", false); + DRW_shgroup_uniform_int_copy(grp, "blendMode", fx->blend_mode); + DRW_shgroup_call_procedural_triangles(grp, NULL, 1); +} - 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(fx_shgrp, fxquad, NULL); - DRW_shgroup_uniform_texture_ref(fx_shgrp, "strokeColor", &stl->g_data->temp_color_tx_a); - DRW_shgroup_uniform_texture_ref(fx_shgrp, "strokeDepth", &stl->g_data->temp_depth_tx_a); +static void gpencil_vfx_wave(WaveShaderFxData *fx, Object *ob, gpIterVfxData *iter) +{ + DRWShadingGroup *grp; - DRW_shgroup_uniform_vec2(fx_shgrp, "Viewport", DRW_viewport_size_get(), 1); + float winmat[4][4], persmat[4][4], wave_center[3]; + float wave_ofs[3], wave_dir[3], wave_phase; + DRW_view_winmat_get(NULL, winmat, false); + DRW_view_persmat_get(NULL, persmat, false); + const float *vp_size = DRW_viewport_size_get(); + const float *vp_size_inv = DRW_viewport_invert_size_get(); - DRW_shgroup_uniform_vec3(fx_shgrp, "loc", fxd->object->obmat[3], 1); + const float w = fabsf(mul_project_m4_v3_zfac(persmat, ob->obmat[3])); + mul_v3_m4v3(wave_center, persmat, ob->obmat[3]); + mul_v3_fl(wave_center, 1.0f / w); - 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); + /* Modify by distance to camera and object scale. */ + float world_pixel_scale = 1.0f / GPENCIL_PIXEL_FACTOR; + float scale = mat4_to_scale(ob->obmat); + float distance_factor = (world_pixel_scale * scale * winmat[1][1] * vp_size[1]) / w; - DRW_shgroup_uniform_float(fx_shgrp, "pixsize", stl->storage->pixsize, 1); - DRW_shgroup_uniform_float(fx_shgrp, "pixfactor", &gpd->pixfactor, 1); + wave_center[0] = wave_center[0] * 0.5f + 0.5f; + wave_center[1] = wave_center[1] * 0.5f + 0.5f; - fxd->runtime.fx_sh = fx_shgrp; + if (fx->orientation == 0) { + /* Horizontal */ + copy_v2_fl2(wave_dir, 1.0f, 0.0f); + } + else { + /* Vertical */ + copy_v2_fl2(wave_dir, 0.0f, 1.0f); + } + /* Rotate 90°. */ + copy_v2_v2(wave_ofs, wave_dir); + SWAP(float, wave_ofs[0], wave_ofs[1]); + wave_ofs[1] *= -1.0f; + /* Keep world space scalling and aspect ratio. */ + mul_v2_fl(wave_dir, 1.0f / (max_ff(1e-8f, fx->period) * distance_factor)); + mul_v2_v2(wave_dir, vp_size); + mul_v2_fl(wave_ofs, fx->amplitude * distance_factor); + mul_v2_v2(wave_ofs, vp_size_inv); + /* Phase start at shadow center. */ + wave_phase = fx->phase - dot_v2v2(wave_center, wave_dir); + + GPUShader *sh = GPENCIL_shader_fx_transform_get(); + + DRWState state = DRW_STATE_WRITE_COLOR; + grp = gpencil_vfx_pass_create("Fx Wave", state, iter, sh); + DRW_shgroup_uniform_vec2_copy(grp, "axisFlip", (float[2]){1.0f, 1.0f}); + DRW_shgroup_uniform_vec2_copy(grp, "waveDir", wave_dir); + DRW_shgroup_uniform_vec2_copy(grp, "waveOffset", wave_ofs); + DRW_shgroup_uniform_float_copy(grp, "wavePhase", wave_phase); + DRW_shgroup_uniform_float_copy(grp, "swirlRadius", 0.0f); + DRW_shgroup_call_procedural_triangles(grp, NULL, 1); } -/* Wave Distortion FX */ -static void gpencil_fx_wave(ShaderFxData *fx, GPENCIL_e_data *e_data, GPENCIL_Data *vedata) +static void gpencil_vfx_swirl(SwirlShaderFxData *fx, Object *UNUSED(ob), gpIterVfxData *iter) { - if (fx == NULL) { + DRWShadingGroup *grp; + + if (fx->object == NULL) { return; } - WaveShaderFxData *fxd = (WaveShaderFxData *)fx; - - GPENCIL_StorageList *stl = ((GPENCIL_Data *)vedata)->stl; - GPENCIL_PassList *psl = ((GPENCIL_Data *)vedata)->psl; - 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(fx_shgrp, fxquad, NULL); - DRW_shgroup_uniform_texture_ref(fx_shgrp, "strokeColor", &stl->g_data->temp_color_tx_a); - DRW_shgroup_uniform_texture_ref(fx_shgrp, "strokeDepth", &stl->g_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); + float winmat[4][4], persmat[4][4], swirl_center[3]; + DRW_view_winmat_get(NULL, winmat, false); + DRW_view_persmat_get(NULL, persmat, false); + const float *vp_size = DRW_viewport_size_get(); - fxd->runtime.fx_sh = fx_shgrp; -} - -/* ************************************************************** */ + copy_v3_v3(swirl_center, fx->object->obmat[3]); -/* 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); + const float w = fabsf(mul_project_m4_v3_zfac(persmat, swirl_center)); + mul_v3_m4v3(swirl_center, persmat, swirl_center); + mul_v3_fl(swirl_center, 1.0f / w); - e_data->gpencil_fx_rim_resolve_sh = DRW_shader_create_fullscreen( - datatoc_gpencil_fx_rim_resolve_frag_glsl, NULL); - } - if (!e_data->gpencil_fx_shadow_prepare_sh) { - e_data->gpencil_fx_shadow_prepare_sh = DRW_shader_create_fullscreen( - datatoc_gpencil_fx_shadow_prepare_frag_glsl, NULL); + /* Modify by distance to camera and object scale. */ + float world_pixel_scale = 1.0f / GPENCIL_PIXEL_FACTOR; + float scale = mat4_to_scale(fx->object->obmat); + float distance_factor = (world_pixel_scale * scale * winmat[1][1] * vp_size[1]) / w; - e_data->gpencil_fx_shadow_resolve_sh = DRW_shader_create_fullscreen( - datatoc_gpencil_fx_shadow_resolve_frag_glsl, NULL); - } - if (!e_data->gpencil_fx_glow_prepare_sh) { - e_data->gpencil_fx_glow_prepare_sh = DRW_shader_create_fullscreen( - datatoc_gpencil_fx_glow_prepare_frag_glsl, NULL); + mul_v2_fl(swirl_center, 0.5f); + add_v2_fl(swirl_center, 0.5f); + mul_v2_v2(swirl_center, vp_size); - e_data->gpencil_fx_glow_resolve_sh = DRW_shader_create_fullscreen( - datatoc_gpencil_fx_glow_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); + float radius = fx->radius * distance_factor; + if (radius < 1.0f) { + return; } -} -/* 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_shadow_prepare_sh); - DRW_SHADER_FREE_SAFE(e_data->gpencil_fx_shadow_resolve_sh); - DRW_SHADER_FREE_SAFE(e_data->gpencil_fx_glow_prepare_sh); - DRW_SHADER_FREE_SAFE(e_data->gpencil_fx_glow_resolve_sh); - DRW_SHADER_FREE_SAFE(e_data->gpencil_fx_swirl_sh); - DRW_SHADER_FREE_SAFE(e_data->gpencil_fx_wave_sh); -} + GPUShader *sh = GPENCIL_shader_fx_transform_get(); -/* 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_ALPHA | - DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS); + DRWState state = DRW_STATE_WRITE_COLOR; + grp = gpencil_vfx_pass_create("Fx Flip", state, iter, sh); + DRW_shgroup_uniform_vec2_copy(grp, "axisFlip", (float[2]){1.0f, 1.0f}); + DRW_shgroup_uniform_vec2_copy(grp, "waveOffset", (float[2]){0.0f, 0.0f}); + DRW_shgroup_uniform_vec2_copy(grp, "swirlCenter", swirl_center); + DRW_shgroup_uniform_float_copy(grp, "swirlAngle", fx->angle); + DRW_shgroup_uniform_float_copy(grp, "swirlRadius", radius); + DRW_shgroup_call_procedural_triangles(grp, NULL, 1); } -/* prepare fx shading groups */ -void gpencil_fx_prepare(GPENCIL_e_data *e_data, - GPENCIL_Data *vedata, - tGPencilObjectCache *cache_ob) +void gpencil_vfx_cache_populate(GPENCIL_Data *vedata, Object *ob, GPENCIL_tObject *tgp_ob) { - GPENCIL_StorageList *stl = ((GPENCIL_Data *)vedata)->stl; - const bool wiremode = (bool)(cache_ob->shading_type[0] == OB_WIRE); - - int ob_idx = cache_ob->idx; - - if ((wiremode) || (cache_ob->shader_fx.first == NULL)) { + bGPdata *gpd = (bGPdata *)ob->data; + GPENCIL_FramebufferList *fbl = vedata->fbl; + GPENCIL_PrivateData *pd = vedata->stl->pd; + /* If simplify enabled, nothing more to do. */ + if (pd->simplify_fx) { return; } - /* loop FX */ - for (ShaderFxData *fx = cache_ob->shader_fx.first; fx; fx = fx->next) { - if (effect_is_active(cache_ob->gpd, fx, stl->storage->is_render)) { + + /* These may not be allocated yet, use adress of future pointer. */ + gpIterVfxData iter = { + .pd = pd, + .tgp_ob = tgp_ob, + .target_fb = &fbl->layer_fb, + .source_fb = &fbl->object_fb, + .target_color_tx = &pd->color_layer_tx, + .source_color_tx = &pd->color_object_tx, + .target_reveal_tx = &pd->reveal_layer_tx, + .source_reveal_tx = &pd->reveal_object_tx, + }; + + LISTBASE_FOREACH (ShaderFxData *, fx, &ob->shader_fx) { + if (effect_is_active(gpd, fx, pd->is_viewport)) { switch (fx->type) { case eShaderFxType_Blur: - gpencil_fx_blur(fx, ob_idx, e_data, vedata, cache_ob); + gpencil_vfx_blur((BlurShaderFxData *)fx, ob, &iter); break; case eShaderFxType_Colorize: - gpencil_fx_colorize(fx, e_data, vedata); + gpencil_vfx_colorize((ColorizeShaderFxData *)fx, ob, &iter); break; case eShaderFxType_Flip: - gpencil_fx_flip(fx, e_data, vedata); - break; - case eShaderFxType_Light: - gpencil_fx_light(fx, e_data, vedata, cache_ob); + gpencil_vfx_flip((FlipShaderFxData *)fx, ob, &iter); break; case eShaderFxType_Pixel: - gpencil_fx_pixel(fx, e_data, vedata, cache_ob); + gpencil_vfx_pixelize((PixelShaderFxData *)fx, ob, &iter); break; case eShaderFxType_Rim: - gpencil_fx_rim(fx, e_data, vedata, cache_ob); + gpencil_vfx_rim((RimShaderFxData *)fx, ob, &iter); break; case eShaderFxType_Shadow: - gpencil_fx_shadow(fx, e_data, vedata, cache_ob); + gpencil_vfx_shadow((ShadowShaderFxData *)fx, ob, &iter); break; case eShaderFxType_Glow: - gpencil_fx_glow(fx, e_data, vedata, cache_ob); + gpencil_vfx_glow((GlowShaderFxData *)fx, ob, &iter); break; case eShaderFxType_Swirl: - gpencil_fx_swirl(fx, e_data, vedata, cache_ob); + gpencil_vfx_swirl((SwirlShaderFxData *)fx, ob, &iter); break; case eShaderFxType_Wave: - gpencil_fx_wave(fx, e_data, vedata); + gpencil_vfx_wave((WaveShaderFxData *)fx, ob, &iter); break; default: break; } } } -} - -/* helper to draw one FX pass and do ping-pong copy */ -static void gpencil_draw_fx_pass(GPENCIL_Data *vedata, DRWShadingGroup *shgrp, bool blend) -{ - if (shgrp == NULL) { - return; - } - - GPENCIL_StorageList *stl = ((GPENCIL_Data *)vedata)->stl; - GPENCIL_PassList *psl = ((GPENCIL_Data *)vedata)->psl; - GPENCIL_FramebufferList *fbl = ((GPENCIL_Data *)vedata)->fbl; - - const float clearcol[4] = {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 */ - 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(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(GPENCIL_Data *vedata, BlurShaderFxData *fxd) -{ - if (fxd->runtime.fx_sh == NULL) { - return; - } - - 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(vedata, shgrp, true); - } - /* vertical */ - if (by > 0) { - fxd->blur[0] = 0; - fxd->blur[1] = by; - gpencil_draw_fx_pass(vedata, shgrp, true); - } - } -} - -/* blur intermediate pass */ -static void draw_gpencil_midpass_blur(GPENCIL_Data *vedata, ShaderFxData_Runtime *runtime) -{ - GPENCIL_PassList *psl = ((GPENCIL_Data *)vedata)->psl; - GPENCIL_FramebufferList *fbl = ((GPENCIL_Data *)vedata)->fbl; - const float clearcol[4] = {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, runtime->fx_sh_b, runtime->fx_sh_b); - - /* copy pass from b for ping-pong frame buffers */ - GPU_framebuffer_bind(fbl->temp_fb_fx); - GPU_framebuffer_clear_color_depth(fbl->temp_fb_fx, clearcol, 1.0f); - DRW_draw_pass(psl->mix_pass_noblend); -} - -/* do blur of mid passes */ -static void draw_gpencil_do_blur( - GPENCIL_Data *vedata, ShaderFxData_Runtime *runtime, int samples, int bx, int by, int blur[2]) -{ - GPENCIL_StorageList *stl = ((GPENCIL_Data *)vedata)->stl; - 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; + if (tgp_ob->vfx.first != NULL) { + /* We need an extra pass to combine result to main buffer. */ + iter.target_fb = &fbl->gpencil_fb; - if ((samples > 0) && ((bx > 0) || (by > 0))) { - for (int x = 0; x < samples; x++) { + GPUShader *sh = GPENCIL_shader_fx_composite_get(); - /* horizontal */ - blur[0] = bx; - blur[1] = 0; - draw_gpencil_midpass_blur(vedata, runtime); + DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_MUL; + DRWShadingGroup *grp = gpencil_vfx_pass_create("GPencil Object Compose", state, &iter, sh); + DRW_shgroup_uniform_int_copy(grp, "isFirstPass", true); + DRW_shgroup_call_procedural_triangles(grp, NULL, 1); - /* Vertical */ - blur[0] = 0; - blur[1] = by; - draw_gpencil_midpass_blur(vedata, runtime); - - blur[0] = bx; - blur[1] = by; - } - } -} - -/* helper to draw RIM passes */ -static void draw_gpencil_rim_passes(GPENCIL_Data *vedata, RimShaderFxData *fxd) -{ - if (fxd->runtime.fx_sh_b == NULL) { - return; - } + /* We cannot do custom blending on MultiTarget framebuffers. + * Workaround by doing 2 passes. */ + grp = DRW_shgroup_create_sub(grp); + DRW_shgroup_state_disable(grp, DRW_STATE_BLEND_MUL); + DRW_shgroup_state_enable(grp, DRW_STATE_BLEND_ADD_FULL); + DRW_shgroup_uniform_int_copy(grp, "isFirstPass", false); + DRW_shgroup_call_procedural_triangles(grp, NULL, 1); - GPENCIL_StorageList *stl = ((GPENCIL_Data *)vedata)->stl; - GPENCIL_PassList *psl = ((GPENCIL_Data *)vedata)->psl; - GPENCIL_FramebufferList *fbl = ((GPENCIL_Data *)vedata)->fbl; - - const float clearcol[4] = {0.0f}; - - /* prepare mask */ - GPU_framebuffer_bind(fbl->temp_fb_fx); - GPU_framebuffer_clear_color_depth(fbl->temp_fb_fx, clearcol, 1.0f); - DRW_draw_pass_subset(psl->fx_shader_pass_blend, fxd->runtime.fx_sh, fxd->runtime.fx_sh); - - /* blur rim */ - draw_gpencil_do_blur( - vedata, &fxd->runtime, fxd->samples, fxd->blur[0], fxd->blur[1], &fxd->blur[0]); - - /* 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 */ - 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(fbl->temp_fb_a, clearcol, 1.0f); - DRW_draw_pass(psl->mix_pass_noblend); -} - -/* helper to draw SHADOW passes */ -static void draw_gpencil_shadow_passes(GPENCIL_Data *vedata, ShadowShaderFxData *fxd) -{ - if (fxd->runtime.fx_sh_b == NULL) { - return; - } - - GPENCIL_StorageList *stl = ((GPENCIL_Data *)vedata)->stl; - GPENCIL_PassList *psl = ((GPENCIL_Data *)vedata)->psl; - GPENCIL_FramebufferList *fbl = ((GPENCIL_Data *)vedata)->fbl; - const float clearcol[4] = {0.0f}; - - /* prepare shadow */ - GPU_framebuffer_bind(fbl->temp_fb_fx); - GPU_framebuffer_clear_color_depth(fbl->temp_fb_fx, clearcol, 1.0f); - DRW_draw_pass_subset(psl->fx_shader_pass_blend, fxd->runtime.fx_sh, fxd->runtime.fx_sh); - - /* blur shadow */ - draw_gpencil_do_blur( - vedata, &fxd->runtime, fxd->samples, fxd->blur[0], fxd->blur[1], &fxd->blur[0]); - - /* 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 */ - 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(fbl->temp_fb_a, clearcol, 1.0f); - DRW_draw_pass(psl->mix_pass_noblend); -} - -/* helper to draw GLOW passes */ -static void draw_gpencil_glow_passes(GPENCIL_Data *vedata, GlowShaderFxData *fxd) -{ - if (fxd->runtime.fx_sh_b == NULL) { - return; - } - - GPENCIL_StorageList *stl = ((GPENCIL_Data *)vedata)->stl; - GPENCIL_PassList *psl = ((GPENCIL_Data *)vedata)->psl; - GPENCIL_FramebufferList *fbl = ((GPENCIL_Data *)vedata)->fbl; - - const float clearcol[4] = {0.0f}; - - /* prepare glow */ - GPU_framebuffer_bind(fbl->temp_fb_fx); - GPU_framebuffer_clear_color_depth(fbl->temp_fb_fx, clearcol, 1.0f); - DRW_draw_pass_subset(psl->fx_shader_pass_blend, fxd->runtime.fx_sh, fxd->runtime.fx_sh); - - /* blur glow */ - draw_gpencil_do_blur( - vedata, &fxd->runtime, fxd->samples, fxd->blur[0], fxd->blur[0], &fxd->blur[0]); - - /* resolve */ - GPU_framebuffer_bind(fbl->temp_fb_b); - GPU_framebuffer_clear_color_depth(fbl->temp_fb_b, clearcol, 1.0f); - - /* reuses blur field to keep alpha mode */ - fxd->blur[1] = (fxd->flag & FX_GLOW_USE_ALPHA) ? 1 : 0; - - 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 */ - 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(fbl->temp_fb_a, clearcol, 1.0f); - DRW_draw_pass(psl->mix_pass_noblend); -} - -/* apply all object fx effects */ -void gpencil_fx_draw(GPENCIL_e_data *UNUSED(e_data), - GPENCIL_Data *vedata, - tGPencilObjectCache *cache_ob) -{ - GPENCIL_StorageList *stl = ((GPENCIL_Data *)vedata)->stl; - - /* loop FX modifiers */ - for (ShaderFxData *fx = cache_ob->shader_fx.first; fx; fx = fx->next) { - if (effect_is_active(cache_ob->gpd, fx, stl->storage->is_render)) { - switch (fx->type) { - - case eShaderFxType_Blur: { - BlurShaderFxData *fxd = (BlurShaderFxData *)fx; - draw_gpencil_blur_passes(vedata, fxd); - break; - } - case eShaderFxType_Colorize: { - ColorizeShaderFxData *fxd = (ColorizeShaderFxData *)fx; - gpencil_draw_fx_pass(vedata, fxd->runtime.fx_sh, false); - break; - } - case eShaderFxType_Flip: { - FlipShaderFxData *fxd = (FlipShaderFxData *)fx; - gpencil_draw_fx_pass(vedata, fxd->runtime.fx_sh, false); - break; - } - case eShaderFxType_Light: { - LightShaderFxData *fxd = (LightShaderFxData *)fx; - gpencil_draw_fx_pass(vedata, fxd->runtime.fx_sh, false); - break; - } - case eShaderFxType_Pixel: { - PixelShaderFxData *fxd = (PixelShaderFxData *)fx; - gpencil_draw_fx_pass(vedata, fxd->runtime.fx_sh, false); - break; - } - case eShaderFxType_Rim: { - RimShaderFxData *fxd = (RimShaderFxData *)fx; - draw_gpencil_rim_passes(vedata, fxd); - break; - } - case eShaderFxType_Shadow: { - ShadowShaderFxData *fxd = (ShadowShaderFxData *)fx; - draw_gpencil_shadow_passes(vedata, fxd); - break; - } - case eShaderFxType_Glow: { - GlowShaderFxData *fxd = (GlowShaderFxData *)fx; - draw_gpencil_glow_passes(vedata, fxd); - break; - } - case eShaderFxType_Swirl: { - SwirlShaderFxData *fxd = (SwirlShaderFxData *)fx; - gpencil_draw_fx_pass(vedata, fxd->runtime.fx_sh, false); - break; - } - case eShaderFxType_Wave: { - WaveShaderFxData *fxd = (WaveShaderFxData *)fx; - gpencil_draw_fx_pass(vedata, fxd->runtime.fx_sh, false); - break; - } - default: - break; - } - } + pd->use_object_fb = true; + pd->use_layer_fb = true; } } 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 deleted file mode 100644 index 0f64f54c67b..00000000000 --- a/source/blender/draw/engines/gpencil/shaders/fx/gpencil_fx_blur_frag.glsl +++ /dev/null @@ -1,85 +0,0 @@ -uniform mat4 ProjectionMatrix; -uniform mat4 ViewMatrix; - -uniform sampler2D strokeColor; -uniform sampler2D strokeDepth; -uniform vec2 Viewport; - -uniform int blur[2]; - -uniform vec3 loc; -uniform float pixsize; /* rv3d->pixsize */ -uniform float pixfactor; - -float defaultpixsize = pixsize * (1000.0 / pixfactor); -vec2 noffset = vec2(blur[0], blur[1]); - -out vec4 FragColor; - -float get_zdepth(ivec2 poxy) -{ - /* if outside viewport set as infinite depth */ - if ((poxy.x < 0) || (poxy.x > Viewport.x)) { - return 1.0f; - } - if ((poxy.y < 0) || (poxy.y > Viewport.y)) { - return 1.0f; - } - - float zdepth = texelFetch(strokeDepth, poxy, 0).r; - return zdepth; -} - -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); - - /* round to avoid shift when add more samples */ - dx = floor(dx) + 1.0; - dy = floor(dy) + 1.0; - - /* apply blurring, using a 9-tap filter with predefined gaussian weights */ - /* depth (get the value of the surrounding pixels) */ - float outdepth = get_zdepth(ivec2(uv.x, uv.y)); - for (int x = -1; x < 2; x++) { - for (int y = -1; y < 2; y++) { - float depth = get_zdepth(ivec2(uv.x + x * dx, uv.y + y * dy)); - if (depth < outdepth) { - outdepth = depth; - break; - } - } - } - 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); - - /* discar extreme values */ - if (outcolor.a < 0.02f) { - discard; - } - if ((outdepth <= 0.000001) || (outdepth >= 0.999999)) { - discard; - } -} 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 deleted file mode 100644 index 52f42d30d06..00000000000 --- a/source/blender/draw/engines/gpencil/shaders/fx/gpencil_fx_colorize_frag.glsl +++ /dev/null @@ -1,82 +0,0 @@ -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_DUOTONE 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_DUOTONE: { - 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 deleted file mode 100644 index 2cd77007b36..00000000000 --- a/source/blender/draw/engines/gpencil/shaders/fx/gpencil_fx_flip_frag.glsl +++ /dev/null @@ -1,37 +0,0 @@ -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_glow_prepare_frag.glsl b/source/blender/draw/engines/gpencil/shaders/fx/gpencil_fx_glow_prepare_frag.glsl deleted file mode 100644 index 676b9b05db9..00000000000 --- a/source/blender/draw/engines/gpencil/shaders/fx/gpencil_fx_glow_prepare_frag.glsl +++ /dev/null @@ -1,68 +0,0 @@ -uniform mat4 ProjectionMatrix; -uniform mat4 ViewMatrix; - -/* ******************************************************************* */ -/* create glow mask */ -/* ******************************************************************* */ -uniform sampler2D strokeColor; -uniform sampler2D strokeDepth; - -uniform vec3 glow_color; -uniform vec3 select_color; -uniform float threshold; -uniform int mode; - -out vec4 FragColor; - -#define MODE_LUMINANCE 0 -#define MODE_COLOR 1 - -/* calc luminance */ -float luma(vec3 color) -{ - /* the color is linear, so do not apply tonemapping */ - return (color.r + color.g + color.b) / 3.0; -} - -bool check_color(vec3 color_a, vec3 color_b) -{ - /* need round the number to avoid precision errors */ - if ((floor(color_a.r * 100) == floor(color_b.r * 100)) && - (floor(color_a.g * 100) == floor(color_b.g * 100)) && - (floor(color_a.b * 100) == floor(color_b.b * 100))) { - return true; - } - - return false; -} - -void main() -{ - vec2 uv = vec2(gl_FragCoord.xy); - - float stroke_depth = texelFetch(strokeDepth, ivec2(uv.xy), 0).r; - vec4 src_pixel = texelFetch(strokeColor, ivec2(uv.xy), 0); - vec4 outcolor; - - /* is transparent */ - if (src_pixel.a == 0.0f) { - discard; - } - - if (mode == MODE_LUMINANCE) { - if (luma(src_pixel.rgb) < threshold) { - discard; - } - } - else if (mode == MODE_COLOR) { - if (!check_color(src_pixel.rgb, select_color.rgb)) { - discard; - } - } - else { - discard; - } - - gl_FragDepth = stroke_depth; - FragColor = vec4(glow_color.rgb, 1.0); -} diff --git a/source/blender/draw/engines/gpencil/shaders/fx/gpencil_fx_glow_resolve_frag.glsl b/source/blender/draw/engines/gpencil/shaders/fx/gpencil_fx_glow_resolve_frag.glsl deleted file mode 100644 index e2aceb9eefe..00000000000 --- a/source/blender/draw/engines/gpencil/shaders/fx/gpencil_fx_glow_resolve_frag.glsl +++ /dev/null @@ -1,46 +0,0 @@ -/* ******************************************************************* */ -/* Resolve GLOW pass */ -/* ******************************************************************* */ -uniform sampler2D strokeColor; -uniform sampler2D strokeDepth; -uniform sampler2D glowColor; -uniform sampler2D glowDepth; -uniform int alpha_mode; - -out vec4 FragColor; - -void main() -{ - vec4 outcolor; - ivec2 uv = ivec2(gl_FragCoord.xy); - - float stroke_depth = texelFetch(strokeDepth, uv.xy, 0).r; - vec4 src_pixel = texelFetch(strokeColor, uv.xy, 0); - vec4 glow_pixel = texelFetch(glowColor, uv.xy, 0); - float glow_depth = texelFetch(glowDepth, uv.xy, 0).r; - - if (alpha_mode == 0) { - outcolor = src_pixel + glow_pixel; - } - else { - if ((src_pixel.a < 0.1) || (glow_pixel.a < 0.1)) { - outcolor = src_pixel + glow_pixel; - } - else { - outcolor = src_pixel; - } - } - - if (src_pixel.a < glow_pixel.a) { - gl_FragDepth = glow_depth; - } - else { - gl_FragDepth = stroke_depth; - } - - if (outcolor.a < 0.001) { - discard; - } - - 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 deleted file mode 100644 index a5c321c20c1..00000000000 --- a/source/blender/draw/engines/gpencil/shaders/fx/gpencil_fx_light_frag.glsl +++ /dev/null @@ -1,70 +0,0 @@ -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 pixfactor; - -out vec4 FragColor; - -float defaultpixsize = pixsize * (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 deleted file mode 100644 index 46b3c4286b4..00000000000 --- a/source/blender/draw/engines/gpencil/shaders/fx/gpencil_fx_pixel_frag.glsl +++ /dev/null @@ -1,51 +0,0 @@ -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 pixfactor; - -out vec4 FragColor; - -int uselines = size[2]; -float defaultpixsize = pixsize * (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 deleted file mode 100644 index 2a17e573978..00000000000 --- a/source/blender/draw/engines/gpencil/shaders/fx/gpencil_fx_rim_prepare_frag.glsl +++ /dev/null @@ -1,65 +0,0 @@ -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 pixfactor; - -float defaultpixsize = pixsize * (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 deleted file mode 100644 index fa010baa32f..00000000000 --- a/source/blender/draw/engines/gpencil/shaders/fx/gpencil_fx_rim_resolve_frag.glsl +++ /dev/null @@ -1,98 +0,0 @@ -/* ******************************************************************* */ -/* 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_shadow_prepare_frag.glsl b/source/blender/draw/engines/gpencil/shaders/fx/gpencil_fx_shadow_prepare_frag.glsl deleted file mode 100644 index d2e20feae18..00000000000 --- a/source/blender/draw/engines/gpencil/shaders/fx/gpencil_fx_shadow_prepare_frag.glsl +++ /dev/null @@ -1,98 +0,0 @@ -uniform mat4 ProjectionMatrix; -uniform mat4 ViewMatrix; - -/* ******************************************************************* */ -/* create shadow */ -/* ******************************************************************* */ -uniform sampler2D strokeColor; -uniform sampler2D strokeDepth; -uniform vec2 Viewport; - -uniform int offset[2]; -uniform float scale[2]; -uniform float rotation; -uniform vec4 shadow_color; - -uniform float amplitude; -uniform float period; -uniform float phase; -uniform int orientation; - -uniform vec3 loc; -uniform float pixsize; /* rv3d->pixsize */ -uniform float pixfactor; - -#define M_PI 3.1415926535897932384626433832795 - -#define HORIZONTAL 0 -#define VERTICAL 1 - -float defaultpixsize = pixsize * (1000.0 / pixfactor); -vec2 noffset = vec2(offset[0], offset[1]); -float cosv = cos(rotation); -float sinv = sin(rotation); - -out vec4 FragColor; - -/* 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() -{ - vec2 uv = vec2(gl_FragCoord.xy); - vec4 nloc = ProjectionMatrix * ViewMatrix * vec4(loc.xyz, 1.0); - vec2 loc2d = toScreenSpace(nloc); - - 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); - - /* move point to new coords system */ - vec2 tpos = vec2(uv.x, uv.y) - loc2d; - - /* rotation */ - if (rotation != 0) { - vec2 rotpoint = vec2((tpos.x * cosv) - (tpos.y * sinv), (tpos.x * sinv) + (tpos.y * cosv)); - tpos = rotpoint; - } - - /* apply offset */ - tpos = vec2(tpos.x - dx, tpos.y - dy); - - /* apply scale */ - tpos.x *= 1.0 / scale[0]; - tpos.y *= 1.0 / scale[1]; - - /* back to original coords system */ - vec2 texpos = tpos + loc2d; - - /* wave */ - if (orientation == HORIZONTAL) { - float pval = (uv.x * M_PI) / Viewport[0]; - texpos.y += amplitude * sin((period * pval) + phase); - } - else if (orientation == VERTICAL) { - float pval = (uv.y * M_PI) / Viewport[1]; - texpos.x += amplitude * sin((period * pval) + phase); - } - - vec4 src_pixel = texelFetch(strokeColor, ivec2(texpos.x, texpos.y), 0); - /* is transparent */ - if (src_pixel.a == 0.0f) { - discard; - } - - gl_FragDepth = texelFetch(strokeDepth, ivec2(texpos.x, texpos.y), 0).r; - FragColor = shadow_color; -} diff --git a/source/blender/draw/engines/gpencil/shaders/fx/gpencil_fx_shadow_resolve_frag.glsl b/source/blender/draw/engines/gpencil/shaders/fx/gpencil_fx_shadow_resolve_frag.glsl deleted file mode 100644 index 3ef11008adf..00000000000 --- a/source/blender/draw/engines/gpencil/shaders/fx/gpencil_fx_shadow_resolve_frag.glsl +++ /dev/null @@ -1,32 +0,0 @@ -/* ******************************************************************* */ -/* Resolve Shadow pass */ -/* ******************************************************************* */ -uniform sampler2D strokeColor; -uniform sampler2D strokeDepth; -uniform sampler2D shadowColor; -uniform sampler2D shadowDepth; - -out vec4 FragColor; - -void main() -{ - ivec2 uv = ivec2(gl_FragCoord.xy); - - float stroke_depth = texelFetch(strokeDepth, uv.xy, 0).r; - float shadow_depth = texelFetch(shadowDepth, uv.xy, 0).r; - vec4 stroke_pixel = texelFetch(strokeColor, uv.xy, 0); - vec4 shadow_pixel = texelFetch(shadowColor, uv.xy, 0); - - /* copy original pixel */ - vec4 outcolor = stroke_pixel; - float outdepth = stroke_depth; - - /* if stroke is not on top, copy shadow */ - if ((stroke_pixel.a <= 0.2) && (shadow_pixel.a > 0.0)) { - outcolor = shadow_pixel; - outdepth = shadow_depth; - } - - gl_FragDepth = outdepth; - 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 deleted file mode 100644 index 01d4fe40195..00000000000 --- a/source/blender/draw/engines/gpencil/shaders/fx/gpencil_fx_swirl_frag.glsl +++ /dev/null @@ -1,74 +0,0 @@ -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 pixfactor; - -out vec4 FragColor; - -float defaultpixsize = pixsize * (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 locpixsize = abs((loc.z * defaultpixsize)); - if (locpixsize == 0) { - locpixsize = 1; - } - float pxradius = (ProjectionMatrix[3][3] == 0.0) ? (radius / locpixsize) : - (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 deleted file mode 100644 index 0a5df9f6d77..00000000000 --- a/source/blender/draw/engines/gpencil/shaders/fx/gpencil_fx_wave_frag.glsl +++ /dev/null @@ -1,44 +0,0 @@ - -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; - - if (outcolor.a < 0.02f) { - discard; - } -} diff --git a/source/blender/draw/engines/gpencil/shaders/gpencil_antialiasing_frag.glsl b/source/blender/draw/engines/gpencil/shaders/gpencil_antialiasing_frag.glsl new file mode 100644 index 00000000000..b512b54e392 --- /dev/null +++ b/source/blender/draw/engines/gpencil/shaders/gpencil_antialiasing_frag.glsl @@ -0,0 +1,64 @@ + +uniform sampler2D edgesTex; +uniform sampler2D areaTex; +uniform sampler2D searchTex; +uniform sampler2D blendTex; +uniform sampler2D colorTex; +uniform sampler2D revealTex; +uniform bool onlyAlpha; +uniform bool doAntiAliasing; + +in vec2 uvs; +in vec2 pixcoord; +in vec4 offset[3]; + +#if SMAA_STAGE == 0 +out vec2 fragColor; +#elif SMAA_STAGE == 1 +out vec4 fragColor; +#elif SMAA_STAGE == 2 +/* Reminder: Blending func is fragRevealage * DST + fragColor .*/ +layout(location = 0, index = 0) out vec4 outColor; +layout(location = 0, index = 1) out vec4 outReveal; +#endif + +void main() +{ +#if SMAA_STAGE == 0 + /* Detect edges in color and revealage buffer. */ + fragColor = SMAALumaEdgeDetectionPS(uvs, offset, colorTex); + fragColor = max(fragColor, SMAALumaEdgeDetectionPS(uvs, offset, revealTex)); + /* Discard if there is no edge. */ + if (dot(fragColor, float2(1.0, 1.0)) == 0.0) { + discard; + } + +#elif SMAA_STAGE == 1 + fragColor = SMAABlendingWeightCalculationPS( + uvs, pixcoord, offset, edgesTex, areaTex, searchTex, vec4(0)); + +#elif SMAA_STAGE == 2 + /* Resolve both buffers. */ + if (doAntiAliasing) { + outColor = SMAANeighborhoodBlendingPS(uvs, offset[0], colorTex, blendTex); + outReveal = SMAANeighborhoodBlendingPS(uvs, offset[0], revealTex, blendTex); + } + else { + outColor = texture(colorTex, uvs); + outReveal = texture(revealTex, uvs); + } + + /* Revealage, how much light passes through. */ + /* Average for alpha channel. */ + outReveal.a = clamp(dot(outReveal.rgb, vec3(0.333334)), 0.0, 1.0); + /* Color buf is already premultiplied. Just add it to the color. */ + /* Add the alpha. */ + outColor.a = 1.0 - outReveal.a; + + if (onlyAlpha) { + /* Special case in wireframe xray mode. */ + outColor = vec4(0.0); + outReveal.rgb = outReveal.aaa; + } +#endif +} diff --git a/source/blender/draw/engines/gpencil/shaders/gpencil_antialiasing_vert.glsl b/source/blender/draw/engines/gpencil/shaders/gpencil_antialiasing_vert.glsl new file mode 100644 index 00000000000..07734d19972 --- /dev/null +++ b/source/blender/draw/engines/gpencil/shaders/gpencil_antialiasing_vert.glsl @@ -0,0 +1,21 @@ + +out vec2 uvs; +out vec2 pixcoord; +out vec4 offset[3]; + +void main() +{ + int v = gl_VertexID % 3; + float x = -1.0 + float((v & 1) << 2); + float y = -1.0 + float((v & 2) << 1); + gl_Position = vec4(x, y, 1.0, 1.0); + uvs = (gl_Position.xy + 1.0) * 0.5; + +#if SMAA_STAGE == 0 + SMAAEdgeDetectionVS(uvs, offset); +#elif SMAA_STAGE == 1 + SMAABlendingWeightCalculationVS(uvs, pixcoord, offset); +#elif SMAA_STAGE == 2 + SMAANeighborhoodBlendingVS(uvs, offset[0]); +#endif +} diff --git a/source/blender/draw/engines/gpencil/shaders/gpencil_background_frag.glsl b/source/blender/draw/engines/gpencil/shaders/gpencil_background_frag.glsl deleted file mode 100644 index 18803bfa3fa..00000000000 --- a/source/blender/draw/engines/gpencil/shaders/gpencil_background_frag.glsl +++ /dev/null @@ -1,12 +0,0 @@ -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_blend_frag.glsl b/source/blender/draw/engines/gpencil/shaders/gpencil_blend_frag.glsl deleted file mode 100644 index 85dee4390a5..00000000000 --- a/source/blender/draw/engines/gpencil/shaders/gpencil_blend_frag.glsl +++ /dev/null @@ -1,157 +0,0 @@ -in vec4 uvcoordsvar; - -out vec4 FragColor; - -uniform sampler2D strokeColor; -uniform sampler2D strokeDepth; -uniform sampler2D blendColor; -uniform sampler2D blendDepth; -uniform int mode; -uniform int mask_layer; -uniform int tonemapping; - -#define ON 1 -#define OFF 0 - -#define MODE_REGULAR 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 (mix_color.a == 0) { - return src_color; - } - - switch (mode) { - case MODE_REGULAR: { - /* premult */ - src_color = vec4(vec3(src_color.rgb / src_color.a), src_color.a); - mix_color = vec4(vec3(mix_color.rgb / mix_color.a), mix_color.a); - - outcolor = vec4(mix(src_color.rgb, mix_color.rgb, mix_color.a), src_color.a); - break; - } - case MODE_OVERLAY: { - src_color = vec4(vec3(src_color.rgb / src_color.a), src_color.a); - mix_color = vec4(vec3(mix_color.rgb / mix_color.a), mix_color.a); - - mix_color.rgb = mix(src_color.rgb, mix_color.rgb, mix_color.a); - 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); - outcolor.a = src_color.a; - break; - } - case MODE_ADD: { - mix_color.rgb = mix(src_color.rgb, mix_color.rgb, mix_color.a); - outcolor = src_color + mix_color; - outcolor.a = src_color.a; - break; - } - case MODE_SUB: { - mix_color.rgb = mix(src_color.rgb, mix_color.rgb, mix_color.a); - outcolor = src_color - mix_color; - outcolor.a = clamp(src_color.a - mix_color.a, 0.0, 1.0); - break; - } - case MODE_MULTIPLY: { - src_color = vec4(vec3(src_color.rgb / src_color.a), src_color.a); - mix_color = vec4(vec3(mix_color.rgb / mix_color.a), mix_color.a); - - mix_color.rgb = mix(src_color.rgb, mix_color.rgb, mix_color.a); - outcolor = src_color * mix_color; - outcolor.a = src_color.a; - break; - } - case MODE_DIVIDE: { - mix_color.rgb = mix(src_color.rgb, mix_color.rgb, mix_color.a); - outcolor = src_color / mix_color; - outcolor.a = src_color.a; - break; - } - default: { - outcolor = mix_color; - outcolor.a = src_color.a; - break; - } - } - return clamp(outcolor, 0.0, 1.0); -} - -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; - } -} - -vec4 tone(vec4 stroke_color) -{ - if (tonemapping == 1) { - vec4 color = vec4(0, 0, 0, stroke_color.a); - color.r = linearrgb_to_srgb(stroke_color.r); - color.g = linearrgb_to_srgb(stroke_color.g); - color.b = linearrgb_to_srgb(stroke_color.b); - return color; - } - else { - return stroke_color; - } -} - -void main() -{ - vec4 outcolor; - ivec2 uv = ivec2(gl_FragCoord.xy); - vec4 stroke_color = texelFetch(strokeColor, uv, 0).rgba; - float stroke_depth = texelFetch(strokeDepth, uv, 0).r; - - vec4 mix_color = texelFetch(blendColor, uv, 0).rgba; - float mix_depth = texelFetch(blendDepth, uv, 0).r; - - if (stroke_color.a > 0) { - if (mix_color.a > 0) { - /* apply blend mode */ - FragColor = get_blend_color(mode, stroke_color, mix_color); - } - else { - FragColor = stroke_color; - } - gl_FragDepth = min(stroke_depth, mix_depth); - } - else { - if (mask_layer == ON) { - discard; - } - else { - /* if not using mask, return mix color */ - FragColor = mix_color; - gl_FragDepth = mix_depth; - } - } - - /* apply tone mapping */ - FragColor = tone(FragColor); -} diff --git a/source/blender/draw/engines/gpencil/shaders/gpencil_common_lib.glsl b/source/blender/draw/engines/gpencil/shaders/gpencil_common_lib.glsl new file mode 100644 index 00000000000..8774b633467 --- /dev/null +++ b/source/blender/draw/engines/gpencil/shaders/gpencil_common_lib.glsl @@ -0,0 +1,593 @@ + +/* Must match C declaration. */ +struct gpMaterial { + vec4 stroke_color; + vec4 fill_color; + vec4 fill_mix_color; + vec4 fill_uv_rot_scale; + vec4 fill_uv_offset; + /* Put float/int at the end to avoid padding error */ + float stroke_texture_mix; + float stroke_u_scale; + float fill_texture_mix; + int flag; + /* Please ensure 16 byte alignment (multiple of vec4). */ +}; + +/* flag */ +#define GP_STROKE_ALIGNMENT_STROKE 1 +#define GP_STROKE_ALIGNMENT_OBJECT 2 +#define GP_STROKE_ALIGNMENT_FIXED 3 +#define GP_STROKE_ALIGNMENT 0x3 +#define GP_STROKE_OVERLAP (1 << 2) +#define GP_STROKE_TEXTURE_USE (1 << 3) +#define GP_STROKE_TEXTURE_STENCIL (1 << 4) +#define GP_STROKE_TEXTURE_PREMUL (1 << 5) +#define GP_STROKE_DOTS (1 << 6) +#define GP_FILL_TEXTURE_USE (1 << 10) +#define GP_FILL_TEXTURE_PREMUL (1 << 11) +#define GP_FILL_TEXTURE_CLIP (1 << 12) +#define GP_FILL_GRADIENT_USE (1 << 13) +#define GP_FILL_GRADIENT_RADIAL (1 << 14) +/* High bits are used to pass material ID to fragment shader. */ +#define GP_MATID_SHIFT 16 + +/* Multiline defines can crash blender with certain GPU drivers. */ +/* clang-format off */ +#define GP_FILL_FLAGS (GP_FILL_TEXTURE_USE | GP_FILL_TEXTURE_PREMUL | GP_FILL_TEXTURE_CLIP | GP_FILL_GRADIENT_USE | GP_FILL_GRADIENT_RADIAL) +/* clang-format on */ + +#define GP_FLAG_TEST(flag, val) (((flag) & (val)) != 0) + +/* Must match C declaration. */ +struct gpLight { + vec4 color_type; + vec4 right; + vec4 up; + vec4 forward; + vec4 position; + /* Please ensure 16 byte alignment (multiple of vec4). */ +}; + +#define spot_size right.w +#define spot_blend up.w + +#define GP_LIGHT_TYPE_POINT 0.0 +#define GP_LIGHT_TYPE_SPOT 1.0 +#define GP_LIGHT_TYPE_SUN 2.0 +#define GP_LIGHT_TYPE_AMBIENT 3.0 + +#ifdef GP_MATERIAL_BUFFER_LEN + +layout(std140) uniform gpMaterialBlock +{ + gpMaterial materials[GP_MATERIAL_BUFFER_LEN]; +}; + +#endif + +#ifdef GPENCIL_LIGHT_BUFFER_LEN + +layout(std140) uniform gpLightBlock +{ + gpLight lights[GPENCIL_LIGHT_BUFFER_LEN]; +}; + +#endif + +/* Must match eGPLayerBlendModes */ +#define MODE_REGULAR 0 +#define MODE_OVERLAY 1 +#define MODE_ADD 2 +#define MODE_SUB 3 +#define MODE_MULTIPLY 4 +#define MODE_DIVIDE 5 +#define MODE_OVERLAY_SECOND_PASS 999 + +void blend_mode_output( + int blend_mode, vec4 color, float opacity, out vec4 frag_color, out vec4 frag_revealage) +{ + switch (blend_mode) { + case MODE_REGULAR: + /* Reminder: Blending func is premult alpha blend (dst.rgba * (1 - src.a) + src.rgb).*/ + color *= opacity; + frag_color = color; + frag_revealage = vec4(0.0, 0.0, 0.0, color.a); + break; + case MODE_MULTIPLY: + /* Reminder: Blending func is multiply blend (dst.rgba * src.rgba).*/ + color.a *= opacity; + frag_revealage = frag_color = (1.0 - color.a) + color.a * color; + break; + case MODE_DIVIDE: + /* Reminder: Blending func is multiply blend (dst.rgba * src.rgba).*/ + color.a *= opacity; + frag_revealage = frag_color = clamp(1.0 / max(vec4(1e-6), 1.0 - color * color.a), 0.0, 1e18); + break; + case MODE_OVERLAY: + /* Reminder: Blending func is multiply blend (dst.rgba * src.rgba).*/ + /** + * We need to separate the overlay equation into 2 term (one mul and one add). + * This is the standard overlay equation (per channel): + * rtn = (src < 0.5) ? (2.0 * src * dst) : (1.0 - 2.0 * (1.0 - src) * (1.0 - dst)); + * We rewrite the second branch like this: + * rtn = 1 - 2 * (1 - src) * (1 - dst); + * rtn = 1 - 2 (1 - dst + src * dst - src); + * rtn = 1 - 2 (1 - dst * (1 - src) - src); + * rtn = 1 - 2 + dst * (2 - 2 * src) + 2 * src; + * rtn = (- 1 + 2 * src) + dst * (2 - 2 * src); + **/ + color = mix(vec4(0.5), color, color.a * opacity); + vec4 s = step(-0.5, -color); + frag_revealage = frag_color = 2.0 * s + 2.0 * color * (1.0 - s * 2.0); + break; + case MODE_OVERLAY_SECOND_PASS: + /* Reminder: Blending func is additive blend (dst.rgba + src.rgba).*/ + color = mix(vec4(0.5), color, color.a * opacity); + frag_revealage = frag_color = (-1.0 + 2.0 * color) * step(-0.5, -color); + break; + case MODE_SUB: + case MODE_ADD: + /* Reminder: Blending func is additive / subtractive blend (dst.rgba +/- src.rgba).*/ + frag_color = color * color.a * opacity; + frag_revealage = vec4(0.0); + break; + } +} + +#ifdef GPU_VERTEX_SHADER +# define IN_OUT out +#else +# define IN_OUT in +#endif + +/* Shader interface. */ +IN_OUT vec4 finalColorMul; +IN_OUT vec4 finalColorAdd; +IN_OUT vec3 finalPos; +IN_OUT vec2 finalUvs; +noperspective IN_OUT float strokeThickness; +noperspective IN_OUT float strokeHardeness; +flat IN_OUT vec2 strokeAspect; +flat IN_OUT vec2 strokePt1; +flat IN_OUT vec2 strokePt2; +flat IN_OUT int matFlag; +flat IN_OUT float depth; + +#ifdef GPU_FRAGMENT_SHADER + +# define linearstep(p0, p1, v) (clamp(((v) - (p0)) / abs((p1) - (p0)), 0.0, 1.0)) + +float stroke_round_cap_mask(vec2 p1, vec2 p2, vec2 aspect, float thickness, float hardfac) +{ + /* We create our own uv space to avoid issues with triangulation and linear + * interpolation artifacts. */ + vec2 line = p2.xy - p1.xy; + vec2 pos = gl_FragCoord.xy - p1.xy; + float line_len = length(line); + float half_line_len = line_len * 0.5; + /* Normalize */ + line = (line_len > 0.0) ? (line / line_len) : vec2(1.0, 0.0); + /* Create a uv space that englobe the whole segment into a capsule. */ + vec2 uv_end; + uv_end.x = max(abs(dot(line, pos) - half_line_len) - half_line_len, 0.0); + uv_end.y = dot(vec2(-line.y, line.x), pos); + /* Divide by stroke radius. */ + uv_end /= thickness; + uv_end *= aspect; + + float dist = clamp(1.0 - length(uv_end) * 2.0, 0.0, 1.0); + if (hardfac > 0.999) { + return step(1e-8, dist); + } + else { + /* Modulate the falloff profile */ + float hardness = 1.0 - hardfac; + dist = pow(dist, mix(0.01, 10.0, hardness)); + return smoothstep(0.0, 1.0, dist); + } +} + +#endif + +uniform vec2 sizeViewport; +uniform vec2 sizeViewportInv; + +/* Per Object */ +uniform bool strokeOrder3d; +uniform int gpMaterialOffset; +uniform float thicknessScale; +uniform float thicknessWorldScale; +#define thicknessIsScreenSpace (thicknessWorldScale < 0.0) +#define MATERIAL(m) materials[m + gpMaterialOffset] + +#ifdef GPU_VERTEX_SHADER + +/* Per Layer */ +uniform float thicknessOffset; +uniform float vertexColorOpacity; +uniform vec4 layerTint; +uniform float layerOpacity; /* Used for onion skin. */ +uniform float strokeIndexOffset = 0.0; + +/* All of these attribs are quad loaded the same way + * as GL_LINES_ADJACENCY would feed a geometry shader: + * - ma reference the previous adjacency point. + * - ma1 reference the current line first point. + * - ma2 reference the current line second point. + * - ma3 reference the next adjacency point. + * Note that we are rendering quad instances and not using any index buffer (except for fills). + */ +in vec4 ma; +in vec4 ma1; +in vec4 ma2; +in vec4 ma3; +# define strength1 ma1.y +# define strength2 ma2.y +# define stroke_id1 ma1.z +# define point_id1 ma1.w +/* Position contains thickness in 4th component. */ +in vec4 pos; /* Prev adj vert */ +in vec4 pos1; /* Current edge */ +in vec4 pos2; /* Current edge */ +in vec4 pos3; /* Next adj vert */ +# define thickness1 pos1.w +# define thickness2 pos2.w +/* xy is UV for fills, z is U of stroke, w is cosine of UV angle with sign of sine. */ +in vec4 uv1; +in vec4 uv2; + +in vec4 col1; +in vec4 col2; + +in vec4 fcol1; + +/* hard.x is aspect. */ +in vec2 hard1; +in vec2 hard2; +# define aspect1 hard1.x +# define aspect2 hard2.x + +void discard_vert() +{ + /* We set the vertex at the camera origin to generate 0 fragments. */ + gl_Position = vec4(0.0, 0.0, -3e36, 0.0); +} + +vec2 project_to_screenspace(vec4 v) +{ + return ((v.xy / v.w) * 0.5 + 0.5) * sizeViewport; +} + +vec2 rotate_90deg(vec2 v) +{ + /* Counter Clock-Wise. */ + return vec2(-v.y, v.x); +} + +mat4 model_matrix_get() +{ + return ModelMatrix; +} + +vec3 transform_point(mat4 m, vec3 v) +{ + return (m * vec4(v, 1.0)).xyz; +} + +vec2 safe_normalize(vec2 v) +{ + float len_sqr = dot(v, v); + if (len_sqr > 0.0) { + return v / sqrt(len_sqr); + } + else { + return vec2(1.0, 0.0); + } +} + +vec2 safe_normalize_len(vec2 v, out float len) +{ + len = sqrt(dot(v, v)); + if (len > 0.0) { + return v / len; + } + else { + return vec2(1.0, 0.0); + } +} + +float stroke_thickness_modulate(float thickness) +{ + /* Modify stroke thickness by object and layer factors.-*/ + thickness *= thicknessScale; + thickness += thicknessOffset; + thickness = max(1.0, thickness); + + if (thicknessIsScreenSpace) { + /* Multiply offset by view Z so that offset is constant in screenspace. + * (e.i: does not change with the distance to camera) */ + thickness *= gl_Position.w; + } + else { + /* World space point size. */ + thickness *= thicknessWorldScale * ProjectionMatrix[1][1] * sizeViewport.y; + } + return thickness; +} + +# ifdef GP_MATERIAL_BUFFER_LEN +void color_output(vec4 stroke_col, vec4 vert_col, float vert_strength, float mix_tex) +{ + /* Mix stroke with other colors. */ + vec4 mixed_col = stroke_col; + mixed_col.rgb = mix(mixed_col.rgb, vert_col.rgb, vert_col.a * vertexColorOpacity); + mixed_col.rgb = mix(mixed_col.rgb, layerTint.rgb, layerTint.a); + mixed_col.a *= vert_strength * layerOpacity; + /** + * This is what the fragment shader looks like. + * out = col * finalColorMul + col.a * finalColorAdd. + * finalColorMul is how much of the texture color to keep. + * finalColorAdd is how much of the mixed color to add. + * Note that we never add alpha. This is to keep the texture act as a stencil. + * We do however, modulate the alpha (reduce it). + **/ + /* We add the mixed color. This is 100% mix (no texture visible). */ + finalColorMul = vec4(mixed_col.aaa, mixed_col.a); + finalColorAdd = vec4(mixed_col.rgb * mixed_col.a, 0.0); + /* Then we blend according to the texture mix factor. + * Note that we keep the alpha modulation. */ + finalColorMul.rgb *= mix_tex; + finalColorAdd.rgb *= 1.0 - mix_tex; +} +# endif + +void stroke_vertex() +{ + int m = int(ma1.x); + bool is_dot = false; + bool is_squares = false; + +# ifdef GP_MATERIAL_BUFFER_LEN + if (m != -1.0) { + is_dot = GP_FLAG_TEST(MATERIAL(m).flag, GP_STROKE_ALIGNMENT); + is_squares = !GP_FLAG_TEST(MATERIAL(m).flag, GP_STROKE_DOTS); + } +# endif + + /* Special Case. Stroke with single vert are rendered as dots. Do not discard them. */ + if (!is_dot && ma.x == -1.0 && ma2.x == -1.0) { + is_dot = true; + is_squares = false; + } + + /* Enpoints, we discard the vertices. */ + if (ma1.x == -1.0 || (!is_dot && ma2.x == -1.0)) { + discard_vert(); + return; + } + + mat4 model_mat = model_matrix_get(); + + /* Avoid using a vertex attrib for quad positioning. */ + float x = float(gl_VertexID & 1) * 2.0 - 1.0; /* [-1..1] */ + float y = float(gl_VertexID & 2) - 1.0; /* [-1..1] */ + + bool use_curr = is_dot || (x == -1.0); + + vec3 wpos_adj = transform_point(model_mat, (use_curr) ? pos.xyz : pos3.xyz); + vec3 wpos1 = transform_point(model_mat, pos1.xyz); + vec3 wpos2 = transform_point(model_mat, pos2.xyz); + + vec4 ndc_adj = point_world_to_ndc(wpos_adj); + vec4 ndc1 = point_world_to_ndc(wpos1); + vec4 ndc2 = point_world_to_ndc(wpos2); + + gl_Position = (use_curr) ? ndc1 : ndc2; + finalPos = (use_curr) ? wpos1 : wpos2; + + vec2 ss_adj = project_to_screenspace(ndc_adj); + vec2 ss1 = project_to_screenspace(ndc1); + vec2 ss2 = project_to_screenspace(ndc2); + /* Screenspace Lines tangents. */ + float line_len; + vec2 line = safe_normalize_len(ss2 - ss1, line_len); + vec2 line_adj = safe_normalize((use_curr) ? (ss1 - ss_adj) : (ss_adj - ss2)); + + float thickness = abs((use_curr) ? thickness1 : thickness2); + thickness = stroke_thickness_modulate(thickness); + + finalUvs = vec2(x, y) * 0.5 + 0.5; + strokeHardeness = (use_curr) ? hard1.y : hard2.y; + + if (is_dot) { +# ifdef GP_MATERIAL_BUFFER_LEN + int alignement = MATERIAL(m).flag & GP_STROKE_ALIGNMENT; +# endif + + vec2 x_axis; +# ifdef GP_MATERIAL_BUFFER_LEN + if (alignement == GP_STROKE_ALIGNMENT_STROKE) { + x_axis = (ma2.x == -1.0) ? line_adj : line; + } + else if (alignement == GP_STROKE_ALIGNMENT_FIXED) { + /* Default for no-material drawing. */ + x_axis = vec2(1.0, 0.0); + } + else +# endif + { /* GP_STROKE_ALIGNMENT_OBJECT */ + vec4 ndc_x = point_world_to_ndc(wpos1 + model_mat[0].xyz); + vec2 ss_x = project_to_screenspace(ndc_x); + x_axis = safe_normalize(ss_x - ss1); + } + + /* Rotation: Encoded as Cos + Sin sign. */ + float rot_sin = sqrt(1.0 - uv1.w * uv1.w) * sign(uv1.w); + float rot_cos = abs(uv1.w); + x_axis = mat2(rot_cos, -rot_sin, rot_sin, rot_cos) * x_axis; + + vec2 y_axis = rotate_90deg(x_axis); + + strokeAspect.x = aspect1; + + if (strokeAspect.x > 1.0) { + strokeAspect.y = strokeAspect.x; + strokeAspect.x = 1.0; + } + else { + strokeAspect.x = 1.0 / strokeAspect.x; + strokeAspect.y = 1.0; + } + + x /= strokeAspect.x; + y /= strokeAspect.y; + + gl_Position.xy += (x * x_axis + y * y_axis) * sizeViewportInv.xy * thickness; + + strokePt1 = ss1; + strokePt2 = ss1 + x_axis * 0.5; + strokeThickness = (is_squares) ? 1e18 : (thickness / gl_Position.w); + } + else { + bool is_stroke_start = (ma.x == -1.0 && x == -1.0); + bool is_stroke_end = (ma3.x == -1.0 && x == 1.0); + + /* Mitter tangent vector. */ + vec2 miter_tan = safe_normalize(line_adj + line); + float miter_dot = dot(miter_tan, line_adj); + /* Break corners after a certain angle to avoid really thick corners. */ + const float miter_limit = 0.5; /* cos(60°) */ + bool miter_break = (miter_dot < miter_limit) || is_stroke_start || is_stroke_end; + miter_tan = (miter_break) ? line : (miter_tan / miter_dot); + + vec2 miter = rotate_90deg(miter_tan); + + strokePt1.xy = ss1; + strokePt2.xy = ss2; + strokeThickness = thickness / gl_Position.w; + strokeAspect = vec2(1.0); + + vec2 screen_ofs = miter * y; + + /* Reminder: we packed the cap flag into the sign of stength and thickness sign. */ + if ((is_stroke_start && strength1 > 0.0) || (is_stroke_end && thickness1 > 0.0) || + miter_break) { + screen_ofs += line * x; + } + + gl_Position.xy += screen_ofs * sizeViewportInv.xy * thickness; + + finalUvs.x = (use_curr) ? uv1.z : uv2.z; +# ifdef GP_MATERIAL_BUFFER_LEN + finalUvs.x *= MATERIAL(m).stroke_u_scale; +# endif + } + +# ifdef GP_MATERIAL_BUFFER_LEN + vec4 vert_col = (use_curr) ? col1 : col2; + float vert_strength = abs((use_curr) ? strength1 : strength2); + vec4 stroke_col = MATERIAL(m).stroke_color; + float mix_tex = MATERIAL(m).stroke_texture_mix; + + color_output(stroke_col, vert_col, vert_strength, mix_tex); + + matFlag = MATERIAL(m).flag & ~GP_FILL_FLAGS; +# endif + + if (strokeOrder3d) { + /* Use the fragment depth (see fragment shader). */ + depth = -1.0; + } +# ifdef GP_MATERIAL_BUFFER_LEN + else if (GP_FLAG_TEST(MATERIAL(m).flag, GP_STROKE_OVERLAP)) { + /* Use the index of the point as depth. + * This means the stroke can overlap itself. */ + depth = (point_id1 + strokeIndexOffset + 1.0) * 0.0000002; + } +# endif + else { + /* Use the index of first point of the stroke as depth. + * We render using a greater depth test this means the stroke + * cannot overlap itself. + * We offset by one so that the fill can be overlapped by its stroke. + * The offset is ok since we pad the strokes data because of adjacency infos. */ + depth = (stroke_id1 + strokeIndexOffset + 1.0) * 0.0000002; + } +} + +void fill_vertex() +{ + mat4 model_mat = model_matrix_get(); + + vec3 wpos = transform_point(model_mat, pos1.xyz); + gl_Position = point_world_to_ndc(wpos); + finalPos = wpos; + +# ifdef GP_MATERIAL_BUFFER_LEN + int m = int(ma1.x); + + vec4 fill_col = MATERIAL(m).fill_color; + float mix_tex = MATERIAL(m).fill_texture_mix; + + /* Special case: We don't modulate alpha in gradient mode. */ + if (GP_FLAG_TEST(MATERIAL(m).flag, GP_FILL_GRADIENT_USE)) { + fill_col.a = 1.0; + } + + /* Decode fill opacity. */ + vec4 fcol_decode = vec4(fcol1.rgb, floor(fcol1.a / 10.0)); + float fill_opacity = fcol1.a - (fcol_decode.a * 10); + fcol_decode.a /= 10000.0f; + + /* Apply opacity. */ + fill_col.a *= fill_opacity; + /* If factor is > 1 force opacity. */ + if (fill_opacity > 1.0) { + fill_col.a += fill_opacity - 1.0f; + } + + fill_col.a = clamp(fill_col.a, 0.0, 1.0); + + color_output(fill_col, fcol_decode, 1.0, mix_tex); + + matFlag = MATERIAL(m).flag & GP_FILL_FLAGS; + matFlag |= m << GP_MATID_SHIFT; + + vec2 loc = MATERIAL(m).fill_uv_offset.xy; + mat2x2 rot_scale = mat2x2(MATERIAL(m).fill_uv_rot_scale.xy, MATERIAL(m).fill_uv_rot_scale.zw); + finalUvs = rot_scale * uv1.xy + loc; +# endif + + strokeThickness = 1e18; + strokeAspect = vec2(1.0); + strokePt1 = strokePt2 = vec2(0.0); + + if (strokeOrder3d) { + /* Use the fragment depth (see fragment shader). */ + depth = -1.0; + /* We still offset the fills a little to avoid overlaps */ + gl_Position.z += 0.000002; + } + else { + /* Use the index of first point of the stroke as depth. */ + depth = (stroke_id1 + strokeIndexOffset) * 0.0000002; + } +} + +void gpencil_vertex() +{ + /* Trick to detect if a drawcall is stroke or fill. + * This does mean that we need to draw an empty stroke segment before starting + * to draw the real stroke segments. */ + bool is_fill = (gl_InstanceID == 0); + + if (!is_fill) { + stroke_vertex(); + } + else { + fill_vertex(); + } +} + +#endif diff --git a/source/blender/draw/engines/gpencil/shaders/gpencil_depth_merge_frag.glsl b/source/blender/draw/engines/gpencil/shaders/gpencil_depth_merge_frag.glsl new file mode 100644 index 00000000000..71597197bd8 --- /dev/null +++ b/source/blender/draw/engines/gpencil/shaders/gpencil_depth_merge_frag.glsl @@ -0,0 +1,17 @@ + +uniform sampler2D depthBuf; +uniform float strokeDepth2d; +uniform bool strokeOrder3d; + +noperspective in vec4 uvcoordsvar; + +void main() +{ + float depth = textureLod(depthBuf, uvcoordsvar.xy, 0).r; + if (strokeOrder3d) { + gl_FragDepth = depth; + } + else { + gl_FragDepth = (depth != 0.0) ? gl_FragCoord.z : 1.0; + } +} diff --git a/source/blender/draw/engines/gpencil/shaders/gpencil_depth_merge_vert.glsl b/source/blender/draw/engines/gpencil/shaders/gpencil_depth_merge_vert.glsl new file mode 100644 index 00000000000..1e5a900f486 --- /dev/null +++ b/source/blender/draw/engines/gpencil/shaders/gpencil_depth_merge_vert.glsl @@ -0,0 +1,14 @@ + +uniform vec4 gpModelMatrix[4]; + +noperspective out vec4 uvcoordsvar; + +void main() +{ + mat4 model_matrix = mat4(gpModelMatrix[0], gpModelMatrix[1], gpModelMatrix[2], gpModelMatrix[3]); + int v = gl_VertexID % 3; + float x = -1.0 + float((v & 1) << 2); + float y = -1.0 + float((v & 2) << 1); + gl_Position = ViewProjectionMatrix * (model_matrix * vec4(x, y, 0.0, 1.0)); + uvcoordsvar = vec4((gl_Position.xy / gl_Position.w + 1.0) * 0.5, 0.0, 0.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 deleted file mode 100644 index 6a2a4f68dc9..00000000000 --- a/source/blender/draw/engines/gpencil/shaders/gpencil_edit_point_frag.glsl +++ /dev/null @@ -1,17 +0,0 @@ -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 deleted file mode 100644 index e0634a7d1a7..00000000000 --- a/source/blender/draw/engines/gpencil/shaders/gpencil_edit_point_geom.glsl +++ /dev/null @@ -1,53 +0,0 @@ -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; -} - -/* get zdepth value */ -float getZdepth(vec4 point) -{ - return min(-0.05, (point.z / point.w)); -} - -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, getZdepth(P0), 1.0); - EmitVertex(); - - mTexCoord = vec2(0, 0); - mColor = finalColor[0]; - gl_Position = vec4(vec2(sp0.x - size, sp0.y - size) / Viewport, getZdepth(P0), 1.0); - EmitVertex(); - - mTexCoord = vec2(1, 1); - mColor = finalColor[0]; - gl_Position = vec4(vec2(sp0.x + size, sp0.y + size) / Viewport, getZdepth(P0), 1.0); - EmitVertex(); - - mTexCoord = vec2(1, 0); - mColor = finalColor[0]; - gl_Position = vec4(vec2(sp0.x + size, sp0.y - size) / Viewport, getZdepth(P0), 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 deleted file mode 100644 index 57908f3251b..00000000000 --- a/source/blender/draw/engines/gpencil/shaders/gpencil_edit_point_vert.glsl +++ /dev/null @@ -1,19 +0,0 @@ - -uniform mat4 gpModelMatrix; - -in vec3 pos; -in vec4 color; -in float size; - -out vec4 finalColor; -out float finalThickness; - -void main() -{ - gl_Position = point_world_to_ndc((gpModelMatrix * vec4(pos, 1.0)).xyz); - finalColor = color; - finalThickness = size; - - /* Dirty fix waiting for new GPencil engine. */ - finalColor.rgb = pow(finalColor.rgb, vec3(2.2)); -} diff --git a/source/blender/draw/engines/gpencil/shaders/gpencil_fill_frag.glsl b/source/blender/draw/engines/gpencil/shaders/gpencil_fill_frag.glsl deleted file mode 100644 index 8285541e0b4..00000000000 --- a/source/blender/draw/engines/gpencil/shaders/gpencil_fill_frag.glsl +++ /dev/null @@ -1,234 +0,0 @@ -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 int drawmode; -uniform float layer_opacity; - -uniform sampler2D myTexture; -uniform bool myTexturePremultiplied; -uniform int texture_clamp; - -uniform int viewport_xray; -uniform int shading_type[2]; -uniform vec4 wire_color; - -uniform int fade_layer; -uniform float fade_layer_factor; -uniform bool fade_ob; -uniform vec3 fade_color; -uniform float fade_ob_factor; - -/* keep this list synchronized with list in gpencil_draw_utils.c */ -#define SOLID 0 -#define GRADIENT 1 -#define RADIAL 2 -#define CHECKER 3 -#define TEXTURE 4 -#define PATTERN 5 - -#define GP_XRAY_FRONT 0 -#define GP_XRAY_3DSPACE 1 - -#define GP_DRAWMODE_2D 0 -#define GP_DRAWMODE_3D 1 - -#define OB_WIRE 2 -#define OB_SOLID 3 - -#define V3D_SHADING_MATERIAL_COLOR 0 -#define V3D_SHADING_TEXTURE_COLOR 3 -#define V3D_SHADING_VERTEX_COLOR 5 - -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); - } - } - ocolor.a *= layer_opacity; -} - -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) ? - texture_read_as_srgb( - myTexture, myTexturePremultiplied, rot_tex * texture_scale) : - texture_read_as_srgb( - myTexture, myTexturePremultiplied, 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 checker_color; - - /* wireframe with x-ray discard */ - if ((viewport_xray == 1) && (shading_type[0] == OB_WIRE)) { - discard; - } - - /* 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); - } - /* Checkerboard */ - if (fill_type == CHECKER) { - 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)) { - checker_color = (texture_flip == 0) ? finalColor : color2; - } - else { - checker_color = (texture_flip == 0) ? color2 : finalColor; - } - /* mix with texture */ - fragColor = (texture_mix == 1) ? mix(checker_color, text_color, mix_factor) : checker_color; - fragColor.a *= layer_opacity; - } - /* texture */ - if (fill_type == TEXTURE) { - fragColor = (texture_mix == 1) ? mix(text_color, finalColor, mix_factor) : text_color; - fragColor.a *= layer_opacity; - } - /* pattern */ - if (fill_type == PATTERN) { - fragColor = finalColor; - fragColor.a = min(text_color.a, finalColor.a) * layer_opacity; - } - } - - /* set zdepth */ - if (xraymode == GP_XRAY_FRONT) { - gl_FragDepth = min(-0.05, (gl_FragCoord.z / gl_FragCoord.w)); - } - else if (xraymode == GP_XRAY_3DSPACE) { - /* if 3D mode, move slightly the fill to avoid z-fighting between stroke and fill on same - * stroke */ - if (drawmode == GP_DRAWMODE_3D) { - gl_FragDepth = gl_FragCoord.z * 1.0001; - } - else { - gl_FragDepth = gl_FragCoord.z; - } - } - else { - gl_FragDepth = 0.000001; - } - - /* if wireframe override colors */ - if (shading_type[0] == OB_WIRE) { - fragColor = wire_color; - } - - /* for solid override color */ - if (shading_type[0] == OB_SOLID) { - if ((shading_type[1] != V3D_SHADING_MATERIAL_COLOR) && - (shading_type[1] != V3D_SHADING_TEXTURE_COLOR) && - (shading_type[1] != V3D_SHADING_VERTEX_COLOR)) { - fragColor = wire_color; - } - if (viewport_xray == 1) { - fragColor.a *= 0.5; - } - } - /* Apply paper opacity */ - if (fade_layer == 1) { - /* Layer is below, mix with background. */ - fragColor.rgb = mix(fade_color.rgb, fragColor.rgb, fade_layer_factor); - } - else if (fade_layer == 2) { - /* Layer is above, change opacity. */ - fragColor.a *= fade_layer_factor; - } - else if (fade_ob == true) { - fragColor.rgb = mix(fade_color.rgb, fragColor.rgb, fade_ob_factor); - } -} diff --git a/source/blender/draw/engines/gpencil/shaders/gpencil_fill_vert.glsl b/source/blender/draw/engines/gpencil/shaders/gpencil_fill_vert.glsl deleted file mode 100644 index 263dc570423..00000000000 --- a/source/blender/draw/engines/gpencil/shaders/gpencil_fill_vert.glsl +++ /dev/null @@ -1,16 +0,0 @@ - -uniform mat4 gpModelMatrix; - -in vec3 pos; -in vec4 color; -in vec2 texCoord; - -out vec4 finalColor; -out vec2 texCoord_interp; - -void main(void) -{ - gl_Position = point_world_to_ndc((gpModelMatrix * vec4(pos, 1.0)).xyz); - finalColor = color; - texCoord_interp = texCoord; -} diff --git a/source/blender/draw/engines/gpencil/shaders/gpencil_frag.glsl b/source/blender/draw/engines/gpencil/shaders/gpencil_frag.glsl new file mode 100644 index 00000000000..8c2032f834a --- /dev/null +++ b/source/blender/draw/engines/gpencil/shaders/gpencil_frag.glsl @@ -0,0 +1,126 @@ + +uniform sampler2D gpFillTexture; +uniform sampler2D gpStrokeTexture; +uniform sampler2D gpSceneDepthTexture; +uniform sampler2D gpMaskTexture; +uniform vec3 gpNormal; + +layout(location = 0) out vec4 fragColor; +layout(location = 1) out vec4 revealColor; + +float length_squared(vec2 v) +{ + return dot(v, v); +} +float length_squared(vec3 v) +{ + return dot(v, v); +} + +vec3 gpencil_lighting(void) +{ + vec3 light_accum = vec3(0.0); + for (int i = 0; i < GPENCIL_LIGHT_BUFFER_LEN; i++) { + if (lights[i].color_type.x == -1.0) { + break; + } + vec3 L = lights[i].position.xyz - finalPos; + float vis = 1.0; + /* Spot Attenuation. */ + if (lights[i].color_type.w == GP_LIGHT_TYPE_SPOT) { + mat3 rot_scale = mat3(lights[i].right.xyz, lights[i].up.xyz, lights[i].forward.xyz); + vec3 local_L = rot_scale * L; + local_L /= abs(local_L.z); + float ellipse = inversesqrt(length_squared(local_L)); + vis *= smoothstep(0.0, 1.0, (ellipse - lights[i].spot_size) / lights[i].spot_blend); + /* Also mask +Z cone. */ + vis *= step(0.0, local_L.z); + } + /* Inverse square decay. Skip for suns. */ + float L_len_sqr = length_squared(L); + if (lights[i].color_type.w < GP_LIGHT_TYPE_SUN) { + vis /= L_len_sqr; + } + else { + L = lights[i].forward.xyz; + L_len_sqr = 1.0; + } + /* Lambertian falloff */ + if (lights[i].color_type.w != GP_LIGHT_TYPE_AMBIENT) { + L /= sqrt(L_len_sqr); + vis *= clamp(dot(gpNormal, L), 0.0, 1.0); + } + light_accum += vis * lights[i].color_type.rgb; + } + /* Clamp to avoid NaNs. */ + return clamp(light_accum, 0.0, 1e10); +} + +void main() +{ + vec4 col; + if (GP_FLAG_TEST(matFlag, GP_STROKE_TEXTURE_USE)) { + bool premul = GP_FLAG_TEST(matFlag, GP_STROKE_TEXTURE_PREMUL); + col = texture_read_as_linearrgb(gpStrokeTexture, premul, finalUvs); + } + else if (GP_FLAG_TEST(matFlag, GP_FILL_TEXTURE_USE)) { + bool use_clip = GP_FLAG_TEST(matFlag, GP_FILL_TEXTURE_CLIP); + vec2 uvs = (use_clip) ? clamp(finalUvs, 0.0, 1.0) : finalUvs; + bool premul = GP_FLAG_TEST(matFlag, GP_FILL_TEXTURE_PREMUL); + col = texture_read_as_linearrgb(gpFillTexture, premul, uvs); + } + else if (GP_FLAG_TEST(matFlag, GP_FILL_GRADIENT_USE)) { + bool radial = GP_FLAG_TEST(matFlag, GP_FILL_GRADIENT_RADIAL); + float fac = clamp(radial ? length(finalUvs * 2.0 - 1.0) : finalUvs.x, 0.0, 1.0); + int matid = matFlag >> GP_MATID_SHIFT; + col = mix(MATERIAL(matid).fill_color, MATERIAL(matid).fill_mix_color, fac); + } + else /* SOLID */ { + col = vec4(1.0); + } + col.rgb *= col.a; + + /* Composite all other colors on top of texture color. + * Everything is premult by col.a to have the stencil effect. */ + fragColor = col * finalColorMul + col.a * finalColorAdd; + + fragColor.rgb *= gpencil_lighting(); + + fragColor *= stroke_round_cap_mask( + strokePt1, strokePt2, strokeAspect, strokeThickness, strokeHardeness); + + /* For compatibility with colored alpha buffer. + * Note that we are limited to mono-chromatic alpha blending here + * because of the blend equation and the limit of 1 color target + * when using custom color blending. */ + revealColor = vec4(0.0, 0.0, 0.0, fragColor.a); + + if (fragColor.a < 0.001) { + discard; + } + + /* Manual depth test */ + vec2 uvs = gl_FragCoord.xy / vec2(textureSize(gpSceneDepthTexture, 0).xy); + float scene_depth = texture(gpSceneDepthTexture, uvs).r; + if (gl_FragCoord.z > scene_depth) { + discard; + } + + /* FIXME(fclem) Grrr. This is bad for performance but it's the easiest way to not get + * depth written where the mask obliterate the layer. */ + float mask = texture(gpMaskTexture, uvs).r; + if (mask < 0.001) { + discard; + } + + /* We override the fragment depth using the fragment shader to ensure a constant value. + * This has a cost as the depth test cannot happen early. + * We could do this in the vertex shader but then perspective interpolation of uvs and + * fragment clipping gets really complicated. */ + if (depth >= 0.0) { + gl_FragDepth = depth; + } + else { + gl_FragDepth = gl_FragCoord.z; + } +} diff --git a/source/blender/draw/engines/gpencil/shaders/gpencil_layer_blend_frag.glsl b/source/blender/draw/engines/gpencil/shaders/gpencil_layer_blend_frag.glsl new file mode 100644 index 00000000000..6fbc7f47dac --- /dev/null +++ b/source/blender/draw/engines/gpencil/shaders/gpencil_layer_blend_frag.glsl @@ -0,0 +1,31 @@ + +uniform sampler2D colorBuf; +uniform sampler2D revealBuf; +uniform sampler2D maskBuf; +uniform int blendMode; +uniform float blendOpacity; + +in vec4 uvcoordsvar; + +/* Reminder: This is considered SRC color in blend equations. + * Same operation on all buffers. */ +layout(location = 0) out vec4 fragColor; +layout(location = 1) out vec4 fragRevealage; + +void main() +{ + vec4 color; + + /* Remember, this is associated alpha (aka. premult). */ + color.rgb = textureLod(colorBuf, uvcoordsvar.xy, 0).rgb; + /* Stroke only render mono-chromatic revealage. We convert to alpha. */ + color.a = 1.0 - textureLod(revealBuf, uvcoordsvar.xy, 0).r; + + float mask = textureLod(maskBuf, uvcoordsvar.xy, 0).r; + mask *= blendOpacity; + + fragColor = vec4(1.0, 0.0, 1.0, 1.0); + fragRevealage = vec4(1.0, 0.0, 1.0, 1.0); + + blend_mode_output(blendMode, color, mask, fragColor, fragRevealage); +} diff --git a/source/blender/draw/engines/gpencil/shaders/gpencil_mask_invert_frag.glsl b/source/blender/draw/engines/gpencil/shaders/gpencil_mask_invert_frag.glsl new file mode 100644 index 00000000000..b21b4147087 --- /dev/null +++ b/source/blender/draw/engines/gpencil/shaders/gpencil_mask_invert_frag.glsl @@ -0,0 +1,11 @@ + +in vec4 uvcoordsvar; + +layout(location = 0) out vec4 fragColor; +layout(location = 1) out vec4 fragRevealage; + +void main() +{ + /* Blend mode does the inversion. */ + fragRevealage = fragColor = vec4(1.0); +} diff --git a/source/blender/draw/engines/gpencil/shaders/gpencil_paper_frag.glsl b/source/blender/draw/engines/gpencil/shaders/gpencil_paper_frag.glsl deleted file mode 100644 index 1d1cd6349dc..00000000000 --- a/source/blender/draw/engines/gpencil/shaders/gpencil_paper_frag.glsl +++ /dev/null @@ -1,9 +0,0 @@ -uniform vec3 color; -uniform float opacity; - -out vec4 FragColor; - -void main() -{ - FragColor = vec4(color, 1.0 - 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 deleted file mode 100644 index d79b8fb4d8a..00000000000 --- a/source/blender/draw/engines/gpencil/shaders/gpencil_point_frag.glsl +++ /dev/null @@ -1,126 +0,0 @@ -uniform int color_type; -uniform int mode; -uniform sampler2D myTexture; -uniform bool myTexturePremultiplied; - -uniform float gradient_f; -uniform vec2 gradient_s; - -uniform vec4 colormix; -uniform float mix_stroke_factor; -uniform int shading_type[2]; - -in vec4 mColor; -in vec2 mTexCoord; -out vec4 fragColor; - -uniform int fade_layer; -uniform float fade_layer_factor; -uniform bool fade_ob; -uniform vec3 fade_color; -uniform float fade_ob_factor; - -#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 - -#define OB_SOLID 3 -#define V3D_SHADING_TEXTURE_COLOR 3 - -bool no_texture = (shading_type[0] == OB_SOLID) && (shading_type[1] != V3D_SHADING_TEXTURE_COLOR); - -/* Function to check the point inside ellipse */ -float check_ellipse_point(vec2 pt, vec2 radius) -{ - float p = (pow(pt.x, 2) / pow(radius.x, 2)) + (pow(pt.y, 2) / pow(radius.y, 2)); - - return p; -} - -/* Function to check the point inside box */ -vec2 check_box_point(vec2 pt, vec2 radius) -{ - vec2 rtn; - rtn.x = abs(pt.x) / radius.x; - rtn.y = abs(pt.y) / radius.y; - - return rtn; -} - -void main() -{ - vec2 centered = mTexCoord - vec2(0.5); - float ellip = check_ellipse_point(centered, vec2(gradient_s / 2.0)); - vec2 box; - - if (mode != GPENCIL_MODE_BOX) { - if (ellip > 1.0) { - discard; - } - } - else { - box = check_box_point(centered, vec2(gradient_s / 2.0)); - if ((box.x > 1.0) || (box.y > 1.0)) { - discard; - } - } - - /* Solid */ - if ((color_type == GPENCIL_COLOR_SOLID) || (no_texture)) { - fragColor = mColor; - } - /* texture */ - if ((color_type == GPENCIL_COLOR_TEXTURE) && (!no_texture)) { - vec4 text_color = texture_read_as_srgb(myTexture, myTexturePremultiplied, mTexCoord); - if (mix_stroke_factor > 0.0) { - fragColor.rgb = mix(text_color.rgb, colormix.rgb, mix_stroke_factor); - fragColor.a = text_color.a; - } - else { - fragColor = text_color; - } - - /* 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) && (!no_texture)) { - vec4 text_color = texture_read_as_srgb(myTexture, myTexturePremultiplied, 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); - } - - if (gradient_f < 1.0) { - float dist = length(centered) * 2.0; - float decay = dist * (1.0 - gradient_f) * fragColor.a; - fragColor.a = clamp(fragColor.a - decay, 0.0, 1.0); - if (mode == GPENCIL_MODE_DOTS) { - fragColor.a = fragColor.a * (1.0 - ellip); - } - } - - if (fragColor.a < 0.0035) { - discard; - } - - /* Apply paper opacity */ - if (fade_layer == 1) { - /* Layer is below, mix with background. */ - fragColor.rgb = mix(fade_color.rgb, fragColor.rgb, fade_layer_factor); - } - else if (fade_layer == 2) { - /* Layer is above, change opacity. */ - fragColor.a *= fade_layer_factor; - } - else if (fade_ob == true) { - fragColor.rgb = mix(fade_color.rgb, fragColor.rgb, fade_ob_factor); - } -} diff --git a/source/blender/draw/engines/gpencil/shaders/gpencil_point_geom.glsl b/source/blender/draw/engines/gpencil/shaders/gpencil_point_geom.glsl deleted file mode 100644 index a2f4c1f9b15..00000000000 --- a/source/blender/draw/engines/gpencil/shaders/gpencil_point_geom.glsl +++ /dev/null @@ -1,142 +0,0 @@ -uniform vec2 Viewport; -uniform int xraymode; -uniform int alignment_mode; - -layout(points) in; -layout(triangle_strip, max_vertices = 4) out; - -in vec4 finalColor[1]; -in float finalThickness[1]; -in vec2 finaluvdata[1]; -in vec4 finalprev_pos[1]; - -out vec4 mColor; -out vec2 mTexCoord; - -#define GP_XRAY_FRONT 0 -#define GP_XRAY_3DSPACE 1 - -#define M_PI 3.14159265358979323846 /* pi */ -#define M_2PI 6.28318530717958647692 /* 2*pi */ -#define FALSE 0 - -/* keep this definition equals to GP_STYLE_FOLLOW_FIXED value */ -#define FIXED 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 min(-0.05, (point.z / point.w)); - } - if (xraymode == GP_XRAY_3DSPACE) { - return (point.z / point.w); - } - - /* in front by default */ - return 0.000001; -} - -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); -} - -vec2 rotatePoint(vec2 center, vec2 point, float angle) -{ - /* translate center of rotation to the center */ - vec2 new_point = point - center; - vec2 rot_point; - rot_point.x = new_point.x * cos(angle) - new_point.y * sin(angle); - rot_point.y = new_point.y * cos(angle) + new_point.x * sin(angle); - return rot_point + center; -} - -/* Calculate angle of the stroke using previous point as reference. - * The angle is calculated using the x axis (1, 0) as 0 degrees */ -float getAngle(vec2 pt0, vec2 pt1) -{ - /* do not rotate one point only (no reference to rotate) */ - if (pt0 == pt1) { - return 0.0; - } - - if (alignment_mode == FIXED) { - return 0.0; - } - - /* default horizontal line (x-axis) in screen space */ - vec2 v0 = vec2(1.0, 0.0); - - /* vector of direction */ - vec2 vn = vec2(normalize(pt1 - pt0)); - - /* angle signed (function ported from angle_signed_v2v2) */ - float perp_dot = (v0[1] * vn[0]) - (v0[0] * vn[1]); - float angle = atan(perp_dot, dot(v0, vn)); - - /* get full circle rotation */ - if (angle > 0.0) { - angle = M_PI + (M_PI - angle); - } - else { - angle *= -1.0; - } - - return angle; -} - -void main(void) -{ - /* receive points */ - vec4 P0 = gl_in[0].gl_Position; - vec2 sp0 = toScreenSpace(P0); - - vec4 P1 = finalprev_pos[0]; - vec2 sp1 = toScreenSpace(P1); - vec2 point; - - float size = finalThickness[0]; - vec2 center = vec2(sp0.x, sp0.y); - - /* get angle of stroke to rotate texture */ - float angle = getAngle(sp0, sp1); - - /* generate the triangle strip */ - mTexCoord = rotateUV(vec2(0, 1), finaluvdata[0].y); - mColor = finalColor[0]; - point = rotatePoint(center, vec2(sp0.x - size, sp0.y + size), angle); - gl_Position = vec4(point / Viewport, getZdepth(P0), 1.0); - EmitVertex(); - - mTexCoord = rotateUV(vec2(0, 0), finaluvdata[0].y); - mColor = finalColor[0]; - point = rotatePoint(center, vec2(sp0.x - size, sp0.y - size), angle); - gl_Position = vec4(point / Viewport, getZdepth(P0), 1.0); - EmitVertex(); - - mTexCoord = rotateUV(vec2(1, 1), finaluvdata[0].y); - mColor = finalColor[0]; - point = rotatePoint(center, vec2(sp0.x + size, sp0.y + size), angle); - gl_Position = vec4(point / Viewport, getZdepth(P0), 1.0); - EmitVertex(); - - mTexCoord = rotateUV(vec2(1, 0), finaluvdata[0].y); - mColor = finalColor[0]; - point = rotatePoint(center, vec2(sp0.x + size, sp0.y - size), angle); - gl_Position = vec4(point / 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 deleted file mode 100644 index 33d7d714231..00000000000 --- a/source/blender/draw/engines/gpencil/shaders/gpencil_point_vert.glsl +++ /dev/null @@ -1,66 +0,0 @@ - -uniform float pixsize; /* rv3d->pixsize */ -uniform int keep_size; -uniform float objscale; -uniform float pixfactor; -uniform int viewport_xray; -uniform int shading_type[2]; -uniform vec4 wire_color; -uniform mat4 gpModelMatrix; - -in vec3 pos; -in vec4 color; -in float thickness; -in vec2 uvdata; -in vec3 prev_pos; - -out vec4 finalColor; -out float finalThickness; -out vec2 finaluvdata; -out vec4 finalprev_pos; - -#define TRUE 1 - -#define OB_WIRE 2 -#define OB_SOLID 3 - -#define V3D_SHADING_MATERIAL_COLOR 0 -#define V3D_SHADING_TEXTURE_COLOR 3 -#define V3D_SHADING_VERTEX_COLOR 5 - -float defaultpixsize = pixsize * (1000.0 / pixfactor); - -void main() -{ - gl_Position = point_world_to_ndc((gpModelMatrix * vec4(pos, 1.0)).xyz); - finalprev_pos = point_world_to_ndc((gpModelMatrix * vec4(prev_pos, 1.0)).xyz); - 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, 0.5); /* set a minimum size */ - } - - /* for wireframe override size and color */ - if (shading_type[0] == OB_WIRE) { - finalThickness = 2.0; - finalColor = wire_color; - } - /* for solid override color */ - if (shading_type[0] == OB_SOLID) { - if ((shading_type[1] != V3D_SHADING_MATERIAL_COLOR) && - (shading_type[1] != V3D_SHADING_TEXTURE_COLOR) && - (shading_type[1] != V3D_SHADING_VERTEX_COLOR)) { - finalColor = wire_color; - } - if (viewport_xray == 1) { - finalColor.a *= 0.5; - } - } - - 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 deleted file mode 100644 index 2f4429a858f..00000000000 --- a/source/blender/draw/engines/gpencil/shaders/gpencil_simple_mix_frag.glsl +++ /dev/null @@ -1,15 +0,0 @@ -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 deleted file mode 100644 index 0f1665b73c2..00000000000 --- a/source/blender/draw/engines/gpencil/shaders/gpencil_stroke_frag.glsl +++ /dev/null @@ -1,110 +0,0 @@ -uniform int color_type; -uniform sampler2D myTexture; -uniform bool myTexturePremultiplied; - -uniform float gradient_f; - -uniform vec4 colormix; -uniform float mix_stroke_factor; -uniform int shading_type[2]; - -uniform int fade_layer; -uniform float fade_layer_factor; -uniform bool fade_ob; -uniform vec3 fade_color; -uniform float fade_ob_factor; - -in vec4 mColor; -in vec2 mTexCoord; -in vec2 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 - -#define ENDCAP 1.0 - -#define OB_SOLID 3 -#define V3D_SHADING_TEXTURE_COLOR 3 - -bool no_texture = (shading_type[0] == OB_SOLID) && (shading_type[1] != V3D_SHADING_TEXTURE_COLOR); - -void main() -{ - - vec4 tColor = vec4(mColor); - /* if uvfac[1] == 1, then encap */ - if (uvfac[1] == ENDCAP) { - vec2 center = vec2(uvfac[0], 0.5); - float dist = length(mTexCoord - center); - if (dist > 0.50) { - discard; - } - } - - if ((color_type == GPENCIL_COLOR_SOLID) || (no_texture)) { - fragColor = tColor; - } - - /* texture for endcaps */ - vec4 text_color; - if (uvfac[1] == ENDCAP) { - text_color = texture_read_as_srgb( - myTexture, myTexturePremultiplied, vec2(mTexCoord.x, mTexCoord.y)); - } - else { - text_color = texture_read_as_srgb(myTexture, myTexturePremultiplied, mTexCoord); - } - - /* texture */ - if ((color_type == GPENCIL_COLOR_TEXTURE) && (!no_texture)) { - if (mix_stroke_factor > 0.0) { - fragColor.rgb = mix(text_color.rgb, colormix.rgb, mix_stroke_factor); - fragColor.a = text_color.a; - } - else { - fragColor = text_color; - } - - /* mult both alpha factor to use strength factor */ - fragColor.a = min(fragColor.a * tColor.a, fragColor.a); - } - /* pattern */ - if ((color_type == GPENCIL_COLOR_PATTERN) && (!no_texture)) { - fragColor = tColor; - /* mult both alpha factor to use strength factor with color alpha limit */ - fragColor.a = min(text_color.a * tColor.a, tColor.a); - } - - /* gradient */ - /* keep this disabled while the line glitch bug exists - if (gradient_f < 1.0) { - float d = abs(mTexCoord.y - 0.5) * (1.1 - gradient_f); - float alpha = 1.0 - clamp((fragColor.a - (d * 2.0)), 0.03, 1.0); - fragColor.a = smoothstep(fragColor.a, 0.0, alpha); - - } - */ - - if (fragColor.a < 0.0035) { - discard; - } - - /* Apply paper opacity */ - if (fade_layer == 1) { - /* Layer is below, mix with background. */ - fragColor.rgb = mix(fade_color.rgb, fragColor.rgb, fade_layer_factor); - } - else if (fade_layer == 2) { - /* Layer is above, change opacity. */ - fragColor.a *= fade_layer_factor; - } - else if (fade_ob == true) { - fragColor.rgb = mix(fade_color.rgb, fragColor.rgb, fade_ob_factor); - } -} diff --git a/source/blender/draw/engines/gpencil/shaders/gpencil_stroke_geom.glsl b/source/blender/draw/engines/gpencil/shaders/gpencil_stroke_geom.glsl deleted file mode 100644 index 3300514dd13..00000000000 --- a/source/blender/draw/engines/gpencil/shaders/gpencil_stroke_geom.glsl +++ /dev/null @@ -1,264 +0,0 @@ -uniform vec2 Viewport; -uniform int xraymode; -uniform int color_type; -uniform int caps_mode[2]; - -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 vec2 uvfac; - -#define GP_XRAY_FRONT 0 -#define GP_XRAY_3DSPACE 1 - -/* 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 - -#define GPENCIL_FLATCAP 1 - -/* 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 min(-0.05, (point.z / point.w)); - } - if (xraymode == GP_XRAY_3DSPACE) { - return (point.z / point.w); - } - - /* in front by default */ - return 0.000001; -} - -/* check equality but with a small tolerance */ -bool is_equal(vec4 p1, vec4 p2) -{ - float limit = 0.0001; - float x = abs(p1.x - p2.x); - float y = abs(p1.y - p2.y); - float z = abs(p1.z - p2.z); - - if ((x < limit) && (y < limit) && (z < limit)) { - return true; - } - - return false; -} - -void main(void) -{ - float MiterLimit = 0.75; - uvfac = vec2(0.0, 0.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; - } - - /* culling behind camera */ - if (P1.w < 0 || P2.w < 0) { - 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 */ - if ((caps_mode[0] != GPENCIL_FLATCAP) && is_equal(P0, P2)) { - vec4 cap_color = finalColor[1]; - - mTexCoord = vec2(2.0, 0.5); - mColor = cap_color; - vec2 svn1 = normalize(sp1 - sp2) * length_a * 4.0; - uvfac = vec2(0.0, 1.0); - gl_Position = vec4((sp1 + svn1) / Viewport, getZdepth(P1), 1.0); - EmitVertex(); - - mTexCoord = vec2(0.0, -0.5); - mColor = cap_color; - uvfac = vec2(0.0, 1.0); - gl_Position = vec4((sp1 - (length_a * 2.0) * miter_a) / Viewport, getZdepth(P1), 1.0); - EmitVertex(); - - mTexCoord = vec2(0.0, 1.5); - mColor = cap_color; - uvfac = vec2(0.0, 1.0); - gl_Position = vec4((sp1 + (length_a * 2.0) * miter_a) / Viewport, getZdepth(P1), 1.0); - EmitVertex(); - } - - float y_a = 0.0; - float y_b = 1.0; - - /* invert uv (vertical) */ - if (finaluvdata[2].x > 1.0) { - if ((finaluvdata[1].y != 0.0) && (finaluvdata[2].y != 0.0)) { - float d = ceil(finaluvdata[2].x) - 1.0; - if (floor(d / 2.0) == (d / 2.0)) { - y_a = 1.0; - y_b = 0.0; - } - } - } - /* generate the triangle strip */ - uvfac = vec2(0.0, 0.0); - mTexCoord = (color_type == GPENCIL_COLOR_SOLID) ? vec2(0, 0) : vec2(finaluvdata[1].x, y_a); - 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, y_b); - 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, y_a); - 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, y_b); - mColor = finalColor[2]; - gl_Position = vec4((sp2 - length_b * miter_b) / Viewport, getZdepth(P2), 1.0); - EmitVertex(); - - /* generate the end endcap */ - if ((caps_mode[1] != GPENCIL_FLATCAP) && is_equal(P1, P3) && (finaluvdata[2].x > 0)) { - vec4 cap_color = finalColor[2]; - - mTexCoord = vec2(finaluvdata[2].x, 1.5); - mColor = cap_color; - uvfac = vec2(finaluvdata[2].x, 1.0); - gl_Position = vec4((sp2 + (length_b * 2.0) * miter_b) / Viewport, getZdepth(P2), 1.0); - EmitVertex(); - - mTexCoord = vec2(finaluvdata[2].x, -0.5); - mColor = cap_color; - uvfac = vec2(finaluvdata[2].x, 1.0); - gl_Position = vec4((sp2 - (length_b * 2.0) * miter_b) / Viewport, getZdepth(P2), 1.0); - EmitVertex(); - - mTexCoord = vec2(finaluvdata[2].x + 2, 0.5); - mColor = cap_color; - uvfac = vec2(finaluvdata[2].x, 1.0); - 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 deleted file mode 100644 index 8df08f0bf68..00000000000 --- a/source/blender/draw/engines/gpencil/shaders/gpencil_stroke_vert.glsl +++ /dev/null @@ -1,63 +0,0 @@ - -uniform float pixsize; /* rv3d->pixsize */ -uniform int keep_size; -uniform float objscale; -uniform float pixfactor; -uniform int viewport_xray; -uniform int shading_type[2]; -uniform vec4 wire_color; -uniform mat4 gpModelMatrix; - -in vec3 pos; -in vec4 color; -in float thickness; -in vec2 uvdata; - -out vec4 finalColor; -out float finalThickness; -out vec2 finaluvdata; - -#define TRUE 1 - -#define OB_WIRE 2 -#define OB_SOLID 3 - -#define V3D_SHADING_MATERIAL_COLOR 0 -#define V3D_SHADING_TEXTURE_COLOR 3 -#define V3D_SHADING_VERTEX_COLOR 5 - -float defaultpixsize = pixsize * (1000.0 / pixfactor); - -void main(void) -{ - gl_Position = point_world_to_ndc((gpModelMatrix * vec4(pos, 1.0)).xyz); - 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); - } - - /* for wireframe override size and color */ - if (shading_type[0] == OB_WIRE) { - finalThickness = 1.0; - finalColor = wire_color; - } - /* for solid override color */ - if (shading_type[0] == OB_SOLID) { - if ((shading_type[1] != V3D_SHADING_MATERIAL_COLOR) && - (shading_type[1] != V3D_SHADING_TEXTURE_COLOR) && - (shading_type[1] != V3D_SHADING_VERTEX_COLOR)) { - finalColor = wire_color; - } - if (viewport_xray == 1) { - finalColor.a *= 0.5; - } - } - - finaluvdata = uvdata; -} diff --git a/source/blender/draw/engines/gpencil/shaders/gpencil_vert.glsl b/source/blender/draw/engines/gpencil/shaders/gpencil_vert.glsl new file mode 100644 index 00000000000..c6cfee5ef2d --- /dev/null +++ b/source/blender/draw/engines/gpencil/shaders/gpencil_vert.glsl @@ -0,0 +1,5 @@ + +void main() +{ + gpencil_vertex(); +}
\ No newline at end of file diff --git a/source/blender/draw/engines/gpencil/shaders/gpencil_vfx_frag.glsl b/source/blender/draw/engines/gpencil/shaders/gpencil_vfx_frag.glsl new file mode 100644 index 00000000000..503248558ad --- /dev/null +++ b/source/blender/draw/engines/gpencil/shaders/gpencil_vfx_frag.glsl @@ -0,0 +1,354 @@ + +uniform sampler2D colorBuf; +uniform sampler2D revealBuf; + +in vec4 uvcoordsvar; + +/* Reminder: This is considered SRC color in blend equations. + * Same operation on all buffers. */ +layout(location = 0) out vec4 fragColor; +layout(location = 1) out vec4 fragRevealage; + +float gaussian_weight(float x) +{ + return exp(-x * x / (2.0 * 0.35 * 0.35)); +} + +#if defined(COMPOSITE) + +uniform bool isFirstPass; + +void main() +{ + if (isFirstPass) { + /* Blend mode is multiply. */ + fragColor.rgb = fragRevealage.rgb = texture(revealBuf, uvcoordsvar.xy).rgb; + fragColor.a = fragRevealage.a = 1.0; + } + else { + /* Blend mode is additive. */ + fragRevealage = vec4(0.0); + fragColor.rgb = texture(colorBuf, uvcoordsvar.xy).rgb; + fragColor.a = 0.0; + } +} + +#elif defined(COLORIZE) + +uniform vec3 lowColor; +uniform vec3 highColor; +uniform float factor; +uniform int mode; + +const mat3 sepia_mat = mat3( + vec3(0.393, 0.349, 0.272), vec3(0.769, 0.686, 0.534), vec3(0.189, 0.168, 0.131)); + +# define MODE_GRAYSCALE 0 +# define MODE_SEPIA 1 +# define MODE_DUOTONE 2 +# define MODE_CUSTOM 3 +# define MODE_TRANSPARENT 4 + +void main() +{ + fragColor = texture(colorBuf, uvcoordsvar.xy); + fragRevealage = texture(revealBuf, uvcoordsvar.xy); + + float luma = dot(fragColor.rgb, vec3(0.2126, 0.7152, 0.723)); + + /* No blending. */ + switch (mode) { + case MODE_GRAYSCALE: + fragColor.rgb = mix(fragColor.rgb, vec3(luma), factor); + break; + case MODE_SEPIA: + fragColor.rgb = mix(fragColor.rgb, sepia_mat * fragColor.rgb, factor); + break; + case MODE_DUOTONE: + fragColor.rgb = luma * ((luma <= factor) ? lowColor : highColor); + break; + case MODE_CUSTOM: + fragColor.rgb = mix(fragColor.rgb, luma * lowColor, factor); + break; + case MODE_TRANSPARENT: + default: + fragColor.rgb *= factor; + fragRevealage.rgb = mix(vec3(1.0), fragRevealage.rgb, factor); + break; + } +} + +#elif defined(BLUR) + +uniform vec2 offset; +uniform int sampCount; + +void main() +{ + vec2 pixel_size = 1.0 / vec2(textureSize(revealBuf, 0).xy); + vec2 ofs = offset * pixel_size; + + fragColor = vec4(0.0); + fragRevealage = vec4(0.0); + + /* No blending. */ + float weight_accum = 0.0; + for (int i = -sampCount; i <= sampCount; i++) { + float x = float(i) / float(sampCount); + float weight = gaussian_weight(x); + weight_accum += weight; + vec2 uv = uvcoordsvar.xy + ofs * x; + fragColor.rgb += texture(colorBuf, uv).rgb * weight; + fragRevealage.rgb += texture(revealBuf, uv).rgb * weight; + } + + fragColor /= weight_accum; + fragRevealage /= weight_accum; +} + +#elif defined(TRANSFORM) + +uniform vec2 axisFlip = vec2(1.0); +uniform vec2 waveDir = vec2(0.0); +uniform vec2 waveOffset = vec2(0.0); +uniform float wavePhase = 0.0; +uniform vec2 swirlCenter = vec2(0.0); +uniform float swirlAngle = 0.0; +uniform float swirlRadius = 0.0; + +void main() +{ + vec2 uv = (uvcoordsvar.xy - 0.5) * axisFlip + 0.5; + + /* Wave deform. */ + float wave_time = dot(uv, waveDir.xy); + uv += sin(wave_time + wavePhase) * waveOffset; + /* Swirl deform. */ + if (swirlRadius > 0.0) { + vec2 tex_size = vec2(textureSize(colorBuf, 0).xy); + vec2 pix_coord = uv * tex_size - swirlCenter; + float dist = length(pix_coord); + float percent = clamp((swirlRadius - dist) / swirlRadius, 0.0, 1.0); + float theta = percent * percent * swirlAngle; + float s = sin(theta); + float c = cos(theta); + mat2 rot = mat2(vec2(c, -s), vec2(s, c)); + uv = (rot * pix_coord + swirlCenter) / tex_size; + } + + fragColor = texture(colorBuf, uv); + fragRevealage = texture(revealBuf, uv); +} + +#elif defined(GLOW) + +uniform vec4 glowColor; +uniform vec2 offset; +uniform int sampCount; +uniform vec3 threshold; +uniform bool firstPass; +uniform bool glowUnder; +uniform int blendMode; + +void main() +{ + vec2 pixel_size = 1.0 / vec2(textureSize(revealBuf, 0).xy); + vec2 ofs = offset * pixel_size; + + fragColor = vec4(0.0); + fragRevealage = vec4(0.0); + + float weight_accum = 0.0; + for (int i = -sampCount; i <= sampCount; i++) { + float x = float(i) / float(sampCount); + float weight = gaussian_weight(x); + weight_accum += weight; + vec2 uv = uvcoordsvar.xy + ofs * x; + vec3 col = texture(colorBuf, uv).rgb; + vec3 rev = texture(revealBuf, uv).rgb; + if (threshold.x > -1.0) { + if (threshold.y > -1.0) { + if (all(lessThan(abs(col - threshold), vec3(0.05)))) { + weight = 0.0; + } + } + else { + if (dot(col, vec3(1.0 / 3.0)) < threshold.x) { + weight = 0.0; + } + } + } + fragColor.rgb += col * weight; + fragRevealage.rgb += (1.0 - rev) * weight; + } + + if (weight_accum > 0.0) { + fragColor *= glowColor.rgbb / weight_accum; + fragRevealage = fragRevealage / weight_accum; + } + fragRevealage = 1.0 - fragRevealage; + + if (glowUnder) { + if (firstPass) { + /* In first pass we copy the revealage buffer in the alpha channel. + * This let us do the alpha under in second pass. */ + vec3 original_revealage = texture(revealBuf, uvcoordsvar.xy).rgb; + fragRevealage.a = clamp(dot(original_revealage.rgb, vec3(0.333334)), 0.0, 1.0); + } + else { + /* Recover original revealage. */ + fragRevealage.a = texture(revealBuf, uvcoordsvar.xy).a; + } + } + + if (!firstPass) { + fragColor.a = clamp(1.0 - dot(fragRevealage.rgb, vec3(0.333334)), 0.0, 1.0); + fragRevealage.a *= glowColor.a; + blend_mode_output(blendMode, fragColor, fragRevealage.a, fragColor, fragRevealage); + } +} + +#elif defined(RIM) + +uniform vec2 blurDir; +uniform vec2 uvOffset; +uniform vec3 rimColor; +uniform vec3 maskColor; +uniform int sampCount; +uniform int blendMode; +uniform bool isFirstPass; + +void main() +{ + /* Blur revealage buffer. */ + fragRevealage = vec4(0.0); + float weight_accum = 0.0; + for (int i = -sampCount; i <= sampCount; i++) { + float x = float(i) / float(sampCount); + float weight = gaussian_weight(x); + weight_accum += weight; + vec2 uv = uvcoordsvar.xy + blurDir * x + uvOffset; + vec3 col = texture(revealBuf, uv).rgb; + if (any(not(equal(vec2(0.0), floor(uv))))) { + col = vec3(0.0); + } + fragRevealage.rgb += col * weight; + } + fragRevealage /= weight_accum; + + if (isFirstPass) { + /* In first pass we copy the reveal buffer. This let us do alpha masking in second pass. */ + fragColor = texture(revealBuf, uvcoordsvar.xy); + /* Also add the masked color to the reveal buffer. */ + vec3 col = texture(colorBuf, uvcoordsvar.xy).rgb; + if (all(lessThan(abs(col - maskColor), vec3(0.05)))) { + fragColor = vec4(1.0); + } + } + else { + /* Premult by foreground alpha (alpha mask). */ + float mask = 1.0 - clamp(dot(vec3(0.333334), texture(colorBuf, uvcoordsvar.xy).rgb), 0.0, 1.0); + + /* fragRevealage is blurred shadow. */ + float rim = clamp(dot(vec3(0.333334), fragRevealage.rgb), 0.0, 1.0); + + vec4 color = vec4(rimColor, 1.0); + + blend_mode_output(blendMode, color, rim * mask, fragColor, fragRevealage); + } +} + +#elif defined(SHADOW) + +uniform vec4 shadowColor; +uniform vec2 uvRotX; +uniform vec2 uvRotY; +uniform vec2 uvOffset; +uniform vec2 blurDir; +uniform vec2 waveDir; +uniform vec2 waveOffset; +uniform float wavePhase; +uniform int sampCount; +uniform bool isFirstPass; + +vec2 compute_uvs(float x) +{ + vec2 uv = uvcoordsvar.xy; + /* Tranform UV (loc, rot, scale) */ + uv = uv.x * uvRotX + uv.y * uvRotY + uvOffset; + uv += blurDir * x; + /* Wave deform. */ + float wave_time = dot(uv, waveDir.xy); + uv += sin(wave_time + wavePhase) * waveOffset; + return uv; +} + +void main() +{ + /* Blur revealage buffer. */ + fragRevealage = vec4(0.0); + float weight_accum = 0.0; + for (int i = -sampCount; i <= sampCount; i++) { + float x = float(i) / float(sampCount); + float weight = gaussian_weight(x); + weight_accum += weight; + vec2 uv = compute_uvs(x); + vec3 col = texture(revealBuf, uv).rgb; + if (any(not(equal(vec2(0.0), floor(uv))))) { + col = vec3(1.0); + } + fragRevealage.rgb += col * weight; + } + fragRevealage /= weight_accum; + + /* No blending in first pass, alpha over premult in second pass. */ + if (isFirstPass) { + /* In first pass we copy the reveal buffer. This let us do alpha under in second pass. */ + fragColor = texture(revealBuf, uvcoordsvar.xy); + } + else { + /* fragRevealage is blurred shadow. */ + float shadow_fac = 1.0 - clamp(dot(vec3(0.333334), fragRevealage.rgb), 0.0, 1.0); + /* Premult by foreground revealage (alpha under). */ + vec3 original_revealage = texture(colorBuf, uvcoordsvar.xy).rgb; + shadow_fac *= clamp(dot(vec3(0.333334), original_revealage), 0.0, 1.0); + /* Modulate by opacity */ + shadow_fac *= shadowColor.a; + /* Apply shadow color. */ + fragColor.rgb = mix(vec3(0.0), shadowColor.rgb, shadow_fac); + /* Alpha over (mask behind the shadow). */ + fragColor.a = shadow_fac; + + fragRevealage.rgb = original_revealage * (1.0 - shadow_fac); + /* Replace the whole revealage buffer. */ + fragRevealage.a = 1.0; + } +} + +#elif defined(PIXELIZE) + +uniform vec2 targetPixelSize; +uniform vec2 targetPixelOffset; +uniform vec2 accumOffset; +uniform int sampCount; + +void main() +{ + vec2 pixel = floor((uvcoordsvar.xy - targetPixelOffset) / targetPixelSize); + vec2 uv = (pixel + 0.5) * targetPixelSize + targetPixelOffset; + + fragColor = vec4(0.0); + fragRevealage = vec4(0.0); + + for (int i = -sampCount; i <= sampCount; i++) { + float x = float(i) / float(sampCount + 1); + vec2 uv_ofs = uv + accumOffset * 0.5 * x; + fragColor += texture(colorBuf, uv_ofs); + fragRevealage += texture(revealBuf, uv_ofs); + } + + fragColor /= float(sampCount) * 2.0 + 1.0; + fragRevealage /= float(sampCount) * 2.0 + 1.0; +} + +#endif 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 deleted file mode 100644 index 926b11e4083..00000000000 --- a/source/blender/draw/engines/gpencil/shaders/gpencil_zdepth_mix_frag.glsl +++ /dev/null @@ -1,76 +0,0 @@ -in vec4 uvcoordsvar; - -out vec4 FragColor; - -uniform sampler2D strokeColor; -uniform sampler2D strokeDepth; -uniform int tonemapping; -uniform vec4 select_color; -uniform int do_select; - -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; - } -} - -bool check_borders(ivec2 uv, int size) -{ - for (int x = -size; x <= size; x++) { - for (int y = -size; y <= size; y++) { - vec4 stroke_color = texelFetch(strokeColor, ivec2(uv.x + x, uv.y + y), 0).rgba; - if (stroke_color.a > 0) { - return true; - } - } - } - - return false; -} - -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 = clamp(stroke_color, 0.0, 1.0); - gl_FragDepth = clamp(stroke_depth, 0.0, 1.0); - - if (do_select == 1) { - if (stroke_color.a == 0) { - if (check_borders(uv, 2)) { - FragColor = select_color; - gl_FragDepth = 0.000001; - /* Dirty fix waiting for new GPencil engine. */ - FragColor.rgb = pow(FragColor.rgb, vec3(2.2)); - } - } - } -} diff --git a/source/blender/draw/engines/overlay/overlay_engine.c b/source/blender/draw/engines/overlay/overlay_engine.c index a8d2c4c6cf0..cfa0fa9eb1a 100644 --- a/source/blender/draw/engines/overlay/overlay_engine.c +++ b/source/blender/draw/engines/overlay/overlay_engine.c @@ -25,6 +25,8 @@ #include "DRW_engine.h" #include "DRW_render.h" +#include "DEG_depsgraph_query.h" + #include "ED_view3d.h" #include "BKE_object.h" @@ -79,6 +81,7 @@ static void OVERLAY_engine_init(void *vedata) pd->xray_enabled = XRAY_ACTIVE(v3d); pd->xray_enabled_and_not_wire = pd->xray_enabled && v3d->shading.type > OB_WIRE; pd->clear_in_front = (v3d->shading.type != OB_SOLID); + pd->cfra = DEG_get_ctime(draw_ctx->depsgraph); OVERLAY_antialiasing_init(vedata); @@ -133,11 +136,14 @@ static void OVERLAY_cache_init(void *vedata) case CTX_MODE_SCULPT: OVERLAY_sculpt_cache_init(vedata); break; - case CTX_MODE_OBJECT: - case CTX_MODE_PAINT_GPENCIL: case CTX_MODE_EDIT_GPENCIL: + case CTX_MODE_PAINT_GPENCIL: case CTX_MODE_SCULPT_GPENCIL: + case CTX_MODE_VERTEX_GPENCIL: case CTX_MODE_WEIGHT_GPENCIL: + OVERLAY_edit_gpencil_cache_init(vedata); + break; + case CTX_MODE_OBJECT: break; default: BLI_assert(!"Draw mode invalid"); @@ -148,6 +154,7 @@ static void OVERLAY_cache_init(void *vedata) OVERLAY_background_cache_init(vedata); OVERLAY_extra_cache_init(vedata); OVERLAY_facing_cache_init(vedata); + OVERLAY_gpencil_cache_init(vedata); OVERLAY_grid_cache_init(vedata); OVERLAY_image_cache_init(vedata); OVERLAY_metaball_cache_init(vedata); @@ -216,8 +223,9 @@ static void OVERLAY_cache_populate(void *vedata, Object *ob) const bool in_paint_mode = (ob == draw_ctx->obact) && (draw_ctx->object_mode & OB_MODE_ALL_PAINT); const bool in_sculpt_mode = (ob == draw_ctx->obact) && (ob->sculpt != NULL); - const bool has_surface = ELEM(ob->type, OB_MESH, OB_CURVE, OB_SURF, OB_MBALL, OB_FONT); - const bool draw_surface = !((ob->dt < OB_WIRE) || (!renderable && (ob->dt != OB_WIRE))); + const bool has_surface = ELEM( + ob->type, OB_MESH, OB_CURVE, OB_SURF, OB_MBALL, OB_FONT, OB_GPENCIL); + const bool draw_surface = (ob->dt >= OB_WIRE) && (renderable || (ob->dt == OB_WIRE)); const bool draw_facing = draw_surface && (pd->overlay.flag & V3D_OVERLAY_FACE_ORIENTATION); const bool draw_bones = (pd->overlay.flag & V3D_OVERLAY_HIDE_BONES) == 0; const bool draw_wires = draw_surface && has_surface && @@ -429,6 +437,7 @@ static void OVERLAY_draw_scene(void *vedata) OVERLAY_armature_draw(vedata); OVERLAY_particle_draw(vedata); OVERLAY_metaball_draw(vedata); + OVERLAY_gpencil_draw(vedata); OVERLAY_extra_draw(vedata); if (DRW_state_is_fbo()) { @@ -491,6 +500,13 @@ static void OVERLAY_draw_scene(void *vedata) case CTX_MODE_SCULPT: OVERLAY_sculpt_draw(vedata); break; + case CTX_MODE_EDIT_GPENCIL: + case CTX_MODE_PAINT_GPENCIL: + case CTX_MODE_SCULPT_GPENCIL: + case CTX_MODE_VERTEX_GPENCIL: + case CTX_MODE_WEIGHT_GPENCIL: + OVERLAY_edit_gpencil_draw(vedata); + break; default: break; } diff --git a/source/blender/draw/engines/overlay/overlay_extra.c b/source/blender/draw/engines/overlay/overlay_extra.c index b40e95d538c..49f266291da 100644 --- a/source/blender/draw/engines/overlay/overlay_extra.c +++ b/source/blender/draw/engines/overlay/overlay_extra.c @@ -38,7 +38,7 @@ #include "DNA_camera_types.h" #include "DNA_constraint_types.h" -#include "DNA_gpencil_types.h" +#include "DNA_curve_types.h" #include "DNA_lightprobe_types.h" #include "DNA_mesh_types.h" #include "DNA_meta_types.h" @@ -1331,89 +1331,6 @@ static void OVERLAY_relationship_lines(OVERLAY_ExtraCallBuffers *cb, /** \} */ /* -------------------------------------------------------------------- */ -/** \name GPencil. - * \{ */ - -static void OVERLAY_gpencil_color_names(Object *ob) -{ - if (ob->mode != OB_MODE_EDIT_GPENCIL) { - return; - } - - bGPdata *gpd = (bGPdata *)ob->data; - if (gpd == NULL) { - return; - } - - const DRWContextState *draw_ctx = DRW_context_state_get(); - ViewLayer *view_layer = draw_ctx->view_layer; - int theme_id = DRW_object_wire_theme_get(ob, view_layer, NULL); - uchar color[4]; - /* Color Management: Exception here as texts are drawn in sRGB space directly. */ - UI_GetThemeColor3ubv(theme_id, color); - color[3] = 255; - struct DRWTextStore *dt = DRW_text_cache_ensure(); - - for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { - if (gpl->flag & GP_LAYER_HIDE) { - continue; - } - bGPDframe *gpf = gpl->actframe; - if (gpf == NULL) { - continue; - } - for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { - Material *ma = BKE_object_material_get(ob, gps->mat_nr + 1); - if (ma == NULL) { - continue; - } - - MaterialGPencilStyle *gp_style = ma->gp_style; - /* skip stroke if it doesn't have any valid data */ - if ((gps->points == NULL) || (gps->totpoints < 1) || (gp_style == NULL)) { - continue; - } - /* check if the color is visible */ - if (gp_style->flag & GP_STYLE_COLOR_HIDE) { - continue; - } - - /* only if selected */ - if (gps->flag & GP_STROKE_SELECT) { - float fpt[3]; - for (int i = 0; i < gps->totpoints; i++) { - bGPDspoint *pt = &gps->points[i]; - if (pt->flag & GP_SPOINT_SELECT) { - mul_v3_m4v3(fpt, ob->obmat, &pt->x); - DRW_text_cache_add(dt, - fpt, - ma->id.name + 2, - strlen(ma->id.name + 2), - 10, - 0, - DRW_TEXT_CACHE_GLOBALSPACE | DRW_TEXT_CACHE_STRING_PTR, - color); - break; - } - } - } - } - } -} - -void OVERLAY_gpencil_cache_populate(OVERLAY_Data *UNUSED(vedata), Object *ob) -{ - /* don't show object extras in set's */ - if ((ob->base_flag & (BASE_FROM_SET | BASE_FROM_DUPLI)) == 0) { - if ((ob->dtx & OB_DRAWNAME) && DRW_state_show_text()) { - OVERLAY_gpencil_color_names(ob); - } - } -} - -/** \} */ - -/* -------------------------------------------------------------------- */ /** \name Volumetric / Smoke sim * \{ */ diff --git a/source/blender/draw/engines/overlay/overlay_gpencil.c b/source/blender/draw/engines/overlay/overlay_gpencil.c new file mode 100644 index 00000000000..c96c448c63b --- /dev/null +++ b/source/blender/draw/engines/overlay/overlay_gpencil.c @@ -0,0 +1,391 @@ +/* + * 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 2020, Blender Foundation. + */ + +/** \file + * \ingroup draw_engine + */ + +#include "DRW_render.h" + +#include "BKE_gpencil.h" + +#include "UI_resources.h" + +#include "DNA_gpencil_types.h" + +#include "DEG_depsgraph_query.h" + +#include "ED_view3d.h" + +#include "overlay_private.h" + +#include "draw_common.h" +#include "draw_manager_text.h" + +void OVERLAY_edit_gpencil_cache_init(OVERLAY_Data *vedata) +{ + OVERLAY_PassList *psl = vedata->psl; + OVERLAY_PrivateData *pd = vedata->stl->pd; + struct GPUShader *sh; + DRWShadingGroup *grp; + + /* Default: Display nothing. */ + pd->edit_gpencil_points_grp = NULL; + pd->edit_gpencil_wires_grp = NULL; + psl->edit_gpencil_ps = NULL; + + const DRWContextState *draw_ctx = DRW_context_state_get(); + View3D *v3d = draw_ctx->v3d; + Object *ob = draw_ctx->obact; + bGPdata *gpd = ob ? (bGPdata *)ob->data : NULL; + Scene *scene = draw_ctx->scene; + ToolSettings *ts = scene->toolsettings; + + if (gpd == NULL || ob->type != OB_GPENCIL) { + return; + } + + /* For sculpt show only if mask mode, and only points if not stroke mode. */ + const bool use_sculpt_mask = (GPENCIL_SCULPT_MODE(gpd) && + GPENCIL_ANY_SCULPT_MASK(ts->gpencil_selectmode_sculpt)); + const bool show_sculpt_points = (GPENCIL_SCULPT_MODE(gpd) && + (ts->gpencil_selectmode_sculpt & + (GP_SCULPT_MASK_SELECTMODE_POINT | + GP_SCULPT_MASK_SELECTMODE_SEGMENT))); + + /* For vertex paint show only if mask mode, and only points if not stroke mode. */ + bool use_vertex_mask = (GPENCIL_VERTEX_MODE(gpd) && + GPENCIL_ANY_VERTEX_MASK(ts->gpencil_selectmode_vertex)); + const bool show_vertex_points = (GPENCIL_VERTEX_MODE(gpd) && + (ts->gpencil_selectmode_vertex & + (GP_VERTEX_MASK_SELECTMODE_POINT | + GP_VERTEX_MASK_SELECTMODE_SEGMENT))); + + /* If Sculpt or Vertex mode and the mask is disabled, the select must be hidden. */ + const bool hide_select = ((GPENCIL_SCULPT_MODE(gpd) && !use_sculpt_mask) || + (GPENCIL_VERTEX_MODE(gpd) && !use_vertex_mask)); + + const bool do_multiedit = GPENCIL_MULTIEDIT_SESSIONS_ON(gpd); + const bool show_multi_edit_lines = (v3d->gp_flag & V3D_GP_SHOW_MULTIEDIT_LINES) != 0; + + const bool show_lines = (v3d->gp_flag & V3D_GP_SHOW_EDIT_LINES) || show_multi_edit_lines; + + const bool hide_lines = !GPENCIL_EDIT_MODE(gpd) && !GPENCIL_WEIGHT_MODE(gpd) && + !use_sculpt_mask && !use_vertex_mask && !show_lines; + + /* Special case when vertex paint and multiedit lines. */ + if (do_multiedit && show_multi_edit_lines && GPENCIL_VERTEX_MODE(gpd)) { + use_vertex_mask = true; + } + + const bool is_weight_paint = (gpd) && (gpd->flag & GP_DATA_STROKE_WEIGHTMODE); + + /* Show Edit points if: + * Edit mode: Not in Stroke selection mode + * Sculpt mode: If use Mask and not Stroke mode + * Weight mode: Always + * Vertex mode: If use Mask and not Stroke mode + */ + const bool show_points = show_sculpt_points || is_weight_paint || show_vertex_points || + (GPENCIL_EDIT_MODE(gpd) && + (ts->gpencil_selectmode_edit != GP_SELECTMODE_STROKE)); + + if ((!GPENCIL_VERTEX_MODE(gpd) && !GPENCIL_PAINT_MODE(gpd)) || use_vertex_mask) { + DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS_EQUAL | + DRW_STATE_BLEND_ALPHA; + DRW_PASS_CREATE(psl->edit_gpencil_ps, state | pd->clipping_state); + + if (show_lines && !hide_lines) { + sh = OVERLAY_shader_edit_gpencil_wire(); + pd->edit_gpencil_wires_grp = grp = DRW_shgroup_create(sh, psl->edit_gpencil_ps); + DRW_shgroup_uniform_block(grp, "globalsBlock", G_draw.block_ubo); + DRW_shgroup_uniform_bool_copy(grp, "doMultiframe", show_multi_edit_lines); + DRW_shgroup_uniform_bool_copy(grp, "doWeightColor", is_weight_paint); + DRW_shgroup_uniform_bool_copy(grp, "hideSelect", hide_select); + DRW_shgroup_uniform_float_copy(grp, "gpEditOpacity", v3d->vertex_opacity); + DRW_shgroup_uniform_texture(grp, "weightTex", G_draw.weight_ramp); + } + + if (show_points && !hide_select) { + sh = OVERLAY_shader_edit_gpencil_point(); + pd->edit_gpencil_points_grp = grp = DRW_shgroup_create(sh, psl->edit_gpencil_ps); + DRW_shgroup_uniform_block(grp, "globalsBlock", G_draw.block_ubo); + DRW_shgroup_uniform_bool_copy(grp, "doMultiframe", do_multiedit); + DRW_shgroup_uniform_bool_copy(grp, "doWeightColor", is_weight_paint); + DRW_shgroup_uniform_float_copy(grp, "gpEditOpacity", v3d->vertex_opacity); + DRW_shgroup_uniform_texture(grp, "weightTex", G_draw.weight_ramp); + } + } + + /* control points for primitives and speed guide */ + const bool is_cppoint = (gpd->runtime.tot_cp_points > 0); + const bool is_speed_guide = (ts->gp_sculpt.guide.use_guide && + (draw_ctx->object_mode == OB_MODE_PAINT_GPENCIL)); + const bool is_show_gizmo = (((v3d->gizmo_flag & V3D_GIZMO_HIDE) == 0) && + ((v3d->gizmo_flag & V3D_GIZMO_HIDE_TOOL) == 0)); + + if ((is_cppoint || is_speed_guide) && (is_show_gizmo)) { + DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_ALPHA; + DRW_PASS_CREATE(psl->edit_gpencil_gizmos_ps, state); + + sh = OVERLAY_shader_edit_gpencil_guide_point(); + grp = DRW_shgroup_create(sh, psl->edit_gpencil_gizmos_ps); + + if (gpd->runtime.cp_points != NULL) { + for (int i = 0; i < gpd->runtime.tot_cp_points; i++) { + bGPDcontrolpoint *cp = &gpd->runtime.cp_points[i]; + grp = DRW_shgroup_create_sub(grp); + DRW_shgroup_uniform_vec3_copy(grp, "pPosition", &cp->x); + DRW_shgroup_uniform_float_copy(grp, "pSize", cp->size * 0.8f * G_draw.block.sizePixel); + DRW_shgroup_uniform_vec4_copy(grp, "pColor", cp->color); + DRW_shgroup_call_procedural_points(grp, NULL, 1); + } + } + + if (ts->gp_sculpt.guide.use_guide) { + float color[4]; + if (ts->gp_sculpt.guide.reference_point == GP_GUIDE_REF_CUSTOM) { + UI_GetThemeColor4fv(TH_GIZMO_PRIMARY, color); + DRW_shgroup_uniform_vec3_copy(grp, "pPosition", ts->gp_sculpt.guide.location); + } + else if (ts->gp_sculpt.guide.reference_point == GP_GUIDE_REF_OBJECT && + ts->gp_sculpt.guide.reference_object != NULL) { + UI_GetThemeColor4fv(TH_GIZMO_SECONDARY, color); + DRW_shgroup_uniform_vec3_copy(grp, "pPosition", ts->gp_sculpt.guide.reference_object->loc); + } + else { + UI_GetThemeColor4fv(TH_REDALERT, color); + DRW_shgroup_uniform_vec3_copy(grp, "pPosition", scene->cursor.location); + } + DRW_shgroup_uniform_vec4_copy(grp, "pColor", color); + DRW_shgroup_uniform_float_copy(grp, "pSize", 8.0f * G_draw.block.sizePixel); + DRW_shgroup_call_procedural_points(grp, NULL, 1); + } + } +} + +void OVERLAY_gpencil_cache_init(OVERLAY_Data *vedata) +{ + OVERLAY_PassList *psl = vedata->psl; + struct GPUShader *sh; + DRWShadingGroup *grp; + + /* Default: Display nothing. */ + psl->gpencil_canvas_ps = NULL; + + const DRWContextState *draw_ctx = DRW_context_state_get(); + View3D *v3d = draw_ctx->v3d; + Object *ob = draw_ctx->obact; + bGPdata *gpd = ob ? (bGPdata *)ob->data : NULL; + Scene *scene = draw_ctx->scene; + ToolSettings *ts = scene->toolsettings; + const View3DCursor *cursor = &scene->cursor; + + if (gpd == NULL || ob->type != OB_GPENCIL) { + return; + } + + const bool show_overlays = (v3d->flag2 & V3D_HIDE_OVERLAYS) == 0; + const bool show_grid = (v3d->gp_flag & V3D_GP_SHOW_GRID) != 0; + + if (show_grid && show_overlays) { + const char *grid_unit = NULL; + float mat[4][4]; + float col_grid[4]; + float size[2]; + + /* set color */ + copy_v3_v3(col_grid, gpd->grid.color); + col_grid[3] = max_ff(v3d->overlay.gpencil_grid_opacity, 0.01f); + + copy_m4_m4(mat, ob->obmat); + + float viewinv[4][4]; + /* Set the grid in the selected axis */ + switch (ts->gp_sculpt.lock_axis) { + case GP_LOCKAXIS_X: + swap_v4_v4(mat[0], mat[2]); + break; + case GP_LOCKAXIS_Y: + swap_v4_v4(mat[1], mat[2]); + break; + case GP_LOCKAXIS_Z: + /* Default. */ + break; + case GP_LOCKAXIS_CURSOR: + loc_eul_size_to_mat4(mat, cursor->location, cursor->rotation_euler, (float[3]){1, 1, 1}); + break; + case GP_LOCKAXIS_VIEW: + /* view aligned */ + DRW_view_viewmat_get(NULL, viewinv, true); + copy_v3_v3(mat[0], viewinv[0]); + copy_v3_v3(mat[1], viewinv[1]); + break; + } + + translate_m4(mat, gpd->grid.offset[0], gpd->grid.offset[1], 0.0f); + mul_v2_v2fl(size, gpd->grid.scale, 2.0f * ED_scene_grid_scale(scene, &grid_unit)); + rescale_m4(mat, (float[3]){size[0], size[1], 0.0f}); + + const int gridlines = (gpd->grid.lines <= 0) ? 1 : gpd->grid.lines; + int line_ct = gridlines * 4 + 2; + + DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_LESS_EQUAL | DRW_STATE_BLEND_ALPHA; + DRW_PASS_CREATE(psl->gpencil_canvas_ps, state); + + sh = OVERLAY_shader_gpencil_canvas(); + grp = DRW_shgroup_create(sh, psl->gpencil_canvas_ps); + DRW_shgroup_uniform_block(grp, "globalsBlock", G_draw.block_ubo); + DRW_shgroup_uniform_vec4_copy(grp, "color", col_grid); + DRW_shgroup_uniform_vec3_copy(grp, "xAxis", mat[0]); + DRW_shgroup_uniform_vec3_copy(grp, "yAxis", mat[1]); + DRW_shgroup_uniform_vec3_copy(grp, "origin", mat[3]); + DRW_shgroup_uniform_int_copy(grp, "halfLineCount", line_ct / 2); + DRW_shgroup_call_procedural_lines(grp, NULL, line_ct); + } +} + +static void OVERLAY_edit_gpencil_cache_populate(OVERLAY_Data *vedata, Object *ob) +{ + OVERLAY_PrivateData *pd = vedata->stl->pd; + bGPdata *gpd = (bGPdata *)ob->data; + const DRWContextState *draw_ctx = DRW_context_state_get(); + View3D *v3d = draw_ctx->v3d; + + if (pd->edit_gpencil_wires_grp) { + DRWShadingGroup *grp = DRW_shgroup_create_sub(pd->edit_gpencil_wires_grp); + DRW_shgroup_uniform_vec4_copy(grp, "gpEditColor", gpd->line_color); + + struct GPUBatch *geom = DRW_cache_gpencil_edit_lines_get(ob, pd->cfra); + DRW_shgroup_call_no_cull(pd->edit_gpencil_wires_grp, geom, ob); + } + + if (pd->edit_gpencil_points_grp) { + const bool show_direction = (v3d->gp_flag & V3D_GP_SHOW_STROKE_DIRECTION) != 0; + + DRWShadingGroup *grp = DRW_shgroup_create_sub(pd->edit_gpencil_points_grp); + DRW_shgroup_uniform_float_copy(grp, "doStrokeEndpoints", show_direction); + + struct GPUBatch *geom = DRW_cache_gpencil_edit_points_get(ob, pd->cfra); + DRW_shgroup_call_no_cull(grp, geom, ob); + } +} + +static void overlay_gpencil_draw_stroke_color_name(bGPDlayer *UNUSED(gpl), + bGPDframe *UNUSED(gpf), + bGPDstroke *gps, + void *thunk) +{ + Object *ob = (Object *)thunk; + Material *ma = BKE_object_material_get(ob, gps->mat_nr + 1); + if (ma == NULL) { + return; + } + MaterialGPencilStyle *gp_style = ma->gp_style; + /* skip stroke if it doesn't have any valid data */ + if ((gps->points == NULL) || (gps->totpoints < 1) || (gp_style == NULL)) { + return; + } + /* check if the color is visible */ + if (gp_style->flag & GP_MATERIAL_HIDE) { + return; + } + /* only if selected */ + if (gps->flag & GP_STROKE_SELECT) { + for (int i = 0; i < gps->totpoints; i++) { + bGPDspoint *pt = &gps->points[i]; + /* Draw name at the first selected point. */ + if (pt->flag & GP_SPOINT_SELECT) { + const DRWContextState *draw_ctx = DRW_context_state_get(); + ViewLayer *view_layer = draw_ctx->view_layer; + + int theme_id = DRW_object_wire_theme_get(ob, view_layer, NULL); + uchar color[4]; + UI_GetThemeColor4ubv(theme_id, color); + + float fpt[3]; + mul_v3_m4v3(fpt, ob->obmat, &pt->x); + + struct DRWTextStore *dt = DRW_text_cache_ensure(); + DRW_text_cache_add(dt, + fpt, + ma->id.name + 2, + strlen(ma->id.name + 2), + 10, + 0, + DRW_TEXT_CACHE_GLOBALSPACE | DRW_TEXT_CACHE_STRING_PTR, + color); + break; + } + } + } +} + +static void OVERLAY_gpencil_color_names(Object *ob) +{ + const DRWContextState *draw_ctx = DRW_context_state_get(); + int cfra = DEG_get_ctime(draw_ctx->depsgraph); + + BKE_gpencil_visible_stroke_iter( + ob, NULL, overlay_gpencil_draw_stroke_color_name, ob, false, cfra); +} + +void OVERLAY_gpencil_cache_populate(OVERLAY_Data *vedata, Object *ob) +{ + const DRWContextState *draw_ctx = DRW_context_state_get(); + View3D *v3d = draw_ctx->v3d; + + bGPdata *gpd = (bGPdata *)ob->data; + if (gpd == NULL) { + return; + } + + if (GPENCIL_ANY_MODE(gpd)) { + OVERLAY_edit_gpencil_cache_populate(vedata, ob); + } + + /* don't show object extras in set's */ + if ((ob->base_flag & (BASE_FROM_SET | BASE_FROM_DUPLI)) == 0) { + if ((v3d->gp_flag & V3D_GP_SHOW_MATERIAL_NAME) && (ob->mode == OB_MODE_EDIT_GPENCIL) && + DRW_state_show_text()) { + OVERLAY_gpencil_color_names(ob); + } + } +} + +void OVERLAY_gpencil_draw(OVERLAY_Data *vedata) +{ + OVERLAY_PassList *psl = vedata->psl; + + if (psl->gpencil_canvas_ps) { + DRW_draw_pass(psl->gpencil_canvas_ps); + } +} + +void OVERLAY_edit_gpencil_draw(OVERLAY_Data *vedata) +{ + OVERLAY_PassList *psl = vedata->psl; + + if (psl->edit_gpencil_gizmos_ps) { + DRW_draw_pass(psl->edit_gpencil_gizmos_ps); + } + + if (psl->edit_gpencil_ps) { + DRW_draw_pass(psl->edit_gpencil_ps); + } +} diff --git a/source/blender/draw/engines/overlay/overlay_motion_path.c b/source/blender/draw/engines/overlay/overlay_motion_path.c index 555e0084c49..29eb4fd12a4 100644 --- a/source/blender/draw/engines/overlay/overlay_motion_path.c +++ b/source/blender/draw/engines/overlay/overlay_motion_path.c @@ -155,7 +155,7 @@ static void motion_path_cache(OVERLAY_Data *vedata, DRW_shgroup_uniform_bool_copy(grp, "selected", selected); DRW_shgroup_uniform_vec3_copy(grp, "customColor", color); /* Only draw the required range. */ - DRW_shgroup_call_range(grp, mpath_batch_line_get(mpath), start_index, len); + DRW_shgroup_call_range(grp, NULL, mpath_batch_line_get(mpath), start_index, len); } /* Draw points. */ @@ -167,7 +167,7 @@ static void motion_path_cache(OVERLAY_Data *vedata, DRW_shgroup_uniform_bool_copy(grp, "showKeyFrames", show_keyframes); DRW_shgroup_uniform_vec3_copy(grp, "customColor", color); /* Only draw the required range. */ - DRW_shgroup_call_range(grp, mpath_batch_points_get(mpath), start_index, len); + DRW_shgroup_call_range(grp, NULL, mpath_batch_points_get(mpath), start_index, len); } /* Draw frame numbers at each frame-step value. */ diff --git a/source/blender/draw/engines/overlay/overlay_outline.c b/source/blender/draw/engines/overlay/overlay_outline.c index 63738b3c214..e77a0a143a9 100644 --- a/source/blender/draw/engines/overlay/overlay_outline.c +++ b/source/blender/draw/engines/overlay/overlay_outline.c @@ -23,13 +23,67 @@ #include "DRW_render.h" #include "BKE_global.h" +#include "BKE_gpencil.h" -#include "DNA_lightprobe_types.h" +#include "BKE_object.h" + +#include "DNA_gpencil_types.h" #include "UI_resources.h" #include "overlay_private.h" +/* Returns the normal plane in ndc space. */ +static void gpencil_depth_plane(Object *ob, float r_plane[4]) +{ + /* TODO put that into private data. */ + float viewinv[4][4]; + DRW_view_viewmat_get(NULL, viewinv, true); + float *camera_z_axis = viewinv[2]; + float *camera_pos = viewinv[3]; + + /* Find the normal most likely to represent the gpObject. */ + /* TODO: This does not work quite well if you use + * strokes not aligned with the object axes. Maybe we could try to + * compute the minimum axis of all strokes. But this would be more + * computationaly heavy and should go into the GPData evaluation. */ + BoundBox *bbox = BKE_object_boundbox_get(ob); + /* Convert bbox to matrix */ + float mat[4][4], size[3], center[3]; + BKE_boundbox_calc_size_aabb(bbox, size); + BKE_boundbox_calc_center_aabb(bbox, center); + unit_m4(mat); + copy_v3_v3(mat[3], center); + /* Avoid division by 0.0 later. */ + add_v3_fl(size, 1e-8f); + rescale_m4(mat, size); + /* BBox space to World. */ + mul_m4_m4m4(mat, ob->obmat, mat); + /* BBox center in world space. */ + copy_v3_v3(center, mat[3]); + /* View Vector. */ + if (DRW_view_is_persp_get(NULL)) { + /* BBox center to camera vector. */ + sub_v3_v3v3(r_plane, camera_pos, mat[3]); + } + else { + copy_v3_v3(r_plane, camera_z_axis); + } + /* World to BBox space. */ + invert_m4(mat); + /* Normalize the vector in BBox space. */ + mul_mat3_m4_v3(mat, r_plane); + normalize_v3(r_plane); + + transpose_m4(mat); + /* mat is now a "normal" matrix which will transform + * BBox space normal to world space. */ + mul_mat3_m4_v3(mat, r_plane); + normalize_v3(r_plane); + + plane_from_point_normal_v3(r_plane, center, r_plane); +} + void OVERLAY_outline_init(OVERLAY_Data *vedata) { OVERLAY_FramebufferList *fbl = vedata->fbl; @@ -79,6 +133,11 @@ void OVERLAY_outline_cache_init(OVERLAY_Data *vedata) pd->outlines_grp = grp = DRW_shgroup_create(sh_geom, psl->outlines_prepass_ps); DRW_shgroup_uniform_bool_copy(grp, "isTransform", (G.moving & G_TRANSFORM_OBJ) != 0); + + GPUShader *sh_gpencil = OVERLAY_shader_outline_prepass_gpencil(); + + pd->outlines_gpencil_grp = grp = DRW_shgroup_create(sh_gpencil, psl->outlines_prepass_ps); + DRW_shgroup_uniform_bool_copy(grp, "isTransform", (G.moving & G_TRANSFORM_OBJ) != 0); } /* outlines_prepass_ps is still needed for selection of probes. */ @@ -107,6 +166,98 @@ void OVERLAY_outline_cache_init(OVERLAY_Data *vedata) } } +typedef struct iterData { + Object *ob; + DRWShadingGroup *stroke_grp; + DRWShadingGroup *fill_grp; + int cfra; + float plane[4]; +} iterData; + +static void gp_layer_cache_populate(bGPDlayer *gpl, + bGPDframe *UNUSED(gpf), + bGPDstroke *UNUSED(gps), + void *thunk) +{ + iterData *iter = (iterData *)thunk; + bGPdata *gpd = (bGPdata *)iter->ob->data; + + const bool is_screenspace = (gpd->flag & GP_DATA_STROKE_KEEPTHICKNESS) != 0; + const bool is_stroke_order_3d = (gpd->draw_mode == GP_DRAWMODE_3D); + + float object_scale = mat4_to_scale(iter->ob->obmat); + /* Negate thickness sign to tag that strokes are in screen space. + * Convert to world units (by default, 1 meter = 2000 px). */ + float thickness_scale = (is_screenspace) ? -1.0f : (gpd->pixfactor / 2000.0f); + + DRWShadingGroup *grp = iter->stroke_grp = DRW_shgroup_create_sub(iter->stroke_grp); + DRW_shgroup_uniform_bool_copy(grp, "strokeOrder3d", is_stroke_order_3d); + DRW_shgroup_uniform_vec2_copy(grp, "sizeViewportInv", DRW_viewport_invert_size_get()); + DRW_shgroup_uniform_vec2_copy(grp, "sizeViewport", DRW_viewport_size_get()); + DRW_shgroup_uniform_float_copy(grp, "thicknessScale", object_scale); + DRW_shgroup_uniform_float_copy(grp, "thicknessOffset", (float)gpl->line_change); + DRW_shgroup_uniform_float_copy(grp, "thicknessWorldScale", thickness_scale); + DRW_shgroup_uniform_vec4_copy(grp, "gpDepthPlane", iter->plane); +} + +static void gp_stroke_cache_populate(bGPDlayer *UNUSED(gpl), + bGPDframe *UNUSED(gpf), + bGPDstroke *gps, + void *thunk) +{ + iterData *iter = (iterData *)thunk; + + MaterialGPencilStyle *gp_style = BKE_gpencil_material_settings(iter->ob, gps->mat_nr + 1); + + bool hide_material = (gp_style->flag & GP_MATERIAL_HIDE) != 0; + bool show_stroke = (gp_style->flag & GP_MATERIAL_STROKE_SHOW) != 0; + // TODO: What about simplify Fill? + bool show_fill = (gps->tot_triangles > 0) && (gp_style->flag & GP_MATERIAL_FILL_SHOW) != 0; + + if (hide_material) { + return; + } + + if (show_fill) { + struct GPUBatch *geom = DRW_cache_gpencil_fills_get(iter->ob, iter->cfra); + int vfirst = gps->runtime.fill_start * 3; + int vcount = gps->tot_triangles * 3; + DRW_shgroup_call_range(iter->fill_grp, iter->ob, geom, vfirst, vcount); + } + + if (show_stroke) { + struct GPUBatch *geom = DRW_cache_gpencil_strokes_get(iter->ob, iter->cfra); + /* Start one vert before to have gl_InstanceID > 0 (see shader). */ + int vfirst = gps->runtime.stroke_start - 1; + /* Include "potential" cyclic vertex and start adj vertex (see shader). */ + int vcount = gps->totpoints + 1 + 1; + DRW_shgroup_call_instance_range(iter->stroke_grp, iter->ob, geom, vfirst, vcount); + } +} + +static void OVERLAY_outline_gpencil(OVERLAY_PrivateData *pd, Object *ob) +{ + /* No outlines in edit mode. */ + bGPdata *gpd = (bGPdata *)ob->data; + if (gpd && GPENCIL_ANY_MODE(gpd)) { + return; + } + + iterData iter = { + .ob = ob, + .stroke_grp = pd->outlines_gpencil_grp, + .fill_grp = DRW_shgroup_create_sub(pd->outlines_gpencil_grp), + .cfra = pd->cfra, + }; + + if (gpd->draw_mode == GP_DRAWMODE_2D) { + gpencil_depth_plane(ob, iter.plane); + } + + BKE_gpencil_visible_stroke_iter( + ob, gp_layer_cache_populate, gp_stroke_cache_populate, &iter, false, pd->cfra); +} + void OVERLAY_outline_cache_populate(OVERLAY_Data *vedata, Object *ob, OVERLAY_DupliData *dupli, @@ -123,6 +274,11 @@ void OVERLAY_outline_cache_populate(OVERLAY_Data *vedata, return; } + if (ob->type == OB_GPENCIL) { + OVERLAY_outline_gpencil(pd, ob); + return; + } + if (dupli && !init_dupli) { geom = dupli->outline_geom; shgroup = dupli->outline_shgrp; diff --git a/source/blender/draw/engines/overlay/overlay_private.h b/source/blender/draw/engines/overlay/overlay_private.h index 185df723301..167a8e940df 100644 --- a/source/blender/draw/engines/overlay/overlay_private.h +++ b/source/blender/draw/engines/overlay/overlay_private.h @@ -60,6 +60,8 @@ typedef struct OVERLAY_PassList { DRWPass *clipping_frustum_ps; DRWPass *edit_curve_wire_ps[2]; DRWPass *edit_curve_handle_ps; + DRWPass *edit_gpencil_ps; + DRWPass *edit_gpencil_gizmos_ps; DRWPass *edit_lattice_ps; DRWPass *edit_mesh_depth_ps[2]; DRWPass *edit_mesh_verts_ps[2]; @@ -76,6 +78,7 @@ typedef struct OVERLAY_PassList { DRWPass *extra_blend_ps; DRWPass *extra_centers_ps; DRWPass *extra_grid_ps; + DRWPass *gpencil_canvas_ps; DRWPass *facing_ps; DRWPass *grid_ps; DRWPass *image_background_ps; @@ -219,6 +222,8 @@ typedef struct OVERLAY_PrivateData { DRWShadingGroup *edit_curve_points_grp; DRWShadingGroup *edit_lattice_points_grp; DRWShadingGroup *edit_lattice_wires_grp; + DRWShadingGroup *edit_gpencil_points_grp; + DRWShadingGroup *edit_gpencil_wires_grp; DRWShadingGroup *edit_mesh_depth_grp[2]; DRWShadingGroup *edit_mesh_faces_grp[2]; DRWShadingGroup *edit_mesh_faces_cage_grp[2]; @@ -238,6 +243,7 @@ typedef struct OVERLAY_PrivateData { DRWShadingGroup *motion_path_lines_grp; DRWShadingGroup *motion_path_points_grp; DRWShadingGroup *outlines_grp; + DRWShadingGroup *outlines_gpencil_grp; DRWShadingGroup *paint_surf_grp; DRWShadingGroup *paint_wire_grp; DRWShadingGroup *paint_wire_selected_grp; @@ -277,6 +283,7 @@ typedef struct OVERLAY_PrivateData { float xray_opacity; short v3d_flag; /* TODO move to View3DOverlay */ short v3d_gridflag; /* TODO move to View3DOverlay */ + int cfra; DRWState clipping_state; OVERLAY_ShadingData shdata; @@ -419,6 +426,12 @@ void OVERLAY_edit_curve_cache_populate(OVERLAY_Data *vedata, Object *ob); void OVERLAY_edit_surf_cache_populate(OVERLAY_Data *vedata, Object *ob); void OVERLAY_edit_curve_draw(OVERLAY_Data *vedata); +void OVERLAY_edit_gpencil_cache_init(OVERLAY_Data *vedata); +void OVERLAY_gpencil_cache_init(OVERLAY_Data *vedata); +void OVERLAY_gpencil_cache_populate(OVERLAY_Data *vedata, Object *ob); +void OVERLAY_gpencil_draw(OVERLAY_Data *vedata); +void OVERLAY_edit_gpencil_draw(OVERLAY_Data *vedata); + void OVERLAY_edit_lattice_cache_init(OVERLAY_Data *vedata); void OVERLAY_edit_lattice_cache_populate(OVERLAY_Data *vedata, Object *ob); void OVERLAY_lattice_cache_populate(OVERLAY_Data *vedata, Object *ob); @@ -446,7 +459,6 @@ void OVERLAY_extra_centers_draw(OVERLAY_Data *vedata); void OVERLAY_camera_cache_populate(OVERLAY_Data *vedata, Object *ob); void OVERLAY_empty_cache_populate(OVERLAY_Data *vedata, Object *ob); -void OVERLAY_gpencil_cache_populate(OVERLAY_Data *vedata, Object *ob); void OVERLAY_light_cache_populate(OVERLAY_Data *vedata, Object *ob); void OVERLAY_lightprobe_cache_populate(OVERLAY_Data *vedata, Object *ob); void OVERLAY_speaker_cache_populate(OVERLAY_Data *vedata, Object *ob); @@ -547,6 +559,9 @@ GPUShader *OVERLAY_shader_depth_only(void); GPUShader *OVERLAY_shader_edit_curve_handle(void); GPUShader *OVERLAY_shader_edit_curve_point(void); GPUShader *OVERLAY_shader_edit_curve_wire(void); +GPUShader *OVERLAY_shader_edit_gpencil_guide_point(void); +GPUShader *OVERLAY_shader_edit_gpencil_point(void); +GPUShader *OVERLAY_shader_edit_gpencil_wire(void); GPUShader *OVERLAY_shader_edit_lattice_point(void); GPUShader *OVERLAY_shader_edit_lattice_wire(void); GPUShader *OVERLAY_shader_edit_mesh_analysis(void); @@ -564,12 +579,14 @@ GPUShader *OVERLAY_shader_extra_wire(bool use_object); GPUShader *OVERLAY_shader_extra_loose_point(void); GPUShader *OVERLAY_shader_extra_point(void); GPUShader *OVERLAY_shader_facing(void); +GPUShader *OVERLAY_shader_gpencil_canvas(void); GPUShader *OVERLAY_shader_grid(void); GPUShader *OVERLAY_shader_image(void); GPUShader *OVERLAY_shader_motion_path_line(void); GPUShader *OVERLAY_shader_motion_path_vert(void); GPUShader *OVERLAY_shader_uniform_color(void); GPUShader *OVERLAY_shader_outline_prepass(bool use_wire); +GPUShader *OVERLAY_shader_outline_prepass_gpencil(void); GPUShader *OVERLAY_shader_extra_grid(void); GPUShader *OVERLAY_shader_outline_detect(void); GPUShader *OVERLAY_shader_paint_face(void); diff --git a/source/blender/draw/engines/overlay/overlay_shader.c b/source/blender/draw/engines/overlay/overlay_shader.c index d33ef239198..86d5f58957e 100644 --- a/source/blender/draw/engines/overlay/overlay_shader.c +++ b/source/blender/draw/engines/overlay/overlay_shader.c @@ -54,6 +54,9 @@ extern char datatoc_edit_curve_handle_geom_glsl[]; extern char datatoc_edit_curve_handle_vert_glsl[]; extern char datatoc_edit_curve_point_vert_glsl[]; extern char datatoc_edit_curve_wire_vert_glsl[]; +extern char datatoc_edit_gpencil_canvas_vert_glsl[]; +extern char datatoc_edit_gpencil_guide_vert_glsl[]; +extern char datatoc_edit_gpencil_vert_glsl[]; extern char datatoc_edit_lattice_point_vert_glsl[]; extern char datatoc_edit_lattice_wire_vert_glsl[]; extern char datatoc_edit_mesh_common_lib_glsl[]; @@ -113,6 +116,8 @@ extern char datatoc_gpu_shader_flat_color_frag_glsl[]; extern char datatoc_gpu_shader_point_varying_color_varying_outline_aa_frag_glsl[]; extern char datatoc_gpu_shader_common_obinfos_lib_glsl[]; +extern char datatoc_gpencil_common_lib_glsl[]; + extern char datatoc_common_colormanagement_lib_glsl[]; extern char datatoc_common_fullscreen_vert_glsl[]; extern char datatoc_common_fxaa_lib_glsl[]; @@ -138,6 +143,9 @@ typedef struct OVERLAY_Shaders { GPUShader *edit_curve_handle; GPUShader *edit_curve_point; GPUShader *edit_curve_wire; + GPUShader *edit_gpencil_guide_point; + GPUShader *edit_gpencil_point; + GPUShader *edit_gpencil_wire; GPUShader *edit_lattice_point; GPUShader *edit_lattice_wire; GPUShader *edit_mesh_vert; @@ -159,11 +167,13 @@ typedef struct OVERLAY_Shaders { GPUShader *extra_lightprobe_grid; GPUShader *extra_loose_point; GPUShader *facing; + GPUShader *gpencil_canvas; GPUShader *grid; GPUShader *image; GPUShader *motion_path_line; GPUShader *motion_path_vert; GPUShader *outline_prepass; + GPUShader *outline_prepass_gpencil; GPUShader *outline_prepass_wire; GPUShader *outline_detect; GPUShader *paint_face; @@ -557,6 +567,58 @@ GPUShader *OVERLAY_shader_edit_curve_wire(void) return sh_data->edit_curve_wire; } +GPUShader *OVERLAY_shader_edit_gpencil_guide_point(void) +{ + OVERLAY_Shaders *sh_data = &e_data.sh_data[0]; + if (!sh_data->edit_gpencil_guide_point) { + sh_data->edit_gpencil_guide_point = GPU_shader_create_from_arrays({ + .vert = (const char *[]){datatoc_common_view_lib_glsl, + datatoc_edit_gpencil_guide_vert_glsl, + NULL}, + .frag = (const char *[]){datatoc_gpu_shader_point_varying_color_frag_glsl, NULL}, + }); + } + return sh_data->edit_gpencil_guide_point; +} + +GPUShader *OVERLAY_shader_edit_gpencil_point(void) +{ + const DRWContextState *draw_ctx = DRW_context_state_get(); + const GPUShaderConfigData *sh_cfg = &GPU_shader_cfg_data[draw_ctx->sh_cfg]; + OVERLAY_Shaders *sh_data = &e_data.sh_data[draw_ctx->sh_cfg]; + if (!sh_data->edit_gpencil_point) { + sh_data->edit_gpencil_point = GPU_shader_create_from_arrays({ + .vert = (const char *[]){sh_cfg->lib, + datatoc_common_globals_lib_glsl, + datatoc_common_view_lib_glsl, + datatoc_edit_gpencil_vert_glsl, + NULL}, + .frag = (const char *[]){datatoc_gpu_shader_point_varying_color_frag_glsl, NULL}, + .defs = (const char *[]){sh_cfg->def, "#define USE_POINTS\n", NULL}, + }); + } + return sh_data->edit_gpencil_point; +} + +GPUShader *OVERLAY_shader_edit_gpencil_wire(void) +{ + const DRWContextState *draw_ctx = DRW_context_state_get(); + const GPUShaderConfigData *sh_cfg = &GPU_shader_cfg_data[draw_ctx->sh_cfg]; + OVERLAY_Shaders *sh_data = &e_data.sh_data[draw_ctx->sh_cfg]; + if (!sh_data->edit_gpencil_wire) { + sh_data->edit_gpencil_wire = GPU_shader_create_from_arrays({ + .vert = (const char *[]){sh_cfg->lib, + datatoc_common_globals_lib_glsl, + datatoc_common_view_lib_glsl, + datatoc_edit_gpencil_vert_glsl, + NULL}, + .frag = (const char *[]){datatoc_gpu_shader_3D_smooth_color_frag_glsl, NULL}, + .defs = (const char *[]){sh_cfg->def, NULL}, + }); + } + return sh_data->edit_gpencil_wire; +} + GPUShader *OVERLAY_shader_edit_lattice_point(void) { const DRWContextState *draw_ctx = DRW_context_state_get(); @@ -883,6 +945,21 @@ GPUShader *OVERLAY_shader_facing(void) return sh_data->facing; } +GPUShader *OVERLAY_shader_gpencil_canvas(void) +{ + OVERLAY_Shaders *sh_data = &e_data.sh_data[0]; + if (!sh_data->gpencil_canvas) { + sh_data->gpencil_canvas = GPU_shader_create_from_arrays({ + .vert = (const char *[]){datatoc_common_globals_lib_glsl, + datatoc_common_view_lib_glsl, + datatoc_edit_gpencil_canvas_vert_glsl, + NULL}, + .frag = (const char *[]){datatoc_common_view_lib_glsl, datatoc_extra_frag_glsl, NULL}, + }); + } + return sh_data->gpencil_canvas; +} + GPUShader *OVERLAY_shader_grid(void) { OVERLAY_Shaders *sh_data = &e_data.sh_data[0]; @@ -998,6 +1075,32 @@ GPUShader *OVERLAY_shader_outline_prepass(bool use_wire) return use_wire ? sh_data->outline_prepass_wire : sh_data->outline_prepass; } +GPUShader *OVERLAY_shader_outline_prepass_gpencil(void) +{ + const DRWContextState *draw_ctx = DRW_context_state_get(); + const GPUShaderConfigData *sh_cfg = &GPU_shader_cfg_data[draw_ctx->sh_cfg]; + OVERLAY_Shaders *sh_data = &e_data.sh_data[draw_ctx->sh_cfg]; + if (!sh_data->outline_prepass_gpencil) { + sh_data->outline_prepass_gpencil = GPU_shader_create_from_arrays({ + .vert = (const char *[]){sh_cfg->lib, + datatoc_common_view_lib_glsl, + datatoc_gpencil_common_lib_glsl, + datatoc_gpu_shader_common_obinfos_lib_glsl, + datatoc_outline_prepass_vert_glsl, + NULL}, + .frag = (const char *[]){datatoc_common_view_lib_glsl, + datatoc_gpencil_common_lib_glsl, + datatoc_outline_prepass_frag_glsl, + NULL}, + .defs = (const char *[]){sh_cfg->def, + "#define USE_GPENCIL\n", + "#define UNIFORM_RESOURCE_ID\n", + NULL}, + }); + } + return sh_data->outline_prepass_gpencil; +} + GPUShader *OVERLAY_shader_outline_detect(void) { OVERLAY_Shaders *sh_data = &e_data.sh_data[0]; diff --git a/source/blender/draw/engines/overlay/overlay_wireframe.c b/source/blender/draw/engines/overlay/overlay_wireframe.c index 5dbdc71dae1..4df9faace18 100644 --- a/source/blender/draw/engines/overlay/overlay_wireframe.c +++ b/source/blender/draw/engines/overlay/overlay_wireframe.c @@ -201,7 +201,11 @@ void OVERLAY_wireframe_cache_populate(OVERLAY_Data *vedata, shgrp = pd->wires_grp[is_xray][use_coloring]; } - if (use_sculpt_pbvh) { + if (ob->type == OB_GPENCIL) { + /* TODO (fclem) Make GPencil objects have correct boundbox. */ + DRW_shgroup_call_no_cull(shgrp, geom, ob); + } + else if (use_sculpt_pbvh) { DRW_shgroup_call_sculpt(shgrp, ob, true, false, false); } else { diff --git a/source/blender/draw/engines/overlay/shaders/edit_gpencil_canvas_vert.glsl b/source/blender/draw/engines/overlay/shaders/edit_gpencil_canvas_vert.glsl new file mode 100644 index 00000000000..5aa7fe78e4e --- /dev/null +++ b/source/blender/draw/engines/overlay/shaders/edit_gpencil_canvas_vert.glsl @@ -0,0 +1,35 @@ + +uniform vec4 color; +uniform vec3 xAxis; +uniform vec3 yAxis; +uniform vec3 origin; +uniform int halfLineCount; + +flat out vec4 finalColor; +flat out vec2 edgeStart; +noperspective out vec2 edgePos; + +void main() +{ + GPU_INTEL_VERTEX_SHADER_WORKAROUND + + vec2 pos; + pos.x = float(gl_VertexID % 2); + pos.y = float(gl_VertexID / 2) / float(halfLineCount - 1); + + if (pos.y > 1.0) { + pos.xy = pos.yx; + pos.x -= 1.0 + 1.0 / float(halfLineCount - 1); + } + + pos -= 0.5; + + vec3 world_pos = xAxis * pos.x + yAxis * pos.y + origin; + + gl_Position = point_world_to_ndc(world_pos); + + finalColor = color; + + /* Convert to screen position [0..sizeVp]. */ + edgePos = edgeStart = ((gl_Position.xy / gl_Position.w) * 0.5 + 0.5) * sizeViewport.xy; +} diff --git a/source/blender/draw/engines/overlay/shaders/edit_gpencil_guide_vert.glsl b/source/blender/draw/engines/overlay/shaders/edit_gpencil_guide_vert.glsl new file mode 100644 index 00000000000..ef68b0f4e6f --- /dev/null +++ b/source/blender/draw/engines/overlay/shaders/edit_gpencil_guide_vert.glsl @@ -0,0 +1,14 @@ +uniform vec4 pColor; +uniform float pSize; +uniform vec3 pPosition; + +out vec4 finalColor; + +void main() +{ + GPU_INTEL_VERTEX_SHADER_WORKAROUND + + gl_Position = point_world_to_ndc(pPosition); + finalColor = pColor; + gl_PointSize = pSize; +} diff --git a/source/blender/draw/engines/overlay/shaders/edit_gpencil_vert.glsl b/source/blender/draw/engines/overlay/shaders/edit_gpencil_vert.glsl new file mode 100644 index 00000000000..cb03ad44615 --- /dev/null +++ b/source/blender/draw/engines/overlay/shaders/edit_gpencil_vert.glsl @@ -0,0 +1,103 @@ + +uniform float normalSize; +uniform bool doMultiframe; +uniform bool doStrokeEndpoints; +uniform bool hideSelect; +uniform bool doWeightColor; +uniform float gpEditOpacity; +uniform vec4 gpEditColor; +uniform sampler1D weightTex; + +in vec3 pos; +in float ma; +in uint vflag; +in float weight; + +out vec4 finalColor; + +void discard_vert() +{ + /* We set the vertex at the camera origin to generate 0 fragments. */ + gl_Position = vec4(0.0, 0.0, -3e36, 0.0); +} + +#define GP_EDIT_POINT_SELECTED (1u << 0u) +#define GP_EDIT_STROKE_SELECTED (1u << 1u) +#define GP_EDIT_MULTIFRAME (1u << 2u) +#define GP_EDIT_STROKE_START (1u << 3u) +#define GP_EDIT_STROKE_END (1u << 4u) + +#ifdef USE_POINTS +# define colorUnselect colorGpencilVertex +# define colorSelect colorGpencilVertexSelect +#else +# define colorUnselect gpEditColor +# define colorSelect (hideSelect ? colorUnselect : colorGpencilVertexSelect) +#endif + +vec3 weight_to_rgb(float t) +{ + if (t < 0.0) { + /* No weight */ + return colorUnselect.rgb; + } + else if (t > 1.0) { + /* Error color */ + return vec3(1.0, 0.0, 1.0); + } + else { + return texture(weightTex, t).rgb; + } +} + +void main() +{ + GPU_INTEL_VERTEX_SHADER_WORKAROUND + + vec3 world_pos = point_object_to_world(pos); + gl_Position = point_world_to_ndc(world_pos); + + bool is_multiframe = (vflag & GP_EDIT_MULTIFRAME) != 0u; + bool is_stroke_sel = (vflag & GP_EDIT_STROKE_SELECTED) != 0u; + bool is_point_sel = (vflag & GP_EDIT_POINT_SELECTED) != 0u; + + if (doWeightColor) { + finalColor.rgb = weight_to_rgb(weight); + finalColor.a = gpEditOpacity; + } + else { + finalColor = (is_point_sel) ? colorSelect : colorUnselect; + finalColor.a *= gpEditOpacity; + } + +#ifdef USE_POINTS + gl_PointSize = sizeVertex * 2.0; + + if (doStrokeEndpoints && !doWeightColor) { + bool is_stroke_start = (vflag & GP_EDIT_STROKE_START) != 0u; + bool is_stroke_end = (vflag & GP_EDIT_STROKE_END) != 0u; + + if (is_stroke_start) { + gl_PointSize *= 2.0; + finalColor.rgb = vec3(0.0, 1.0, 0.0); + } + else if (is_stroke_end) { + gl_PointSize *= 1.5; + finalColor.rgb = vec3(1.0, 0.0, 0.0); + } + } + + if ((!is_stroke_sel && !doWeightColor) || (!doMultiframe && is_multiframe)) { + discard_vert(); + } +#endif + + /* Discard unwanted padding vertices. */ + if (ma == -1.0 || (is_multiframe && !doMultiframe)) { + discard_vert(); + } + +#ifdef USE_WORLD_CLIP_PLANES + world_clip_planes_calc_clip_distance(world_pos); +#endif +} diff --git a/source/blender/draw/engines/overlay/shaders/outline_prepass_frag.glsl b/source/blender/draw/engines/overlay/shaders/outline_prepass_frag.glsl index f6e3724eb51..85f79e94263 100644 --- a/source/blender/draw/engines/overlay/shaders/outline_prepass_frag.glsl +++ b/source/blender/draw/engines/overlay/shaders/outline_prepass_frag.glsl @@ -1,10 +1,46 @@ +uniform vec4 gpDepthPlane; + flat in uint objectId; /* using uint because 16bit uint can contain more ids than int. */ out uint outId; +vec3 ray_plane_intersection(vec3 ray_ori, vec3 ray_dir, vec4 plane) +{ + float d = dot(plane.xyz, ray_dir); + vec3 plane_co = plane.xyz * (-plane.w / dot(plane.xyz, plane.xyz)); + vec3 h = ray_ori - plane_co; + float lambda = -dot(plane.xyz, h) / ((abs(d) < 1e-8) ? 1e-8 : d); + return ray_ori + ray_dir * lambda; +} + void main() { - outId = objectId; +#ifdef USE_GPENCIL + if (stroke_round_cap_mask(strokePt1, strokePt2, strokeAspect, strokeThickness, strokeHardeness) < + 0.001) { + discard; + } + + if (depth != -1.0) { + /* Stroke order 2D. */ + bool is_persp = ProjectionMatrix[3][3] == 0.0; + vec2 uvs = vec2(gl_FragCoord.xy) * sizeViewportInv; + vec3 pos_ndc = vec3(uvs, gl_FragCoord.z) * 2.0 - 1.0; + vec4 pos_world = ViewProjectionMatrixInverse * vec4(pos_ndc, 1.0); + vec3 pos = pos_world.xyz / pos_world.w; + + vec3 ray_ori = pos; + vec3 ray_dir = (is_persp) ? (ViewMatrixInverse[3].xyz - pos) : ViewMatrixInverse[2].xyz; + vec3 isect = ray_plane_intersection(ray_ori, ray_dir, gpDepthPlane); + vec4 ndc = point_world_to_ndc(isect); + gl_FragDepth = (ndc.z / ndc.w) * 0.5 + 0.5; + } + else { + gl_FragDepth = gl_FragCoord.z; + } +#endif + + outId = uint(objectId); } diff --git a/source/blender/draw/engines/overlay/shaders/outline_prepass_vert.glsl b/source/blender/draw/engines/overlay/shaders/outline_prepass_vert.glsl index 984e55b0c46..a2021759196 100644 --- a/source/blender/draw/engines/overlay/shaders/outline_prepass_vert.glsl +++ b/source/blender/draw/engines/overlay/shaders/outline_prepass_vert.glsl @@ -1,7 +1,9 @@ uniform bool isTransform; +#ifndef USE_GPENCIL in vec3 pos; +#endif #ifdef USE_GEOM out vec3 vPos; @@ -47,11 +49,19 @@ uint outline_colorid_get(void) void main() { +#ifdef USE_GPENCIL + gpencil_vertex(); +# ifdef USE_WORLD_CLIP_PLANES + vec3 world_pos = point_object_to_world(pos1.xyz); +# endif + +#else vec3 world_pos = point_object_to_world(pos); -#ifdef USE_GEOM + gl_Position = point_world_to_ndc(world_pos); +# ifdef USE_GEOM vPos = point_world_to_view(world_pos); +# endif #endif - gl_Position = point_world_to_ndc(world_pos); /* Small bias to always be on top of the geom. */ gl_Position.z -= 1e-3; diff --git a/source/blender/draw/engines/overlay/shaders/wireframe_vert.glsl b/source/blender/draw/engines/overlay/shaders/wireframe_vert.glsl index 31ac9a2b181..1abac302cda 100644 --- a/source/blender/draw/engines/overlay/shaders/wireframe_vert.glsl +++ b/source/blender/draw/engines/overlay/shaders/wireframe_vert.glsl @@ -101,8 +101,10 @@ void wire_object_color_get(out vec3 rim_col, out vec3 wire_col) void main() { + bool no_attrib = all(equal(nor, vec3(0))); + vec3 wnor = no_attrib ? ViewMatrixInverse[2].xyz : normalize(normal_object_to_world(nor)); + vec3 wpos = point_object_to_world(pos); - vec3 wnor = normalize(normal_object_to_world(nor)); bool is_persp = (ProjectionMatrix[3][3] == 0.0); vec3 V = (is_persp) ? normalize(ViewMatrixInverse[3].xyz - wpos) : ViewMatrixInverse[2].xyz; @@ -147,7 +149,7 @@ void main() #endif /* Cull flat edges below threshold. */ - if (get_edge_sharpness(wd) < 0.0) { + if (!no_attrib && (get_edge_sharpness(wd) < 0.0)) { edgeStart = vec2(-1.0); } diff --git a/source/blender/draw/engines/workbench/workbench_render.c b/source/blender/draw/engines/workbench/workbench_render.c index c8f74120113..6e005e7ccaf 100644 --- a/source/blender/draw/engines/workbench/workbench_render.c +++ b/source/blender/draw/engines/workbench/workbench_render.c @@ -117,6 +117,60 @@ static bool workbench_render_framebuffers_init(void) return ok; } +static void workbench_render_result_z(struct RenderLayer *rl, + const char *viewname, + const rcti *rect) +{ + DefaultFramebufferList *dfbl = DRW_viewport_framebuffer_list_get(); + const DRWContextState *draw_ctx = DRW_context_state_get(); + ViewLayer *view_layer = draw_ctx->view_layer; + + if ((view_layer->passflag & SCE_PASS_Z) != 0) { + RenderPass *rp = RE_pass_find_by_name(rl, RE_PASSNAME_Z, viewname); + + GPU_framebuffer_bind(dfbl->default_fb); + GPU_framebuffer_read_depth(dfbl->default_fb, + rect->xmin, + rect->ymin, + BLI_rcti_size_x(rect), + BLI_rcti_size_y(rect), + rp->rect); + + float winmat[4][4]; + DRW_view_winmat_get(NULL, winmat, false); + + int pix_ct = BLI_rcti_size_x(rect) * BLI_rcti_size_y(rect); + + /* Convert ogl depth [0..1] to view Z [near..far] */ + if (DRW_view_is_persp_get(NULL)) { + for (int i = 0; i < pix_ct; i++) { + if (rp->rect[i] == 1.0f) { + rp->rect[i] = 1e10f; /* Background */ + } + else { + rp->rect[i] = rp->rect[i] * 2.0f - 1.0f; + rp->rect[i] = winmat[3][2] / (rp->rect[i] + winmat[2][2]); + } + } + } + else { + /* Keep in mind, near and far distance are negatives. */ + float near = DRW_view_near_distance_get(NULL); + float far = DRW_view_far_distance_get(NULL); + float range = fabsf(far - near); + + for (int i = 0; i < pix_ct; i++) { + if (rp->rect[i] == 1.0f) { + rp->rect[i] = 1e10f; /* Background */ + } + else { + rp->rect[i] = -rp->rect[i] * range + near; + } + } + } + } +} + static void workbench_render_framebuffers_finish(void) { } @@ -195,8 +249,8 @@ void workbench_render(WORKBENCH_Data *data, const char *viewname = RE_GetActiveRenderView(engine->re); RenderPass *rp = RE_pass_find_by_name(render_layer, RE_PASSNAME_COMBINED, viewname); - GPU_framebuffer_bind(dfbl->color_only_fb); - GPU_framebuffer_read_color(dfbl->color_only_fb, + GPU_framebuffer_bind(dfbl->default_fb); + GPU_framebuffer_read_color(dfbl->default_fb, rect->xmin, rect->ymin, BLI_rcti_size_x(rect), @@ -205,6 +259,8 @@ void workbench_render(WORKBENCH_Data *data, 0, rp->rect); + workbench_render_result_z(render_layer, viewname, rect); + workbench_render_framebuffers_finish(); } |