From 99f7934e19ddf6796e866d2ddc0c3882ed8fcc2b Mon Sep 17 00:00:00 2001 From: Antonioya Date: Mon, 26 Nov 2018 18:12:39 +0100 Subject: GP: New Blend Layers functionality Now it's possible define the blend mode between layers including the option to clamp the layer using underlying layers. Also a new Simplify option has been added to disable blend layers. --- .../startup/bl_ui/properties_data_gpencil.py | 15 ++- .../bl_ui/properties_grease_pencil_common.py | 5 +- release/scripts/startup/bl_ui/properties_render.py | 3 +- release/scripts/startup/bl_ui/space_topbar.py | 14 ++- source/blender/draw/CMakeLists.txt | 1 + .../draw/engines/gpencil/gpencil_cache_utils.c | 8 +- .../draw/engines/gpencil/gpencil_draw_utils.c | 125 ++++++++++++++------ .../blender/draw/engines/gpencil/gpencil_engine.c | 124 ++++++++++++++++++-- .../blender/draw/engines/gpencil/gpencil_engine.h | 22 +++- .../gpencil/shaders/gpencil_blend_frag.glsl | 130 +++++++++++++++++++++ source/blender/makesdna/DNA_gpencil_types.h | 15 +++ source/blender/makesdna/DNA_scene_types.h | 4 +- source/blender/makesrna/intern/rna_gpencil.c | 22 ++++ source/blender/makesrna/intern/rna_scene.c | 5 + 14 files changed, 428 insertions(+), 65 deletions(-) create mode 100644 source/blender/draw/engines/gpencil/shaders/gpencil_blend_frag.glsl diff --git a/release/scripts/startup/bl_ui/properties_data_gpencil.py b/release/scripts/startup/bl_ui/properties_data_gpencil.py index 949a48ed7f1..558c3c190b6 100644 --- a/release/scripts/startup/bl_ui/properties_data_gpencil.py +++ b/release/scripts/startup/bl_ui/properties_data_gpencil.py @@ -135,13 +135,22 @@ class DATA_PT_gpencil_datapanel(Panel): col.template_list("GPENCIL_UL_layer", "", gpd, "layers", gpd.layers, "active_index", rows=layer_rows, reverse=True) + gpl = context.active_gpencil_layer + if gpl: + srow = col.row(align=True) + srow.prop(gpl, "blend_mode", text="Blend") + + srow = col.row(align=True) + srow.prop(gpl, "opacity", text="Opacity", slider=True) + srow.prop(gpl, "clamp_layer", text="", + icon='MOD_MASK' if gpl.clamp_layer else 'ONIONSKIN_OFF') + col = row.column() sub = col.column(align=True) sub.operator("gpencil.layer_add", icon='ADD', text="") sub.operator("gpencil.layer_remove", icon='REMOVE', text="") - gpl = context.active_gpencil_layer if gpl: sub.menu("GPENCIL_MT_layer_specials", icon='DOWNARROW_HLT', text="") @@ -158,10 +167,6 @@ class DATA_PT_gpencil_datapanel(Panel): sub.operator("gpencil.layer_isolate", icon='LOCKED', text="").affect_visibility = False sub.operator("gpencil.layer_isolate", icon='RESTRICT_VIEW_ON', text="").affect_visibility = True - row = layout.row(align=True) - if gpl: - row.prop(gpl, "opacity", text="Opacity", slider=True) - class DATA_PT_gpencil_layer_optionpanel(LayerDataButtonsPanel, Panel): bl_space_type = 'PROPERTIES' diff --git a/release/scripts/startup/bl_ui/properties_grease_pencil_common.py b/release/scripts/startup/bl_ui/properties_grease_pencil_common.py index 296e05a709d..b18254a9102 100644 --- a/release/scripts/startup/bl_ui/properties_grease_pencil_common.py +++ b/release/scripts/startup/bl_ui/properties_grease_pencil_common.py @@ -766,7 +766,6 @@ class AnnotationDataPanel: layout.prop(tool_settings, "annotation_thickness", text="Thickness") if gpl: - # layout.prop(gpl, "opacity", text="Opacity", slider=True) # Full-Row - Frame Locking (and Delete Frame) row = layout.row(align=True) row.active = not gpl.lock @@ -873,6 +872,10 @@ class GPENCIL_UL_layer(UIList): row.prop(gpl, "info", text="", emboss=False) row = layout.row(align=True) + row.prop(gpl, "clamp_layer", text="", + icon='MOD_MASK' if gpl.clamp_layer else 'ONIONSKIN_OFF', + emboss=False) + row.prop(gpl, "lock", text="", emboss=False) row.prop(gpl, "hide", text="", emboss=False) subrow = row.row(align=True) diff --git a/release/scripts/startup/bl_ui/properties_render.py b/release/scripts/startup/bl_ui/properties_render.py index 026b306b49e..8cfbb09ad04 100644 --- a/release/scripts/startup/bl_ui/properties_render.py +++ b/release/scripts/startup/bl_ui/properties_render.py @@ -650,6 +650,7 @@ class RENDER_PT_simplify_greasepencil(RenderButtonsPanel, Panel): def draw(self, context): layout = self.layout layout.use_property_split = True + layout.use_property_decorate = False rd = context.scene.render @@ -659,8 +660,8 @@ class RENDER_PT_simplify_greasepencil(RenderButtonsPanel, Panel): col.prop(rd, "simplify_gpencil_onplay", text="Playback Only") col.prop(rd, "simplify_gpencil_view_modifier", text="Modifiers") col.prop(rd, "simplify_gpencil_shader_fx", text="ShaderFX") + col.prop(rd, "simplify_gpencil_blend", text="Layers Blending") - col = layout.column(align=True) col.prop(rd, "simplify_gpencil_view_fill") sub = col.column() sub.active = rd.simplify_gpencil_view_fill diff --git a/release/scripts/startup/bl_ui/space_topbar.py b/release/scripts/startup/bl_ui/space_topbar.py index 4fc2b12e950..a769268bb22 100644 --- a/release/scripts/startup/bl_ui/space_topbar.py +++ b/release/scripts/startup/bl_ui/space_topbar.py @@ -516,6 +516,16 @@ class TOPBAR_PT_gpencil_layers(Panel): col.template_list("GPENCIL_UL_layer", "", gpd, "layers", gpd.layers, "active_index", rows=layer_rows, reverse=True) + gpl = context.active_gpencil_layer + if gpl: + srow = col.row(align=True) + srow.prop(gpl, "blend_mode", text="Blend") + + srow = col.row(align=True) + srow.prop(gpl, "opacity", text="Opacity", slider=True) + srow.prop(gpl, "clamp_layer", text="", + icon='MOD_MASK' if gpl.clamp_layer else 'ONIONSKIN_OFF') + col = row.column() sub = col.column(align=True) @@ -539,10 +549,6 @@ class TOPBAR_PT_gpencil_layers(Panel): sub.operator("gpencil.layer_isolate", icon='LOCKED', text="").affect_visibility = False sub.operator("gpencil.layer_isolate", icon='HIDE_OFF', text="").affect_visibility = True - row = layout.row(align=True) - if gpl: - row.prop(gpl, "opacity", text="Opacity", slider=True) - class TOPBAR_MT_editor_menus(Menu): bl_idname = "TOPBAR_MT_editor_menus" diff --git a/source/blender/draw/CMakeLists.txt b/source/blender/draw/CMakeLists.txt index 05865ba5636..70dfa589cdc 100644 --- a/source/blender/draw/CMakeLists.txt +++ b/source/blender/draw/CMakeLists.txt @@ -327,6 +327,7 @@ data_to_c_simple(engines/gpencil/shaders/gpencil_stroke_geom.glsl SRC) data_to_c_simple(engines/gpencil/shaders/gpencil_stroke_frag.glsl SRC) data_to_c_simple(engines/gpencil/shaders/gpencil_zdepth_mix_frag.glsl SRC) data_to_c_simple(engines/gpencil/shaders/gpencil_simple_mix_frag.glsl SRC) +data_to_c_simple(engines/gpencil/shaders/gpencil_blend_frag.glsl SRC) data_to_c_simple(engines/gpencil/shaders/gpencil_point_vert.glsl SRC) data_to_c_simple(engines/gpencil/shaders/gpencil_point_geom.glsl SRC) data_to_c_simple(engines/gpencil/shaders/gpencil_point_frag.glsl SRC) diff --git a/source/blender/draw/engines/gpencil/gpencil_cache_utils.c b/source/blender/draw/engines/gpencil/gpencil_cache_utils.c index 15ac3f37add..5478c4a60b9 100644 --- a/source/blender/draw/engines/gpencil/gpencil_cache_utils.c +++ b/source/blender/draw/engines/gpencil/gpencil_cache_utils.c @@ -83,8 +83,12 @@ tGPencilObjectCache *gpencil_object_cache_add( cache_elem->pixfactor = cache_elem->gpd->pixfactor; cache_elem->shader_fx = ob_orig->shader_fx; - cache_elem->init_grp = NULL; - cache_elem->end_grp = NULL; + /* 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__); + } /* calculate zdepth from point of view */ float zdepth = 0.0; diff --git a/source/blender/draw/engines/gpencil/gpencil_draw_utils.c b/source/blender/draw/engines/gpencil/gpencil_draw_utils.c index c8b70953f87..e4d6fa582b8 100644 --- a/source/blender/draw/engines/gpencil/gpencil_draw_utils.c +++ b/source/blender/draw/engines/gpencil/gpencil_draw_utils.c @@ -244,13 +244,15 @@ static void gpencil_stroke_2d_flat(const bGPDspoint *points, int totpoints, floa } /* recalc the internal geometry caches for fill and uvs */ -static void DRW_gpencil_recalc_geometry_caches(Object *ob, MaterialGPencilStyle *gp_style, bGPDstroke *gps) +static void DRW_gpencil_recalc_geometry_caches( + Object *ob, bGPDlayer *gpl, MaterialGPencilStyle *gp_style, bGPDstroke *gps) { if (gps->flag & GP_STROKE_RECALC_CACHES) { /* Calculate triangles cache for filling area (must be done only after changes) */ if ((gps->tot_triangles == 0) || (gps->triangles == NULL)) { if ((gps->totpoints > 2) && - ((gp_style->fill_rgba[3] > GPENCIL_ALPHA_OPACITY_THRESH) || (gp_style->fill_style > 0))) + ((gp_style->fill_rgba[3] > GPENCIL_ALPHA_OPACITY_THRESH) || + (gp_style->fill_style > 0) || (gpl->blend_mode != eGplBlendMode_Normal))) { DRW_gpencil_triangulate_stroke_fill(ob, gps); } @@ -559,7 +561,9 @@ static void gpencil_add_fill_vertexdata( /* 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)) { + if ((tfill[3] > GPENCIL_ALPHA_OPACITY_THRESH) || + (gp_style->fill_style > 0) || + (gpl->blend_mode != eGplBlendMode_Normal)) { if (cache->is_dirty) { const float *color; if (!onion) { @@ -581,7 +585,8 @@ static void gpencil_add_fill_vertexdata( /* 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->grp_cache, gpl, gpf, gps, + eGpencilBatchGroupType_Fill, onion, cache->b_fill.vbo_len, &cache->grp_size, &cache->grp_used); } @@ -637,7 +642,8 @@ static void gpencil_add_stroke_vertexdata( /* 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->grp_cache, gpl, gpf, gps, + eGpencilBatchGroupType_Stroke, onion, cache->b_stroke.vbo_len, &cache->grp_size, &cache->grp_used); } @@ -687,7 +693,8 @@ static void gpencil_add_editpoints_vertexdata( /* add to list of groups */ cache->grp_cache = gpencil_group_cache_add( - cache->grp_cache, gpl, gpf, gps, eGpencilBatchGroupType_Edlin, false, + cache->grp_cache, gpl, gpf, gps, + eGpencilBatchGroupType_Edlin, false, cache->b_edlin.vbo_len, &cache->grp_size, &cache->grp_used); } @@ -699,7 +706,8 @@ static void gpencil_add_editpoints_vertexdata( /* add to list of groups */ cache->grp_cache = gpencil_group_cache_add( - cache->grp_cache, gpl, gpf, gps, eGpencilBatchGroupType_Edit, false, + cache->grp_cache, gpl, gpf, gps, + eGpencilBatchGroupType_Edit, false, cache->b_edit.vbo_len, &cache->grp_size, &cache->grp_used); } @@ -771,13 +779,14 @@ static void gpencil_draw_strokes( /* be sure recalc all cache in source stroke to avoid recalculation when frame change * and improve fps */ if (src_gps) { - DRW_gpencil_recalc_geometry_caches(ob, gp_style, src_gps); + DRW_gpencil_recalc_geometry_caches(ob, gpl, gp_style, src_gps); } /* if the fill has any value, it's considered a fill and is not drawn if simplify fill is enabled */ if ((stl->storage->simplify_fill) && (scene->r.simplify_gpencil & SIMPLIFY_GPENCIL_REMOVE_FILL_LINE)) { if ((gp_style->fill_rgba[3] > GPENCIL_ALPHA_OPACITY_THRESH) || - (gp_style->fill_style > GP_STYLE_FILL_STYLE_SOLID)) + (gp_style->fill_style > GP_STYLE_FILL_STYLE_SOLID) || + (gpl->blend_mode != eGplBlendMode_Normal)) { GP_SET_SRC_GPS(src_gps); continue; @@ -798,21 +807,28 @@ static void gpencil_draw_strokes( } } - /* fill */ - if ((gp_style->flag & GP_STYLE_FILL_SHOW) && - (!stl->storage->simplify_fill)) + /* hide any blend layer */ + if ((!stl->storage->simplify_blend) || + (gpl->blend_mode == eGplBlendMode_Normal)) { - gpencil_add_fill_vertexdata( - cache, ob, gpl, derived_gpf, gps, - opacity, tintcolor, false, custonion); - } - /* stroke */ - if ((gp_style->flag & GP_STYLE_STROKE_SHOW) && - (gp_style->stroke_rgba[3] > GPENCIL_ALPHA_OPACITY_THRESH)) - { - gpencil_add_stroke_vertexdata( - cache, ob, gpl, derived_gpf, gps, - opacity, tintcolor, false, custonion); + /* 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, derived_gpf, gps, + opacity, tintcolor, false, custonion); + } + /* stroke */ + if ((gp_style->flag & GP_STYLE_STROKE_SHOW) && + ((gp_style->stroke_rgba[3] > GPENCIL_ALPHA_OPACITY_THRESH) || + (gpl->blend_mode == eGplBlendMode_Normal))) + { + gpencil_add_stroke_vertexdata( + cache, ob, gpl, derived_gpf, gps, + opacity, tintcolor, false, custonion); + } } } @@ -1253,12 +1269,21 @@ static void DRW_gpencil_create_batches(GpencilBatchCache *cache) /* create all shading groups */ static void DRW_gpencil_shgroups_create( GPENCIL_e_data *e_data, void *vedata, - Object *ob, bGPdata *gpd, + 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; + + 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; int start_stroke = 0; int start_point = 0; @@ -1266,12 +1291,28 @@ static void DRW_gpencil_shgroups_create( int start_edit = 0; int start_edlin = 0; - cache_ob->init_grp = NULL; - cache_ob->end_grp = NULL; - for (int i = 0; i < cache->grp_used; i++) { - GpencilBatchGroup *elm = &cache->grp_cache[i]; - bGPDlayer *gpl = elm->gpl; + elm = &cache->grp_cache[i]; + array_elm = &cache_ob->shgrp_array[idx]; + + /* 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_Normal: gpl->blend_mode; + array_elm->end_shgrp = shgrp; + gpl_prev = elm->gpl; + tag_first = true; + idx++; + } + } + + gpl = elm->gpl; bGPDframe *gpf = elm->gpf; bGPDstroke *gps = elm->gps; MaterialGPencilStyle *gp_style = BKE_material_gpencil_settings_get(ob, gps->mat_nr + 1); @@ -1365,14 +1406,22 @@ static void DRW_gpencil_shgroups_create( } } /* save first group */ - if ((shgrp != NULL) && (cache_ob->init_grp == NULL)) { - cache_ob->init_grp = shgrp; + if ((shgrp != NULL) && (tag_first)) { + array_elm = &cache_ob->shgrp_array[idx]; + array_elm->mode = idx == 0 ? eGplBlendMode_Normal: gpl->blend_mode; + array_elm->clamp_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) { - cache_ob->end_grp = shgrp; + array_elm->mode = idx == 0 ? eGplBlendMode_Normal : gpl->blend_mode; + array_elm->end_shgrp = shgrp; } } /* populate a datablock for multiedit (no onions, no modifiers) */ @@ -1425,7 +1474,7 @@ void DRW_gpencil_populate_multiedit( /* create batchs and shading groups */ DRW_gpencil_create_batches(cache); - DRW_gpencil_shgroups_create(e_data, vedata, ob, gpd, cache, cache_ob); + DRW_gpencil_shgroups_create(e_data, vedata, ob, cache, cache_ob); cache->is_dirty = false; } @@ -1465,7 +1514,7 @@ void DRW_gpencil_populate_datablock( /* if object is duplicate, only create shading groups */ if (cache_ob->is_dup_ob) { - DRW_gpencil_shgroups_create(e_data, vedata, ob, gpd, cache, cache_ob); + DRW_gpencil_shgroups_create(e_data, vedata, ob, cache, cache_ob); return; } @@ -1481,8 +1530,9 @@ void DRW_gpencil_populate_datablock( /* 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) + if (gpl->flag & GP_LAYER_HIDE) { continue; + } /* filter view layer to gp layers in the same view layer (for compo) */ if ((stl->storage->is_render) && (gpl->viewlayername[0] != '\0')) { @@ -1560,7 +1610,7 @@ void DRW_gpencil_populate_datablock( /* create batchs and shading groups */ DRW_gpencil_create_batches(cache); - DRW_gpencil_shgroups_create(e_data, vedata, ob, gpd, cache, cache_ob); + DRW_gpencil_shgroups_create(e_data, vedata, ob, cache, cache_ob); cache->is_dirty = false; } @@ -1574,9 +1624,8 @@ void DRW_gpencil_populate_particles(GPENCIL_e_data *e_data, void *vedata) tGPencilObjectCache *cache_ob = &stl->g_data->gp_object_cache[i]; Object *ob = cache_ob->ob; if (cache_ob->is_dup_ob) { - bGPdata *gpd = (bGPdata *)ob->data; GpencilBatchCache *cache = ob->runtime.gpencil_cache; - DRW_gpencil_shgroups_create(e_data, vedata, ob, gpd, cache, cache_ob); + DRW_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 7be06d501f1..a80ad133d09 100644 --- a/source/blender/draw/engines/gpencil/gpencil_engine.c +++ b/source/blender/draw/engines/gpencil/gpencil_engine.c @@ -64,6 +64,7 @@ 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[]; /* *********** STATIC *********** */ static GPENCIL_e_data e_data = {NULL}; /* Engine data */ @@ -221,6 +222,11 @@ static void GPENCIL_create_shaders(void) e_data.gpencil_simple_fullscreen_sh = DRW_shader_create_fullscreen(datatoc_gpencil_simple_mix_frag_glsl, NULL); } + /* blend */ + if (!e_data.gpencil_blend_fullscreen_sh) { + e_data.gpencil_blend_fullscreen_sh = DRW_shader_create_fullscreen(datatoc_gpencil_blend_frag_glsl, NULL); + } + /* shaders for use when drawing */ if (!e_data.gpencil_background_sh) { e_data.gpencil_background_sh = DRW_shader_create_fullscreen(datatoc_gpencil_background_frag_glsl, NULL); @@ -266,6 +272,7 @@ static void GPENCIL_engine_free(void) DRW_SHADER_FREE_SAFE(e_data.gpencil_edit_point_sh); DRW_SHADER_FREE_SAFE(e_data.gpencil_fullscreen_sh); DRW_SHADER_FREE_SAFE(e_data.gpencil_simple_fullscreen_sh); + DRW_SHADER_FREE_SAFE(e_data.gpencil_blend_fullscreen_sh); DRW_SHADER_FREE_SAFE(e_data.gpencil_background_sh); DRW_SHADER_FREE_SAFE(e_data.gpencil_paper_sh); @@ -371,6 +378,7 @@ void GPENCIL_cache_init(void *vedata) stl->storage->simplify_fill = GP_SIMPLIFY_FILL(scene, stl->storage->is_playing); stl->storage->simplify_modif = GP_SIMPLIFY_MODIF(scene, stl->storage->is_playing); stl->storage->simplify_fx = GP_SIMPLIFY_FX(scene, stl->storage->is_playing); + stl->storage->simplify_blend = GP_SIMPLIFY_BLEND(scene, stl->storage->is_playing); /* save pixsize */ stl->storage->pixsize = DRW_viewport_pixelsize_get(); @@ -485,6 +493,20 @@ void GPENCIL_cache_init(void *vedata) stl->g_data->shgrps_grid = DRW_shgroup_create(e_data.gpencil_line_sh, psl->grid_pass); } + /* blend layers pass */ + psl->blend_pass = DRW_pass_create( + "GPencil Blend Layers Pass", + DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND | 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_add(blend_shgrp, quad, NULL); + DRW_shgroup_uniform_texture_ref(blend_shgrp, "strokeColor", &e_data.temp_color_tx_a); + DRW_shgroup_uniform_texture_ref(blend_shgrp, "strokeDepth", &e_data.temp_depth_tx_a); + DRW_shgroup_uniform_texture_ref(blend_shgrp, "blendColor", &e_data.temp_color_tx_fx); + DRW_shgroup_uniform_texture_ref(blend_shgrp, "blendDepth", &e_data.temp_depth_tx_fx); + DRW_shgroup_uniform_int(blend_shgrp, "mode", &stl->storage->blend_mode, 1); + DRW_shgroup_uniform_int(blend_shgrp, "clamp_layer", &stl->storage->clamp_layer, 1); + DRW_shgroup_uniform_float(blend_shgrp, "blend_opacity", &stl->storage->blend_opacity, 1); + /* create effects passes */ if (!stl->storage->simplify_fx) { GPENCIL_create_fx_passes(psl); @@ -639,12 +661,40 @@ static void gpencil_free_obj_runtime(GPENCIL_StorageList *stl) tGPencilObjectCache *cache_ob = &stl->g_data->gp_object_cache[i]; 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->shgrp_array); } /* free the cache itself */ MEM_SAFE_FREE(stl->g_data->gp_object_cache); } +static void gpencil_draw_pass_range( + GPENCIL_FramebufferList *fbl, GPENCIL_StorageList *stl, + GPENCIL_PassList *psl, GPENCIL_TextureList *txl, + GPUFrameBuffer *fb, + DRWShadingGroup *init_shgrp, DRWShadingGroup *end_shgrp, bool multi) +{ + if (init_shgrp == NULL) { + return; + } + + /* previews don't use AA */ + if ((!stl->storage->is_mat_preview) && (multi)) { + MULTISAMPLE_GP_SYNC_ENABLE(stl->storage->multisamples, fbl); + } + + DRW_draw_pass_subset( + psl->stroke_pass, init_shgrp, end_shgrp); + + if ((!stl->storage->is_mat_preview) && (multi)) { + MULTISAMPLE_GP_SYNC_DISABLE(stl->storage->multisamples, fbl, fb, txl); + } + +} + /* draw scene */ void GPENCIL_draw_scene(void *ved) { @@ -657,6 +707,10 @@ void GPENCIL_draw_scene(void *ved) GPENCIL_TextureList *txl = ((GPENCIL_Data *)vedata)->txl; tGPencilObjectCache *cache_ob; + tGPencilObjectCache_shgrp *array_elm = NULL; + DRWShadingGroup *init_shgrp = NULL; + DRWShadingGroup *end_shgrp = NULL; + const float clearcol[4] = { 0.0f, 0.0f, 0.0f, 0.0f }; const DRWContextState *draw_ctx = DRW_context_state_get(); @@ -712,7 +766,7 @@ void GPENCIL_draw_scene(void *ved) for (int i = 0; i < stl->g_data->gp_cache_used; i++) { cache_ob = &stl->g_data->gp_object_cache[i]; 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(fbl->temp_fb_a, clearcol, 1.0f); @@ -720,19 +774,67 @@ void GPENCIL_draw_scene(void *ved) /* Stroke Pass: * draw only a subset that usually starts with a fill and ends with stroke */ - if (cache_ob->init_grp) { - /* previews don't use AA */ - if (!stl->storage->is_mat_preview) { - MULTISAMPLE_GP_SYNC_ENABLE(stl->storage->multisamples, fbl); - } + 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_Normal) && + (!use_blend) && (!array_elm->clamp_layer)) || + ( e == 0)) + { + if (init_shgrp == NULL) { + init_shgrp = array_elm->init_shgrp; + } + end_shgrp = array_elm->end_shgrp; + } + else { + use_blend = true; + /* draw pending groups */ + gpencil_draw_pass_range( + fbl, stl, psl, txl, fbl->temp_fb_a, + init_shgrp, end_shgrp, is_last); + + /* draw current group in separated texture */ + init_shgrp = array_elm->init_shgrp; + end_shgrp = array_elm->end_shgrp; + + GPU_framebuffer_bind(fbl->temp_fb_fx); + GPU_framebuffer_clear_color_depth(fbl->temp_fb_fx, clearcol, 1.0f); + gpencil_draw_pass_range( + fbl, stl, psl, txl, fbl->temp_fb_fx, + init_shgrp, end_shgrp, + is_last); + + /* Blend A texture and FX texture */ + GPU_framebuffer_bind(fbl->temp_fb_b); + GPU_framebuffer_clear_color_depth(fbl->temp_fb_b, clearcol, 1.0f); + stl->storage->blend_mode = array_elm->mode; + stl->storage->clamp_layer = (int)array_elm->clamp_layer; + stl->storage->blend_opacity = array_elm->blend_opacity; + DRW_draw_pass(psl->blend_pass); + + /* Copy B texture to A texture to follow loop */ + e_data.input_depth_tx = e_data.temp_depth_tx_b; + e_data.input_color_tx = e_data.temp_color_tx_b; + + GPU_framebuffer_bind(fbl->temp_fb_a); + GPU_framebuffer_clear_color_depth(fbl->temp_fb_a, clearcol, 1.0f); + DRW_draw_pass(psl->mix_pass_noblend); + + /* prepare next group */ + init_shgrp = NULL; + } - DRW_draw_pass_subset( - psl->stroke_pass, cache_ob->init_grp, cache_ob->end_grp); - - if (!stl->storage->is_mat_preview) { - MULTISAMPLE_GP_SYNC_DISABLE(stl->storage->multisamples, fbl, fbl->temp_fb_a, txl); } + /* last group */ + gpencil_draw_pass_range( + fbl, stl, psl, txl, fbl->temp_fb_a, + init_shgrp, end_shgrp, + true); } + /* Current buffer drawing */ if ((!is_render) && (cache_ob->is_dup_ob == false)) { DRW_draw_pass(psl->drawing_pass); diff --git a/source/blender/draw/engines/gpencil/gpencil_engine.h b/source/blender/draw/engines/gpencil/gpencil_engine.h index 0fe25ba9f0f..b8b526cb873 100644 --- a/source/blender/draw/engines/gpencil/gpencil_engine.h +++ b/source/blender/draw/engines/gpencil/gpencil_engine.h @@ -54,17 +54,23 @@ struct RenderLayer; #define GP_SIMPLIFY_FILL(scene, playing) ((GP_SIMPLIFY_ONPLAY(playing) && (GP_SIMPLIFY(scene)) && (scene->r.simplify_gpencil & SIMPLIFY_GPENCIL_FILL))) #define GP_SIMPLIFY_MODIF(scene, playing) ((GP_SIMPLIFY_ONPLAY(playing) && (GP_SIMPLIFY(scene)) && (scene->r.simplify_gpencil & SIMPLIFY_GPENCIL_MODIFIER))) #define GP_SIMPLIFY_FX(scene, playing) ((GP_SIMPLIFY_ONPLAY(playing) && (GP_SIMPLIFY(scene)) && (scene->r.simplify_gpencil & SIMPLIFY_GPENCIL_FX))) +#define GP_SIMPLIFY_BLEND(scene, playing) ((GP_SIMPLIFY_ONPLAY(playing) && (GP_SIMPLIFY(scene)) && (scene->r.simplify_gpencil & SIMPLIFY_GPENCIL_BLEND))) #define GP_IS_CAMERAVIEW ((rv3d != NULL) && (rv3d->persp == RV3D_CAMOB && v3d->camera)) /* *********** OBJECTS CACHE *********** */ +typedef struct tGPencilObjectCache_shgrp { + int mode; + bool clamp_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; - DRWShadingGroup *init_grp; - DRWShadingGroup *end_grp; int idx; /*original index, can change after sort */ /* effects */ @@ -90,6 +96,11 @@ typedef struct tGPencilObjectCache { /* GPU data size */ int tot_vertex; int tot_triangles; + + /* Save shader groups by layer */ + int tot_layers; + tGPencilObjectCache_shgrp *shgrp_array; + } tGPencilObjectCache; /* *********** LISTS *********** */ @@ -127,10 +138,15 @@ typedef struct GPENCIL_Storage { int tonemapping; short multisamples; + int blend_mode; + int clamp_layer; + float blend_opacity; + /* simplify settings*/ bool simplify_fill; bool simplify_modif; bool simplify_fx; + bool simplify_blend; /* Render Matrices and data */ float persmat[4][4], persinv[4][4]; @@ -158,6 +174,7 @@ typedef struct GPENCIL_PassList { struct DRWPass *background_pass; struct DRWPass *paper_pass; struct DRWPass *grid_pass; + struct DRWPass *blend_pass; /* effects */ struct DRWPass *fx_shader_pass; @@ -232,6 +249,7 @@ typedef struct GPENCIL_e_data { 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; diff --git a/source/blender/draw/engines/gpencil/shaders/gpencil_blend_frag.glsl b/source/blender/draw/engines/gpencil/shaders/gpencil_blend_frag.glsl new file mode 100644 index 00000000000..2ba02beecdb --- /dev/null +++ b/source/blender/draw/engines/gpencil/shaders/gpencil_blend_frag.glsl @@ -0,0 +1,130 @@ +in vec4 uvcoordsvar; + +out vec4 FragColor; + +uniform sampler2D strokeColor; +uniform sampler2D strokeDepth; +uniform sampler2D blendColor; +uniform sampler2D blendDepth; +uniform int mode; +uniform int clamp_layer; +uniform float blend_opacity; + +#define ON 1 +#define OFF 0 + +#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 blend_color) +{ + vec4 mix_color = blend_color; + vec4 outcolor; + + if (mix_color.a == 0) { + outcolor = src_color; + } + else if (mode == MODE_OVERLAY) { + mix_color.rgb = mix_color.rgb * mix_color.a * blend_opacity; + 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; + } + else if (mode == MODE_ADD){ + mix_color.rgb = mix_color.rgb * mix_color.a * blend_opacity; + outcolor = src_color + mix_color; + outcolor.a = src_color.a; + } + else if (mode == MODE_SUB){ + outcolor = src_color - mix_color; + outcolor.a = clamp(src_color.a - (mix_color.a * blend_opacity), 0.0, 1.0); + } + else if (mode == MODE_MULTIPLY) { + mix_color.rgb = mix_color.rgb * mix_color.a * blend_opacity; + outcolor = src_color * mix_color; + outcolor.a = src_color.a; + } + else if (mode == MODE_DIVIDE) { + mix_color.rgb = mix_color.rgb * mix_color.a * blend_opacity; + outcolor = src_color / mix_color; + outcolor.a = src_color.a; + } + else { + outcolor = mix_color * blend_opacity;; + outcolor.a = src_color.a; + } + + return outcolor; +} + +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; + + /* 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); + } + if (mix_color.a > 0) { + mix_color = vec4(vec3(mix_color.rgb / mix_color.a), mix_color.a); + } + + /* Normal mode */ + if (mode == MODE_NORMAL) { + if (stroke_color.a > 0) { + if (mix_color.a > 0) { + FragColor = vec4(mix(stroke_color.rgb, mix_color.rgb, mix_color.a), stroke_color.a); + gl_FragDepth = mix_depth; + } + else { + FragColor = stroke_color; + gl_FragDepth = stroke_depth; + } + } + else { + if (clamp_layer == ON) { + discard; + } + else { + FragColor = mix_color; + gl_FragDepth = mix_depth; + } + } + return; + } + + /* if not using mask, return mix color */ + if ((stroke_color.a == 0) && (clamp_layer == OFF)) { + FragColor = mix_color; + gl_FragDepth = mix_depth; + return; + } + + /* apply blend mode */ + FragColor = get_blend_color(mode, stroke_color, mix_color); + gl_FragDepth = stroke_depth; +} diff --git a/source/blender/makesdna/DNA_gpencil_types.h b/source/blender/makesdna/DNA_gpencil_types.h index 8a1bccc6957..cebdc4b29b9 100644 --- a/source/blender/makesdna/DNA_gpencil_types.h +++ b/source/blender/makesdna/DNA_gpencil_types.h @@ -269,6 +269,9 @@ typedef struct bGPDlayer { float opacity; /* Opacity of the layer */ char viewlayername[64]; /* Name of the layer used to filter render output */ + int blend_mode; /* blend modes */ + char pad_[4]; + bGPDlayer_Runtime runtime; } bGPDlayer; @@ -292,6 +295,8 @@ typedef enum eGPDlayer_Flag { GP_LAYER_VOLUMETRIC = (1 << 10), /* Unlock color */ GP_LAYER_UNLOCK_COLOR = (1 << 12), + /* Mask Layer */ + GP_LAYER_USE_MASK = (1 << 13), } eGPDlayer_Flag; /* bGPDlayer->onion_flag */ @@ -300,6 +305,16 @@ typedef enum eGPDlayer_OnionFlag { GP_LAYER_ONIONSKIN = (1 << 0), } eGPDlayer_OnionFlag; +/* layer blend_mode */ +typedef enum eGPLayerBlendModes { + eGplBlendMode_Normal = 0, + eGplBlendMode_Overlay = 1, + eGplBlendMode_Add = 2, + eGplBlendMode_Subtract = 3, + eGplBlendMode_Multiply = 4, + eGplBlendMode_Divide = 5, +} eGPLayerBlendModes; + /* ***************************************** */ /* GP Datablock */ diff --git a/source/blender/makesdna/DNA_scene_types.h b/source/blender/makesdna/DNA_scene_types.h index 7ab4187de1a..e2cfd79ca93 100644 --- a/source/blender/makesdna/DNA_scene_types.h +++ b/source/blender/makesdna/DNA_scene_types.h @@ -2144,7 +2144,9 @@ typedef enum eGPencil_SimplifyFlags { /* Remove fill external line */ SIMPLIFY_GPENCIL_REMOVE_FILL_LINE = (1 << 4), /* Simplify Shader FX */ - SIMPLIFY_GPENCIL_FX = (1 << 5) + SIMPLIFY_GPENCIL_FX = (1 << 5), + /* Simplify layer blending */ + SIMPLIFY_GPENCIL_BLEND = (1 << 6), } eGPencil_SimplifyFlags; /* ToolSettings.gpencil_*_align - Stroke Placement mode flags */ diff --git a/source/blender/makesrna/intern/rna_gpencil.c b/source/blender/makesrna/intern/rna_gpencil.c index 8c11909d022..d6257aa0c1b 100644 --- a/source/blender/makesrna/intern/rna_gpencil.c +++ b/source/blender/makesrna/intern/rna_gpencil.c @@ -78,6 +78,15 @@ const EnumPropertyItem rna_enum_gplayer_move_type_items[] = { {0, NULL, 0, NULL, NULL} }; +static const EnumPropertyItem rna_enum_layer_blend_modes_items[] = { + {eGplBlendMode_Normal, "NORMAL", 0, "Normal", "" }, + {eGplBlendMode_Overlay, "OVERLAY", 0, "Overlay", "" }, + {eGplBlendMode_Add, "ADD", 0, "Add", "" }, + {eGplBlendMode_Subtract, "SUBTRACT", 0, "Subtract", "" }, + {eGplBlendMode_Multiply, "MULTIPLY", 0, "Multiply", "" }, + {eGplBlendMode_Divide, "DIVIDE", 0, "Divide", "" }, + {0, NULL, 0, NULL, NULL } +}; #endif #ifdef RNA_RUNTIME @@ -1159,6 +1168,13 @@ static void rna_def_gpencil_layer(BlenderRNA *brna) RNA_def_property_ui_text(prop, "ViewLayer", "Only include Layer in this View Layer render output (leave blank to include always)"); + /* blend mode */ + prop = RNA_def_property(srna, "blend_mode", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "blend_mode"); + RNA_def_property_enum_items(prop, rna_enum_layer_blend_modes_items); + RNA_def_property_ui_text(prop, "Blend Mode", "Blend mode"); + RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); + /* Flags */ prop = RNA_def_property(srna, "hide", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_LAYER_HIDE); @@ -1184,6 +1200,12 @@ static void rna_def_gpencil_layer(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Lock Material", "Disable Material editing"); RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, NULL); + prop = RNA_def_property(srna, "clamp_layer", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_LAYER_USE_MASK); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_ui_text(prop, "Clamp Layer", + "Clamp any pixel outside underlying layers drawing"); + RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, NULL); /* exposed as layers.active */ #if 0 diff --git a/source/blender/makesrna/intern/rna_scene.c b/source/blender/makesrna/intern/rna_scene.c index eeb48b67d28..9d943dca9ec 100644 --- a/source/blender/makesrna/intern/rna_scene.c +++ b/source/blender/makesrna/intern/rna_scene.c @@ -5344,6 +5344,11 @@ static void rna_def_scene_render_data(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Simplify Shaders", "Do not apply shader fx"); RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); + prop = RNA_def_property(srna, "simplify_gpencil_blend", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "simplify_gpencil", SIMPLIFY_GPENCIL_BLEND); + RNA_def_property_ui_text(prop, "Layers Blending", "Do not display blend layers"); + RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); + /* persistent data */ prop = RNA_def_property(srna, "use_persistent_data", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "mode", R_PERSISTENT_DATA); -- cgit v1.2.3