Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/source
diff options
context:
space:
mode:
authorClément Foucault <foucault.clem@gmail.com>2022-03-19 23:59:29 +0300
committerClément Foucault <foucault.clem@gmail.com>2022-03-20 00:05:34 +0300
commiteccb0b222e3465baa71430223c5ee2f0206a7b02 (patch)
tree1b8888acea69d78466a2a727aac93b3fb1ea2e8f /source
parent36b02c3815af8f76aa9d7ce017fd00b66e8b1965 (diff)
GPencil: Port main object shader to ShaderCreateInfo
This is quite a huge cleanup. Making use of the `common_gpencil_lib.glsl` to share more codes and use more consistent codestyle. The gpencil engine specifics are now out of the `gpencil_vertex()` function making it easier to add more features. There should be no regression as all workarounds are kept as is.
Diffstat (limited to 'source')
-rw-r--r--source/blender/draw/CMakeLists.txt4
-rw-r--r--source/blender/draw/engines/gpencil/gpencil_cache_utils.c18
-rw-r--r--source/blender/draw/engines/gpencil/gpencil_defines.h8
-rw-r--r--source/blender/draw/engines/gpencil/gpencil_draw_data.c35
-rw-r--r--source/blender/draw/engines/gpencil/gpencil_engine.c14
-rw-r--r--source/blender/draw/engines/gpencil/gpencil_engine.h61
-rw-r--r--source/blender/draw/engines/gpencil/gpencil_shader.c47
-rw-r--r--source/blender/draw/engines/gpencil/gpencil_shader_shared.h113
-rw-r--r--source/blender/draw/engines/gpencil/shaders/gpencil_frag.glsl70
-rw-r--r--source/blender/draw/engines/gpencil/shaders/gpencil_vert.glsl138
-rw-r--r--source/blender/draw/engines/gpencil/shaders/infos/gpencil_info.hh44
-rw-r--r--source/blender/draw/intern/draw_cache_impl_gpencil.c4
-rw-r--r--source/blender/makesdna/DNA_gpencil_types.h2
13 files changed, 384 insertions, 174 deletions
diff --git a/source/blender/draw/CMakeLists.txt b/source/blender/draw/CMakeLists.txt
index 994fc923aa4..78c01ce5a23 100644
--- a/source/blender/draw/CMakeLists.txt
+++ b/source/blender/draw/CMakeLists.txt
@@ -364,6 +364,7 @@ set(GLSL_SRC
intern/shaders/common_colormanagement_lib.glsl
intern/shaders/common_globals_lib.glsl
+ intern/shaders/common_gpencil_lib.glsl
intern/shaders/common_pointcloud_lib.glsl
intern/shaders/common_hair_lib.glsl
intern/shaders/common_hair_refine_vert.glsl
@@ -402,6 +403,9 @@ set(GLSL_SRC
engines/gpencil/shaders/gpencil_depth_merge_vert.glsl
engines/gpencil/shaders/gpencil_vfx_frag.glsl
+ engines/gpencil/gpencil_defines.h
+ engines/gpencil/gpencil_shader_shared.h
+
engines/select/shaders/selection_id_3D_vert.glsl
engines/select/shaders/selection_id_frag.glsl
diff --git a/source/blender/draw/engines/gpencil/gpencil_cache_utils.c b/source/blender/draw/engines/gpencil/gpencil_cache_utils.c
index eec38f98788..7bfbcfa9e21 100644
--- a/source/blender/draw/engines/gpencil/gpencil_cache_utils.c
+++ b/source/blender/draw/engines/gpencil/gpencil_cache_utils.c
@@ -268,7 +268,7 @@ GPENCIL_tLayer *gpencil_layer_cache_add(GPENCIL_PrivateData *pd,
!BLI_listbase_is_empty(&gpl->mask_layers);
float vert_col_opacity = (override_vertcol) ?
- (is_vert_col_mode ? pd->vertex_paint_opacity : 0.0f) :
+ (is_vert_col_mode ? pd->vertex_paint_opacity : 0.0f) :
pd->is_render ? gpl->vertex_paint_opacity :
pd->vertex_paint_opacity;
/* Negate thickness sign to tag that strokes are in screen space.
@@ -388,13 +388,11 @@ GPENCIL_tLayer *gpencil_layer_cache_add(GPENCIL_PrivateData *pd,
DRW_shgroup_uniform_texture(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_bool_copy(grp, "gpStrokeOrder3d", tgp_ob->is_drawmode3d);
+ DRW_shgroup_uniform_float_copy(grp, "gpThicknessScale", tgp_ob->object_scale);
+ DRW_shgroup_uniform_float_copy(grp, "gpThicknessOffset", (float)gpl->line_change);
+ DRW_shgroup_uniform_float_copy(grp, "gpThicknessWorldScale", thickness_scale);
+ DRW_shgroup_uniform_float_copy(grp, "gpVertexColorOpacity", vert_col_opacity);
/* If random color type, need color by layer. */
float gpl_color[4];
@@ -403,9 +401,9 @@ GPENCIL_tLayer *gpencil_layer_cache_add(GPENCIL_PrivateData *pd,
gpencil_layer_random_color_get(ob, gpl, gpl_color);
gpl_color[3] = 1.0f;
}
- DRW_shgroup_uniform_vec4_copy(grp, "layerTint", gpl_color);
+ DRW_shgroup_uniform_vec4_copy(grp, "gpLayerTint", gpl_color);
- DRW_shgroup_uniform_float_copy(grp, "layerOpacity", layer_alpha);
+ DRW_shgroup_uniform_float_copy(grp, "gpLayerOpacity", layer_alpha);
DRW_shgroup_stencil_mask(grp, 0xFF);
}
diff --git a/source/blender/draw/engines/gpencil/gpencil_defines.h b/source/blender/draw/engines/gpencil/gpencil_defines.h
new file mode 100644
index 00000000000..6eb7bd23e4e
--- /dev/null
+++ b/source/blender/draw/engines/gpencil/gpencil_defines.h
@@ -0,0 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#define GPENCIL_MATERIAL_BUFFER_LEN 256
+
+#define GPENCIL_LIGHT_BUFFER_LEN 128
+
+/* High bits are used to pass material ID to fragment shader. */
+#define GPENCIl_MATID_SHIFT 16u
diff --git a/source/blender/draw/engines/gpencil/gpencil_draw_data.c b/source/blender/draw/engines/gpencil/gpencil_draw_data.c
index c3f96ecd0a2..65ddb80ad55 100644
--- a/source/blender/draw/engines/gpencil/gpencil_draw_data.c
+++ b/source/blender/draw/engines/gpencil/gpencil_draw_data.c
@@ -58,7 +58,8 @@ static struct GPUTexture *gpencil_image_texture_get(Image *image, bool *r_alpha_
static void gpencil_uv_transform_get(const float ofs[2],
const float scale[2],
const float rotation,
- float r_uvmat[3][2])
+ float r_rot_scale[2][2],
+ float r_offset[2])
{
/* OPTI this could use 3x2 matrices and reduce the number of operations drastically. */
float mat[4][4];
@@ -70,9 +71,9 @@ static void gpencil_uv_transform_get(const float ofs[2],
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]);
+ copy_v2_v2(r_rot_scale[0], mat[0]);
+ copy_v2_v2(r_rot_scale[1], mat[1]);
+ copy_v2_v2(r_offset, mat[3]);
}
static void gpencil_shade_color(float color[3])
@@ -167,7 +168,7 @@ GPENCIL_MaterialPool *gpencil_material_pool_create(GPENCIL_PrivateData *pd, Obje
int mat_len = max_ii(1, BKE_object_material_count_eval(ob));
- bool reuse_matpool = matpool && ((matpool->used_count + mat_len) <= GP_MATERIAL_BUFFER_LEN);
+ bool reuse_matpool = matpool && ((matpool->used_count + mat_len) <= GPENCIL_MATERIAL_BUFFER_LEN);
if (reuse_matpool) {
/* Share the matpool with other objects. Return offset to first material. */
@@ -188,7 +189,7 @@ GPENCIL_MaterialPool *gpencil_material_pool_create(GPENCIL_PrivateData *pd, Obje
GPENCIL_MaterialPool *pool = matpool;
for (int i = 0; i < mat_len; i++) {
- if ((i > 0) && (pool->used_count == GP_MATERIAL_BUFFER_LEN)) {
+ if ((i > 0) && (pool->used_count == GPENCIL_MATERIAL_BUFFER_LEN)) {
pool->next = gpencil_material_pool_add(pd);
pool = pool->next;
}
@@ -235,8 +236,8 @@ GPENCIL_MaterialPool *gpencil_material_pool_create(GPENCIL_PrivateData *pd, Obje
gp_style = gpencil_viewport_material_overrides(pd, ob, color_type, gp_style, lighting_mode);
/* Dots or Squares rotation. */
- mat_data->alignment_rot_cos = cosf(gp_style->alignment_rotation);
- mat_data->alignment_rot_sin = sinf(gp_style->alignment_rotation);
+ mat_data->alignment_rot[0] = cosf(gp_style->alignment_rotation);
+ mat_data->alignment_rot[1] = sinf(gp_style->alignment_rotation);
/* Stroke Style */
if ((gp_style->stroke_style == GP_MATERIAL_STROKE_STYLE_TEXTURE) && (gp_style->sima)) {
@@ -266,7 +267,8 @@ GPENCIL_MaterialPool *gpencil_material_pool_create(GPENCIL_PrivateData *pd, Obje
gpencil_uv_transform_get(gp_style->texture_offset,
gp_style->texture_scale,
gp_style->texture_angle,
- mat_data->fill_uv_transform);
+ (float(*)[2])mat_data->fill_uv_rot_scale,
+ mat_data->fill_uv_offset);
copy_v4_v4(mat_data->fill_color, gp_style->fill_rgba);
mat_data->fill_texture_mix = 1.0f - gp_style->mix_factor;
}
@@ -278,7 +280,8 @@ GPENCIL_MaterialPool *gpencil_material_pool_create(GPENCIL_PrivateData *pd, Obje
gpencil_uv_transform_get(gp_style->texture_offset,
gp_style->texture_scale,
gp_style->texture_angle,
- mat_data->fill_uv_transform);
+ (float(*)[2])mat_data->fill_uv_rot_scale,
+ mat_data->fill_uv_offset);
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;
@@ -303,11 +306,11 @@ void gpencil_material_resources_get(GPENCIL_MaterialPool *first_pool,
GPUUniformBuf **r_ubo_mat)
{
GPENCIL_MaterialPool *matpool = first_pool;
- int pool_id = mat_id / GP_MATERIAL_BUFFER_LEN;
+ int pool_id = mat_id / GPENCIL_MATERIAL_BUFFER_LEN;
for (int i = 0; i < pool_id; i++) {
matpool = matpool->next;
}
- mat_id = mat_id % GP_MATERIAL_BUFFER_LEN;
+ mat_id = mat_id % GPENCIL_MATERIAL_BUFFER_LEN;
*r_ubo_mat = matpool->ubo;
if (r_tex_fill) {
*r_tex_fill = matpool->tex_fill[mat_id];
@@ -379,16 +382,16 @@ void gpencil_light_pool_populate(GPENCIL_LightPool *lightpool, Object *ob)
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;
+ gp_light->spot_size = cosf(la->spotsize * 0.5f);
+ gp_light->spot_blend = (1.0f - gp_light->spot_size) * 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_2);
- gp_light->spotblend = (1.0f - gp_light->spotsize) * 1.0f;
+ gp_light->spot_size = cosf(M_PI_2);
+ gp_light->spot_blend = (1.0f - gp_light->spot_size) * 1.0f;
}
else if (la->type == LA_SUN) {
normalize_v3_v3(gp_light->forward, ob->obmat[2]);
diff --git a/source/blender/draw/engines/gpencil/gpencil_engine.c b/source/blender/draw/engines/gpencil/gpencil_engine.c
index b7bba35e298..585a508d9ce 100644
--- a/source/blender/draw/engines/gpencil/gpencil_engine.c
+++ b/source/blender/draw/engines/gpencil/gpencil_engine.c
@@ -276,7 +276,7 @@ void GPENCIL_cache_init(void *ved)
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_bool(grp, "gpStrokeOrder3d", &pd->is_stroke_order_3d, 1);
DRW_shgroup_uniform_vec4(grp, "gpModelMatrix", pd->object_bound_mat[0], 4);
DRW_shgroup_call_procedural_triangles(grp, NULL, 1);
}
@@ -405,8 +405,8 @@ static void gpencil_sbuffer_cache_populate(gpIterPopulateData *iter)
* 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);
+ DRW_shgroup_uniform_block(iter->grp, "materials", iter->ubo_mat);
+ DRW_shgroup_uniform_float_copy(iter->grp, "gpStrokeIndexOffset", iter->stroke_index_last);
const DRWContextState *ctx = DRW_context_state_get();
ToolSettings *ts = ctx->scene->toolsettings;
@@ -453,12 +453,12 @@ static void gpencil_layer_cache_populate(bGPDlayer *gpl,
/* Iterator dependent uniforms. */
DRWShadingGroup *grp = iter->grp = tgp_layer->base_shgrp;
- DRW_shgroup_uniform_block(grp, "gpLightBlock", iter->ubo_lights);
- DRW_shgroup_uniform_block(grp, "gpMaterialBlock", iter->ubo_mat);
+ DRW_shgroup_uniform_block(grp, "lights", iter->ubo_lights);
+ DRW_shgroup_uniform_block(grp, "materials", 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);
+ DRW_shgroup_uniform_float_copy(grp, "gpStrokeIndexOffset", iter->stroke_index_offset);
}
static void gpencil_stroke_cache_populate(bGPDlayer *gpl,
@@ -500,7 +500,7 @@ static void gpencil_stroke_cache_populate(bGPDlayer *gpl,
iter->grp = DRW_shgroup_create_sub(iter->grp);
if (iter->ubo_mat != ubo_mat) {
- DRW_shgroup_uniform_block(iter->grp, "gpMaterialBlock", ubo_mat);
+ DRW_shgroup_uniform_block(iter->grp, "materials", ubo_mat);
iter->ubo_mat = ubo_mat;
}
if (tex_fill) {
diff --git a/source/blender/draw/engines/gpencil/gpencil_engine.h b/source/blender/draw/engines/gpencil/gpencil_engine.h
index 9ef558c7a75..332c7f67c64 100644
--- a/source/blender/draw/engines/gpencil/gpencil_engine.h
+++ b/source/blender/draw/engines/gpencil/gpencil_engine.h
@@ -19,6 +19,9 @@
extern "C" {
#endif
+#include "gpencil_defines.h"
+#include "gpencil_shader_shared.h"
+
extern DrawEngineType draw_engine_gpencil_type;
struct GPENCIL_Data;
@@ -39,69 +42,17 @@ struct bGPDstroke;
#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], alignment_rot_cos, alignment_rot_sin;
- 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_STROKE_HOLDOUT (1 << 7)
-#define GP_FILL_HOLDOUT (1 << 8)
-#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 Data *********** */
typedef struct GPENCIL_MaterialPool {
/* Single linked-list. */
struct GPENCIL_MaterialPool *next;
/* GPU representation of materials. */
- gpMaterial mat_data[GP_MATERIAL_BUFFER_LEN];
+ gpMaterial mat_data[GPENCIL_MATERIAL_BUFFER_LEN];
/* Matching ubo. */
struct GPUUniformBuf *ubo;
/* Texture per material. NULL means none. */
- struct GPUTexture *tex_fill[GP_MATERIAL_BUFFER_LEN];
- struct GPUTexture *tex_stroke[GP_MATERIAL_BUFFER_LEN];
+ struct GPUTexture *tex_fill[GPENCIL_MATERIAL_BUFFER_LEN];
+ struct GPUTexture *tex_stroke[GPENCIL_MATERIAL_BUFFER_LEN];
/* Number of material used in this pool. */
int used_count;
} GPENCIL_MaterialPool;
diff --git a/source/blender/draw/engines/gpencil/gpencil_shader.c b/source/blender/draw/engines/gpencil/gpencil_shader.c
index fca2e700346..dd12fcc70d8 100644
--- a/source/blender/draw/engines/gpencil/gpencil_shader.c
+++ b/source/blender/draw/engines/gpencil/gpencil_shader.c
@@ -46,18 +46,6 @@ static struct {
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)
@@ -78,17 +66,6 @@ void GPENCIL_shader_free(void)
DRW_SHADER_FREE_SAFE(g_shaders.fx_rim_sh);
DRW_SHADER_FREE_SAFE(g_shaders.fx_shadow_sh);
DRW_SHADER_FREE_SAFE(g_shaders.fx_transform_sh);
- DRW_SHADER_FREE_SAFE(g_shaders.gpencil_fill_sh);
- DRW_SHADER_FREE_SAFE(g_shaders.gpencil_stroke_sh);
- DRW_SHADER_FREE_SAFE(g_shaders.gpencil_point_sh);
- DRW_SHADER_FREE_SAFE(g_shaders.gpencil_edit_point_sh);
- DRW_SHADER_FREE_SAFE(g_shaders.gpencil_line_sh);
- DRW_SHADER_FREE_SAFE(g_shaders.gpencil_drawing_fill_sh);
- DRW_SHADER_FREE_SAFE(g_shaders.gpencil_fullscreen_sh);
- DRW_SHADER_FREE_SAFE(g_shaders.gpencil_simple_fullscreen_sh);
- DRW_SHADER_FREE_SAFE(g_shaders.gpencil_blend_fullscreen_sh);
- DRW_SHADER_FREE_SAFE(g_shaders.gpencil_background_sh);
- DRW_SHADER_FREE_SAFE(g_shaders.gpencil_paper_sh);
}
GPUShader *GPENCIL_shader_antialiasing(int stage)
@@ -106,29 +83,7 @@ GPUShader *GPENCIL_shader_antialiasing(int 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,
- },
- });
+ g_shaders.gpencil_sh = GPU_shader_create_from_info_name("gpencil_geometry");
}
return g_shaders.gpencil_sh;
}
diff --git a/source/blender/draw/engines/gpencil/gpencil_shader_shared.h b/source/blender/draw/engines/gpencil/gpencil_shader_shared.h
new file mode 100644
index 00000000000..889652c739b
--- /dev/null
+++ b/source/blender/draw/engines/gpencil/gpencil_shader_shared.h
@@ -0,0 +1,113 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#ifndef GPU_SHADER
+# include "GPU_shader_shared_utils.h"
+
+typedef struct gpMaterial gpMaterial;
+typedef struct gpLight gpLight;
+typedef enum gpMaterialFlag gpMaterialFlag;
+typedef enum gpLightType gpLightType;
+#endif
+
+enum gpMaterialFlag {
+ GP_STROKE_ALIGNMENT_STROKE = 1u,
+ GP_STROKE_ALIGNMENT_OBJECT = 2u,
+ GP_STROKE_ALIGNMENT_FIXED = 3u,
+ GP_STROKE_ALIGNMENT = 0x3u,
+ GP_STROKE_OVERLAP = (1u << 2u),
+ GP_STROKE_TEXTURE_USE = (1u << 3u),
+ GP_STROKE_TEXTURE_STENCIL = (1u << 4u),
+ GP_STROKE_TEXTURE_PREMUL = (1u << 5u),
+ GP_STROKE_DOTS = (1u << 6u),
+ GP_STROKE_HOLDOUT = (1u << 7u),
+ GP_FILL_HOLDOUT = (1u << 8u),
+ GP_FILL_TEXTURE_USE = (1u << 10u),
+ GP_FILL_TEXTURE_PREMUL = (1u << 11u),
+ GP_FILL_TEXTURE_CLIP = (1u << 12u),
+ GP_FILL_GRADIENT_USE = (1u << 13u),
+ GP_FILL_GRADIENT_RADIAL = (1u << 14u),
+ GP_FILL_FLAGS = (GP_FILL_TEXTURE_USE | GP_FILL_TEXTURE_PREMUL | GP_FILL_TEXTURE_CLIP |
+ GP_FILL_GRADIENT_USE | GP_FILL_GRADIENT_RADIAL | GP_FILL_HOLDOUT),
+};
+
+enum gpLightType {
+ GP_LIGHT_TYPE_POINT = 0u,
+ GP_LIGHT_TYPE_SPOT = 1u,
+ GP_LIGHT_TYPE_SUN = 2u,
+ GP_LIGHT_TYPE_AMBIENT = 3u,
+};
+
+/* Avoid compiler funkiness with enum types not being strongly typed in C. */
+#ifndef GPU_SHADER
+# define gpMaterialFlag uint
+# define gpLightType uint
+#endif
+
+struct gpMaterial {
+ float4 stroke_color;
+ float4 fill_color;
+ float4 fill_mix_color;
+ float4 fill_uv_rot_scale;
+#ifndef GPU_SHADER
+ float2 fill_uv_offset;
+ float2 alignment_rot;
+ float stroke_texture_mix;
+ float stroke_u_scale;
+ float fill_texture_mix;
+ gpMaterialFlag flag;
+#else
+ /* Some drivers are completely messing the alignment or the fetches here.
+ * We are forced to pack these into vec4 otherwise we only get 0.0 as value. */
+ /* NOTE(@fclem): This was the case on MacOS OpenGL implementation.
+ * This might be fixed in newer APIs. */
+ float4 packed1;
+ float4 packed2;
+# define _fill_uv_offset packed1.xy
+# define _alignment_rot packed1.zw
+# define _stroke_texture_mix packed2.x
+# define _stroke_u_scale packed2.y
+# define _fill_texture_mix packed2.z
+ /** NOTE(@fclem): Needs floatBitsToUint(). */
+# define _flag packed2.w
+#endif
+};
+BLI_STATIC_ASSERT_ALIGN(gpMaterial, 16)
+
+struct gpLight {
+#ifndef GPU_SHADER
+ float3 color;
+ gpLightType type;
+ float3 right;
+ float spot_size;
+ float3 up;
+ float spot_blend;
+ float3 forward;
+ float _pad0;
+ float3 position;
+ float _pad1;
+#else
+ /* Some drivers are completely messing the alignment or the fetches here.
+ * We are forced to pack these into vec4 otherwise we only get 0.0 as value. */
+ /* NOTE(@fclem): This was the case on MacOS OpenGL implementation.
+ * This might be fixed in newer APIs. */
+ float4 packed0;
+ float4 packed1;
+ float4 packed2;
+ float4 packed3;
+ float4 packed4;
+# define _color packed0.xyz
+# define _type packed0.w
+# define _right packed1.xyz
+# define _spot_size packed1.w
+# define _up packed2.xyz
+# define _spot_blend packed2.w
+# define _forward packed3.xyz
+# define _position packed4.xyz
+#endif
+};
+BLI_STATIC_ASSERT_ALIGN(gpLight, 16)
+
+#ifndef GPU_SHADER
+# undef gpMaterialFlag
+# undef gpLightType
+#endif
diff --git a/source/blender/draw/engines/gpencil/shaders/gpencil_frag.glsl b/source/blender/draw/engines/gpencil/shaders/gpencil_frag.glsl
index 87365c2844d..d0dcea34c6e 100644
--- a/source/blender/draw/engines/gpencil/shaders/gpencil_frag.glsl
+++ b/source/blender/draw/engines/gpencil/shaders/gpencil_frag.glsl
@@ -1,12 +1,6 @@
-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;
+#pragma BLENDER_REQUIRE(common_gpencil_lib.glsl)
+#pragma BLENDER_REQUIRE(common_colormanagement_lib.glsl)
float length_squared(vec2 v)
{
@@ -21,36 +15,37 @@ 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) {
+ if (lights[i]._color.x == -1.0) {
break;
}
- vec3 L = lights[i].position.xyz - finalPos;
+ vec3 L = lights[i]._position - gp_interp.pos;
float vis = 1.0;
+ gpLightType type = floatBitsToUint(lights[i]._type);
/* 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);
+ if (type == GP_LIGHT_TYPE_SPOT) {
+ mat3 rot_scale = mat3(lights[i]._right, lights[i]._up, lights[i]._forward);
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);
+ 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) {
+ if (type < GP_LIGHT_TYPE_SUN) {
vis /= L_len_sqr;
}
else {
- L = lights[i].forward.xyz;
+ L = lights[i]._forward;
L_len_sqr = 1.0;
}
/* Lambertian falloff */
- if (lights[i].color_type.w != GP_LIGHT_TYPE_AMBIENT) {
+ if (type != 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;
+ light_accum += vis * lights[i]._color;
}
/* Clamp to avoid NaNs. */
return clamp(light_accum, 0.0, 1e10);
@@ -59,21 +54,21 @@ vec3 gpencil_lighting(void)
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);
+ if (flag_test(gp_interp.mat_flag, GP_STROKE_TEXTURE_USE)) {
+ bool premul = flag_test(gp_interp.mat_flag, GP_STROKE_TEXTURE_PREMUL);
+ col = texture_read_as_linearrgb(gpStrokeTexture, premul, gp_interp.uv);
}
- 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);
+ else if (flag_test(gp_interp.mat_flag, GP_FILL_TEXTURE_USE)) {
+ bool use_clip = flag_test(gp_interp.mat_flag, GP_FILL_TEXTURE_CLIP);
+ vec2 uvs = (use_clip) ? clamp(gp_interp.uv, 0.0, 1.0) : gp_interp.uv;
+ bool premul = flag_test(gp_interp.mat_flag, 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 if (flag_test(gp_interp.mat_flag, GP_FILL_GRADIENT_USE)) {
+ bool radial = flag_test(gp_interp.mat_flag, GP_FILL_GRADIENT_RADIAL);
+ float fac = clamp(radial ? length(gp_interp.uv * 2.0 - 1.0) : gp_interp.uv.x, 0.0, 1.0);
+ uint matid = gp_interp.mat_flag >> GPENCIl_MATID_SHIFT;
+ col = mix(materials[matid].fill_color, materials[matid].fill_mix_color, fac);
}
else /* SOLID */ {
col = vec4(1.0);
@@ -82,18 +77,21 @@ void main()
/* 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 = col * gp_interp.color_mul + col.a * gp_interp.color_add;
fragColor.rgb *= gpencil_lighting();
- fragColor *= stroke_round_cap_mask(
- strokePt1, strokePt2, strokeAspect, strokeThickness, strokeHardeness);
+ fragColor *= gpencil_stroke_round_cap_mask(gp_interp.sspos.xy,
+ gp_interp.sspos.zw,
+ gp_interp.aspect,
+ gp_interp.thickness.x,
+ gp_interp.hardness);
/* To avoid aliasing artifacts, we reduce the opacity of small strokes. */
- fragColor *= smoothstep(0.0, 1.0, unclampedThickness);
+ fragColor *= smoothstep(0.0, 1.0, gp_interp.thickness.y);
/* Holdout materials. */
- if (GP_FLAG_TEST(matFlag, GP_STROKE_HOLDOUT | GP_FILL_HOLDOUT)) {
+ if (flag_test(gp_interp.mat_flag, GP_STROKE_HOLDOUT | GP_FILL_HOLDOUT)) {
revealColor = fragColor.aaaa;
}
else {
@@ -129,8 +127,8 @@ void main()
* 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;
+ if (gp_interp.depth >= 0.0) {
+ gl_FragDepth = gp_interp.depth;
}
else {
gl_FragDepth = gl_FragCoord.z;
diff --git a/source/blender/draw/engines/gpencil/shaders/gpencil_vert.glsl b/source/blender/draw/engines/gpencil/shaders/gpencil_vert.glsl
index 225601eb9ba..c0ff8d945f2 100644
--- a/source/blender/draw/engines/gpencil/shaders/gpencil_vert.glsl
+++ b/source/blender/draw/engines/gpencil/shaders/gpencil_vert.glsl
@@ -1,5 +1,141 @@
+#pragma BLENDER_REQUIRE(common_gpencil_lib.glsl)
+
+void gpencil_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 * gpVertexColorOpacity);
+ mixed_col.rgb = mix(mixed_col.rgb, gpLayerTint.rgb, gpLayerTint.a);
+ mixed_col.a *= vert_strength * gpLayerOpacity;
+ /**
+ * This is what the fragment shader looks like.
+ * out = col * gp_interp.color_mul + col.a * gp_interp.color_add.
+ * gp_interp.color_mul is how much of the texture color to keep.
+ * gp_interp.color_add 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). */
+ gp_interp.color_mul = vec4(mixed_col.aaa, mixed_col.a);
+ gp_interp.color_add = 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. */
+ gp_interp.color_mul.rgb *= mix_tex;
+ gp_interp.color_add.rgb *= 1.0 - mix_tex;
+}
+
void main()
{
- gpencil_vertex();
+ float vert_strength;
+ vec4 vert_color;
+ vec3 vert_N;
+
+ gpMaterial gp_mat = materials[ma1.x + gpMaterialOffset];
+ gpMaterialFlag gp_flag = floatBitsToInt(gp_mat._flag);
+
+ gl_Position = gpencil_vertex(ma,
+ ma1,
+ ma2,
+ ma3,
+ pos,
+ pos1,
+ pos2,
+ pos3,
+ uv1,
+ uv2,
+ col1,
+ col2,
+ fcol1,
+ vec4(drw_view.viewport_size, drw_view.viewport_size_inverse),
+ gp_flag,
+ gp_mat._alignment_rot,
+ gp_interp.pos,
+ vert_N,
+ vert_color,
+ vert_strength,
+ gp_interp.uv,
+ gp_interp.sspos,
+ gp_interp.aspect,
+ gp_interp.thickness,
+ gp_interp.hardness);
+
+ if (GPENCIL_IS_STROKE_VERTEX) {
+ gp_interp.uv.x *= gp_mat._stroke_u_scale;
+
+ /* Special case: We don't use vertex color if material Holdout. */
+ if (flag_test(gp_flag, GP_STROKE_HOLDOUT)) {
+ vert_color = vec4(0.0);
+ }
+
+ gpencil_color_output(
+ gp_mat.stroke_color, vert_color, vert_strength, gp_mat._stroke_texture_mix);
+
+ gp_interp.mat_flag = gp_flag & ~GP_FILL_FLAGS;
+
+ if (gpStrokeOrder3d) {
+ /* Use the fragment depth (see fragment shader). */
+ gp_interp.depth = -1.0;
+ }
+ else if (flag_test(gp_flag, GP_STROKE_OVERLAP)) {
+ /* Use the index of the point as depth.
+ * This means the stroke can overlap itself. */
+ float point_index = float(ma1.z);
+ gp_interp.depth = (point_index + gpStrokeIndexOffset + 1.0) * 0.0000002;
+ }
+ 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. */
+ float stroke_index = float(ma1.y);
+ gp_interp.depth = (stroke_index + gpStrokeIndexOffset + 1.0) * 0.0000002;
+ }
+ }
+ else {
+ vec4 fill_col = gp_mat.fill_color;
+
+ /* Special case: We don't modulate alpha in gradient mode. */
+ if (flag_test(gp_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.0;
+
+ /* Special case: We don't use vertex color if material Holdout. */
+ if (flag_test(gp_flag, GP_FILL_HOLDOUT)) {
+ fcol_decode = vec4(0.0);
+ }
+
+ /* Apply opacity. */
+ fill_col.a *= fill_opacity;
+ /* If factor is > 1 force opacity. */
+ if (fill_opacity > 1.0) {
+ fill_col.a += fill_opacity - 1.0;
+ }
+
+ fill_col.a = clamp(fill_col.a, 0.0, 1.0);
+
+ gpencil_color_output(fill_col, fcol_decode, 1.0, gp_mat._fill_texture_mix);
+
+ gp_interp.mat_flag = gp_flag & GP_FILL_FLAGS;
+ gp_interp.mat_flag |= uint(ma1.x) << GPENCIl_MATID_SHIFT;
+
+ gp_interp.uv = mat2(gp_mat.fill_uv_rot_scale.xy, gp_mat.fill_uv_rot_scale.zw) * uv1.xy +
+ gp_mat._fill_uv_offset;
+
+ if (gpStrokeOrder3d) {
+ /* Use the fragment depth (see fragment shader). */
+ gp_interp.depth = -1.0;
+ }
+ else {
+ /* Use the index of first point of the stroke as depth. */
+ float stroke_index = float(ma1.y);
+ gp_interp.depth = (stroke_index + gpStrokeIndexOffset) * 0.0000002;
+ }
+ }
}
diff --git a/source/blender/draw/engines/gpencil/shaders/infos/gpencil_info.hh b/source/blender/draw/engines/gpencil/shaders/infos/gpencil_info.hh
index 76fa8ae609b..3b4de704c00 100644
--- a/source/blender/draw/engines/gpencil/shaders/infos/gpencil_info.hh
+++ b/source/blender/draw/engines/gpencil/shaders/infos/gpencil_info.hh
@@ -3,6 +3,50 @@
#include "gpu_shader_create_info.hh"
/* -------------------------------------------------------------------- */
+/** \name GPencil Object rendering
+ * \{ */
+
+GPU_SHADER_INTERFACE_INFO(gpencil_geometry_iface, "gp_interp")
+ .smooth(Type::VEC4, "color_mul")
+ .smooth(Type::VEC4, "color_add")
+ .smooth(Type::VEC3, "pos")
+ .smooth(Type::VEC2, "uv")
+ .no_perspective(Type::VEC2, "thickness")
+ .no_perspective(Type::FLOAT, "hardness")
+ .flat(Type::VEC2, "aspect")
+ .flat(Type::VEC4, "sspos")
+ .flat(Type::UINT, "mat_flag")
+ .flat(Type::FLOAT, "depth");
+
+GPU_SHADER_CREATE_INFO(gpencil_geometry)
+ .do_static_compilation(true)
+ .typedef_source("gpencil_defines.h")
+ .typedef_source("gpencil_shader_shared.h")
+ .sampler(0, ImageType::FLOAT_2D, "gpFillTexture")
+ .sampler(1, ImageType::FLOAT_2D, "gpStrokeTexture")
+ .sampler(2, ImageType::DEPTH_2D, "gpSceneDepthTexture")
+ .sampler(3, ImageType::FLOAT_2D, "gpMaskTexture")
+ .uniform_buf(2, "gpMaterial", "materials[GPENCIL_MATERIAL_BUFFER_LEN]", Frequency::BATCH)
+ .uniform_buf(3, "gpLight", "lights[GPENCIL_LIGHT_BUFFER_LEN]", Frequency::BATCH)
+ /* Per Object */
+ .push_constant(Type::VEC3, "gpNormal")
+ .push_constant(Type::BOOL, "gpStrokeOrder3d")
+ .push_constant(Type::INT, "gpMaterialOffset")
+ /* Per Layer */
+ .push_constant(Type::FLOAT, "gpVertexColorOpacity")
+ .push_constant(Type::VEC4, "gpLayerTint")
+ .push_constant(Type::FLOAT, "gpLayerOpacity")
+ .push_constant(Type::FLOAT, "gpStrokeIndexOffset")
+ .fragment_out(0, Type::VEC4, "fragColor")
+ .fragment_out(1, Type::VEC4, "revealColor")
+ .vertex_out(gpencil_geometry_iface)
+ .vertex_source("gpencil_vert.glsl")
+ .fragment_source("gpencil_frag.glsl")
+ .additional_info("draw_gpencil");
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
/** \name Fullscreen shaders
* \{ */
diff --git a/source/blender/draw/intern/draw_cache_impl_gpencil.c b/source/blender/draw/intern/draw_cache_impl_gpencil.c
index 08c33555b71..a4465b9aed4 100644
--- a/source/blender/draw/intern/draw_cache_impl_gpencil.c
+++ b/source/blender/draw/intern/draw_cache_impl_gpencil.c
@@ -28,6 +28,8 @@
#include "draw_cache.h"
#include "draw_cache_impl.h"
+#include "../engines/gpencil/gpencil_defines.h"
+
#define BEZIER_HANDLE (1 << 3)
#define COLOR_SHIFT 5
@@ -321,7 +323,7 @@ static void gpencil_buffer_add_point(gpStrokeVert *verts,
vert->point_id = v;
vert->thickness = max_ff(0.0f, gps->thickness * pt->pressure) * (round_cap1 ? 1.0f : -1.0f);
/* Tag endpoint material to -1 so they get discarded by vertex shader. */
- vert->mat = (is_endpoint) ? -1 : (gps->mat_nr % GP_MATERIAL_BUFFER_LEN);
+ vert->mat = (is_endpoint) ? -1 : (gps->mat_nr % GPENCIL_MATERIAL_BUFFER_LEN);
float aspect_ratio = gps->aspect_ratio[0] / max_ff(gps->aspect_ratio[1], 1e-8);
diff --git a/source/blender/makesdna/DNA_gpencil_types.h b/source/blender/makesdna/DNA_gpencil_types.h
index fe3613548a6..8a30a7e8c99 100644
--- a/source/blender/makesdna/DNA_gpencil_types.h
+++ b/source/blender/makesdna/DNA_gpencil_types.h
@@ -25,8 +25,6 @@ struct MDeformVert;
#define GP_DEFAULT_GRID_LINES 4
#define GP_MAX_INPUT_SAMPLES 10
-#define GP_MATERIAL_BUFFER_LEN 256
-
#define GP_DEFAULT_CURVE_RESOLUTION 32
#define GP_DEFAULT_CURVE_ERROR 0.1f
#define GP_DEFAULT_CURVE_EDIT_CORNER_ANGLE M_PI_2